xref: /haiku/src/system/libroot/posix/pthread/pthread_cancel.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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 & THREAD_CANCEL_ENABLED) != 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