1/*
2 * Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org.
3 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include <UserEvent.h>
9
10#include <ksignal.h>
11#include <thread_types.h>
12#include <util/AutoLock.h>
13
14
15// #pragma mark - UserEvent
16
17
18UserEvent::~UserEvent()
19{
20}
21
22
23// #pragma mark - SignalEvent
24
25
26struct SignalEvent::EventSignal : Signal {
27	EventSignal(uint32 number, int32 signalCode, int32 errorCode,
28		pid_t sendingProcess)
29		:
30		Signal(number, signalCode, errorCode, sendingProcess),
31		fInUse(0)
32	{
33	}
34
35	bool MarkUsed()
36	{
37		return atomic_get_and_set(&fInUse, 1) != 0;
38	}
39
40	void SetUnused()
41	{
42		// mark not-in-use
43		atomic_set(&fInUse, 0);
44	}
45
46	virtual void Handled()
47	{
48		SetUnused();
49
50		Signal::Handled();
51	}
52
53private:
54	int32				fInUse;
55};
56
57
58SignalEvent::SignalEvent(EventSignal* signal)
59	:
60	fSignal(signal),
61	fPendingDPC(0)
62{
63}
64
65
66SignalEvent::~SignalEvent()
67{
68	fSignal->ReleaseReference();
69}
70
71
72void
73SignalEvent::SetUserValue(union sigval userValue)
74{
75	fSignal->SetUserValue(userValue);
76}
77
78
79status_t
80SignalEvent::Fire()
81{
82	bool wasPending = atomic_get_and_set(&fPendingDPC, 1) != 0;
83	if (wasPending)
84		return B_BUSY;
85
86	if (fSignal->MarkUsed()) {
87		atomic_set(&fPendingDPC, 0);
88		return B_BUSY;
89	}
90
91	AcquireReference();
92	DPCQueue::DefaultQueue(B_NORMAL_PRIORITY)->Add(this);
93
94	return B_OK;
95}
96
97
98// #pragma mark - TeamSignalEvent
99
100
101TeamSignalEvent::TeamSignalEvent(Team* team, EventSignal* signal)
102	:
103	SignalEvent(signal),
104	fTeam(team)
105{
106}
107
108
109/*static*/ TeamSignalEvent*
110TeamSignalEvent::Create(Team* team, uint32 signalNumber, int32 signalCode,
111	int32 errorCode)
112{
113	// create the signal
114	EventSignal* signal = new(std::nothrow) EventSignal(signalNumber,
115		signalCode, errorCode, team->id);
116	if (signal == NULL)
117		return NULL;
118
119	// create the event
120	TeamSignalEvent* event = new TeamSignalEvent(team, signal);
121	if (event == NULL) {
122		delete signal;
123		return NULL;
124	}
125
126	return event;
127}
128
129
130status_t
131TeamSignalEvent::Fire()
132{
133	// We need a reference to the team to guarantee that it is still there when
134	// the DPC actually runs.
135	fTeam->AcquireReference();
136	status_t result = SignalEvent::Fire();
137	if (result != B_OK)
138		fTeam->ReleaseReference();
139
140	return result;
141}
142
143
144void
145TeamSignalEvent::DoDPC(DPCQueue* queue)
146{
147	fSignal->AcquireReference();
148		// one reference is transferred to send_signal_to_team_locked
149
150	InterruptsSpinLocker locker(fTeam->signal_lock);
151	status_t error = send_signal_to_team_locked(fTeam, fSignal->Number(),
152		fSignal, B_DO_NOT_RESCHEDULE);
153	locker.Unlock();
154	fTeam->ReleaseReference();
155
156	// There are situations (for certain signals), in which
157	// send_signal_to_team_locked() succeeds without queuing the signal.
158	if (error != B_OK || !fSignal->IsPending())
159		fSignal->SetUnused();
160
161	// We're no longer queued in the DPC queue, so we can be reused.
162	atomic_set(&fPendingDPC, 0);
163
164	ReleaseReference();
165}
166
167
168// #pragma mark - ThreadSignalEvent
169
170
171ThreadSignalEvent::ThreadSignalEvent(Thread* thread, EventSignal* signal)
172	:
173	SignalEvent(signal),
174	fThread(thread)
175{
176}
177
178
179/*static*/ ThreadSignalEvent*
180ThreadSignalEvent::Create(Thread* thread, uint32 signalNumber, int32 signalCode,
181	int32 errorCode, pid_t sendingTeam)
182{
183	// create the signal
184	EventSignal* signal = new(std::nothrow) EventSignal(signalNumber,
185		signalCode, errorCode, sendingTeam);
186	if (signal == NULL)
187		return NULL;
188
189	// create the event
190	ThreadSignalEvent* event = new ThreadSignalEvent(thread, signal);
191	if (event == NULL) {
192		delete signal;
193		return NULL;
194	}
195
196	return event;
197}
198
199
200status_t
201ThreadSignalEvent::Fire()
202{
203	// We need a reference to the thread to guarantee that it is still there
204	// when the DPC actually runs.
205	fThread->AcquireReference();
206	status_t result = SignalEvent::Fire();
207	if (result != B_OK)
208		fThread->ReleaseReference();
209
210	return result;
211}
212
213
214void
215ThreadSignalEvent::DoDPC(DPCQueue* queue)
216{
217	fSignal->AcquireReference();
218		// one reference is transferred to send_signal_to_team_locked
219	InterruptsReadSpinLocker teamLocker(fThread->team_lock);
220	SpinLocker locker(fThread->team->signal_lock);
221	status_t error = send_signal_to_thread_locked(fThread, fSignal->Number(),
222		fSignal, B_DO_NOT_RESCHEDULE);
223	locker.Unlock();
224	teamLocker.Unlock();
225	fThread->ReleaseReference();
226
227	// There are situations (for certain signals), in which
228	// send_signal_to_team_locked() succeeds without queuing the signal.
229	if (error != B_OK || !fSignal->IsPending())
230		fSignal->SetUnused();
231
232	// We're no longer queued in the DPC queue, so we can be reused.
233	atomic_set(&fPendingDPC, 0);
234
235	ReleaseReference();
236}
237
238
239// #pragma mark - UserEvent
240
241
242CreateThreadEvent::CreateThreadEvent(const ThreadCreationAttributes& attributes)
243	:
244	fCreationAttributes(attributes),
245	fPendingDPC(0)
246{
247	// attributes.name is a pointer to a temporary buffer. Copy the name into
248	// our own buffer and replace the name pointer.
249	strlcpy(fThreadName, attributes.name, sizeof(fThreadName));
250	fCreationAttributes.name = fThreadName;
251}
252
253
254/*static*/ CreateThreadEvent*
255CreateThreadEvent::Create(const ThreadCreationAttributes& attributes)
256{
257	return new(std::nothrow) CreateThreadEvent(attributes);
258}
259
260
261status_t
262CreateThreadEvent::Fire()
263{
264	bool wasPending = atomic_get_and_set(&fPendingDPC, 1) != 0;
265	if (wasPending)
266		return B_BUSY;
267
268	AcquireReference();
269	DPCQueue::DefaultQueue(B_NORMAL_PRIORITY)->Add(this);
270
271	return B_OK;
272}
273
274
275void
276CreateThreadEvent::DoDPC(DPCQueue* queue)
277{
278	// We're no longer queued in the DPC queue, so we can be reused.
279	atomic_set(&fPendingDPC, 0);
280
281	// create the thread
282	thread_id threadID = thread_create_thread(fCreationAttributes, false);
283	if (threadID >= 0)
284		resume_thread(threadID);
285
286	ReleaseReference();
287}
288