1/* 2 * David Leonard <d@openbsd.org>, 1999. Public domain. 3 * $FreeBSD$ 4 */ 5#include "namespace.h" 6#include <sys/errno.h> 7#include <pthread.h> 8#include "un-namespace.h" 9#include "thr_private.h" 10 11__weak_reference(_pthread_cancel, pthread_cancel); 12__weak_reference(_pthread_setcancelstate, pthread_setcancelstate); 13__weak_reference(_pthread_setcanceltype, pthread_setcanceltype); 14__weak_reference(_pthread_testcancel, pthread_testcancel); 15 16static inline int 17checkcancel(struct pthread *curthread) 18{ 19 if ((curthread->cancelflags & THR_CANCELLING) != 0) { 20 /* 21 * It is possible for this thread to be swapped out 22 * while performing cancellation; do not allow it 23 * to be cancelled again. 24 */ 25 if ((curthread->flags & THR_FLAGS_EXITING) != 0) { 26 /* 27 * This may happen once, but after this, it 28 * shouldn't happen again. 29 */ 30 curthread->cancelflags &= ~THR_CANCELLING; 31 return (0); 32 } 33 if ((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) { 34 curthread->cancelflags &= ~THR_CANCELLING; 35 return (1); 36 } 37 } 38 return (0); 39} 40 41static inline void 42testcancel(struct pthread *curthread) 43{ 44 if (checkcancel(curthread) != 0) { 45 /* Unlock before exiting: */ 46 THR_THREAD_UNLOCK(curthread, curthread); 47 48 _thr_exit_cleanup(); 49 _pthread_exit(PTHREAD_CANCELED); 50 PANIC("cancel"); 51 } 52} 53 54int 55_pthread_cancel(pthread_t pthread) 56{ 57 struct pthread *curthread = _get_curthread(); 58 struct pthread *joinee = NULL; 59 struct kse_mailbox *kmbx = NULL; 60 int ret; 61 62 if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) { 63 /* 64 * Take the thread's lock while we change the cancel flags. 65 */ 66 THR_THREAD_LOCK(curthread, pthread); 67 THR_SCHED_LOCK(curthread, pthread); 68 if (pthread->flags & THR_FLAGS_EXITING) { 69 THR_SCHED_UNLOCK(curthread, pthread); 70 THR_THREAD_UNLOCK(curthread, pthread); 71 _thr_ref_delete(curthread, pthread); 72 return (ESRCH); 73 } 74 if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) || 75 (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && 76 ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0))) 77 /* Just mark it for cancellation: */ 78 pthread->cancelflags |= THR_CANCELLING; 79 else { 80 /* 81 * Check if we need to kick it back into the 82 * run queue: 83 */ 84 switch (pthread->state) { 85 case PS_RUNNING: 86 /* No need to resume: */ 87 pthread->cancelflags |= THR_CANCELLING; 88 break; 89 90 case PS_LOCKWAIT: 91 /* 92 * These can't be removed from the queue. 93 * Just mark it as cancelling and tell it 94 * to yield once it leaves the critical 95 * region. 96 */ 97 pthread->cancelflags |= THR_CANCELLING; 98 pthread->critical_yield = 1; 99 break; 100 101 case PS_SLEEP_WAIT: 102 case PS_SIGSUSPEND: 103 case PS_SIGWAIT: 104 /* Interrupt and resume: */ 105 pthread->interrupted = 1; 106 pthread->cancelflags |= THR_CANCELLING; 107 kmbx = _thr_setrunnable_unlocked(pthread); 108 break; 109 110 case PS_JOIN: 111 /* Disconnect the thread from the joinee: */ 112 joinee = pthread->join_status.thread; 113 pthread->join_status.thread = NULL; 114 pthread->cancelflags |= THR_CANCELLING; 115 kmbx = _thr_setrunnable_unlocked(pthread); 116 if ((joinee != NULL) && 117 (pthread->kseg == joinee->kseg)) { 118 /* Remove the joiner from the joinee. */ 119 joinee->joiner = NULL; 120 joinee = NULL; 121 } 122 break; 123 124 case PS_SUSPENDED: 125 case PS_MUTEX_WAIT: 126 case PS_COND_WAIT: 127 /* 128 * Threads in these states may be in queues. 129 * In order to preserve queue integrity, the 130 * cancelled thread must remove itself from the 131 * queue. Mark the thread as interrupted and 132 * needing cancellation, and set the state to 133 * running. When the thread resumes, it will 134 * remove itself from the queue and call the 135 * cancellation completion routine. 136 */ 137 pthread->interrupted = 1; 138 pthread->cancelflags |= THR_CANCEL_NEEDED; 139 kmbx = _thr_setrunnable_unlocked(pthread); 140 pthread->continuation = 141 _thr_finish_cancellation; 142 break; 143 144 case PS_DEAD: 145 case PS_DEADLOCK: 146 case PS_STATE_MAX: 147 /* Ignore - only here to silence -Wall: */ 148 break; 149 } 150 if ((pthread->cancelflags & THR_AT_CANCEL_POINT) && 151 (pthread->blocked != 0 || 152 pthread->attr.flags & PTHREAD_SCOPE_SYSTEM)) 153 kse_thr_interrupt(&pthread->tcb->tcb_tmbx, 154 KSE_INTR_INTERRUPT, 0); 155 } 156 157 /* 158 * Release the thread's lock and remove the 159 * reference: 160 */ 161 THR_SCHED_UNLOCK(curthread, pthread); 162 THR_THREAD_UNLOCK(curthread, pthread); 163 _thr_ref_delete(curthread, pthread); 164 if (kmbx != NULL) 165 kse_wakeup(kmbx); 166 167 if ((joinee != NULL) && 168 (_thr_ref_add(curthread, joinee, /* include dead */1) == 0)) { 169 /* Remove the joiner from the joinee. */ 170 THR_SCHED_LOCK(curthread, joinee); 171 joinee->joiner = NULL; 172 THR_SCHED_UNLOCK(curthread, joinee); 173 _thr_ref_delete(curthread, joinee); 174 } 175 } 176 return (ret); 177} 178 179int 180_pthread_setcancelstate(int state, int *oldstate) 181{ 182 struct pthread *curthread = _get_curthread(); 183 int ostate; 184 int ret; 185 int need_exit = 0; 186 187 /* Take the thread's lock while fiddling with the state: */ 188 THR_THREAD_LOCK(curthread, curthread); 189 190 ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE; 191 192 switch (state) { 193 case PTHREAD_CANCEL_ENABLE: 194 curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE; 195 if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0) 196 need_exit = checkcancel(curthread); 197 ret = 0; 198 break; 199 case PTHREAD_CANCEL_DISABLE: 200 curthread->cancelflags |= PTHREAD_CANCEL_DISABLE; 201 ret = 0; 202 break; 203 default: 204 ret = EINVAL; 205 } 206 207 THR_THREAD_UNLOCK(curthread, curthread); 208 if (need_exit != 0) { 209 _thr_exit_cleanup(); 210 _pthread_exit(PTHREAD_CANCELED); 211 PANIC("cancel"); 212 } 213 if (ret == 0 && oldstate != NULL) 214 *oldstate = ostate; 215 216 return (ret); 217} 218 219int 220_pthread_setcanceltype(int type, int *oldtype) 221{ 222 struct pthread *curthread = _get_curthread(); 223 int otype; 224 int ret; 225 int need_exit = 0; 226 227 /* Take the thread's lock while fiddling with the state: */ 228 THR_THREAD_LOCK(curthread, curthread); 229 230 otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS; 231 switch (type) { 232 case PTHREAD_CANCEL_ASYNCHRONOUS: 233 curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS; 234 need_exit = checkcancel(curthread); 235 ret = 0; 236 break; 237 case PTHREAD_CANCEL_DEFERRED: 238 curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS; 239 ret = 0; 240 break; 241 default: 242 ret = EINVAL; 243 } 244 245 THR_THREAD_UNLOCK(curthread, curthread); 246 if (need_exit != 0) { 247 _thr_exit_cleanup(); 248 _pthread_exit(PTHREAD_CANCELED); 249 PANIC("cancel"); 250 } 251 if (ret == 0 && oldtype != NULL) 252 *oldtype = otype; 253 254 return (ret); 255} 256 257void 258_pthread_testcancel(void) 259{ 260 struct pthread *curthread = _get_curthread(); 261 262 THR_THREAD_LOCK(curthread, curthread); 263 testcancel(curthread); 264 THR_THREAD_UNLOCK(curthread, curthread); 265} 266 267void 268_thr_cancel_enter(struct pthread *thread) 269{ 270 /* Look for a cancellation before we block: */ 271 THR_THREAD_LOCK(thread, thread); 272 testcancel(thread); 273 thread->cancelflags |= THR_AT_CANCEL_POINT; 274 THR_THREAD_UNLOCK(thread, thread); 275} 276 277void 278_thr_cancel_leave(struct pthread *thread, int check) 279{ 280 THR_THREAD_LOCK(thread, thread); 281 thread->cancelflags &= ~THR_AT_CANCEL_POINT; 282 /* Look for a cancellation after we unblock: */ 283 if (check) 284 testcancel(thread); 285 THR_THREAD_UNLOCK(thread, thread); 286} 287 288void 289_thr_finish_cancellation(void *arg __unused) 290{ 291 struct pthread *curthread = _get_curthread(); 292 293 curthread->continuation = NULL; 294 curthread->interrupted = 0; 295 296 THR_THREAD_LOCK(curthread, curthread); 297 if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) { 298 curthread->cancelflags &= ~THR_CANCEL_NEEDED; 299 THR_THREAD_UNLOCK(curthread, curthread); 300 _thr_exit_cleanup(); 301 _pthread_exit(PTHREAD_CANCELED); 302 } 303 THR_THREAD_UNLOCK(curthread, curthread); 304} 305