]>
Commit | Line | Data |
---|---|---|
7af964d1 A |
1 | /* |
2 | * Copyright (c) 2007 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /*********************************************************************** | |
25 | * objc-os.h | |
26 | * OS portability layer. | |
27 | **********************************************************************/ | |
28 | ||
29 | #ifndef _OBJC_OS_H | |
30 | #define _OBJC_OS_H | |
31 | ||
32 | #include <TargetConditionals.h> | |
7257e56c A |
33 | #include "objc-config.h" |
34 | ||
35 | #ifdef __LP64__ | |
36 | # define WORD_SHIFT 3UL | |
37 | # define WORD_MASK 7UL | |
8070259c | 38 | # define WORD_BITS 64 |
7257e56c A |
39 | #else |
40 | # define WORD_SHIFT 2UL | |
41 | # define WORD_MASK 3UL | |
8070259c | 42 | # define WORD_BITS 32 |
7257e56c | 43 | #endif |
7af964d1 | 44 | |
8070259c A |
45 | static inline uint32_t word_align(uint32_t x) { |
46 | return (x + WORD_MASK) & ~WORD_MASK; | |
47 | } | |
48 | static inline size_t word_align(size_t x) { | |
49 | return (x + WORD_MASK) & ~WORD_MASK; | |
50 | } | |
51 | ||
7af964d1 A |
52 | #if TARGET_OS_MAC |
53 | ||
8972963c A |
54 | # ifndef __STDC_LIMIT_MACROS |
55 | # define __STDC_LIMIT_MACROS | |
56 | # endif | |
57 | ||
7af964d1 A |
58 | # include <stdio.h> |
59 | # include <stdlib.h> | |
60 | # include <stdint.h> | |
61 | # include <stdarg.h> | |
62 | # include <string.h> | |
63 | # include <ctype.h> | |
64 | # include <errno.h> | |
65 | # include <dlfcn.h> | |
66 | # include <fcntl.h> | |
67 | # include <assert.h> | |
68 | # include <limits.h> | |
69 | # include <syslog.h> | |
70 | # include <unistd.h> | |
71 | # include <pthread.h> | |
72 | # include <crt_externs.h> | |
73 | # include <AssertMacros.h> | |
8972963c | 74 | # undef check |
7257e56c | 75 | # include <Availability.h> |
7af964d1 A |
76 | # include <TargetConditionals.h> |
77 | # include <sys/mman.h> | |
78 | # include <sys/time.h> | |
79 | # include <sys/stat.h> | |
80 | # include <sys/param.h> | |
81 | # include <mach/mach.h> | |
7257e56c | 82 | # include <mach/vm_param.h> |
7af964d1 A |
83 | # include <mach-o/dyld.h> |
84 | # include <mach-o/ldsyms.h> | |
85 | # include <mach-o/loader.h> | |
86 | # include <mach-o/getsect.h> | |
87 | # include <mach-o/dyld_priv.h> | |
88 | # include <malloc/malloc.h> | |
7257e56c | 89 | # include <os/lock_private.h> |
7af964d1 A |
90 | # include <libkern/OSAtomic.h> |
91 | # include <libkern/OSCacheControl.h> | |
92 | # include <System/pthread_machdep.h> | |
93 | # include "objc-probes.h" // generated dtrace probe definitions. | |
94 | ||
7257e56c A |
95 | // Some libc functions call objc_msgSend() |
96 | // so we can't use them without deadlocks. | |
97 | void syslog(int, const char *, ...) UNAVAILABLE_ATTRIBUTE; | |
98 | void vsyslog(int, const char *, va_list) UNAVAILABLE_ATTRIBUTE; | |
cd5f04f5 | 99 | |
cd5f04f5 | 100 | |
8070259c A |
101 | #define ALWAYS_INLINE inline __attribute__((always_inline)) |
102 | #define NEVER_INLINE inline __attribute__((noinline)) | |
103 | ||
104 | ||
105 | ||
106 | static ALWAYS_INLINE uintptr_t | |
107 | addc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout) | |
108 | { | |
109 | return __builtin_addcl(lhs, rhs, carryin, carryout); | |
110 | } | |
111 | ||
112 | static ALWAYS_INLINE uintptr_t | |
113 | subc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout) | |
114 | { | |
115 | return __builtin_subcl(lhs, rhs, carryin, carryout); | |
116 | } | |
117 | ||
118 | ||
119 | #if __arm64__ | |
120 | ||
121 | static ALWAYS_INLINE | |
122 | uintptr_t | |
123 | LoadExclusive(uintptr_t *src) | |
124 | { | |
125 | uintptr_t result; | |
126 | asm("ldxr %x0, [%x1]" | |
127 | : "=r" (result) | |
128 | : "r" (src), "m" (*src)); | |
129 | return result; | |
130 | } | |
131 | ||
132 | static ALWAYS_INLINE | |
133 | bool | |
134 | StoreExclusive(uintptr_t *dst, uintptr_t oldvalue __unused, uintptr_t value) | |
135 | { | |
136 | uint32_t result; | |
137 | asm("stxr %w0, %x2, [%x3]" | |
138 | : "=r" (result), "=m" (*dst) | |
139 | : "r" (value), "r" (dst)); | |
140 | return !result; | |
141 | } | |
142 | ||
143 | ||
144 | static ALWAYS_INLINE | |
145 | bool | |
146 | StoreReleaseExclusive(uintptr_t *dst, uintptr_t oldvalue __unused, uintptr_t value) | |
147 | { | |
148 | uint32_t result; | |
149 | asm("stlxr %w0, %x2, [%x3]" | |
150 | : "=r" (result), "=m" (*dst) | |
151 | : "r" (value), "r" (dst)); | |
152 | return !result; | |
153 | } | |
154 | ||
155 | ||
156 | #elif __arm__ | |
157 | ||
158 | static ALWAYS_INLINE | |
159 | uintptr_t | |
160 | LoadExclusive(uintptr_t *src) | |
161 | { | |
162 | return *src; | |
163 | } | |
164 | ||
165 | static ALWAYS_INLINE | |
166 | bool | |
167 | StoreExclusive(uintptr_t *dst, uintptr_t oldvalue, uintptr_t value) | |
168 | { | |
169 | return OSAtomicCompareAndSwapPtr((void *)oldvalue, (void *)value, | |
170 | (void **)dst); | |
171 | } | |
172 | ||
173 | static ALWAYS_INLINE | |
174 | bool | |
175 | StoreReleaseExclusive(uintptr_t *dst, uintptr_t oldvalue, uintptr_t value) | |
176 | { | |
177 | return OSAtomicCompareAndSwapPtrBarrier((void *)oldvalue, (void *)value, | |
178 | (void **)dst); | |
179 | } | |
180 | ||
181 | ||
182 | #elif __x86_64__ || __i386__ | |
183 | ||
184 | static ALWAYS_INLINE | |
185 | uintptr_t | |
186 | LoadExclusive(uintptr_t *src) | |
187 | { | |
188 | return *src; | |
189 | } | |
190 | ||
191 | static ALWAYS_INLINE | |
192 | bool | |
193 | StoreExclusive(uintptr_t *dst, uintptr_t oldvalue, uintptr_t value) | |
194 | { | |
195 | ||
196 | return __sync_bool_compare_and_swap((void **)dst, (void *)oldvalue, (void *)value); | |
197 | } | |
198 | ||
199 | static ALWAYS_INLINE | |
200 | bool | |
201 | StoreReleaseExclusive(uintptr_t *dst, uintptr_t oldvalue, uintptr_t value) | |
202 | { | |
203 | return StoreExclusive(dst, oldvalue, value); | |
204 | } | |
205 | ||
206 | #else | |
207 | # error unknown architecture | |
208 | #endif | |
209 | ||
210 | ||
7257e56c A |
211 | #define spinlock_t os_lock_handoff_s |
212 | #define spinlock_trylock(l) os_lock_trylock(l) | |
213 | #define spinlock_lock(l) os_lock_lock(l) | |
214 | #define spinlock_unlock(l) os_lock_unlock(l) | |
215 | #define SPINLOCK_INITIALIZER OS_LOCK_HANDOFF_INIT | |
cd5f04f5 A |
216 | |
217 | ||
8972963c A |
218 | #if !TARGET_OS_IPHONE |
219 | # include <CrashReporterClient.h> | |
220 | #else | |
221 | // CrashReporterClient not yet available on iOS | |
222 | __BEGIN_DECLS | |
223 | extern const char *CRSetCrashLogMessage(const char *msg); | |
224 | extern const char *CRGetCrashLogMessage(void); | |
225 | extern const char *CRSetCrashLogMessage2(const char *msg); | |
226 | __END_DECLS | |
227 | #endif | |
228 | ||
7af964d1 A |
229 | # if __cplusplus |
230 | # include <vector> | |
231 | # include <algorithm> | |
cd5f04f5 | 232 | # include <functional> |
7af964d1 | 233 | using namespace std; |
7af964d1 A |
234 | # endif |
235 | ||
8972963c A |
236 | # define PRIVATE_EXTERN __attribute__((visibility("hidden"))) |
237 | # undef __private_extern__ | |
238 | # define __private_extern__ use_PRIVATE_EXTERN_instead | |
239 | # undef private_extern | |
240 | # define private_extern use_PRIVATE_EXTERN_instead | |
241 | ||
242 | /* Use this for functions that are intended to be breakpoint hooks. | |
243 | If you do not, the compiler may optimize them away. | |
244 | BREAKPOINT_FUNCTION( void stop_on_error(void) ); */ | |
8070259c A |
245 | # define BREAKPOINT_FUNCTION(prototype) \ |
246 | OBJC_EXTERN __attribute__((noinline, used, visibility("hidden"))) \ | |
8972963c A |
247 | prototype { asm(""); } |
248 | ||
7af964d1 A |
249 | #elif TARGET_OS_WIN32 |
250 | ||
251 | # define WINVER 0x0501 // target Windows XP and later | |
252 | # define _WIN32_WINNT 0x0501 // target Windows XP and later | |
253 | # define WIN32_LEAN_AND_MEAN | |
254 | // hack: windef.h typedefs BOOL as int | |
255 | # define BOOL WINBOOL | |
256 | # include <windows.h> | |
257 | # undef BOOL | |
258 | ||
259 | # include <stdio.h> | |
260 | # include <stdlib.h> | |
261 | # include <stdint.h> | |
262 | # include <stdarg.h> | |
263 | # include <string.h> | |
264 | # include <assert.h> | |
265 | # include <malloc.h> | |
7257e56c | 266 | # include <Availability.h> |
7af964d1 A |
267 | |
268 | # if __cplusplus | |
269 | # include <vector> | |
270 | # include <algorithm> | |
cd5f04f5 | 271 | # include <functional> |
7af964d1 | 272 | using namespace std; |
7af964d1 A |
273 | # define __BEGIN_DECLS extern "C" { |
274 | # define __END_DECLS } | |
275 | # else | |
276 | # define __BEGIN_DECLS /*empty*/ | |
277 | # define __END_DECLS /*empty*/ | |
278 | # endif | |
279 | ||
8972963c | 280 | # define PRIVATE_EXTERN |
7af964d1 A |
281 | # define __attribute__(x) |
282 | # define inline __inline | |
283 | ||
8972963c A |
284 | /* Use this for functions that are intended to be breakpoint hooks. |
285 | If you do not, the compiler may optimize them away. | |
286 | BREAKPOINT_FUNCTION( void MyBreakpointFunction(void) ); */ | |
287 | # define BREAKPOINT_FUNCTION(prototype) \ | |
288 | __declspec(noinline) prototype { __asm { } } | |
289 | ||
7af964d1 A |
290 | /* stub out dtrace probes */ |
291 | # define OBJC_RUNTIME_OBJC_EXCEPTION_RETHROW() do {} while(0) | |
292 | # define OBJC_RUNTIME_OBJC_EXCEPTION_THROW(arg0) do {} while(0) | |
293 | ||
294 | #else | |
295 | # error unknown OS | |
296 | #endif | |
297 | ||
298 | ||
299 | #include <objc/objc.h> | |
300 | #include <objc/objc-api.h> | |
301 | ||
302 | __BEGIN_DECLS | |
303 | ||
304 | extern void _objc_fatal(const char *fmt, ...) __attribute__((noreturn, format (printf, 1, 2))); | |
305 | ||
306 | #define INIT_ONCE_PTR(var, create, delete) \ | |
307 | do { \ | |
308 | if (var) break; \ | |
309 | typeof(var) v = create; \ | |
310 | while (!var) { \ | |
8972963c | 311 | if (OSAtomicCompareAndSwapPtrBarrier(0, (void*)v, (void**)&var)){ \ |
7af964d1 A |
312 | goto done; \ |
313 | } \ | |
314 | } \ | |
315 | delete; \ | |
316 | done:; \ | |
317 | } while (0) | |
318 | ||
319 | #define INIT_ONCE_32(var, create, delete) \ | |
320 | do { \ | |
321 | if (var) break; \ | |
322 | typeof(var) v = create; \ | |
323 | while (!var) { \ | |
324 | if (OSAtomicCompareAndSwap32Barrier(0, v, (volatile int32_t *)&var)) { \ | |
325 | goto done; \ | |
326 | } \ | |
327 | } \ | |
328 | delete; \ | |
329 | done:; \ | |
330 | } while (0) | |
331 | ||
332 | ||
333 | // Thread keys reserved by libc for our use. | |
334 | // Keys [0..4] are used by autozone. | |
335 | #if defined(__PTK_FRAMEWORK_OBJC_KEY5) | |
8972963c | 336 | # define SUPPORT_DIRECT_THREAD_KEYS 1 |
7af964d1 A |
337 | # define TLS_DIRECT_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY5) |
338 | # define SYNC_DATA_DIRECT_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY6) | |
339 | # define SYNC_COUNT_DIRECT_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY7) | |
8972963c A |
340 | # define AUTORELEASE_POOL_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY8) |
341 | # if SUPPORT_RETURN_AUTORELEASE | |
342 | # define AUTORELEASE_POOL_RECLAIM_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY9) | |
343 | # endif | |
7af964d1 | 344 | #else |
8972963c | 345 | # define SUPPORT_DIRECT_THREAD_KEYS 0 |
7af964d1 A |
346 | #endif |
347 | ||
348 | ||
349 | #if TARGET_OS_WIN32 | |
350 | ||
351 | // Compiler compatibility | |
352 | ||
353 | // OS compatibility | |
354 | ||
355 | #define strdup _strdup | |
356 | ||
357 | #define issetugid() 0 | |
358 | ||
359 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) | |
360 | ||
361 | static __inline void bcopy(const void *src, void *dst, size_t size) { memcpy(dst, src, size); } | |
362 | static __inline void bzero(void *dst, size_t size) { memset(dst, 0, size); } | |
363 | ||
364 | int asprintf(char **dstp, const char *format, ...); | |
365 | ||
366 | typedef void * malloc_zone_t; | |
367 | ||
368 | static __inline malloc_zone_t malloc_default_zone(void) { return (malloc_zone_t)-1; } | |
369 | static __inline void *malloc_zone_malloc(malloc_zone_t z, size_t size) { return malloc(size); } | |
370 | static __inline void *malloc_zone_calloc(malloc_zone_t z, size_t size, size_t count) { return calloc(size, count); } | |
371 | static __inline void *malloc_zone_realloc(malloc_zone_t z, void *p, size_t size) { return realloc(p, size); } | |
372 | static __inline void malloc_zone_free(malloc_zone_t z, void *p) { free(p); } | |
373 | static __inline malloc_zone_t malloc_zone_from_ptr(const void *p) { return (malloc_zone_t)-1; } | |
374 | static __inline size_t malloc_size(const void *p) { return _msize((void*)p); /* fixme invalid pointer check? */ } | |
375 | ||
376 | ||
377 | // AssertMacros | |
378 | ||
379 | #define require_action_string(cond, dest, act, msg) do { if (!(cond)) { { act; } goto dest; } } while (0) | |
380 | #define require_noerr_string(err, dest, msg) do { if (err) goto dest; } while (0) | |
381 | #define require_string(cond, dest, msg) do { if (!(cond)) goto dest; } while (0) | |
382 | ||
383 | ||
384 | // OSAtomic | |
385 | ||
386 | static __inline BOOL OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst) | |
387 | { | |
388 | // fixme barrier is overkill | |
389 | long original = InterlockedCompareExchange(dst, newl, oldl); | |
390 | return (original == oldl); | |
391 | } | |
392 | ||
393 | static __inline BOOL OSAtomicCompareAndSwapPtrBarrier(void *oldp, void *newp, void * volatile *dst) | |
394 | { | |
395 | void *original = InterlockedCompareExchangePointer(dst, newp, oldp); | |
396 | return (original == oldp); | |
397 | } | |
398 | ||
399 | static __inline BOOL OSAtomicCompareAndSwap32Barrier(int32_t oldl, int32_t newl, int32_t volatile *dst) | |
400 | { | |
401 | long original = InterlockedCompareExchange((volatile long *)dst, newl, oldl); | |
402 | return (original == oldl); | |
403 | } | |
404 | ||
405 | static __inline int32_t OSAtomicDecrement32Barrier(volatile int32_t *dst) | |
406 | { | |
407 | return InterlockedDecrement((volatile long *)dst); | |
408 | } | |
409 | ||
410 | static __inline int32_t OSAtomicIncrement32Barrier(volatile int32_t *dst) | |
411 | { | |
412 | return InterlockedIncrement((volatile long *)dst); | |
413 | } | |
414 | ||
415 | ||
416 | // Internal data types | |
417 | ||
418 | typedef DWORD objc_thread_t; // thread ID | |
419 | static __inline int thread_equal(objc_thread_t t1, objc_thread_t t2) { | |
420 | return t1 == t2; | |
421 | } | |
422 | static __inline objc_thread_t thread_self(void) { | |
423 | return GetCurrentThreadId(); | |
424 | } | |
425 | ||
426 | typedef struct { | |
427 | DWORD key; | |
428 | void (*dtor)(void *); | |
429 | } tls_key_t; | |
8972963c | 430 | static __inline tls_key_t tls_create(void (*dtor)(void*)) { |
7af964d1 | 431 | // fixme need dtor registry for DllMain to call on thread detach |
8972963c A |
432 | tls_key_t k; |
433 | k.key = TlsAlloc(); | |
434 | k.dtor = dtor; | |
435 | return k; | |
7af964d1 A |
436 | } |
437 | static __inline void *tls_get(tls_key_t k) { | |
438 | return TlsGetValue(k.key); | |
439 | } | |
440 | static __inline void tls_set(tls_key_t k, void *value) { | |
441 | TlsSetValue(k.key, value); | |
442 | } | |
443 | ||
444 | typedef struct { | |
445 | CRITICAL_SECTION *lock; | |
446 | } mutex_t; | |
447 | #define MUTEX_INITIALIZER {0}; | |
448 | extern void mutex_init(mutex_t *m); | |
449 | static __inline int _mutex_lock_nodebug(mutex_t *m) { | |
450 | // fixme error check | |
451 | if (!m->lock) { | |
452 | mutex_init(m); | |
453 | } | |
454 | EnterCriticalSection(m->lock); | |
455 | return 0; | |
456 | } | |
cd5f04f5 | 457 | static __inline bool _mutex_try_lock_nodebug(mutex_t *m) { |
7af964d1 A |
458 | // fixme error check |
459 | if (!m->lock) { | |
460 | mutex_init(m); | |
461 | } | |
462 | return TryEnterCriticalSection(m->lock); | |
463 | } | |
464 | static __inline int _mutex_unlock_nodebug(mutex_t *m) { | |
465 | // fixme error check | |
466 | LeaveCriticalSection(m->lock); | |
467 | return 0; | |
468 | } | |
469 | ||
470 | ||
7257e56c A |
471 | typedef mutex_t spinlock_t; |
472 | #define spinlock_lock(l) mutex_lock(l) | |
473 | #define spinlock_unlock(l) mutex_unlock(l) | |
474 | #define SPINLOCK_INITIALIZER MUTEX_INITIALIZER | |
7af964d1 A |
475 | |
476 | ||
477 | typedef struct { | |
478 | HANDLE mutex; | |
479 | } recursive_mutex_t; | |
480 | #define RECURSIVE_MUTEX_INITIALIZER {0}; | |
481 | #define RECURSIVE_MUTEX_NOT_LOCKED 1 | |
482 | extern void recursive_mutex_init(recursive_mutex_t *m); | |
483 | static __inline int _recursive_mutex_lock_nodebug(recursive_mutex_t *m) { | |
484 | assert(m->mutex); | |
485 | return WaitForSingleObject(m->mutex, INFINITE); | |
486 | } | |
cd5f04f5 | 487 | static __inline bool _recursive_mutex_try_lock_nodebug(recursive_mutex_t *m) { |
7af964d1 A |
488 | assert(m->mutex); |
489 | return (WAIT_OBJECT_0 == WaitForSingleObject(m->mutex, 0)); | |
490 | } | |
491 | static __inline int _recursive_mutex_unlock_nodebug(recursive_mutex_t *m) { | |
492 | assert(m->mutex); | |
493 | return ReleaseMutex(m->mutex) ? 0 : RECURSIVE_MUTEX_NOT_LOCKED; | |
494 | } | |
495 | ||
496 | ||
497 | /* | |
498 | typedef HANDLE mutex_t; | |
499 | static inline void mutex_init(HANDLE *m) { *m = CreateMutex(NULL, FALSE, NULL); } | |
500 | static inline void _mutex_lock(mutex_t *m) { WaitForSingleObject(*m, INFINITE); } | |
cd5f04f5 | 501 | static inline bool mutex_try_lock(mutex_t *m) { return WaitForSingleObject(*m, 0) == WAIT_OBJECT_0; } |
7af964d1 A |
502 | static inline void _mutex_unlock(mutex_t *m) { ReleaseMutex(*m); } |
503 | */ | |
504 | ||
505 | // based on http://www.cs.wustl.edu/~schmidt/win32-cv-1.html | |
506 | // Vista-only CONDITION_VARIABLE would be better | |
507 | typedef struct { | |
508 | HANDLE mutex; | |
509 | HANDLE waiters; // semaphore for those in cond_wait() | |
510 | HANDLE waitersDone; // auto-reset event after everyone gets a broadcast | |
511 | CRITICAL_SECTION waitCountLock; // guards waitCount and didBroadcast | |
512 | unsigned int waitCount; | |
513 | int didBroadcast; | |
514 | } monitor_t; | |
515 | #define MONITOR_INITIALIZER { 0 } | |
516 | #define MONITOR_NOT_ENTERED 1 | |
517 | extern int monitor_init(monitor_t *c); | |
518 | ||
519 | static inline int _monitor_enter_nodebug(monitor_t *c) { | |
520 | if (!c->mutex) { | |
521 | int err = monitor_init(c); | |
522 | if (err) return err; | |
523 | } | |
524 | return WaitForSingleObject(c->mutex, INFINITE); | |
525 | } | |
526 | static inline int _monitor_exit_nodebug(monitor_t *c) { | |
527 | if (!ReleaseMutex(c->mutex)) return MONITOR_NOT_ENTERED; | |
528 | else return 0; | |
529 | } | |
530 | static inline int _monitor_wait_nodebug(monitor_t *c) { | |
531 | int last; | |
532 | EnterCriticalSection(&c->waitCountLock); | |
533 | c->waitCount++; | |
534 | LeaveCriticalSection(&c->waitCountLock); | |
535 | ||
536 | SignalObjectAndWait(c->mutex, c->waiters, INFINITE, FALSE); | |
537 | ||
538 | EnterCriticalSection(&c->waitCountLock); | |
539 | c->waitCount--; | |
540 | last = c->didBroadcast && c->waitCount == 0; | |
541 | LeaveCriticalSection(&c->waitCountLock); | |
542 | ||
543 | if (last) { | |
544 | // tell broadcaster that all waiters have awoken | |
545 | SignalObjectAndWait(c->waitersDone, c->mutex, INFINITE, FALSE); | |
546 | } else { | |
547 | WaitForSingleObject(c->mutex, INFINITE); | |
548 | } | |
549 | ||
550 | // fixme error checking | |
551 | return 0; | |
552 | } | |
553 | static inline int monitor_notify(monitor_t *c) { | |
554 | int haveWaiters; | |
555 | ||
556 | EnterCriticalSection(&c->waitCountLock); | |
557 | haveWaiters = c->waitCount > 0; | |
558 | LeaveCriticalSection(&c->waitCountLock); | |
559 | ||
560 | if (haveWaiters) { | |
561 | ReleaseSemaphore(c->waiters, 1, 0); | |
562 | } | |
563 | ||
564 | // fixme error checking | |
565 | return 0; | |
566 | } | |
567 | static inline int monitor_notifyAll(monitor_t *c) { | |
568 | EnterCriticalSection(&c->waitCountLock); | |
569 | if (c->waitCount == 0) { | |
570 | LeaveCriticalSection(&c->waitCountLock); | |
571 | return 0; | |
572 | } | |
573 | c->didBroadcast = 1; | |
574 | ReleaseSemaphore(c->waiters, c->waitCount, 0); | |
575 | LeaveCriticalSection(&c->waitCountLock); | |
576 | ||
577 | // fairness: wait for everyone to move from waiters to mutex | |
578 | WaitForSingleObject(c->waitersDone, INFINITE); | |
579 | // not under waitCountLock, but still under mutex | |
580 | c->didBroadcast = 0; | |
581 | ||
582 | // fixme error checking | |
583 | return 0; | |
584 | } | |
585 | ||
586 | ||
587 | // fixme no rwlock yet | |
588 | ||
589 | #define rwlock_t mutex_t | |
590 | #define rwlock_init(r) mutex_init(r) | |
591 | #define _rwlock_read_nodebug(m) _mutex_lock_nodebug(m) | |
592 | #define _rwlock_write_nodebug(m) _mutex_lock_nodebug(m) | |
593 | #define _rwlock_try_read_nodebug(m) _mutex_try_lock_nodebug(m) | |
594 | #define _rwlock_try_write_nodebug(m) _mutex_try_lock_nodebug(m) | |
595 | #define _rwlock_unlock_read_nodebug(m) _mutex_unlock_nodebug(m) | |
596 | #define _rwlock_unlock_write_nodebug(m) _mutex_unlock_nodebug(m) | |
597 | ||
598 | ||
7af964d1 A |
599 | typedef IMAGE_DOS_HEADER headerType; |
600 | // fixme YES bundle? NO bundle? sometimes? | |
601 | #define headerIsBundle(hi) YES | |
602 | OBJC_EXTERN IMAGE_DOS_HEADER __ImageBase; | |
603 | #define libobjc_header ((headerType *)&__ImageBase) | |
604 | ||
605 | // Prototypes | |
606 | ||
607 | ||
608 | #elif TARGET_OS_MAC | |
609 | ||
610 | ||
611 | // OS headers | |
8972963c A |
612 | #include <mach-o/loader.h> |
613 | #ifndef __LP64__ | |
614 | # define SEGMENT_CMD LC_SEGMENT | |
615 | #else | |
616 | # define SEGMENT_CMD LC_SEGMENT_64 | |
617 | #endif | |
618 | ||
619 | #ifndef VM_MEMORY_OBJC_DISPATCHERS | |
620 | # define VM_MEMORY_OBJC_DISPATCHERS 0 | |
621 | #endif | |
7af964d1 A |
622 | |
623 | ||
624 | // Compiler compatibility | |
625 | ||
626 | // OS compatibility | |
627 | ||
628 | // Internal data types | |
629 | ||
630 | typedef pthread_t objc_thread_t; | |
631 | ||
632 | static __inline int thread_equal(objc_thread_t t1, objc_thread_t t2) { | |
633 | return pthread_equal(t1, t2); | |
634 | } | |
635 | static __inline objc_thread_t thread_self(void) { | |
636 | return pthread_self(); | |
637 | } | |
638 | ||
639 | ||
640 | typedef pthread_key_t tls_key_t; | |
641 | ||
8972963c A |
642 | static inline tls_key_t tls_create(void (*dtor)(void*)) { |
643 | tls_key_t k; | |
644 | pthread_key_create(&k, dtor); | |
645 | return k; | |
7af964d1 A |
646 | } |
647 | static inline void *tls_get(tls_key_t k) { | |
648 | return pthread_getspecific(k); | |
649 | } | |
650 | static inline void tls_set(tls_key_t k, void *value) { | |
651 | pthread_setspecific(k, value); | |
652 | } | |
653 | ||
8972963c | 654 | #if SUPPORT_DIRECT_THREAD_KEYS |
cd5f04f5 A |
655 | |
656 | #if !NDEBUG | |
657 | static bool is_valid_direct_key(tls_key_t k) { | |
658 | return ( k == SYNC_DATA_DIRECT_KEY | |
659 | || k == SYNC_COUNT_DIRECT_KEY | |
660 | || k == AUTORELEASE_POOL_KEY | |
661 | # if SUPPORT_RETURN_AUTORELEASE | |
662 | || k == AUTORELEASE_POOL_RECLAIM_KEY | |
663 | # endif | |
664 | ); | |
665 | } | |
666 | #endif | |
667 | ||
668 | #if __arm__ | |
669 | ||
670 | // rdar://9162780 _pthread_get/setspecific_direct are inefficient | |
671 | // copied from libdispatch | |
672 | ||
8070259c A |
673 | __attribute__((const)) |
674 | static ALWAYS_INLINE void** | |
cd5f04f5 A |
675 | tls_base(void) |
676 | { | |
677 | uintptr_t p; | |
678 | #if defined(__arm__) && defined(_ARM_ARCH_6) | |
679 | __asm__("mrc p15, 0, %[p], c13, c0, 3" : [p] "=&r" (p)); | |
680 | return (void**)(p & ~0x3ul); | |
681 | #else | |
682 | #error tls_base not implemented | |
683 | #endif | |
684 | } | |
685 | ||
8070259c A |
686 | |
687 | static ALWAYS_INLINE void | |
cd5f04f5 A |
688 | tls_set_direct(void **tsdb, tls_key_t k, void *v) |
689 | { | |
690 | assert(is_valid_direct_key(k)); | |
691 | ||
692 | tsdb[k] = v; | |
693 | } | |
694 | #define tls_set_direct(k, v) \ | |
695 | tls_set_direct(tls_base(), (k), (v)) | |
696 | ||
8070259c A |
697 | |
698 | static ALWAYS_INLINE void * | |
cd5f04f5 A |
699 | tls_get_direct(void **tsdb, tls_key_t k) |
700 | { | |
701 | assert(is_valid_direct_key(k)); | |
702 | ||
703 | return tsdb[k]; | |
704 | } | |
705 | #define tls_get_direct(k) \ | |
706 | tls_get_direct(tls_base(), (k)) | |
707 | ||
708 | // arm | |
709 | #else | |
710 | // not arm | |
711 | ||
7af964d1 A |
712 | static inline void *tls_get_direct(tls_key_t k) |
713 | { | |
cd5f04f5 | 714 | assert(is_valid_direct_key(k)); |
7af964d1 A |
715 | |
716 | if (_pthread_has_direct_tsd()) { | |
717 | return _pthread_getspecific_direct(k); | |
718 | } else { | |
719 | return pthread_getspecific(k); | |
720 | } | |
721 | } | |
722 | static inline void tls_set_direct(tls_key_t k, void *value) | |
723 | { | |
cd5f04f5 | 724 | assert(is_valid_direct_key(k)); |
7af964d1 A |
725 | |
726 | if (_pthread_has_direct_tsd()) { | |
727 | _pthread_setspecific_direct(k, value); | |
728 | } else { | |
729 | pthread_setspecific(k, value); | |
730 | } | |
731 | } | |
cd5f04f5 A |
732 | |
733 | // not arm | |
734 | #endif | |
735 | ||
736 | // SUPPORT_DIRECT_THREAD_KEYS | |
7af964d1 A |
737 | #endif |
738 | ||
739 | ||
740 | typedef pthread_mutex_t mutex_t; | |
741 | #define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER; | |
742 | ||
7af964d1 | 743 | static inline int _mutex_lock_nodebug(mutex_t *m) { |
7af964d1 A |
744 | return pthread_mutex_lock(m); |
745 | } | |
cd5f04f5 | 746 | static inline bool _mutex_try_lock_nodebug(mutex_t *m) { |
7af964d1 A |
747 | return !pthread_mutex_trylock(m); |
748 | } | |
749 | static inline int _mutex_unlock_nodebug(mutex_t *m) { | |
7af964d1 A |
750 | return pthread_mutex_unlock(m); |
751 | } | |
752 | ||
753 | ||
754 | typedef struct { | |
755 | pthread_mutex_t *mutex; | |
756 | } recursive_mutex_t; | |
757 | #define RECURSIVE_MUTEX_INITIALIZER {0}; | |
758 | #define RECURSIVE_MUTEX_NOT_LOCKED EPERM | |
759 | extern void recursive_mutex_init(recursive_mutex_t *m); | |
760 | ||
761 | static inline int _recursive_mutex_lock_nodebug(recursive_mutex_t *m) { | |
762 | assert(m->mutex); | |
7af964d1 A |
763 | return pthread_mutex_lock(m->mutex); |
764 | } | |
cd5f04f5 | 765 | static inline bool _recursive_mutex_try_lock_nodebug(recursive_mutex_t *m) { |
7af964d1 | 766 | assert(m->mutex); |
7af964d1 A |
767 | return !pthread_mutex_trylock(m->mutex); |
768 | } | |
769 | static inline int _recursive_mutex_unlock_nodebug(recursive_mutex_t *m) { | |
770 | assert(m->mutex); | |
7af964d1 A |
771 | return pthread_mutex_unlock(m->mutex); |
772 | } | |
773 | ||
774 | ||
775 | typedef struct { | |
776 | pthread_mutex_t mutex; | |
777 | pthread_cond_t cond; | |
778 | } monitor_t; | |
779 | #define MONITOR_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER } | |
780 | #define MONITOR_NOT_ENTERED EPERM | |
781 | ||
782 | static inline int monitor_init(monitor_t *c) { | |
783 | int err = pthread_mutex_init(&c->mutex, NULL); | |
784 | if (err) return err; | |
785 | err = pthread_cond_init(&c->cond, NULL); | |
786 | if (err) { | |
787 | pthread_mutex_destroy(&c->mutex); | |
788 | return err; | |
789 | } | |
790 | return 0; | |
791 | } | |
792 | static inline int _monitor_enter_nodebug(monitor_t *c) { | |
7af964d1 A |
793 | return pthread_mutex_lock(&c->mutex); |
794 | } | |
795 | static inline int _monitor_exit_nodebug(monitor_t *c) { | |
796 | return pthread_mutex_unlock(&c->mutex); | |
797 | } | |
798 | static inline int _monitor_wait_nodebug(monitor_t *c) { | |
799 | return pthread_cond_wait(&c->cond, &c->mutex); | |
800 | } | |
801 | static inline int monitor_notify(monitor_t *c) { | |
802 | return pthread_cond_signal(&c->cond); | |
803 | } | |
804 | static inline int monitor_notifyAll(monitor_t *c) { | |
805 | return pthread_cond_broadcast(&c->cond); | |
806 | } | |
807 | ||
808 | ||
809 | // semaphore_create formatted for INIT_ONCE use | |
810 | static inline semaphore_t create_semaphore(void) | |
811 | { | |
812 | semaphore_t sem; | |
813 | kern_return_t k; | |
814 | k = semaphore_create(mach_task_self(), &sem, SYNC_POLICY_FIFO, 0); | |
815 | if (k) _objc_fatal("semaphore_create failed (0x%x)", k); | |
816 | return sem; | |
817 | } | |
818 | ||
819 | ||
820 | /* Custom read-write lock | |
821 | - reader is atomic add/subtract | |
822 | - writer is pthread mutex plus atomic add/subtract | |
823 | - fairness: new readers wait if a writer wants in | |
824 | - fairness: when writer completes, readers (probably) precede new writer | |
825 | ||
826 | state: xxxxxxxx xxxxxxxx yyyyyyyy yyyyyyyz | |
827 | x: blocked reader count | |
828 | y: active reader count | |
829 | z: readers allowed flag | |
830 | */ | |
831 | typedef struct { | |
cd5f04f5 | 832 | pthread_rwlock_t rwl; |
7af964d1 A |
833 | } rwlock_t; |
834 | ||
7af964d1 A |
835 | static inline void rwlock_init(rwlock_t *l) |
836 | { | |
cd5f04f5 A |
837 | int err __unused = pthread_rwlock_init(&l->rwl, NULL); |
838 | assert(err == 0); | |
7af964d1 A |
839 | } |
840 | ||
841 | static inline void _rwlock_read_nodebug(rwlock_t *l) | |
842 | { | |
cd5f04f5 A |
843 | int err __unused = pthread_rwlock_rdlock(&l->rwl); |
844 | assert(err == 0); | |
7af964d1 A |
845 | } |
846 | ||
847 | static inline void _rwlock_unlock_read_nodebug(rwlock_t *l) | |
848 | { | |
cd5f04f5 A |
849 | int err __unused = pthread_rwlock_unlock(&l->rwl); |
850 | assert(err == 0); | |
7af964d1 A |
851 | } |
852 | ||
853 | ||
cd5f04f5 | 854 | static inline bool _rwlock_try_read_nodebug(rwlock_t *l) |
7af964d1 | 855 | { |
cd5f04f5 A |
856 | int err = pthread_rwlock_tryrdlock(&l->rwl); |
857 | assert(err == 0 || err == EBUSY); | |
858 | return (err == 0); | |
7af964d1 A |
859 | } |
860 | ||
861 | ||
862 | static inline void _rwlock_write_nodebug(rwlock_t *l) | |
863 | { | |
cd5f04f5 A |
864 | int err __unused = pthread_rwlock_wrlock(&l->rwl); |
865 | assert(err == 0); | |
7af964d1 A |
866 | } |
867 | ||
868 | static inline void _rwlock_unlock_write_nodebug(rwlock_t *l) | |
869 | { | |
cd5f04f5 A |
870 | int err __unused = pthread_rwlock_unlock(&l->rwl); |
871 | assert(err == 0); | |
7af964d1 A |
872 | } |
873 | ||
cd5f04f5 | 874 | static inline bool _rwlock_try_write_nodebug(rwlock_t *l) |
7af964d1 | 875 | { |
cd5f04f5 A |
876 | int err = pthread_rwlock_trywrlock(&l->rwl); |
877 | assert(err == 0 || err == EBUSY); | |
878 | return (err == 0); | |
7af964d1 A |
879 | } |
880 | ||
881 | ||
882 | #ifndef __LP64__ | |
883 | typedef struct mach_header headerType; | |
884 | typedef struct segment_command segmentType; | |
885 | typedef struct section sectionType; | |
886 | #else | |
887 | typedef struct mach_header_64 headerType; | |
888 | typedef struct segment_command_64 segmentType; | |
889 | typedef struct section_64 sectionType; | |
890 | #endif | |
891 | #define headerIsBundle(hi) (hi->mhdr->filetype == MH_BUNDLE) | |
892 | #define libobjc_header ((headerType *)&_mh_dylib_header) | |
893 | ||
7af964d1 A |
894 | // Prototypes |
895 | ||
896 | /* Secure /tmp usage */ | |
897 | extern int secure_open(const char *filename, int flags, uid_t euid); | |
898 | ||
899 | ||
900 | #else | |
901 | ||
902 | ||
903 | #error unknown OS | |
904 | ||
905 | ||
906 | #endif | |
907 | ||
908 | __END_DECLS | |
909 | ||
910 | #endif |