/src/open62541/arch/posix/eventloop_posix_interrupt.c
Line | Count | Source |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
4 | | * |
5 | | * Copyright 2021, 2024 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
6 | | * Copyright 2026 (c) o6 Automation GmbH (Author: Julius Pfrommer) |
7 | | */ |
8 | | |
9 | | #include "eventloop_posix.h" |
10 | | #include <signal.h> |
11 | | |
12 | | #if defined(UA_ARCHITECTURE_POSIX) && !defined(UA_ARCHITECTURE_LWIP) |
13 | | |
14 | 12.9k | #define UA_MAX_INTERRUPT_MANAGERS 8 |
15 | | #ifdef NSIG |
16 | 0 | # define UA_INTERRUPT_SIGNAL_SLOTS NSIG |
17 | | #else |
18 | | # define UA_INTERRUPT_SIGNAL_SLOTS 128 |
19 | | #endif |
20 | | |
21 | | typedef struct UA_RegisteredSignal { |
22 | | LIST_ENTRY(UA_RegisteredSignal) listPointers; |
23 | | UA_EventSource *eventSource; |
24 | | |
25 | | UA_InterruptCallback signalCallback; |
26 | | void *context; |
27 | | int signal; /* POSIX identifier of the interrupt signal */ |
28 | | |
29 | | UA_Boolean active; /* Signals are only active when the EventLoop is started */ |
30 | | } UA_RegisteredSignal; |
31 | | |
32 | | typedef struct UA_POSIXInterruptManager { |
33 | | UA_InterruptManager im; |
34 | | |
35 | | LIST_HEAD(, UA_RegisteredSignal) signals; /* Registered signals */ |
36 | | |
37 | | UA_RegisteredFD readfd; |
38 | | UA_FD writefd; |
39 | | UA_atomic(struct UA_POSIXInterruptManager*)* managerSlot; |
40 | | } UA_POSIXInterruptManager; |
41 | | |
42 | | /* Signal handlers are process-global. Route each signal marker to all active |
43 | | * interrupt managers so multiple EventLoops can coexist. */ |
44 | | static UA_atomic(UA_POSIXInterruptManager*) interruptManagers[UA_MAX_INTERRUPT_MANAGERS]; |
45 | | static UA_atomic(uintptr_t) interruptWriteFDs[UA_INTERRUPT_SIGNAL_SLOTS][UA_MAX_INTERRUPT_MANAGERS]; |
46 | | static UA_atomic(uintptr_t) signalRefCounts[UA_INTERRUPT_SIGNAL_SLOTS]; |
47 | | |
48 | | /* Keep the previous action to restore when we deregister a signal */ |
49 | | static struct sigaction previousActions[UA_INTERRUPT_SIGNAL_SLOTS]; |
50 | | |
51 | | static UA_Boolean |
52 | 0 | signalIsSupported(int signal) { |
53 | 0 | return (signal >= 0 && |
54 | 0 | signal < UA_INTERRUPT_SIGNAL_SLOTS && |
55 | 0 | signal <= UCHAR_MAX); |
56 | 0 | } |
57 | | |
58 | | static UA_Boolean |
59 | 12.9k | registerInterruptManager(UA_POSIXInterruptManager *pim) { |
60 | 12.9k | for(size_t i = 0; i < UA_MAX_INTERRUPT_MANAGERS; i++) { |
61 | 12.9k | UA_POSIXInterruptManager *prev = NULL; |
62 | 12.9k | UA_atomic_cmpxchg(&interruptManagers[i], &prev, pim); |
63 | 12.9k | if(prev == NULL) { |
64 | 12.9k | pim->managerSlot = &interruptManagers[i]; |
65 | 12.9k | return true; |
66 | 12.9k | } |
67 | 12.9k | } |
68 | 0 | return false; |
69 | 12.9k | } |
70 | | |
71 | | static void |
72 | 12.9k | unregisterInterruptManager(UA_POSIXInterruptManager *pim) { |
73 | 12.9k | UA_assert(*pim->managerSlot == pim); |
74 | 12.9k | UA_atomic_store(pim->managerSlot, NULL); |
75 | 12.9k | pim->managerSlot = NULL; |
76 | 12.9k | } |
77 | | |
78 | | static void |
79 | 12.8k | closeSignalPipe(UA_POSIXInterruptManager *pim) { |
80 | 12.8k | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pim->im.eventSource.eventLoop; |
81 | 12.8k | (void)el; |
82 | 12.8k | UA_LOCK_ASSERT(&el->elMutex); |
83 | | |
84 | 12.8k | if(pim->readfd.fd == UA_INVALID_FD) |
85 | 0 | return; |
86 | | |
87 | 12.8k | UA_EventLoopPOSIX_deregisterFD(el, &pim->readfd); |
88 | 12.8k | UA_close(pim->readfd.fd); |
89 | 12.8k | UA_close(pim->writefd); |
90 | 12.8k | pim->readfd.fd = UA_INVALID_FD; |
91 | 12.8k | pim->writefd = UA_INVALID_FD; |
92 | 12.8k | } |
93 | | |
94 | | static void |
95 | | handlePOSIXInterruptEvent(UA_EventSource *es, UA_RegisteredFD *rfd, |
96 | 0 | short event) { |
97 | 0 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)es->eventLoop; |
98 | 0 | (void)el; |
99 | 0 | (void)event; |
100 | 0 | UA_LOCK_ASSERT(&el->elMutex); |
101 | |
|
102 | 0 | UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager*)es; |
103 | 0 | unsigned char buf[32]; |
104 | |
|
105 | 0 | for(;;) { |
106 | 0 | ssize_t received = UA_recv(rfd->fd, (char*)buf, sizeof(buf), 0); |
107 | 0 | if(received > 0) { |
108 | 0 | for(ssize_t i = 0; i < received; i++) { |
109 | 0 | UA_RegisteredSignal *rs; |
110 | 0 | LIST_FOREACH(rs, &pim->signals, listPointers) { |
111 | 0 | if(rs->signal == (int)buf[i] && rs->active) |
112 | 0 | break; |
113 | 0 | } |
114 | 0 | if(!rs) |
115 | 0 | continue; |
116 | | |
117 | 0 | UA_LOG_DEBUG(es->eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP, |
118 | 0 | "Interrupt %u\t| Received signal %u", |
119 | 0 | (unsigned)rfd->fd, (unsigned)rs->signal); |
120 | 0 | UA_UNLOCK(&el->elMutex); |
121 | 0 | rs->signalCallback((UA_InterruptManager *)es, |
122 | 0 | (uintptr_t)rs->signal, rs->context, |
123 | 0 | &UA_KEYVALUEMAP_NULL); |
124 | 0 | UA_LOCK(&el->elMutex); |
125 | 0 | } |
126 | 0 | continue; |
127 | 0 | } |
128 | | |
129 | 0 | if(received == 0) { |
130 | 0 | closeSignalPipe(pim); |
131 | 0 | return; |
132 | 0 | } |
133 | | |
134 | 0 | if(UA_ERRNO == UA_INTERRUPTED) |
135 | 0 | continue; |
136 | | |
137 | 0 | if(UA_ERRNO == UA_AGAIN || UA_ERRNO == UA_WOULDBLOCK) |
138 | 0 | return; |
139 | | |
140 | 0 | closeSignalPipe(pim); |
141 | 0 | return; |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | | static UA_StatusCode |
146 | 12.8k | openSignalPipe(UA_POSIXInterruptManager *pim) { |
147 | 12.8k | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pim->im.eventSource.eventLoop; |
148 | 12.8k | (void)el; |
149 | 12.8k | UA_LOCK_ASSERT(&el->elMutex); |
150 | | |
151 | 12.8k | if(pim->readfd.fd != UA_INVALID_FD) |
152 | 0 | return UA_STATUSCODE_GOOD; |
153 | | |
154 | 12.8k | UA_FD fds[2]; |
155 | 12.8k | int err = UA_EventLoopPOSIX_pipe(fds); |
156 | 12.8k | if(err != 0) { |
157 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
158 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
159 | 0 | "Interrupt\t| Could not create the signal pipe: %s", |
160 | 0 | errno_str)); |
161 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
162 | 0 | } |
163 | | |
164 | 12.8k | pim->readfd.es = &pim->im.eventSource; |
165 | 12.8k | pim->readfd.eventSourceCB = handlePOSIXInterruptEvent; |
166 | 12.8k | pim->readfd.listenEvents = UA_FDEVENT_IN; |
167 | 12.8k | pim->readfd.fd = fds[0]; |
168 | 12.8k | pim->writefd = fds[1]; |
169 | | |
170 | 12.8k | UA_StatusCode res = UA_EventLoopPOSIX_registerFD(el, &pim->readfd); |
171 | 12.8k | if(res != UA_STATUSCODE_GOOD) { |
172 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
173 | 0 | "Interrupt\t| Could not register the signal pipe in " |
174 | 0 | "the EventLoop"); |
175 | 0 | UA_close(pim->readfd.fd); |
176 | 0 | UA_close(pim->writefd); |
177 | 0 | pim->readfd.fd = UA_INVALID_FD; |
178 | 0 | pim->writefd = UA_INVALID_FD; |
179 | 0 | return res; |
180 | 0 | } |
181 | | |
182 | 12.8k | return UA_STATUSCODE_GOOD; |
183 | 12.8k | } |
184 | | |
185 | | /* Signal handlers can only perform async-signal-safe operations. Writing one |
186 | | * byte into the signal-specific pipe wakes up the EventLoop. */ |
187 | | static void |
188 | 0 | triggerPOSIXInterruptEvent(int sig) { |
189 | 0 | if(!signalIsSupported(sig)) |
190 | 0 | return; |
191 | | |
192 | | |
193 | 0 | int savedErrno = errno; |
194 | 0 | unsigned char signalMarker = (unsigned char)sig; |
195 | |
|
196 | 0 | for(size_t i = 0; i < UA_MAX_INTERRUPT_MANAGERS; i++) { |
197 | 0 | uintptr_t encodedFD = UA_atomic_load(&interruptWriteFDs[sig][i]); |
198 | 0 | UA_FD writefd = UA_INVALID_FD; |
199 | 0 | if(encodedFD != 0) |
200 | 0 | writefd = (UA_FD)(encodedFD - 1u); |
201 | 0 | if(writefd == UA_INVALID_FD) |
202 | 0 | continue; |
203 | | |
204 | 0 | ssize_t res; |
205 | 0 | do { |
206 | 0 | res = UA_send(writefd, (const char*)&signalMarker, 1, 0); |
207 | 0 | } while(res == -1 && UA_ERRNO == UA_INTERRUPTED); |
208 | 0 | } |
209 | |
|
210 | 0 | errno = savedErrno; |
211 | 0 | } |
212 | | |
213 | | static void |
214 | 0 | activateSignal(UA_RegisteredSignal *rs) { |
215 | 0 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)rs->eventSource->eventLoop; |
216 | 0 | (void)el; |
217 | 0 | UA_LOCK_ASSERT(&el->elMutex); |
218 | |
|
219 | 0 | if(rs->active) |
220 | 0 | return; |
221 | | |
222 | 0 | UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager*)rs->eventSource; |
223 | 0 | UA_assert(pim->managerSlot != NULL); |
224 | | |
225 | 0 | size_t managerSlot = (size_t)(pim->managerSlot - interruptManagers); |
226 | | |
227 | | /* Store the fd to be picked up by triggerPOSIXInterruptEvent */ |
228 | 0 | rs->active = true; |
229 | 0 | UA_atomic_store(&interruptWriteFDs[rs->signal][managerSlot], |
230 | 0 | ((uintptr_t)pim->writefd) + 1u); |
231 | | |
232 | | /* signalRefCounts is an atomic state machine: |
233 | | * 0 -> signal handler not installed |
234 | | * 1..N -> installed and referenced by N managers |
235 | | * >= UINT16_MAX -> handler is being installed (sentinel) |
236 | | * Only the thread that flips 0 -> UINT16_MAX installs the handler. |
237 | | * Other threads can increment the refcount even if the sentinel is present. */ |
238 | 0 | for(;;) { |
239 | 0 | uintptr_t refCount = UA_atomic_load(&signalRefCounts[rs->signal]); |
240 | | |
241 | | /* Interrupt installing or already installed. Just increment. */ |
242 | 0 | if(refCount > 0) { |
243 | 0 | uintptr_t expected = refCount; |
244 | 0 | UA_atomic_cmpxchg(&signalRefCounts[rs->signal], &expected, refCount + 1); |
245 | 0 | if(expected != refCount) |
246 | 0 | continue; /* Another thread changed the refCount. Retry. */ |
247 | 0 | return; /* Done */ |
248 | 0 | } |
249 | | |
250 | | /* Set the sentinel */ |
251 | 0 | uintptr_t expected = 0; |
252 | 0 | UA_atomic_cmpxchg(&signalRefCounts[rs->signal], &expected, UINT16_MAX); |
253 | 0 | if(expected != 0) |
254 | 0 | continue; /* Another thread changed the refCount. Retry. */ |
255 | 0 | break; /* This thread installs the interrupt handler */ |
256 | 0 | } |
257 | | |
258 | 0 | UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
259 | 0 | "Interrupt %i\t| Register Wait %u", rs->signal, |
260 | 0 | signalRefCounts[rs->signal]); |
261 | | |
262 | | /* From here on we are the first (and only) thread to register the signal. |
263 | | * There might still be an ongoing deactivateSignal which reached |
264 | | * refCount==0 just before. Loop until previousAction is zeroed to make sure |
265 | | * no deactivateSignal is ongoing. */ |
266 | 0 | UA_atomic(uintptr_t)* action_sentinel = |
267 | 0 | (UA_atomic(uintptr_t)*)&previousActions[rs->signal]; |
268 | 0 | for(;;) { |
269 | 0 | if(UA_atomic_load(action_sentinel) == 0) |
270 | 0 | break; |
271 | 0 | } |
272 | |
|
273 | 0 | UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
274 | 0 | "Interrupt %i\t| Register Begin %u", rs->signal, |
275 | 0 | signalRefCounts[rs->signal]); |
276 | |
|
277 | 0 | UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
278 | 0 | "Interrupt\t| Registering the handler for signal %i", rs->signal); |
279 | | |
280 | | /* Install the interrupt handler */ |
281 | 0 | struct sigaction previousAction; |
282 | 0 | struct sigaction action; |
283 | 0 | memset(&action, 0, sizeof(action)); |
284 | 0 | action.sa_handler = triggerPOSIXInterruptEvent; |
285 | 0 | sigemptyset(&action.sa_mask); |
286 | 0 | action.sa_flags = 0; |
287 | 0 | if(sigaction(rs->signal, &action, &previousAction) == 0) { |
288 | | /* Store previous action with non-NULL value. |
289 | | * Use 0x01 to indicate no previous action was stored. */ |
290 | 0 | uintptr_t *prevContent = (uintptr_t*)&previousAction; |
291 | 0 | if(*prevContent == 0) |
292 | 0 | *prevContent = 0x01; |
293 | 0 | previousActions[rs->signal] = previousAction; |
294 | 0 | } else { |
295 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
296 | 0 | UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
297 | 0 | "Interrupt\t| Could not register the signal handler: %s", |
298 | 0 | errno_str)); |
299 | 0 | } |
300 | | |
301 | | /* Subtract UINT16_MAX - 1 for the final refcount */ |
302 | 0 | for(;;) { |
303 | 0 | uintptr_t refCount = UA_atomic_load(&signalRefCounts[rs->signal]); |
304 | 0 | UA_assert(refCount >= UA_UINT16_MAX); |
305 | 0 | uintptr_t expected = refCount; |
306 | 0 | UA_atomic_cmpxchg(&signalRefCounts[rs->signal], &expected, |
307 | 0 | refCount - (UA_UINT16_MAX - 1u)); |
308 | 0 | if(expected == refCount) |
309 | 0 | break; |
310 | 0 | } |
311 | | |
312 | 0 | UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
313 | 0 | "Interrupt %i\t| Register End %u", rs->signal, |
314 | 0 | signalRefCounts[rs->signal]); |
315 | 0 | } |
316 | | |
317 | | static void |
318 | 0 | deactivateSignal(UA_RegisteredSignal *rs) { |
319 | 0 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)rs->eventSource->eventLoop; |
320 | 0 | (void)el; |
321 | 0 | UA_LOCK_ASSERT(&el->elMutex); |
322 | |
|
323 | 0 | UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager*)rs->eventSource; |
324 | 0 | UA_assert(pim->managerSlot != NULL); |
325 | | |
326 | 0 | if(!rs->active) |
327 | 0 | return; |
328 | | |
329 | 0 | UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
330 | 0 | "Interrupt %i\t| Register Decrease Begin %u", rs->signal, |
331 | 0 | signalRefCounts[rs->signal]); |
332 | |
|
333 | 0 | size_t managerSlot = (size_t)(pim->managerSlot - interruptManagers); |
334 | | |
335 | | /* Stop routing new signals to this interrupt manager */ |
336 | 0 | UA_atomic_store(&interruptWriteFDs[rs->signal][managerSlot], 0); |
337 | 0 | rs->active = false; |
338 | | |
339 | | /* Decrease the refcount */ |
340 | 0 | uintptr_t refCount; |
341 | 0 | for(;;) { |
342 | 0 | refCount = UA_atomic_load(&signalRefCounts[rs->signal]); |
343 | 0 | uintptr_t expected = refCount; |
344 | 0 | UA_atomic_cmpxchg(&signalRefCounts[rs->signal], &expected, refCount - 1); |
345 | 0 | if(expected == refCount) |
346 | 0 | break; |
347 | 0 | } |
348 | 0 | refCount--; /* Adjust, this is still the old refCount before decreasing */ |
349 | |
|
350 | 0 | UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
351 | 0 | "Interrupt %i\t| Register Decrease End %u", rs->signal, |
352 | 0 | signalRefCounts[rs->signal]); |
353 | | |
354 | | /* Another interrupt manager still uses the signal */ |
355 | 0 | if(refCount > 0) |
356 | 0 | return; |
357 | | |
358 | 0 | UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
359 | 0 | "Interrupt %i\t| Deregister Begin", rs->signal); |
360 | |
|
361 | 0 | UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
362 | 0 | "Interrupt\t| Deregistering the handler for signal %i", rs->signal); |
363 | | |
364 | | /* Deactivate the signal, reset to the previous action */ |
365 | 0 | struct sigaction prev = previousActions[rs->signal]; |
366 | 0 | uintptr_t *prevContent = (uintptr_t*)&prev; |
367 | 0 | if(*prevContent == 0x01) |
368 | 0 | *prevContent = 0x0; |
369 | 0 | sigaction(rs->signal, &prev, NULL); |
370 | | |
371 | | /* Zero out the previousAction. So an activateSignal that has already |
372 | | * started knows we are done. */ |
373 | 0 | UA_atomic(uintptr_t)* action_sentinel = |
374 | 0 | (UA_atomic(uintptr_t)*)&previousActions[rs->signal]; |
375 | 0 | UA_atomic_store(action_sentinel, 0); |
376 | |
|
377 | 0 | UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
378 | 0 | "Interrupt %i\t| Deregister End", rs->signal); |
379 | 0 | } |
380 | | |
381 | | static UA_StatusCode |
382 | | registerPOSIXInterrupt(UA_InterruptManager *im, uintptr_t interruptHandle, |
383 | | const UA_KeyValueMap *params, |
384 | 0 | UA_InterruptCallback callback, void *interruptContext) { |
385 | 0 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)im->eventSource.eventLoop; |
386 | 0 | if(!UA_KeyValueMap_isEmpty(params)) { |
387 | 0 | UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
388 | 0 | "Interrupt\t| Supplied parameters invalid for the " |
389 | 0 | "POSIX InterruptManager"); |
390 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
391 | 0 | } |
392 | | |
393 | 0 | UA_LOCK(&el->elMutex); |
394 | | |
395 | | /* Was the signal already registered? */ |
396 | 0 | int signal = (int)interruptHandle; |
397 | 0 | UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager *)im; |
398 | 0 | UA_RegisteredSignal *rs; |
399 | 0 | LIST_FOREACH(rs, &pim->signals, listPointers) { |
400 | 0 | if(rs->signal == signal) |
401 | 0 | break; |
402 | 0 | } |
403 | 0 | if(rs) { |
404 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
405 | 0 | "Interrupt\t| Signal %u already registered", |
406 | 0 | (unsigned)interruptHandle); |
407 | 0 | UA_UNLOCK(&el->elMutex); |
408 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
409 | 0 | } |
410 | | |
411 | | /* Create and populate the new context object */ |
412 | 0 | rs = (UA_RegisteredSignal *)UA_calloc(1, sizeof(UA_RegisteredSignal)); |
413 | 0 | if(!rs) { |
414 | 0 | UA_UNLOCK(&el->elMutex); |
415 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
416 | 0 | } |
417 | | |
418 | 0 | if(!signalIsSupported(signal)) { |
419 | 0 | UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
420 | 0 | "Interrupt\t| Signal %u is out of range", |
421 | 0 | (unsigned)interruptHandle); |
422 | 0 | UA_free(rs); |
423 | 0 | UA_UNLOCK(&el->elMutex); |
424 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
425 | 0 | } |
426 | | |
427 | | /* Set the callback and context */ |
428 | 0 | rs->eventSource = &im->eventSource; |
429 | 0 | rs->signal = (int)interruptHandle; |
430 | 0 | rs->signalCallback = callback; |
431 | 0 | rs->context = interruptContext; |
432 | | |
433 | | /* Add to the InterruptManager */ |
434 | 0 | LIST_INSERT_HEAD(&pim->signals, rs, listPointers); |
435 | | |
436 | | /* Activate if we are already running */ |
437 | 0 | if(pim->im.eventSource.state == UA_EVENTSOURCESTATE_STARTED) |
438 | 0 | activateSignal(rs); |
439 | |
|
440 | 0 | UA_UNLOCK(&el->elMutex); |
441 | 0 | return UA_STATUSCODE_GOOD; |
442 | 0 | } |
443 | | |
444 | | static void |
445 | 0 | deregisterPOSIXInterrupt(UA_InterruptManager *im, uintptr_t interruptHandle) { |
446 | 0 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)im->eventSource.eventLoop; |
447 | 0 | (void)el; |
448 | 0 | UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager *)im; |
449 | 0 | UA_LOCK(&el->elMutex); |
450 | |
|
451 | 0 | int signal = (int)interruptHandle; |
452 | 0 | UA_RegisteredSignal *rs; |
453 | 0 | LIST_FOREACH(rs, &pim->signals, listPointers) { |
454 | 0 | if(rs->signal == signal) |
455 | 0 | break; |
456 | 0 | } |
457 | 0 | if(rs) { |
458 | 0 | deactivateSignal(rs); |
459 | 0 | LIST_REMOVE(rs, listPointers); |
460 | 0 | UA_free(rs); |
461 | 0 | } |
462 | |
|
463 | 0 | UA_UNLOCK(&el->elMutex); |
464 | 0 | } |
465 | | |
466 | | static UA_StatusCode |
467 | 12.8k | startPOSIXInterruptManager(UA_EventSource *es) { |
468 | 12.8k | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)es->eventLoop; |
469 | 12.8k | (void)el; |
470 | 12.8k | UA_LOCK(&el->elMutex); |
471 | | |
472 | | /* Check the state */ |
473 | 12.8k | if(es->state != UA_EVENTSOURCESTATE_STOPPED) { |
474 | 0 | UA_LOG_ERROR(es->eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP, |
475 | 0 | "Interrupt\t| To start the InterruptManager, " |
476 | 0 | "it has to be registered in an EventLoop and not started"); |
477 | 0 | UA_UNLOCK(&el->elMutex); |
478 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
479 | 0 | } |
480 | | |
481 | 12.8k | UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager *)es; |
482 | 12.8k | UA_LOG_DEBUG(es->eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP, |
483 | 12.8k | "Interrupt\t| Starting the InterruptManager"); |
484 | | |
485 | 12.8k | UA_StatusCode res = openSignalPipe(pim); |
486 | 12.8k | if(res != UA_STATUSCODE_GOOD) { |
487 | 0 | UA_UNLOCK(&el->elMutex); |
488 | 0 | return res; |
489 | 0 | } |
490 | | |
491 | | /* Activate the registered signal handlers */ |
492 | 12.8k | UA_RegisteredSignal*rs; |
493 | 12.8k | LIST_FOREACH(rs, &pim->signals, listPointers) { |
494 | 0 | activateSignal(rs); |
495 | 0 | } |
496 | | |
497 | | /* Set the EventSource to the started state */ |
498 | 12.8k | es->state = UA_EVENTSOURCESTATE_STARTED; |
499 | | |
500 | 12.8k | UA_UNLOCK(&el->elMutex); |
501 | 12.8k | return UA_STATUSCODE_GOOD; |
502 | 12.8k | } |
503 | | |
504 | | static void |
505 | 18.7k | stopPOSIXInterruptManager(UA_EventSource *es) { |
506 | 18.7k | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)es->eventLoop; |
507 | 18.7k | (void)el; |
508 | 18.7k | UA_LOCK(&el->elMutex); |
509 | | |
510 | 18.7k | if(es->state != UA_EVENTSOURCESTATE_STARTED) { |
511 | 0 | UA_UNLOCK(&el->elMutex); |
512 | 0 | return; |
513 | 0 | } |
514 | | |
515 | 18.7k | UA_LOG_DEBUG(es->eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP, |
516 | 18.7k | "Interrupt\t| Stopping the InterruptManager"); |
517 | | |
518 | | /* Close all registered signals */ |
519 | 18.7k | UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager *)es; |
520 | 18.7k | UA_RegisteredSignal*rs; |
521 | 18.7k | LIST_FOREACH(rs, &pim->signals, listPointers) { |
522 | 0 | deactivateSignal(rs); |
523 | 0 | } |
524 | | |
525 | 18.7k | closeSignalPipe(pim); |
526 | | |
527 | | /* Immediately set to stopped */ |
528 | 18.7k | es->state = UA_EVENTSOURCESTATE_STOPPED; |
529 | | |
530 | 18.7k | UA_UNLOCK(&el->elMutex); |
531 | 18.7k | } |
532 | | |
533 | | static UA_StatusCode |
534 | 18.8k | freePOSIXInterruptmanager(UA_EventSource *es) { |
535 | 18.8k | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)es->eventLoop; |
536 | 18.8k | (void)el; |
537 | 18.8k | UA_LOCK_ASSERT(&el->elMutex); |
538 | | |
539 | 18.8k | if(es->state >= UA_EVENTSOURCESTATE_STARTING) { |
540 | 0 | UA_LOG_ERROR(es->eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP, |
541 | 0 | "Interrupt\t| The EventSource must be stopped " |
542 | 0 | "before it can be deleted"); |
543 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
544 | 0 | } |
545 | | |
546 | | /* Deactivate and remove all registered signals */ |
547 | 18.8k | UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager *)es; |
548 | 18.8k | UA_RegisteredSignal *rs, *rs_tmp; |
549 | 18.8k | LIST_FOREACH_SAFE(rs, &pim->signals, listPointers, rs_tmp) { |
550 | 0 | deactivateSignal(rs); |
551 | 0 | LIST_REMOVE(rs, listPointers); |
552 | 0 | UA_free(rs); |
553 | 0 | } |
554 | | |
555 | 18.8k | UA_String_clear(&es->name); |
556 | 18.8k | unregisterInterruptManager(pim); |
557 | 18.8k | UA_free(es); |
558 | | |
559 | 18.8k | return UA_STATUSCODE_GOOD; |
560 | 18.8k | } |
561 | | |
562 | | UA_InterruptManager * |
563 | 12.9k | UA_InterruptManager_new_POSIX(const UA_String eventSourceName) { |
564 | 12.9k | UA_POSIXInterruptManager *pim = (UA_POSIXInterruptManager *) |
565 | 12.9k | UA_calloc(1, sizeof(UA_POSIXInterruptManager)); |
566 | 12.9k | if(!pim) |
567 | 0 | return NULL; |
568 | | |
569 | | /* Initialize the FD as invalid before adding the interrupt manager to the |
570 | | * global array */ |
571 | 12.9k | pim->readfd.fd = UA_INVALID_FD; |
572 | 12.9k | pim->writefd = UA_INVALID_FD; |
573 | 12.9k | pim->managerSlot = NULL; |
574 | | |
575 | 12.9k | if(!registerInterruptManager(pim)) { |
576 | 0 | UA_free(pim); |
577 | 0 | return NULL; |
578 | 0 | } |
579 | | |
580 | 12.9k | UA_InterruptManager *im = &pim->im; |
581 | 12.9k | im->eventSource.eventSourceType = UA_EVENTSOURCETYPE_INTERRUPTMANAGER; |
582 | 12.9k | UA_String_copy(&eventSourceName, &im->eventSource.name); |
583 | 12.9k | im->eventSource.start = startPOSIXInterruptManager; |
584 | 12.9k | im->eventSource.stop = stopPOSIXInterruptManager; |
585 | 12.9k | im->eventSource.free = freePOSIXInterruptmanager; |
586 | 12.9k | im->registerInterrupt = registerPOSIXInterrupt; |
587 | 12.9k | im->deregisterInterrupt = deregisterPOSIXInterrupt; |
588 | 12.9k | return im; |
589 | 12.9k | } |
590 | | |
591 | | #endif |