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
13static inline void
14test_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*/
28static void
29asynchronous_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
43int
44pthread_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
62int
63pthread_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
90int
91pthread_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
119void
120pthread_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