2 * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
4 * @APPLE_APACHE_LICENSE_HEADER_START@
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * @APPLE_APACHE_LICENSE_HEADER_END@
24 #include "protocolServer.h"
26 #include <sys/mount.h>
28 #ifndef DISPATCH_NO_LEGACY
29 struct dispatch_source_attr_vtable_s {
30 DISPATCH_VTABLE_HEADER(dispatch_source_attr_s);
33 struct dispatch_source_attr_s {
34 DISPATCH_STRUCT_HEADER(dispatch_source_attr_s, dispatch_source_attr_vtable_s);
36 dispatch_source_finalizer_function_t finalizer_func;
39 #endif /* DISPATCH_NO_LEGACY */
41 #define _dispatch_source_call_block ((void *)-1)
42 static void _dispatch_source_latch_and_call(dispatch_source_t ds);
43 static void _dispatch_source_cancel_callout(dispatch_source_t ds);
44 static size_t dispatch_source_debug_attr(dispatch_source_t ds, char* buf, size_t bufsiz);
47 dispatch_source_cancel(dispatch_source_t ds)
50 dispatch_debug(ds, __FUNCTION__);
52 // Right after we set the cancel flag, someone else
53 // could potentially invoke the source, do the cancelation,
54 // unregister the source, and deallocate it. We would
55 // need to therefore retain/release before setting the bit
58 dispatch_atomic_or(&ds->ds_atomic_flags, DSF_CANCELED);
60 _dispatch_release(ds);
65 _dispatch_source_xref_release(dispatch_source_t ds)
67 #ifndef DISPATCH_NO_LEGACY
68 if (ds->ds_is_legacy) {
69 if (!(ds->ds_timer.flags & DISPATCH_TIMER_ONESHOT)) {
70 dispatch_source_cancel(ds);
72 // Clients often leave sources suspended at the last release
73 dispatch_atomic_and(&ds->do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_LOCK);
76 if (slowpath(DISPATCH_OBJECT_SUSPENDED(ds))) {
77 // Arguments for and against this assert are within 6705399
78 DISPATCH_CLIENT_CRASH("Release of a suspended object");
81 _dispatch_release(ds);
85 dispatch_source_testcancel(dispatch_source_t ds)
87 return (bool)(ds->ds_atomic_flags & DSF_CANCELED);
92 dispatch_source_get_mask(dispatch_source_t ds)
94 return ds->ds_pending_data_mask;
98 dispatch_source_get_handle(dispatch_source_t ds)
100 return (int)ds->ds_ident_hack;
104 dispatch_source_get_data(dispatch_source_t ds)
110 _dispatch_source_invoke(dispatch_source_t ds)
112 // This function performs all source actions. Each action is responsible
113 // for verifying that it takes place on the appropriate queue. If the
114 // current queue is not the correct queue for this action, the correct queue
115 // will be returned and the invoke will be re-driven on that queue.
117 // The order of tests here in invoke and in probe should be consistent.
119 dispatch_queue_t dq = _dispatch_queue_get_current();
121 if (!ds->ds_is_installed) {
122 // The source needs to be installed on the manager queue.
123 if (dq != &_dispatch_mgr_q) {
124 return &_dispatch_mgr_q;
126 _dispatch_kevent_merge(ds);
127 } else if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) {
128 // The source has been cancelled and needs to be uninstalled from the
129 // manager queue. After uninstallation, the cancellation handler needs
130 // to be delivered to the target queue.
132 if (dq != &_dispatch_mgr_q) {
133 return &_dispatch_mgr_q;
135 _dispatch_kevent_release(ds);
136 return ds->do_targetq;
137 } else if (ds->ds_cancel_handler) {
138 if (dq != ds->do_targetq) {
139 return ds->do_targetq;
142 _dispatch_source_cancel_callout(ds);
143 } else if (ds->ds_pending_data) {
144 // The source has pending data to deliver via the event handler callback
145 // on the target queue. Some sources need to be rearmed on the manager
146 // queue after event delivery.
147 if (dq != ds->do_targetq) {
148 return ds->do_targetq;
150 _dispatch_source_latch_and_call(ds);
151 if (ds->ds_needs_rearm) {
152 return &_dispatch_mgr_q;
154 } else if (ds->ds_needs_rearm && !ds->ds_is_armed) {
155 // The source needs to be rearmed on the manager queue.
156 if (dq != &_dispatch_mgr_q) {
157 return &_dispatch_mgr_q;
159 _dispatch_source_kevent_resume(ds, 0, 0);
160 ds->ds_is_armed = true;
167 _dispatch_source_probe(dispatch_source_t ds)
169 // This function determines whether the source needs to be invoked.
170 // The order of tests here in probe and in invoke should be consistent.
172 if (!ds->ds_is_installed) {
173 // The source needs to be installed on the manager queue.
175 } else if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) {
176 // The source needs to be uninstalled from the manager queue, or the
177 // cancellation handler needs to be delivered to the target queue.
178 // Note: cancellation assumes installation.
179 if (ds->ds_dkev || ds->ds_cancel_handler) {
182 } else if (ds->ds_pending_data) {
183 // The source has pending data to deliver to the target queue.
185 } else if (ds->ds_needs_rearm && !ds->ds_is_armed) {
186 // The source needs to be rearmed on the manager queue.
194 _dispatch_source_dispose(dispatch_source_t ds)
196 _dispatch_queue_dispose((dispatch_queue_t)ds);
200 _dispatch_source_latch_and_call(dispatch_source_t ds)
204 if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) {
207 prev = dispatch_atomic_xchg(&ds->ds_pending_data, 0);
208 if (ds->ds_is_level) {
213 if (dispatch_assume(prev)) {
214 if (ds->ds_handler_func) {
215 #ifndef DISPATCH_NO_LEGACY
216 ((dispatch_source_handler_function_t)ds->ds_handler_func)(ds->ds_handler_ctxt, ds);
218 ds->ds_handler_func(ds->ds_handler_ctxt);
225 _dispatch_source_cancel_callout(dispatch_source_t ds)
227 ds->ds_pending_data_mask = 0;
228 ds->ds_pending_data = 0;
232 if (ds->ds_handler_is_block) {
233 Block_release(ds->ds_handler_ctxt);
234 ds->ds_handler_is_block = false;
235 ds->ds_handler_func = NULL;
236 ds->ds_handler_ctxt = NULL;
240 if (!ds->ds_cancel_handler) {
243 if (ds->ds_cancel_is_block) {
245 dispatch_block_t b = ds->ds_cancel_handler;
246 if (ds->ds_atomic_flags & DSF_CANCELED) {
249 Block_release(ds->ds_cancel_handler);
250 ds->ds_cancel_is_block = false;
253 dispatch_function_t f = ds->ds_cancel_handler;
254 if (ds->ds_atomic_flags & DSF_CANCELED) {
258 ds->ds_cancel_handler = NULL;
262 dispatch_source_debug_attr(dispatch_source_t ds, char* buf, size_t bufsiz)
264 dispatch_queue_t target = ds->do_targetq;
265 return snprintf(buf, bufsiz,
266 "target = %s[%p], pending_data = 0x%lx, pending_data_mask = 0x%lx, ",
267 target ? target->dq_label : "", target,
268 ds->ds_pending_data, ds->ds_pending_data_mask);
272 _dispatch_source_debug(dispatch_source_t ds, char* buf, size_t bufsiz)
275 offset += snprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ", dx_kind(ds), ds);
276 offset += dispatch_object_debug_attr(ds, &buf[offset], bufsiz - offset);
277 offset += dispatch_source_debug_attr(ds, &buf[offset], bufsiz - offset);
281 #ifndef DISPATCH_NO_LEGACY
283 dispatch_source_attr_dispose(dispatch_source_attr_t attr)
285 // release the finalizer block if necessary
286 dispatch_source_attr_set_finalizer(attr, NULL);
287 _dispatch_dispose(attr);
290 static const struct dispatch_source_attr_vtable_s dispatch_source_attr_vtable = {
291 .do_type = DISPATCH_SOURCE_ATTR_TYPE,
292 .do_kind = "source-attr",
293 .do_dispose = dispatch_source_attr_dispose,
296 dispatch_source_attr_t
297 dispatch_source_attr_create(void)
299 dispatch_source_attr_t rval = calloc(1, sizeof(struct dispatch_source_attr_s));
302 rval->do_vtable = &dispatch_source_attr_vtable;
303 rval->do_next = DISPATCH_OBJECT_LISTLESS;
304 rval->do_targetq = dispatch_get_global_queue(0, 0);
305 rval->do_ref_cnt = 1;
306 rval->do_xref_cnt = 1;
313 dispatch_source_attr_set_finalizer_f(dispatch_source_attr_t attr,
314 void *context, dispatch_source_finalizer_function_t finalizer)
317 if (attr->finalizer_func == (void*)_dispatch_call_block_and_release2) {
318 Block_release(attr->finalizer_ctxt);
322 attr->finalizer_ctxt = context;
323 attr->finalizer_func = finalizer;
328 dispatch_source_attr_set_finalizer(dispatch_source_attr_t attr,
329 dispatch_source_finalizer_t finalizer)
332 dispatch_source_finalizer_function_t func;
335 if (!(ctxt = Block_copy(finalizer))) {
338 func = (void *)_dispatch_call_block_and_release2;
344 dispatch_source_attr_set_finalizer_f(attr, ctxt, func);
349 dispatch_source_finalizer_t
350 dispatch_source_attr_get_finalizer(dispatch_source_attr_t attr)
352 if (attr->finalizer_func == (void*)_dispatch_call_block_and_release2) {
353 return (dispatch_source_finalizer_t)attr->finalizer_ctxt;
354 } else if (attr->finalizer_func == NULL) {
357 abort(); // finalizer is not a block...
363 dispatch_source_attr_set_context(dispatch_source_attr_t attr, void *context)
365 attr->context = context;
368 dispatch_source_attr_t
369 dispatch_source_attr_copy(dispatch_source_attr_t proto)
371 dispatch_source_attr_t rval = NULL;
373 if (proto && (rval = malloc(sizeof(struct dispatch_source_attr_s)))) {
374 memcpy(rval, proto, sizeof(struct dispatch_source_attr_s));
376 if (rval->finalizer_func == (void*)_dispatch_call_block_and_release2) {
377 rval->finalizer_ctxt = Block_copy(rval->finalizer_ctxt);
381 rval = dispatch_source_attr_create();
385 #endif /* DISPATCH_NO_LEGACY */
389 dispatch_source_create(dispatch_source_type_t type,
394 dispatch_source_t ds = NULL;
395 static char source_label[sizeof(ds->dq_label)] = "source";
398 if (type == NULL || (mask & ~type->mask)) {
402 ds = calloc(1ul, sizeof(struct dispatch_source_s));
407 // Initialize as a queue first, then override some settings below.
408 _dispatch_queue_init((dispatch_queue_t)ds);
409 memcpy(ds->dq_label, source_label, sizeof(source_label));
412 ds->do_vtable = &_dispatch_source_kevent_vtable;
413 ds->do_ref_cnt++; // the reference the manger queue holds
414 ds->do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_INTERVAL;
415 // do_targetq will be retained below, past point of no-return
418 if (slowpath(!type->init(ds, type, handle, mask, q))) {
422 dispatch_assert(!(ds->ds_is_level && ds->ds_is_adder));
424 dispatch_debug(ds, __FUNCTION__);
427 _dispatch_retain(ds->do_targetq);
436 // 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol
438 _dispatch_source_set_event_handler2(void *context)
440 struct Block_layout *bl = context;
442 dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current();
443 dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable);
445 if (ds->ds_handler_is_block && ds->ds_handler_ctxt) {
446 Block_release(ds->ds_handler_ctxt);
448 ds->ds_handler_func = bl ? (void *)bl->invoke : NULL;
449 ds->ds_handler_ctxt = bl;
450 ds->ds_handler_is_block = true;
454 dispatch_source_set_event_handler(dispatch_source_t ds, dispatch_block_t handler)
456 dispatch_assert(!ds->ds_is_legacy);
457 handler = _dispatch_Block_copy(handler);
458 dispatch_barrier_async_f((dispatch_queue_t)ds,
459 handler, _dispatch_source_set_event_handler2);
461 #endif /* __BLOCKS__ */
464 _dispatch_source_set_event_handler_f(void *context)
466 dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current();
467 dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable);
470 if (ds->ds_handler_is_block && ds->ds_handler_ctxt) {
471 Block_release(ds->ds_handler_ctxt);
474 ds->ds_handler_func = context;
475 ds->ds_handler_ctxt = ds->do_ctxt;
476 ds->ds_handler_is_block = false;
480 dispatch_source_set_event_handler_f(dispatch_source_t ds,
481 dispatch_function_t handler)
483 dispatch_assert(!ds->ds_is_legacy);
484 dispatch_barrier_async_f((dispatch_queue_t)ds,
485 handler, _dispatch_source_set_event_handler_f);
489 // 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol
491 _dispatch_source_set_cancel_handler2(void *context)
493 dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current();
494 dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable);
496 if (ds->ds_cancel_is_block && ds->ds_cancel_handler) {
497 Block_release(ds->ds_cancel_handler);
499 ds->ds_cancel_handler = context;
500 ds->ds_cancel_is_block = true;
504 dispatch_source_set_cancel_handler(dispatch_source_t ds,
505 dispatch_block_t handler)
507 dispatch_assert(!ds->ds_is_legacy);
508 handler = _dispatch_Block_copy(handler);
509 dispatch_barrier_async_f((dispatch_queue_t)ds,
510 handler, _dispatch_source_set_cancel_handler2);
512 #endif /* __BLOCKS__ */
515 _dispatch_source_set_cancel_handler_f(void *context)
517 dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current();
518 dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable);
521 if (ds->ds_cancel_is_block && ds->ds_cancel_handler) {
522 Block_release(ds->ds_cancel_handler);
525 ds->ds_cancel_handler = context;
526 ds->ds_cancel_is_block = false;
530 dispatch_source_set_cancel_handler_f(dispatch_source_t ds,
531 dispatch_function_t handler)
533 dispatch_assert(!ds->ds_is_legacy);
534 dispatch_barrier_async_f((dispatch_queue_t)ds,
535 handler, _dispatch_source_set_cancel_handler_f);
538 #ifndef DISPATCH_NO_LEGACY
539 // 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol
541 _dispatch_source_create2(dispatch_source_t ds,
542 dispatch_source_attr_t attr,
544 dispatch_source_handler_function_t handler)
546 if (ds == NULL || handler == NULL) {
550 ds->ds_is_legacy = true;
552 ds->ds_handler_func = (dispatch_function_t)handler;
553 ds->ds_handler_ctxt = context;
555 if (attr && attr != DISPATCH_SOURCE_CREATE_SUSPENDED) {
556 ds->dq_finalizer_ctxt = attr->finalizer_ctxt;
557 ds->dq_finalizer_func = (typeof(ds->dq_finalizer_func))attr->finalizer_func;
558 ds->do_ctxt = attr->context;
561 if (ds->dq_finalizer_func == (void*)_dispatch_call_block_and_release2) {
562 ds->dq_finalizer_ctxt = Block_copy(ds->dq_finalizer_ctxt);
563 if (!ds->dq_finalizer_ctxt) {
567 if (handler == _dispatch_source_call_block) {
568 struct Block_layout *bl = ds->ds_handler_ctxt = Block_copy(context);
569 if (!ds->ds_handler_ctxt) {
570 if (ds->dq_finalizer_func == (void*)_dispatch_call_block_and_release2) {
571 Block_release(ds->dq_finalizer_ctxt);
575 ds->ds_handler_func = (void *)bl->invoke;
576 ds->ds_handler_is_block = true;
579 // all legacy sources get a cancellation event on the normal event handler.
580 dispatch_function_t func = ds->ds_handler_func;
581 dispatch_source_handler_t block = ds->ds_handler_ctxt;
582 void *ctxt = ds->ds_handler_ctxt;
583 bool handler_is_block = ds->ds_handler_is_block;
585 ds->ds_cancel_is_block = true;
586 if (handler_is_block) {
587 ds->ds_cancel_handler = _dispatch_Block_copy(^{
591 ds->ds_cancel_handler = _dispatch_Block_copy(^{
592 ((dispatch_source_handler_function_t)func)(ctxt, ds);
596 if (attr != DISPATCH_SOURCE_CREATE_SUSPENDED) {
610 dispatch_source_get_error(dispatch_source_t ds, long *err_out)
612 // 6863892 don't report ECANCELED until kevent is unregistered
613 if ((ds->ds_atomic_flags & DSF_CANCELED) && !ds->ds_dkev) {
615 *err_out = ECANCELED;
617 return DISPATCH_ERROR_DOMAIN_POSIX;
619 return DISPATCH_ERROR_DOMAIN_NO_ERROR;
622 #endif /* DISPATCH_NO_LEGACY */
624 // To be called from the context of the _dispatch_mgr_q
626 _dispatch_source_set_timer2(void *context)
628 struct dispatch_set_timer_params *params = context;
629 dispatch_source_t ds = params->ds;
630 ds->ds_ident_hack = params->ident;
631 ds->ds_timer = params->values;
632 _dispatch_timer_list_update(ds);
634 dispatch_release(ds);
639 dispatch_source_set_timer(dispatch_source_t ds,
640 dispatch_time_t start,
644 struct dispatch_set_timer_params *params;
646 // we use zero internally to mean disabled
649 } else if ((int64_t)interval < 0) {
650 // 6866347 - make sure nanoseconds won't overflow
651 interval = INT64_MAX;
654 // Suspend the source so that it doesn't fire with pending changes
655 // The use of suspend/resume requires the external retain/release
657 dispatch_suspend(ds);
659 if (start == DISPATCH_TIME_NOW) {
660 start = _dispatch_absolute_time();
661 } else if (start == DISPATCH_TIME_FOREVER) {
664 if ((int64_t)leeway < 0) {
668 while (!(params = malloc(sizeof(struct dispatch_set_timer_params)))) {
673 params->values.flags = ds->ds_timer.flags;
675 if ((int64_t)start < 0) {
677 params->ident = DISPATCH_TIMER_INDEX_WALL;
678 params->values.start = -((int64_t)start);
679 params->values.target = -((int64_t)start);
680 params->values.interval = interval;
681 params->values.leeway = leeway;
682 params->values.flags |= DISPATCH_TIMER_WALL_CLOCK;
685 params->ident = DISPATCH_TIMER_INDEX_MACH;
686 params->values.start = start;
687 params->values.target = start;
688 params->values.interval = _dispatch_time_nano2mach(interval);
689 params->values.leeway = _dispatch_time_nano2mach(leeway);
690 params->values.flags &= ~DISPATCH_TIMER_WALL_CLOCK;
693 dispatch_barrier_async_f(&_dispatch_mgr_q, params, _dispatch_source_set_timer2);
696 #ifndef DISPATCH_NO_LEGACY
699 dispatch_source_timer_set_time(dispatch_source_t ds, uint64_t nanoseconds, uint64_t leeway)
701 dispatch_time_t start;
702 if (nanoseconds == 0) {
705 if (ds->ds_timer.flags == (DISPATCH_TIMER_ABSOLUTE|DISPATCH_TIMER_WALL_CLOCK)) {
706 static const struct timespec t0;
707 start = dispatch_walltime(&t0, nanoseconds);
708 } else if (ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK) {
709 start = dispatch_walltime(DISPATCH_TIME_NOW, nanoseconds);
711 start = dispatch_time(DISPATCH_TIME_NOW, nanoseconds);
713 if (ds->ds_timer.flags & (DISPATCH_TIMER_ABSOLUTE|DISPATCH_TIMER_ONESHOT)) {
714 // 6866347 - make sure nanoseconds won't overflow
715 nanoseconds = INT64_MAX; // non-repeating (~292 years)
717 dispatch_source_set_timer(ds, start, nanoseconds, leeway);
723 dispatch_event_get_nanoseconds(dispatch_source_t ds)
725 if (ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK) {
726 return ds->ds_timer.interval;
728 return _dispatch_time_mach2nano(ds->ds_timer.interval);
731 #endif /* DISPATCH_NO_LEGACY */