1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "platform/impl/socket_handle_waiter_posix.h"
6
7 #include <time.h>
8
9 #include <algorithm>
10 #include <vector>
11
12 #include "platform/base/error.h"
13 #include "platform/impl/socket_handle_posix.h"
14 #include "platform/impl/timeval_posix.h"
15 #include "platform/impl/udp_socket_posix.h"
16 #include "util/osp_logging.h"
17
18 namespace openscreen {
19
SocketHandleWaiterPosix(ClockNowFunctionPtr now_function)20 SocketHandleWaiterPosix::SocketHandleWaiterPosix(
21 ClockNowFunctionPtr now_function)
22 : SocketHandleWaiter(now_function) {}
23
24 SocketHandleWaiterPosix::~SocketHandleWaiterPosix() = default;
25
26 ErrorOr<std::vector<SocketHandleWaiterPosix::ReadyHandle>>
AwaitSocketsReadable(const std::vector<SocketHandleRef> & socket_handles,const Clock::duration & timeout)27 SocketHandleWaiterPosix::AwaitSocketsReadable(
28 const std::vector<SocketHandleRef>& socket_handles,
29 const Clock::duration& timeout) {
30 int max_fd = -1;
31 fd_set read_handles;
32 fd_set write_handles;
33
34 FD_ZERO(&read_handles);
35 FD_ZERO(&write_handles);
36 for (const SocketHandle& handle : socket_handles) {
37 FD_SET(handle.fd, &read_handles);
38 FD_SET(handle.fd, &write_handles);
39 max_fd = std::max(max_fd, handle.fd);
40 }
41 if (max_fd < 0) {
42 return Error::Code::kIOFailure;
43 }
44
45 struct timeval tv = ToTimeval(timeout);
46 // This value is set to 'max_fd + 1' by convention. Also, select() is
47 // level-triggered so incomplete reads/writes by the caller are fine and will
48 // be picked up again on the next select() call. For more information, see:
49 // http://man7.org/linux/man-pages/man2/select.2.html
50 int max_fd_to_watch = max_fd + 1;
51 const int rv =
52 select(max_fd_to_watch, &read_handles, &write_handles, nullptr, &tv);
53 if (rv == -1) {
54 // This is the case when an error condition is hit within the select(...)
55 // command.
56 return Error::Code::kIOFailure;
57 } else if (rv == 0) {
58 // This occurs when no sockets have a pending read.
59 return Error::Code::kAgain;
60 }
61
62 std::vector<ReadyHandle> changed_handles;
63 for (const SocketHandleRef& handle : socket_handles) {
64 uint32_t flags = 0;
65 if (FD_ISSET(handle.get().fd, &read_handles)) {
66 flags |= Flags::kReadable;
67 }
68 if (FD_ISSET(handle.get().fd, &write_handles)) {
69 flags |= Flags::kWriteable;
70 }
71 if (flags) {
72 changed_handles.push_back({handle, flags});
73 }
74 }
75
76 return changed_handles;
77 }
78
RunUntilStopped()79 void SocketHandleWaiterPosix::RunUntilStopped() {
80 const bool was_running = is_running_.exchange(true);
81 OSP_CHECK(!was_running);
82
83 constexpr Clock::duration kHandleReadyTimeout = std::chrono::milliseconds(50);
84 while (is_running_) {
85 ProcessHandles(kHandleReadyTimeout);
86 }
87 }
88
RequestStopSoon()89 void SocketHandleWaiterPosix::RequestStopSoon() {
90 is_running_.store(false);
91 }
92
93 } // namespace openscreen
94