• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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