annotate osx/include/kj/async-unix.h @ 83:ae30d91d2ffe

Replace these with versions built using an older toolset (so as to avoid ABI compatibilities when linking on Ubuntu 14.04 for packaging purposes)
author Chris Cannam
date Fri, 07 Feb 2020 11:51:13 +0000
parents 0994c39f1e94
children
rev   line source
cannam@62 1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
cannam@62 2 // Licensed under the MIT License:
cannam@62 3 //
cannam@62 4 // Permission is hereby granted, free of charge, to any person obtaining a copy
cannam@62 5 // of this software and associated documentation files (the "Software"), to deal
cannam@62 6 // in the Software without restriction, including without limitation the rights
cannam@62 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
cannam@62 8 // copies of the Software, and to permit persons to whom the Software is
cannam@62 9 // furnished to do so, subject to the following conditions:
cannam@62 10 //
cannam@62 11 // The above copyright notice and this permission notice shall be included in
cannam@62 12 // all copies or substantial portions of the Software.
cannam@62 13 //
cannam@62 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
cannam@62 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
cannam@62 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
cannam@62 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
cannam@62 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
cannam@62 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
cannam@62 20 // THE SOFTWARE.
cannam@62 21
cannam@62 22 #ifndef KJ_ASYNC_UNIX_H_
cannam@62 23 #define KJ_ASYNC_UNIX_H_
cannam@62 24
cannam@62 25 #if _WIN32
cannam@62 26 #error "This file is Unix-specific. On Windows, include async-win32.h instead."
cannam@62 27 #endif
cannam@62 28
cannam@62 29 #if defined(__GNUC__) && !KJ_HEADER_WARNINGS
cannam@62 30 #pragma GCC system_header
cannam@62 31 #endif
cannam@62 32
cannam@62 33 #include "async.h"
cannam@62 34 #include "time.h"
cannam@62 35 #include "vector.h"
cannam@62 36 #include "io.h"
cannam@62 37 #include <signal.h>
cannam@62 38
cannam@62 39 #if __linux__ && !__BIONIC__ && !defined(KJ_USE_EPOLL)
cannam@62 40 // Default to epoll on Linux, except on Bionic (Android) which doesn't have signalfd.h.
cannam@62 41 #define KJ_USE_EPOLL 1
cannam@62 42 #endif
cannam@62 43
cannam@62 44 namespace kj {
cannam@62 45
cannam@62 46 class UnixEventPort: public EventPort {
cannam@62 47 // An EventPort implementation which can wait for events on file descriptors as well as signals.
cannam@62 48 // This API only makes sense on Unix.
cannam@62 49 //
cannam@62 50 // The implementation uses `poll()` or possibly a platform-specific API (e.g. epoll, kqueue).
cannam@62 51 // To also wait on signals without race conditions, the implementation may block signals until
cannam@62 52 // just before `poll()` while using a signal handler which `siglongjmp()`s back to just before
cannam@62 53 // the signal was unblocked, or it may use a nicer platform-specific API like signalfd.
cannam@62 54 //
cannam@62 55 // The implementation reserves a signal for internal use. By default, it uses SIGUSR1. If you
cannam@62 56 // need to use SIGUSR1 for something else, you must offer a different signal by calling
cannam@62 57 // setReservedSignal() at startup.
cannam@62 58 //
cannam@62 59 // WARNING: A UnixEventPort can only be used in the thread and process that created it. In
cannam@62 60 // particular, note that after a fork(), a UnixEventPort created in the parent process will
cannam@62 61 // not work correctly in the child, even if the parent ceases to use its copy. In particular
cannam@62 62 // note that this means that server processes which daemonize themselves at startup must wait
cannam@62 63 // until after daemonization to create a UnixEventPort.
cannam@62 64
cannam@62 65 public:
cannam@62 66 UnixEventPort();
cannam@62 67 ~UnixEventPort() noexcept(false);
cannam@62 68
cannam@62 69 class FdObserver;
cannam@62 70 // Class that watches an fd for readability or writability. See definition below.
cannam@62 71
cannam@62 72 Promise<siginfo_t> onSignal(int signum);
cannam@62 73 // When the given signal is delivered to this thread, return the corresponding siginfo_t.
cannam@62 74 // The signal must have been captured using `captureSignal()`.
cannam@62 75 //
cannam@62 76 // If `onSignal()` has not been called, the signal will remain blocked in this thread.
cannam@62 77 // Therefore, a signal which arrives before `onSignal()` was called will not be "missed" -- the
cannam@62 78 // next call to 'onSignal()' will receive it. Also, you can control which thread receives a
cannam@62 79 // process-wide signal by only calling `onSignal()` on that thread's event loop.
cannam@62 80 //
cannam@62 81 // The result of waiting on the same signal twice at once is undefined.
cannam@62 82
cannam@62 83 static void captureSignal(int signum);
cannam@62 84 // Arranges for the given signal to be captured and handled via UnixEventPort, so that you may
cannam@62 85 // then pass it to `onSignal()`. This method is static because it registers a signal handler
cannam@62 86 // which applies process-wide. If any other threads exist in the process when `captureSignal()`
cannam@62 87 // is called, you *must* set the signal mask in those threads to block this signal, otherwise
cannam@62 88 // terrible things will happen if the signal happens to be delivered to those threads. If at
cannam@62 89 // all possible, call `captureSignal()` *before* creating threads, so that threads you create in
cannam@62 90 // the future will inherit the proper signal mask.
cannam@62 91 //
cannam@62 92 // To un-capture a signal, simply install a different signal handler and then un-block it from
cannam@62 93 // the signal mask.
cannam@62 94
cannam@62 95 static void setReservedSignal(int signum);
cannam@62 96 // Sets the signal number which `UnixEventPort` reserves for internal use. If your application
cannam@62 97 // needs to use SIGUSR1, call this at startup (before any calls to `captureSignal()` and before
cannam@62 98 // constructing an `UnixEventPort`) to offer a different signal.
cannam@62 99
cannam@62 100 Timer& getTimer() { return timerImpl; }
cannam@62 101
cannam@62 102 // implements EventPort ------------------------------------------------------
cannam@62 103 bool wait() override;
cannam@62 104 bool poll() override;
cannam@62 105 void wake() const override;
cannam@62 106
cannam@62 107 private:
cannam@62 108 struct TimerSet; // Defined in source file to avoid STL include.
cannam@62 109 class TimerPromiseAdapter;
cannam@62 110 class SignalPromiseAdapter;
cannam@62 111
cannam@62 112 TimerImpl timerImpl;
cannam@62 113
cannam@62 114 SignalPromiseAdapter* signalHead = nullptr;
cannam@62 115 SignalPromiseAdapter** signalTail = &signalHead;
cannam@62 116
cannam@62 117 TimePoint readClock();
cannam@62 118 void gotSignal(const siginfo_t& siginfo);
cannam@62 119
cannam@62 120 friend class TimerPromiseAdapter;
cannam@62 121
cannam@62 122 #if KJ_USE_EPOLL
cannam@62 123 AutoCloseFd epollFd;
cannam@62 124 AutoCloseFd signalFd;
cannam@62 125 AutoCloseFd eventFd; // Used for cross-thread wakeups.
cannam@62 126
cannam@62 127 sigset_t signalFdSigset;
cannam@62 128 // Signal mask as currently set on the signalFd. Tracked so we can detect whether or not it
cannam@62 129 // needs updating.
cannam@62 130
cannam@62 131 bool doEpollWait(int timeout);
cannam@62 132
cannam@62 133 #else
cannam@62 134 class PollContext;
cannam@62 135
cannam@62 136 FdObserver* observersHead = nullptr;
cannam@62 137 FdObserver** observersTail = &observersHead;
cannam@62 138
cannam@62 139 unsigned long long threadId; // actually pthread_t
cannam@62 140 #endif
cannam@62 141 };
cannam@62 142
cannam@62 143 class UnixEventPort::FdObserver {
cannam@62 144 // Object which watches a file descriptor to determine when it is readable or writable.
cannam@62 145 //
cannam@62 146 // For listen sockets, "readable" means that there is a connection to accept(). For everything
cannam@62 147 // else, it means that read() (or recv()) will return data.
cannam@62 148 //
cannam@62 149 // The presence of out-of-band data should NOT fire this event. However, the event may
cannam@62 150 // occasionally fire spuriously (when there is actually no data to read), and one thing that can
cannam@62 151 // cause such spurious events is the arrival of OOB data on certain platforms whose event
cannam@62 152 // interfaces fail to distinguish between regular and OOB data (e.g. Mac OSX).
cannam@62 153 //
cannam@62 154 // WARNING: The exact behavior of this class differs across systems, since event interfaces
cannam@62 155 // vary wildly. Be sure to read the documentation carefully and avoid depending on unspecified
cannam@62 156 // behavior. If at all possible, use the higher-level AsyncInputStream interface instead.
cannam@62 157
cannam@62 158 public:
cannam@62 159 enum Flags {
cannam@62 160 OBSERVE_READ = 1,
cannam@62 161 OBSERVE_WRITE = 2,
cannam@62 162 OBSERVE_URGENT = 4,
cannam@62 163 OBSERVE_READ_WRITE = OBSERVE_READ | OBSERVE_WRITE
cannam@62 164 };
cannam@62 165
cannam@62 166 FdObserver(UnixEventPort& eventPort, int fd, uint flags);
cannam@62 167 // Begin watching the given file descriptor for readability. Only one ReadObserver may exist
cannam@62 168 // for a given file descriptor at a time.
cannam@62 169
cannam@62 170 ~FdObserver() noexcept(false);
cannam@62 171
cannam@62 172 KJ_DISALLOW_COPY(FdObserver);
cannam@62 173
cannam@62 174 Promise<void> whenBecomesReadable();
cannam@62 175 // Resolves the next time the file descriptor transitions from having no data to read to having
cannam@62 176 // some data to read.
cannam@62 177 //
cannam@62 178 // KJ uses "edge-triggered" event notification whenever possible. As a result, it is an error
cannam@62 179 // to call this method when there is already data in the read buffer which has been there since
cannam@62 180 // prior to the last turn of the event loop or prior to creation FdWatcher. In this case, it is
cannam@62 181 // unspecified whether the promise will ever resolve -- it depends on the underlying event
cannam@62 182 // mechanism being used.
cannam@62 183 //
cannam@62 184 // In order to avoid this problem, make sure that you only call `whenBecomesReadable()`
cannam@62 185 // only at times when you know the buffer is empty. You know this for sure when one of the
cannam@62 186 // following happens:
cannam@62 187 // * read() or recv() fails with EAGAIN or EWOULDBLOCK. (You MUST have non-blocking mode
cannam@62 188 // enabled on the fd!)
cannam@62 189 // * The file descriptor is a regular byte-oriented object (like a socket or pipe),
cannam@62 190 // read() or recv() returns fewer than the number of bytes requested, and `atEndHint()`
cannam@62 191 // returns false. This can only happen if the buffer is empty but EOF is not reached. (Note,
cannam@62 192 // though, that for record-oriented file descriptors like Linux's inotify interface, this
cannam@62 193 // rule does not hold, because it could simply be that the next record did not fit into the
cannam@62 194 // space available.)
cannam@62 195 //
cannam@62 196 // It is an error to call `whenBecomesReadable()` again when the promise returned previously
cannam@62 197 // has not yet resolved. If you do this, the previous promise may throw an exception.
cannam@62 198
cannam@62 199 inline Maybe<bool> atEndHint() { return atEnd; }
cannam@62 200 // Returns true if the event system has indicated that EOF has been received. There may still
cannam@62 201 // be data in the read buffer, but once that is gone, there's nothing left.
cannam@62 202 //
cannam@62 203 // Returns false if the event system has indicated that EOF had NOT been received as of the
cannam@62 204 // last turn of the event loop.
cannam@62 205 //
cannam@62 206 // Returns nullptr if the event system does not know whether EOF has been reached. In this
cannam@62 207 // case, the only way to know for sure is to call read() or recv() and check if it returns
cannam@62 208 // zero.
cannam@62 209 //
cannam@62 210 // This hint may be useful as an optimization to avoid an unnecessary system call.
cannam@62 211
cannam@62 212 Promise<void> whenBecomesWritable();
cannam@62 213 // Resolves the next time the file descriptor transitions from having no space available in the
cannam@62 214 // write buffer to having some space available.
cannam@62 215 //
cannam@62 216 // KJ uses "edge-triggered" event notification whenever possible. As a result, it is an error
cannam@62 217 // to call this method when there is already space in the write buffer which has been there
cannam@62 218 // since prior to the last turn of the event loop or prior to creation FdWatcher. In this case,
cannam@62 219 // it is unspecified whether the promise will ever resolve -- it depends on the underlying
cannam@62 220 // event mechanism being used.
cannam@62 221 //
cannam@62 222 // In order to avoid this problem, make sure that you only call `whenBecomesWritable()`
cannam@62 223 // only at times when you know the buffer is full. You know this for sure when one of the
cannam@62 224 // following happens:
cannam@62 225 // * write() or send() fails with EAGAIN or EWOULDBLOCK. (You MUST have non-blocking mode
cannam@62 226 // enabled on the fd!)
cannam@62 227 // * write() or send() succeeds but accepts fewer than the number of bytes provided. This can
cannam@62 228 // only happen if the buffer is full.
cannam@62 229 //
cannam@62 230 // It is an error to call `whenBecomesWritable()` again when the promise returned previously
cannam@62 231 // has not yet resolved. If you do this, the previous promise may throw an exception.
cannam@62 232
cannam@62 233 Promise<void> whenUrgentDataAvailable();
cannam@62 234 // Resolves the next time the file descriptor's read buffer contains "urgent" data.
cannam@62 235 //
cannam@62 236 // The conditions for availability of urgent data are specific to the file descriptor's
cannam@62 237 // underlying implementation.
cannam@62 238 //
cannam@62 239 // It is an error to call `whenUrgentDataAvailable()` again when the promise returned previously
cannam@62 240 // has not yet resolved. If you do this, the previous promise may throw an exception.
cannam@62 241 //
cannam@62 242 // WARNING: This has some known weird behavior on macOS. See
cannam@62 243 // https://github.com/sandstorm-io/capnproto/issues/374.
cannam@62 244
cannam@62 245 private:
cannam@62 246 UnixEventPort& eventPort;
cannam@62 247 int fd;
cannam@62 248 uint flags;
cannam@62 249
cannam@62 250 kj::Maybe<Own<PromiseFulfiller<void>>> readFulfiller;
cannam@62 251 kj::Maybe<Own<PromiseFulfiller<void>>> writeFulfiller;
cannam@62 252 kj::Maybe<Own<PromiseFulfiller<void>>> urgentFulfiller;
cannam@62 253 // Replaced each time `whenBecomesReadable()` or `whenBecomesWritable()` is called. Reverted to
cannam@62 254 // null every time an event is fired.
cannam@62 255
cannam@62 256 Maybe<bool> atEnd;
cannam@62 257
cannam@62 258 void fire(short events);
cannam@62 259
cannam@62 260 #if !KJ_USE_EPOLL
cannam@62 261 FdObserver* next;
cannam@62 262 FdObserver** prev;
cannam@62 263 // Linked list of observers which currently have a non-null readFulfiller or writeFulfiller.
cannam@62 264 // If `prev` is null then the observer is not currently in the list.
cannam@62 265
cannam@62 266 short getEventMask();
cannam@62 267 #endif
cannam@62 268
cannam@62 269 friend class UnixEventPort;
cannam@62 270 };
cannam@62 271
cannam@62 272 } // namespace kj
cannam@62 273
cannam@62 274 #endif // KJ_ASYNC_UNIX_H_