xref: /haiku/src/system/kernel/UserEvent.cpp (revision 909af08f4328301fbdef1ffb41f566c3b5bec0c7)
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 
18 UserEvent::~UserEvent()
19 {
20 }
21 
22 
23 // #pragma mark - SignalEvent
24 
25 
26 struct 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 
53 private:
54 	int32				fInUse;
55 };
56 
57 
58 SignalEvent::SignalEvent(EventSignal* signal)
59 	:
60 	fSignal(signal),
61 	fPendingDPC(0)
62 {
63 }
64 
65 
66 SignalEvent::~SignalEvent()
67 {
68 	fSignal->ReleaseReference();
69 }
70 
71 
72 void
73 SignalEvent::SetUserValue(union sigval userValue)
74 {
75 	fSignal->SetUserValue(userValue);
76 }
77 
78 
79 status_t
80 SignalEvent::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 
101 TeamSignalEvent::TeamSignalEvent(Team* team, EventSignal* signal)
102 	:
103 	SignalEvent(signal),
104 	fTeam(team)
105 {
106 }
107 
108 
109 /*static*/ TeamSignalEvent*
110 TeamSignalEvent::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(std::nothrow) TeamSignalEvent(team, signal);
121 	if (event == NULL) {
122 		delete signal;
123 		return NULL;
124 	}
125 
126 	return event;
127 }
128 
129 
130 status_t
131 TeamSignalEvent::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 
144 void
145 TeamSignalEvent::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 
171 ThreadSignalEvent::ThreadSignalEvent(Thread* thread, EventSignal* signal)
172 	:
173 	SignalEvent(signal),
174 	fThread(thread)
175 {
176 }
177 
178 
179 /*static*/ ThreadSignalEvent*
180 ThreadSignalEvent::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(std::nothrow) ThreadSignalEvent(thread, signal);
191 	if (event == NULL) {
192 		delete signal;
193 		return NULL;
194 	}
195 
196 	return event;
197 }
198 
199 
200 status_t
201 ThreadSignalEvent::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 
214 void
215 ThreadSignalEvent::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 
242 CreateThreadEvent::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*
255 CreateThreadEvent::Create(const ThreadCreationAttributes& attributes)
256 {
257 	return new(std::nothrow) CreateThreadEvent(attributes);
258 }
259 
260 
261 status_t
262 CreateThreadEvent::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 
275 void
276 CreateThreadEvent::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