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 int result = _kern_cancel_thread(thread->id, &asynchronous_cancel_thread); 57 if (result == B_BAD_THREAD_ID) 58 return ESRCH; 59 return result; 60 } 61 62 return 0; 63 } 64 65 66 int 67 pthread_setcancelstate(int state, int *_oldState) 68 { 69 pthread_thread* thread = pthread_self(); 70 if (thread == NULL) 71 return EINVAL; 72 73 // set the new flags 74 int32 oldFlags; 75 if (state == PTHREAD_CANCEL_ENABLE) { 76 oldFlags = atomic_or(&thread->flags, THREAD_CANCEL_ENABLED); 77 test_asynchronous_cancel(oldFlags | THREAD_CANCEL_ENABLED); 78 } else if (state == PTHREAD_CANCEL_DISABLE) { 79 oldFlags = atomic_and(&thread->flags, ~(int32)THREAD_CANCEL_ENABLED); 80 test_asynchronous_cancel(oldFlags); 81 } else 82 return EINVAL; 83 84 // return the old state 85 if (_oldState != NULL) { 86 *_oldState = (oldFlags & THREAD_CANCEL_ENABLED) != 0 87 ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE; 88 } 89 90 return 0; 91 } 92 93 94 int 95 pthread_setcanceltype(int type, int *_oldType) 96 { 97 pthread_thread* thread = pthread_self(); 98 if (thread == NULL) 99 return EINVAL; 100 101 // set the new type 102 int32 oldFlags; 103 if (type == PTHREAD_CANCEL_DEFERRED) { 104 oldFlags = atomic_and(&thread->flags, 105 ~(int32)THREAD_CANCEL_ASYNCHRONOUS); 106 test_asynchronous_cancel(oldFlags); 107 } else if (type == PTHREAD_CANCEL_ASYNCHRONOUS) { 108 oldFlags = atomic_or(&thread->flags, THREAD_CANCEL_ASYNCHRONOUS); 109 test_asynchronous_cancel(oldFlags | THREAD_CANCEL_ASYNCHRONOUS); 110 } else 111 return EINVAL; 112 113 // return the old type 114 if (_oldType != NULL) { 115 *_oldType = (oldFlags & THREAD_CANCEL_ASYNCHRONOUS) != 0 116 ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED; 117 } 118 119 return 0; 120 } 121 122 123 void 124 pthread_testcancel(void) 125 { 126 pthread_thread* thread = pthread_self(); 127 if (thread == NULL) 128 return; 129 130 static const int32 kFlags = THREAD_CANCELED | THREAD_CANCEL_ENABLED; 131 132 if ((~atomic_get(&thread->flags) & kFlags) == 0) 133 pthread_exit(NULL); 134 } 135