xref: /haiku/src/system/libroot/posix/pthread/pthread_cancel.cpp (revision 344ded80d400028c8f561b4b876257b94c12db4a)
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(PTHREAD_CANCELED);
134 }
135