]> git.saurik.com Git - apple/libdispatch.git/blob - src/once.c
libdispatch-913.30.4.tar.gz
[apple/libdispatch.git] / src / once.c
1 /*
2 * Copyright (c) 2008-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
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
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
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.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20
21 #include "internal.h"
22
23 #undef dispatch_once
24 #undef dispatch_once_f
25
26
27 typedef struct _dispatch_once_waiter_s {
28 volatile struct _dispatch_once_waiter_s *volatile dow_next;
29 dispatch_thread_event_s dow_event;
30 mach_port_t dow_thread;
31 } *_dispatch_once_waiter_t;
32
33 #define DISPATCH_ONCE_DONE ((_dispatch_once_waiter_t)~0l)
34
35 #ifdef __BLOCKS__
36 void
37 dispatch_once(dispatch_once_t *val, dispatch_block_t block)
38 {
39 dispatch_once_f(val, block, _dispatch_Block_invoke(block));
40 }
41 #endif
42
43 #if DISPATCH_ONCE_INLINE_FASTPATH
44 #define DISPATCH_ONCE_SLOW_INLINE inline DISPATCH_ALWAYS_INLINE
45 #else
46 #define DISPATCH_ONCE_SLOW_INLINE DISPATCH_NOINLINE
47 #endif // DISPATCH_ONCE_INLINE_FASTPATH
48
49 DISPATCH_ONCE_SLOW_INLINE
50 static void
51 dispatch_once_f_slow(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
52 {
53 #if DISPATCH_GATE_USE_FOR_DISPATCH_ONCE
54 dispatch_once_gate_t l = (dispatch_once_gate_t)val;
55
56 if (_dispatch_once_gate_tryenter(l)) {
57 _dispatch_client_callout(ctxt, func);
58 _dispatch_once_gate_broadcast(l);
59 } else {
60 _dispatch_once_gate_wait(l);
61 }
62 #else
63 _dispatch_once_waiter_t volatile *vval = (_dispatch_once_waiter_t*)val;
64 struct _dispatch_once_waiter_s dow = { };
65 _dispatch_once_waiter_t tail = &dow, next, tmp;
66 dispatch_thread_event_t event;
67
68 if (os_atomic_cmpxchg(vval, NULL, tail, acquire)) {
69 dow.dow_thread = _dispatch_tid_self();
70 _dispatch_client_callout(ctxt, func);
71
72 next = (_dispatch_once_waiter_t)_dispatch_once_xchg_done(val);
73 while (next != tail) {
74 tmp = (_dispatch_once_waiter_t)_dispatch_wait_until(next->dow_next);
75 event = &next->dow_event;
76 next = tmp;
77 _dispatch_thread_event_signal(event);
78 }
79 } else {
80 _dispatch_thread_event_init(&dow.dow_event);
81 next = *vval;
82 for (;;) {
83 if (next == DISPATCH_ONCE_DONE) {
84 break;
85 }
86 if (os_atomic_cmpxchgv(vval, next, tail, &next, release)) {
87 dow.dow_thread = next->dow_thread;
88 dow.dow_next = next;
89 if (dow.dow_thread) {
90 pthread_priority_t pp = _dispatch_get_priority();
91 _dispatch_thread_override_start(dow.dow_thread, pp, val);
92 }
93 _dispatch_thread_event_wait(&dow.dow_event);
94 if (dow.dow_thread) {
95 _dispatch_thread_override_end(dow.dow_thread, val);
96 }
97 break;
98 }
99 }
100 _dispatch_thread_event_destroy(&dow.dow_event);
101 }
102 #endif
103 }
104
105 DISPATCH_NOINLINE
106 void
107 dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
108 {
109 #if !DISPATCH_ONCE_INLINE_FASTPATH
110 if (likely(os_atomic_load(val, acquire) == DLOCK_ONCE_DONE)) {
111 return;
112 }
113 #endif // !DISPATCH_ONCE_INLINE_FASTPATH
114 return dispatch_once_f_slow(val, ctxt, func);
115 }