blob: 650488b8234c7a42984ea7f5185dae48d520a40b [file] [log] [blame]
ctiller58393c22015-01-07 14:03:30 -08001/*
2 *
Craig Tiller06059952015-02-18 08:34:56 -08003 * Copyright 2015, Google Inc.
ctiller58393c22015-01-07 14:03:30 -08004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
Craig Tillerd14a1a52015-01-21 15:26:29 -080034#include <grpc/support/port_platform.h>
35
36#ifdef GPR_POSIX_SOCKET
37
ctiller58393c22015-01-07 14:03:30 -080038#include "src/core/iomgr/fd_posix.h"
39
40#include <assert.h>
41#include <unistd.h>
42
43#include "src/core/iomgr/iomgr_internal.h"
44#include <grpc/support/alloc.h>
45#include <grpc/support/log.h>
46#include <grpc/support/useful.h>
47
Craig Tillerf95e37f2015-02-18 15:15:29 -080048enum descriptor_state {
49 NOT_READY = 0,
50 READY = 1
51}; /* or a pointer to a closure to call */
ctiller58393c22015-01-07 14:03:30 -080052
David Klempnerd1785242015-01-28 17:00:21 -080053/* We need to keep a freelist not because of any concerns of malloc performance
54 * but instead so that implementations with multiple threads in (for example)
55 * epoll_wait deal with the race between pollset removal and incoming poll
56 * notifications.
57 *
58 * The problem is that the poller ultimately holds a reference to this
59 * object, so it is very difficult to know when is safe to free it, at least
60 * without some expensive synchronization.
61 *
62 * If we keep the object freelisted, in the worst case losing this race just
63 * becomes a spurious read notification on a reused fd.
64 */
65/* TODO(klempner): We could use some form of polling generation count to know
66 * when these are safe to free. */
67/* TODO(klempner): Consider disabling freelisting if we don't have multiple
68 * threads in poll on the same fd */
69/* TODO(klempner): Batch these allocations to reduce fragmentation */
70static grpc_fd *fd_freelist = NULL;
71static gpr_mu fd_freelist_mu;
72
73static void freelist_fd(grpc_fd *fd) {
David Klempnerd1785242015-01-28 17:00:21 -080074 gpr_mu_lock(&fd_freelist_mu);
75 fd->freelist_next = fd_freelist;
76 fd_freelist = fd;
77 gpr_mu_unlock(&fd_freelist_mu);
78}
79
80static grpc_fd *alloc_fd(int fd) {
81 grpc_fd *r = NULL;
82 gpr_mu_lock(&fd_freelist_mu);
83 if (fd_freelist != NULL) {
84 r = fd_freelist;
85 fd_freelist = fd_freelist->freelist_next;
86 }
87 gpr_mu_unlock(&fd_freelist_mu);
88 if (r == NULL) {
89 r = gpr_malloc(sizeof(grpc_fd));
90 gpr_mu_init(&r->set_state_mu);
91 gpr_mu_init(&r->watcher_mu);
92 }
93 gpr_atm_rel_store(&r->refst, 1);
Craig Tiller0fcd53c2015-02-18 15:10:53 -080094 gpr_atm_rel_store(&r->readst, NOT_READY);
95 gpr_atm_rel_store(&r->writest, NOT_READY);
David Klempnerd1785242015-01-28 17:00:21 -080096 gpr_atm_rel_store(&r->shutdown, 0);
97 r->fd = fd;
Craig Tiller7d413212015-02-09 08:00:02 -080098 r->watcher_root.next = r->watcher_root.prev = &r->watcher_root;
David Klempnerd1785242015-01-28 17:00:21 -080099 r->freelist_next = NULL;
100 return r;
101}
102
103static void destroy(grpc_fd *fd) {
104 gpr_mu_destroy(&fd->set_state_mu);
105 gpr_mu_destroy(&fd->watcher_mu);
ctiller58393c22015-01-07 14:03:30 -0800106 gpr_free(fd);
ctiller58393c22015-01-07 14:03:30 -0800107}
108
109static void ref_by(grpc_fd *fd, int n) {
Craig Tiller23139ae2015-02-17 15:46:13 -0800110 GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0);
ctiller58393c22015-01-07 14:03:30 -0800111}
112
113static void unref_by(grpc_fd *fd, int n) {
Craig Tiller23139ae2015-02-17 15:46:13 -0800114 gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n);
115 if (old == n) {
David Klempnerd1785242015-01-28 17:00:21 -0800116 grpc_iomgr_add_callback(fd->on_done, fd->on_done_user_data);
117 freelist_fd(fd);
118 grpc_iomgr_unref();
Craig Tiller23139ae2015-02-17 15:46:13 -0800119 } else {
120 GPR_ASSERT(old > n);
David Klempnerd1785242015-01-28 17:00:21 -0800121 }
122}
123
Craig Tiller7d413212015-02-09 08:00:02 -0800124void grpc_fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); }
David Klempnerd1785242015-01-28 17:00:21 -0800125
126void grpc_fd_global_shutdown(void) {
127 while (fd_freelist != NULL) {
128 grpc_fd *fd = fd_freelist;
129 fd_freelist = fd_freelist->freelist_next;
ctiller58393c22015-01-07 14:03:30 -0800130 destroy(fd);
131 }
David Klempnerd1785242015-01-28 17:00:21 -0800132 gpr_mu_destroy(&fd_freelist_mu);
ctiller58393c22015-01-07 14:03:30 -0800133}
134
135static void do_nothing(void *ignored, int success) {}
136
137grpc_fd *grpc_fd_create(int fd) {
David Klempnerd1785242015-01-28 17:00:21 -0800138 grpc_fd *r = alloc_fd(fd);
ctiller58393c22015-01-07 14:03:30 -0800139 grpc_iomgr_ref();
ctiller58393c22015-01-07 14:03:30 -0800140 grpc_pollset_add_fd(grpc_backup_pollset(), r);
141 return r;
142}
143
144int grpc_fd_is_orphaned(grpc_fd *fd) {
145 return (gpr_atm_acq_load(&fd->refst) & 1) == 0;
146}
147
148static void wake_watchers(grpc_fd *fd) {
Craig Tiller7d413212015-02-09 08:00:02 -0800149 grpc_fd_watcher *watcher;
ctiller58393c22015-01-07 14:03:30 -0800150 gpr_mu_lock(&fd->watcher_mu);
Craig Tiller7d413212015-02-09 08:00:02 -0800151 for (watcher = fd->watcher_root.next; watcher != &fd->watcher_root;
152 watcher = watcher->next) {
153 grpc_pollset_force_kick(watcher->pollset);
ctiller58393c22015-01-07 14:03:30 -0800154 }
155 gpr_mu_unlock(&fd->watcher_mu);
156}
157
158void grpc_fd_orphan(grpc_fd *fd, grpc_iomgr_cb_func on_done, void *user_data) {
159 fd->on_done = on_done ? on_done : do_nothing;
160 fd->on_done_user_data = user_data;
161 ref_by(fd, 1); /* remove active status, but keep referenced */
162 wake_watchers(fd);
163 close(fd->fd);
164 unref_by(fd, 2); /* drop the reference */
165}
166
167/* increment refcount by two to avoid changing the orphan bit */
168void grpc_fd_ref(grpc_fd *fd) { ref_by(fd, 2); }
169
170void grpc_fd_unref(grpc_fd *fd) { unref_by(fd, 2); }
171
ctiller58393c22015-01-07 14:03:30 -0800172static void make_callback(grpc_iomgr_cb_func cb, void *arg, int success,
173 int allow_synchronous_callback) {
174 if (allow_synchronous_callback) {
175 cb(arg, success);
176 } else {
177 grpc_iomgr_add_delayed_callback(cb, arg, success);
178 }
179}
180
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800181static void make_callbacks(grpc_iomgr_closure *callbacks, size_t n, int success,
ctiller58393c22015-01-07 14:03:30 -0800182 int allow_synchronous_callback) {
183 size_t i;
184 for (i = 0; i < n; i++) {
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800185 make_callback(callbacks[i].cb, callbacks[i].cb_arg, success,
ctiller58393c22015-01-07 14:03:30 -0800186 allow_synchronous_callback);
187 }
188}
189
Craig Tillerf95e37f2015-02-18 15:15:29 -0800190static void notify_on(grpc_fd *fd, gpr_atm *st, grpc_iomgr_closure *closure,
191 int allow_synchronous_callback) {
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800192 switch (gpr_atm_acq_load(st)) {
ctiller58393c22015-01-07 14:03:30 -0800193 case NOT_READY:
194 /* There is no race if the descriptor is already ready, so we skip
195 the interlocked op in that case. As long as the app doesn't
196 try to set the same upcall twice (which it shouldn't) then
197 oldval should never be anything other than READY or NOT_READY. We
198 don't
199 check for user error on the fast path. */
Craig Tillerf95e37f2015-02-18 15:15:29 -0800200 if (gpr_atm_rel_cas(st, NOT_READY, (gpr_intptr)closure)) {
ctiller58393c22015-01-07 14:03:30 -0800201 /* swap was successful -- the closure will run after the next
202 set_ready call. NOTE: we don't have an ABA problem here,
203 since we should never have concurrent calls to the same
204 notify_on function. */
205 wake_watchers(fd);
206 return;
207 }
208 /* swap was unsuccessful due to an intervening set_ready call.
209 Fall through to the READY code below */
210 case READY:
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800211 assert(gpr_atm_acq_load(st) == READY);
212 gpr_atm_rel_store(st, NOT_READY);
Craig Tillerf95e37f2015-02-18 15:15:29 -0800213 make_callback(closure->cb, closure->cb_arg,
214 !gpr_atm_acq_load(&fd->shutdown),
ctiller58393c22015-01-07 14:03:30 -0800215 allow_synchronous_callback);
216 return;
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800217 default: /* WAITING */
ctiller58393c22015-01-07 14:03:30 -0800218 /* upcallptr was set to a different closure. This is an error! */
219 gpr_log(GPR_ERROR,
220 "User called a notify_on function with a previous callback still "
221 "pending");
222 abort();
223 }
224 gpr_log(GPR_ERROR, "Corrupt memory in &st->state");
225 abort();
226}
227
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800228static void set_ready_locked(gpr_atm *st, grpc_iomgr_closure *callbacks,
ctiller58393c22015-01-07 14:03:30 -0800229 size_t *ncallbacks) {
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800230 gpr_intptr state = gpr_atm_acq_load(st);
ctiller58393c22015-01-07 14:03:30 -0800231
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800232 switch (state) {
233 case READY:
234 /* duplicate ready, ignore */
235 return;
ctiller58393c22015-01-07 14:03:30 -0800236 case NOT_READY:
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800237 if (gpr_atm_rel_cas(st, NOT_READY, READY)) {
ctiller58393c22015-01-07 14:03:30 -0800238 /* swap was successful -- the closure will run after the next
239 notify_on call. */
240 return;
241 }
Craig Tillerf95e37f2015-02-18 15:15:29 -0800242 /* swap was unsuccessful due to an intervening set_ready call.
243 Fall through to the WAITING code below */
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800244 state = gpr_atm_acq_load(st);
245 default: /* waiting */
Craig Tillerf95e37f2015-02-18 15:15:29 -0800246 assert(gpr_atm_acq_load(st) != READY &&
247 gpr_atm_acq_load(st) != NOT_READY);
248 callbacks[(*ncallbacks)++] = *(grpc_iomgr_closure *)state;
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800249 gpr_atm_rel_store(st, NOT_READY);
ctiller58393c22015-01-07 14:03:30 -0800250 return;
251 }
252}
253
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800254static void set_ready(grpc_fd *fd, gpr_atm *st,
ctiller58393c22015-01-07 14:03:30 -0800255 int allow_synchronous_callback) {
256 /* only one set_ready can be active at once (but there may be a racing
257 notify_on) */
258 int success;
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800259 grpc_iomgr_closure cb;
ctiller58393c22015-01-07 14:03:30 -0800260 size_t ncb = 0;
261 gpr_mu_lock(&fd->set_state_mu);
262 set_ready_locked(st, &cb, &ncb);
263 gpr_mu_unlock(&fd->set_state_mu);
264 success = !gpr_atm_acq_load(&fd->shutdown);
265 make_callbacks(&cb, ncb, success, allow_synchronous_callback);
266}
267
268void grpc_fd_shutdown(grpc_fd *fd) {
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800269 grpc_iomgr_closure cb[2];
ctiller58393c22015-01-07 14:03:30 -0800270 size_t ncb = 0;
271 gpr_mu_lock(&fd->set_state_mu);
272 GPR_ASSERT(!gpr_atm_acq_load(&fd->shutdown));
273 gpr_atm_rel_store(&fd->shutdown, 1);
274 set_ready_locked(&fd->readst, cb, &ncb);
275 set_ready_locked(&fd->writest, cb, &ncb);
276 gpr_mu_unlock(&fd->set_state_mu);
277 make_callbacks(cb, ncb, 0, 0);
278}
279
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800280void grpc_fd_notify_on_read(grpc_fd *fd, grpc_iomgr_closure *closure) {
281 notify_on(fd, &fd->readst, closure, 0);
ctiller58393c22015-01-07 14:03:30 -0800282}
283
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800284void grpc_fd_notify_on_write(grpc_fd *fd, grpc_iomgr_closure *closure) {
285 notify_on(fd, &fd->writest, closure, 0);
ctiller58393c22015-01-07 14:03:30 -0800286}
287
288gpr_uint32 grpc_fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset,
Craig Tiller7d413212015-02-09 08:00:02 -0800289 gpr_uint32 read_mask, gpr_uint32 write_mask,
290 grpc_fd_watcher *watcher) {
ctiller58393c22015-01-07 14:03:30 -0800291 /* keep track of pollers that have requested our events, in case they change
292 */
293 gpr_mu_lock(&fd->watcher_mu);
Craig Tiller7d413212015-02-09 08:00:02 -0800294 watcher->next = &fd->watcher_root;
295 watcher->prev = watcher->next->prev;
296 watcher->next->prev = watcher->prev->next = watcher;
297 watcher->pollset = pollset;
298 watcher->fd = fd;
ctiller58393c22015-01-07 14:03:30 -0800299 gpr_mu_unlock(&fd->watcher_mu);
300
Craig Tiller0fcd53c2015-02-18 15:10:53 -0800301 return (gpr_atm_acq_load(&fd->readst) != READY ? read_mask : 0) |
302 (gpr_atm_acq_load(&fd->writest) != READY ? write_mask : 0);
ctiller58393c22015-01-07 14:03:30 -0800303}
304
Craig Tiller7d413212015-02-09 08:00:02 -0800305void grpc_fd_end_poll(grpc_fd_watcher *watcher) {
306 gpr_mu_lock(&watcher->fd->watcher_mu);
307 watcher->next->prev = watcher->prev;
308 watcher->prev->next = watcher->next;
309 gpr_mu_unlock(&watcher->fd->watcher_mu);
ctiller58393c22015-01-07 14:03:30 -0800310}
311
312void grpc_fd_become_readable(grpc_fd *fd, int allow_synchronous_callback) {
313 set_ready(fd, &fd->readst, allow_synchronous_callback);
314}
315
316void grpc_fd_become_writable(grpc_fd *fd, int allow_synchronous_callback) {
317 set_ready(fd, &fd->writest, allow_synchronous_callback);
318}
Craig Tillerd14a1a52015-01-21 15:26:29 -0800319
Craig Tiller190d3602015-02-18 09:23:38 -0800320#endif