1 /* 2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "pthread_private.h" 9 10 #include <syscalls.h> 11 12 13 static inline void 14 test_asynchronous_cancel(int32 flags) 15 { 16 static const int32 kFlags = THREAD_CANCELED | THREAD_CANCEL_ENABLED 17 | THREAD_CANCEL_ASYNCHRONOUS; 18 19 if ((~flags & kFlags) == 0) 20 pthread_exit(PTHREAD_CANCELED); 21 } 22 23 24 /*! Signal handler like function invoked when this thread has been canceled. 25 Has the simple signal handler signature, since it is invoked just like a 26 signal handler. 27 */ 28 static void 29 asynchronous_cancel_thread(int) 30 { 31 pthread_t thread = pthread_self(); 32 33 // Exit when asynchronous cancellation is enabled, otherwise we don't have 34 // to do anything -- the syscall interrupting side effect is all we need. 35 if ((atomic_get(&thread->flags) & THREAD_CANCEL_ASYNCHRONOUS) != 0) 36 pthread_exit(PTHREAD_CANCELED); 37 } 38 39 40 // #pragma mark - public API 41 42 43 int 44 pthread_cancel(pthread_t thread) 45 { 46 // set the canceled flag 47 int32 oldFlags = atomic_or(&thread->flags, THREAD_CANCELED); 48 49 // If the flag was already set, we're done. 50 if ((oldFlags & THREAD_CANCELED) != 0) 51 return 0; 52 53 // If cancellation is enabled, notify the thread. This will call the 54 // asynchronous_cancel_thread() handler. 55 if ((oldFlags & THREAD_CANCEL_ENABLED) != 0) 56 return _kern_cancel_thread(thread->id, &asynchronous_cancel_thread); 57 58 return 0; 59 } 60 61 62 int 63 pthread_setcancelstate(int state, int *_oldState) 64 { 65 pthread_thread* thread = pthread_self(); 66 if (thread == NULL) 67 return EINVAL; 68 69 // set the new flags 70 int32 oldFlags; 71 if (state == PTHREAD_CANCEL_ENABLE) { 72 oldFlags = atomic_or(&thread->flags, THREAD_CANCEL_ENABLED); 73 test_asynchronous_cancel(oldFlags | THREAD_CANCEL_ENABLED); 74 } else if (state == PTHREAD_CANCEL_DISABLE) { 75 oldFlags = atomic_and(&thread->flags, ~(int32)THREAD_CANCEL_ENABLED); 76 test_asynchronous_cancel(oldFlags); 77 } else 78 return EINVAL; 79 80 // return the old state 81 if (_oldState != NULL) { 82 *_oldState = (oldFlags & PTHREAD_CANCEL_ENABLE) != 0 83 ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE; 84 } 85 86 return 0; 87 } 88 89 90 int 91 pthread_setcanceltype(int type, int *_oldType) 92 { 93 pthread_thread* thread = pthread_self(); 94 if (thread == NULL) 95 return EINVAL; 96 97 // set the new type 98 int32 oldFlags; 99 if (type == PTHREAD_CANCEL_DEFERRED) { 100 oldFlags = atomic_and(&thread->flags, 101 ~(int32)THREAD_CANCEL_ASYNCHRONOUS); 102 test_asynchronous_cancel(oldFlags); 103 } else if (type == PTHREAD_CANCEL_ASYNCHRONOUS) { 104 oldFlags = atomic_or(&thread->flags, THREAD_CANCEL_ASYNCHRONOUS); 105 test_asynchronous_cancel(oldFlags | THREAD_CANCEL_ASYNCHRONOUS); 106 } else 107 return EINVAL; 108 109 // return the old type 110 if (_oldType != NULL) { 111 *_oldType = (oldFlags & THREAD_CANCEL_ASYNCHRONOUS) != 0 112 ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED; 113 } 114 115 return 0; 116 } 117 118 119 void 120 pthread_testcancel(void) 121 { 122 pthread_thread* thread = pthread_self(); 123 if (thread == NULL) 124 return; 125 126 static const int32 kFlags = THREAD_CANCELED | THREAD_CANCEL_ENABLED; 127 128 if ((~atomic_get(&thread->flags) & kFlags) == 0) 129 pthread_exit(NULL); 130 } 131