/src/open62541_15/arch/posix/eventloop_posix.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 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
6 | | * Copyright 2021 (c) Fraunhofer IOSB (Author: Jan Hermes) |
7 | | */ |
8 | | |
9 | | #include "eventloop_posix.h" |
10 | | #include "open62541/plugin/eventloop.h" |
11 | | |
12 | | #if defined(UA_ARCHITECTURE_POSIX) && !defined(UA_ARCHITECTURE_LWIP) || defined(UA_ARCHITECTURE_WIN32) |
13 | | |
14 | | /*********/ |
15 | | /* Timer */ |
16 | | /*********/ |
17 | | |
18 | | static UA_DateTime |
19 | 1.91k | UA_EventLoopPOSIX_nextTimer(UA_EventLoop *public_el) { |
20 | 1.91k | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el; |
21 | 1.91k | if(el->delayedHead1 > (UA_DelayedCallback *)0x01 || |
22 | 1.81k | el->delayedHead2 > (UA_DelayedCallback *)0x01) |
23 | 139 | return el->eventLoop.dateTime_nowMonotonic(&el->eventLoop); |
24 | 1.77k | return UA_Timer_next(&el->timer); |
25 | 1.91k | } |
26 | | |
27 | | static UA_StatusCode |
28 | | UA_EventLoopPOSIX_addTimer(UA_EventLoop *public_el, UA_Callback cb, |
29 | | void *application, void *data, UA_Double interval_ms, |
30 | | UA_DateTime *baseTime, UA_TimerPolicy timerPolicy, |
31 | 2.51k | UA_UInt64 *callbackId) { |
32 | 2.51k | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el; |
33 | 2.51k | return UA_Timer_add(&el->timer, cb, application, data, interval_ms, |
34 | 2.51k | public_el->dateTime_nowMonotonic(public_el), |
35 | 2.51k | baseTime, timerPolicy, callbackId); |
36 | 2.51k | } |
37 | | |
38 | | static UA_StatusCode |
39 | | UA_EventLoopPOSIX_modifyTimer(UA_EventLoop *public_el, |
40 | | UA_UInt64 callbackId, |
41 | | UA_Double interval_ms, |
42 | | UA_DateTime *baseTime, |
43 | 0 | UA_TimerPolicy timerPolicy) { |
44 | 0 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el; |
45 | 0 | return UA_Timer_modify(&el->timer, callbackId, interval_ms, |
46 | 0 | public_el->dateTime_nowMonotonic(public_el), |
47 | 0 | baseTime, timerPolicy); |
48 | 0 | } |
49 | | |
50 | | static void |
51 | | UA_EventLoopPOSIX_removeTimer(UA_EventLoop *public_el, |
52 | 2.55k | UA_UInt64 callbackId) { |
53 | 2.55k | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el; |
54 | 2.55k | UA_Timer_remove(&el->timer, callbackId); |
55 | 2.55k | } |
56 | | |
57 | | void |
58 | | UA_EventLoopPOSIX_addDelayedCallback(UA_EventLoop *public_el, |
59 | 8.86k | UA_DelayedCallback *dc) { |
60 | 8.86k | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el; |
61 | 8.86k | dc->next = NULL; |
62 | | |
63 | | /* el->delayedTail points either to prev->next or to the head. |
64 | | * We need to update two locations: |
65 | | * 1: el->delayedTail = &dc->next; |
66 | | * 2: *oldtail = dc; (equal to &dc->next) |
67 | | * |
68 | | * Once we have (1), we "own" the previous-to-last entry. No need to worry |
69 | | * about (2), we can adjust it with a delay. This makes the queue |
70 | | * "eventually consistent". */ |
71 | 8.86k | UA_DelayedCallback **oldtail = (UA_DelayedCallback**) |
72 | 8.86k | UA_atomic_xchg((void**)&el->delayedTail, &dc->next); |
73 | 8.86k | UA_atomic_xchg((void**)oldtail, &dc->next); |
74 | 8.86k | } |
75 | | |
76 | | /* Resets the delayed queue and returns the previous head and tail */ |
77 | | static void |
78 | | resetDelayedQueue(UA_EventLoopPOSIX *el, UA_DelayedCallback **oldHead, |
79 | 10.8k | UA_DelayedCallback **oldTail) { |
80 | 10.8k | if(el->delayedHead1 <= (UA_DelayedCallback *)0x01 && |
81 | 5.73k | el->delayedHead2 <= (UA_DelayedCallback *)0x01) |
82 | 5.41k | return; /* The queue is empty */ |
83 | | |
84 | 5.42k | UA_Boolean active1 = (el->delayedHead1 != (UA_DelayedCallback*)0x01); |
85 | 5.42k | UA_DelayedCallback **activeHead = (active1) ? &el->delayedHead1 : &el->delayedHead2; |
86 | 5.42k | UA_DelayedCallback **inactiveHead = (active1) ? &el->delayedHead2 : &el->delayedHead1; |
87 | | |
88 | | /* Switch active/inactive by resetting the sentinel values. The (old) active |
89 | | * head points to an element which we return. Parallel threads continue to |
90 | | * add elements to the queue "below" the first element. */ |
91 | 5.42k | UA_atomic_xchg((void**)inactiveHead, NULL); |
92 | 5.42k | *oldHead = (UA_DelayedCallback *) |
93 | 5.42k | UA_atomic_xchg((void**)activeHead, (void*)0x01); |
94 | | |
95 | | /* Make the tail point to the (new) active head. Return the value of last |
96 | | * tail. When iterating over the queue elements, we need to find this tail |
97 | | * as the last element. If we find a NULL next-pointer before hitting the |
98 | | * tail spinlock until the pointer updates (eventually consistent). */ |
99 | 5.42k | *oldTail = (UA_DelayedCallback*) |
100 | 5.42k | UA_atomic_xchg((void**)&el->delayedTail, inactiveHead); |
101 | 5.42k | } |
102 | | |
103 | | static void |
104 | | UA_EventLoopPOSIX_removeDelayedCallback(UA_EventLoop *public_el, |
105 | 205 | UA_DelayedCallback *dc) { |
106 | 205 | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el; |
107 | 205 | UA_LOCK(&el->elMutex); |
108 | | |
109 | | /* Reset and get the old head and tail */ |
110 | 205 | UA_DelayedCallback *cur = NULL, *tail = NULL; |
111 | 205 | resetDelayedQueue(el, &cur, &tail); |
112 | | |
113 | | /* Loop until we reach the tail (or head and tail are both NULL) */ |
114 | 205 | UA_DelayedCallback *next; |
115 | 205 | for(; cur; cur = next) { |
116 | | /* Spin-loop until the next-pointer of cur is updated. |
117 | | * The element pointed to by tail must appear eventually. */ |
118 | 0 | next = cur->next; |
119 | 0 | while(!next && cur != tail) |
120 | 0 | next = (UA_DelayedCallback *)UA_atomic_load((void**)&cur->next); |
121 | 0 | if(cur == dc) |
122 | 0 | continue; |
123 | 0 | UA_EventLoopPOSIX_addDelayedCallback(public_el, cur); |
124 | 0 | } |
125 | | |
126 | 205 | UA_UNLOCK(&el->elMutex); |
127 | 205 | } |
128 | | |
129 | | static void |
130 | 27.4k | processDelayed(UA_EventLoopPOSIX *el) { |
131 | 27.4k | UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
132 | 27.4k | "Process delayed callbacks"); |
133 | | |
134 | 27.4k | UA_LOCK_ASSERT(&el->elMutex); |
135 | | |
136 | | /* Reset and get the old head and tail */ |
137 | 27.4k | UA_DelayedCallback *dc = NULL, *tail = NULL; |
138 | 27.4k | resetDelayedQueue(el, &dc, &tail); |
139 | | |
140 | | /* Loop until we reach the tail (or head and tail are both NULL) */ |
141 | 27.4k | UA_DelayedCallback *next; |
142 | 47.7k | for(; dc; dc = next) { |
143 | 20.2k | next = dc->next; |
144 | 20.2k | while(!next && dc != tail) |
145 | 0 | next = (UA_DelayedCallback *)UA_atomic_load((void**)&dc->next); |
146 | 20.2k | if(!dc->callback) |
147 | 0 | continue; |
148 | 20.2k | dc->callback(dc->application, dc->context); |
149 | 20.2k | } |
150 | 27.4k | } |
151 | | |
152 | | /***********************/ |
153 | | /* EventLoop Lifecycle */ |
154 | | /***********************/ |
155 | | |
156 | | static UA_StatusCode |
157 | 13.5k | UA_EventLoopPOSIX_start(UA_EventLoopPOSIX *el) { |
158 | 13.5k | UA_LOCK(&el->elMutex); |
159 | | |
160 | 13.5k | if(el->eventLoop.state != UA_EVENTLOOPSTATE_FRESH && |
161 | 0 | el->eventLoop.state != UA_EVENTLOOPSTATE_STOPPED) { |
162 | 0 | UA_UNLOCK(&el->elMutex); |
163 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
164 | 0 | } |
165 | | |
166 | 13.5k | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
167 | 13.5k | "Starting the EventLoop"); |
168 | | |
169 | | /* Setting custom clock source */ |
170 | 13.5k | const UA_Int32 *cs = (const UA_Int32*) |
171 | 13.5k | UA_KeyValueMap_getScalar(&el->eventLoop.params, |
172 | 13.5k | UA_QUALIFIEDNAME(0, "clock-source"), |
173 | 13.5k | &UA_TYPES[UA_TYPES_INT32]); |
174 | 13.5k | if(cs) |
175 | 0 | el->clockSource = *cs; |
176 | | |
177 | 13.5k | const UA_Int32 *csm = (const UA_Int32*) |
178 | 13.5k | UA_KeyValueMap_getScalar(&el->eventLoop.params, |
179 | 13.5k | UA_QUALIFIEDNAME(0, "clock-source-monotonic"), |
180 | 13.5k | &UA_TYPES[UA_TYPES_INT32]); |
181 | 13.5k | if(csm) { |
182 | 0 | if(el->clockSourceMonotonic != *csm && el->timer.idTree.root) { |
183 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
184 | 0 | "Eventloop\t| Setting a different monotonic clock, ", |
185 | 0 | "but existing timers have been registered with a " |
186 | 0 | "different clock source"); |
187 | 0 | } |
188 | 0 | el->clockSourceMonotonic = *csm; |
189 | 0 | } |
190 | | |
191 | | #if !defined(UA_ARCHITECTURE_POSIX) |
192 | | if(cs || csm) { |
193 | | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
194 | | "Eventloop\t| Setting a different clock source ", |
195 | | "not supported for this architecture"); |
196 | | } |
197 | | #endif |
198 | | |
199 | | /* Create the self-pipe */ |
200 | 13.5k | int err = UA_EventLoopPOSIX_pipe(el->selfpipe); |
201 | 13.5k | if(err != 0) { |
202 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
203 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
204 | 0 | "Eventloop\t| Could not create the self-pipe (%s)", |
205 | 0 | errno_str)); |
206 | 0 | UA_UNLOCK(&el->elMutex); |
207 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
208 | 0 | } |
209 | | |
210 | | /* Create the epoll socket */ |
211 | 13.5k | #ifdef UA_HAVE_EPOLL |
212 | 13.5k | el->epollfd = epoll_create1(0); |
213 | 13.5k | if(el->epollfd == -1) { |
214 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
215 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
216 | 0 | "Eventloop\t| Could not create the epoll socket (%s)", |
217 | 0 | errno_str)); |
218 | 0 | UA_close(el->selfpipe[0]); |
219 | 0 | UA_close(el->selfpipe[1]); |
220 | 0 | UA_UNLOCK(&el->elMutex); |
221 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
222 | 0 | } |
223 | | |
224 | | /* epoll always listens on the self-pipe. This is the only epoll_event that |
225 | | * has a NULL data pointer. */ |
226 | 13.5k | struct epoll_event event; |
227 | 13.5k | memset(&event, 0, sizeof(struct epoll_event)); |
228 | 13.5k | event.events = EPOLLIN; |
229 | 13.5k | err = epoll_ctl(el->epollfd, EPOLL_CTL_ADD, el->selfpipe[0], &event); |
230 | 13.5k | if(err != 0) { |
231 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
232 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
233 | 0 | "Eventloop\t| Could not register the self-pipe for epoll (%s)", |
234 | 0 | errno_str)); |
235 | 0 | UA_close(el->selfpipe[0]); |
236 | 0 | UA_close(el->selfpipe[1]); |
237 | 0 | close(el->epollfd); |
238 | 0 | UA_UNLOCK(&el->elMutex); |
239 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
240 | 0 | } |
241 | 13.5k | #endif |
242 | | |
243 | | /* Start the EventSources */ |
244 | 13.5k | UA_StatusCode res = UA_STATUSCODE_GOOD; |
245 | 13.5k | UA_EventSource *es = el->eventLoop.eventSources; |
246 | 67.5k | while(es) { |
247 | 54.0k | res |= es->start(es); |
248 | 54.0k | es = es->next; |
249 | 54.0k | } |
250 | | |
251 | | /* Dirty-write the state that is const "from the outside" */ |
252 | 13.5k | *(UA_EventLoopState*)(uintptr_t)&el->eventLoop.state = |
253 | 13.5k | UA_EVENTLOOPSTATE_STARTED; |
254 | | |
255 | 13.5k | UA_UNLOCK(&el->elMutex); |
256 | 13.5k | return res; |
257 | 13.5k | } |
258 | | |
259 | | static void |
260 | 25.5k | checkClosed(UA_EventLoopPOSIX *el) { |
261 | 25.5k | UA_LOCK_ASSERT(&el->elMutex); |
262 | | |
263 | 25.5k | UA_EventSource *es = el->eventLoop.eventSources; |
264 | 127k | while(es) { |
265 | 102k | if(es->state != UA_EVENTSOURCESTATE_STOPPED) |
266 | 0 | return; |
267 | 102k | es = es->next; |
268 | 102k | } |
269 | | |
270 | | /* Not closed until all delayed callbacks are processed */ |
271 | 25.5k | if(el->delayedHead1 != NULL && el->delayedHead2 != NULL) |
272 | 12.0k | return; |
273 | | |
274 | | /* Close the self-pipe when everything else is done */ |
275 | 13.5k | UA_close(el->selfpipe[0]); |
276 | 13.5k | UA_close(el->selfpipe[1]); |
277 | | |
278 | | /* Dirty-write the state that is const "from the outside" */ |
279 | 13.5k | *(UA_EventLoopState*)(uintptr_t)&el->eventLoop.state = |
280 | 13.5k | UA_EVENTLOOPSTATE_STOPPED; |
281 | | |
282 | | /* Close the epoll/IOCP socket once all EventSources have shut down */ |
283 | 13.5k | #ifdef UA_HAVE_EPOLL |
284 | 13.5k | UA_close(el->epollfd); |
285 | 13.5k | #endif |
286 | | |
287 | 13.5k | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
288 | 13.5k | "The EventLoop has stopped"); |
289 | 13.5k | } |
290 | | |
291 | | static void |
292 | 13.5k | UA_EventLoopPOSIX_stop(UA_EventLoopPOSIX *el) { |
293 | 13.5k | UA_LOCK(&el->elMutex); |
294 | | |
295 | 13.5k | if(el->eventLoop.state != UA_EVENTLOOPSTATE_STARTED) { |
296 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
297 | 0 | "The EventLoop is not running, cannot be stopped"); |
298 | 0 | UA_UNLOCK(&el->elMutex); |
299 | 0 | return; |
300 | 0 | } |
301 | | |
302 | 13.5k | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
303 | 13.5k | "Stopping the EventLoop"); |
304 | | |
305 | | /* Set to STOPPING to prevent "normal use" */ |
306 | 13.5k | *(UA_EventLoopState*)(uintptr_t)&el->eventLoop.state = |
307 | 13.5k | UA_EVENTLOOPSTATE_STOPPING; |
308 | | |
309 | | /* Stop all event sources (asynchronous) */ |
310 | 13.5k | UA_EventSource *es = el->eventLoop.eventSources; |
311 | 67.5k | for(; es; es = es->next) { |
312 | 54.0k | if(es->state == UA_EVENTSOURCESTATE_STARTING || |
313 | 54.0k | es->state == UA_EVENTSOURCESTATE_STARTED) { |
314 | 54.0k | es->stop(es); |
315 | 54.0k | } |
316 | 54.0k | } |
317 | | |
318 | | /* Set to STOPPED if all EventSources are STOPPED */ |
319 | 13.5k | checkClosed(el); |
320 | | |
321 | 13.5k | UA_UNLOCK(&el->elMutex); |
322 | 13.5k | } |
323 | | |
324 | | static UA_StatusCode |
325 | 13.9k | UA_EventLoopPOSIX_run(UA_EventLoopPOSIX *el, UA_UInt32 timeout) { |
326 | 13.9k | UA_LOCK(&el->elMutex); |
327 | | |
328 | 13.9k | if(el->executing) { |
329 | 0 | UA_LOG_ERROR(el->eventLoop.logger, |
330 | 0 | UA_LOGCATEGORY_EVENTLOOP, |
331 | 0 | "Cannot run EventLoop from the run method itself"); |
332 | 0 | UA_UNLOCK(&el->elMutex); |
333 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
334 | 0 | } |
335 | | |
336 | 13.9k | el->executing = true; |
337 | | |
338 | 13.9k | if(el->eventLoop.state == UA_EVENTLOOPSTATE_FRESH || |
339 | 13.9k | el->eventLoop.state == UA_EVENTLOOPSTATE_STOPPED) { |
340 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
341 | 0 | "Cannot run a stopped EventLoop"); |
342 | 0 | el->executing = false; |
343 | 0 | UA_UNLOCK(&el->elMutex); |
344 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
345 | 0 | } |
346 | | |
347 | 13.9k | UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
348 | 13.9k | "Iterate the EventLoop"); |
349 | | |
350 | | /* Process cyclic callbacks */ |
351 | 13.9k | UA_DateTime dateBefore = |
352 | 13.9k | el->eventLoop.dateTime_nowMonotonic(&el->eventLoop); |
353 | | |
354 | 13.9k | UA_DateTime dateNext = UA_Timer_process(&el->timer, dateBefore); |
355 | | |
356 | | /* Process delayed callbacks here: |
357 | | * - Removes closed sockets already here instead of polling them again. |
358 | | * - The timeout for polling is selected to be ready in time for the next |
359 | | * cyclic callback. So we want to do little work between the timeout |
360 | | * running out and executing the due cyclic callbacks. */ |
361 | 13.9k | processDelayed(el); |
362 | | |
363 | | /* A delayed callback could create another delayed callback (or re-add |
364 | | * itself). In that case we don't want to wait (indefinitely) for an event |
365 | | * to happen. Process queued events but don't sleep. Then process the |
366 | | * delayed callbacks in the next iteration. */ |
367 | 13.9k | if(el->delayedHead1 != NULL && el->delayedHead2 != NULL) |
368 | 0 | timeout = 0; |
369 | | |
370 | | /* Compute the remaining time */ |
371 | 13.9k | UA_DateTime maxDate = dateBefore + (timeout * UA_DATETIME_MSEC); |
372 | 13.9k | if(dateNext > maxDate) |
373 | 13.9k | dateNext = maxDate; |
374 | 13.9k | UA_DateTime listenTimeout = |
375 | 13.9k | dateNext - el->eventLoop.dateTime_nowMonotonic(&el->eventLoop); |
376 | 13.9k | if(listenTimeout < 0) |
377 | 1.02k | listenTimeout = 0; |
378 | | |
379 | | /* Listen on the active file-descriptors (sockets) from the |
380 | | * ConnectionManagers */ |
381 | 13.9k | UA_StatusCode rv = UA_EventLoopPOSIX_pollFDs(el, listenTimeout); |
382 | | |
383 | | /* Check if the last EventSource was successfully stopped */ |
384 | 13.9k | if(el->eventLoop.state == UA_EVENTLOOPSTATE_STOPPING) |
385 | 12.0k | checkClosed(el); |
386 | | |
387 | 13.9k | el->executing = false; |
388 | 13.9k | UA_UNLOCK(&el->elMutex); |
389 | 13.9k | return rv; |
390 | 13.9k | } |
391 | | |
392 | | /*****************************/ |
393 | | /* Registering Event Sources */ |
394 | | /*****************************/ |
395 | | |
396 | | static UA_StatusCode |
397 | | UA_EventLoopPOSIX_registerEventSource(UA_EventLoopPOSIX *el, |
398 | 54.1k | UA_EventSource *es) { |
399 | 54.1k | UA_LOCK(&el->elMutex); |
400 | | |
401 | | /* Already registered? */ |
402 | 54.1k | if(es->state != UA_EVENTSOURCESTATE_FRESH) { |
403 | 0 | UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
404 | 0 | "Cannot register the EventSource \"%.*s\": " |
405 | 0 | "already registered", |
406 | 0 | (int)es->name.length, (char*)es->name.data); |
407 | 0 | UA_UNLOCK(&el->elMutex); |
408 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
409 | 0 | } |
410 | | |
411 | | /* Add to linked list */ |
412 | 54.1k | es->next = el->eventLoop.eventSources; |
413 | 54.1k | el->eventLoop.eventSources = es; |
414 | | |
415 | 54.1k | es->eventLoop = &el->eventLoop; |
416 | 54.1k | es->state = UA_EVENTSOURCESTATE_STOPPED; |
417 | | |
418 | | /* Start if the entire EventLoop is started */ |
419 | 54.1k | UA_StatusCode res = UA_STATUSCODE_GOOD; |
420 | 54.1k | if(el->eventLoop.state == UA_EVENTLOOPSTATE_STARTED) |
421 | 0 | res = es->start(es); |
422 | | |
423 | 54.1k | UA_UNLOCK(&el->elMutex); |
424 | 54.1k | return res; |
425 | 54.1k | } |
426 | | |
427 | | static UA_StatusCode |
428 | | UA_EventLoopPOSIX_deregisterEventSource(UA_EventLoopPOSIX *el, |
429 | 54.1k | UA_EventSource *es) { |
430 | 54.1k | UA_LOCK(&el->elMutex); |
431 | | |
432 | 54.1k | if(es->state != UA_EVENTSOURCESTATE_STOPPED) { |
433 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
434 | 0 | "Cannot deregister the EventSource %.*s: " |
435 | 0 | "Has to be stopped first", |
436 | 0 | (int)es->name.length, es->name.data); |
437 | 0 | UA_UNLOCK(&el->elMutex); |
438 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
439 | 0 | } |
440 | | |
441 | | /* Remove from the linked list */ |
442 | 54.1k | UA_EventSource **s = &el->eventLoop.eventSources; |
443 | 54.1k | while(*s) { |
444 | 54.1k | if(*s == es) { |
445 | 54.1k | *s = es->next; |
446 | 54.1k | break; |
447 | 54.1k | } |
448 | 0 | s = &(*s)->next; |
449 | 0 | } |
450 | | |
451 | | /* Set the state to non-registered */ |
452 | 54.1k | es->state = UA_EVENTSOURCESTATE_FRESH; |
453 | | |
454 | 54.1k | UA_UNLOCK(&el->elMutex); |
455 | 54.1k | return UA_STATUSCODE_GOOD; |
456 | 54.1k | } |
457 | | |
458 | | /***************/ |
459 | | /* Time Domain */ |
460 | | /***************/ |
461 | | |
462 | | static UA_DateTime |
463 | 14.9M | UA_EventLoopPOSIX_DateTime_now(UA_EventLoop *el) { |
464 | 14.9M | #if defined(UA_ARCHITECTURE_POSIX) |
465 | 14.9M | UA_EventLoopPOSIX *pel = (UA_EventLoopPOSIX*)el; |
466 | 14.9M | struct timespec ts; |
467 | 14.9M | int res = clock_gettime(pel->clockSource, &ts); |
468 | 14.9M | if(UA_UNLIKELY(res != 0)) |
469 | 0 | return 0; |
470 | 14.9M | return (ts.tv_sec * UA_DATETIME_SEC) + (ts.tv_nsec / 100) + UA_DATETIME_UNIX_EPOCH; |
471 | | #else |
472 | | return UA_DateTime_now(); |
473 | | #endif |
474 | 14.9M | } |
475 | | |
476 | | static UA_DateTime |
477 | 51.5k | UA_EventLoopPOSIX_DateTime_nowMonotonic(UA_EventLoop *el) { |
478 | 51.5k | #if defined(UA_ARCHITECTURE_POSIX) |
479 | 51.5k | UA_EventLoopPOSIX *pel = (UA_EventLoopPOSIX*)el; |
480 | 51.5k | struct timespec ts; |
481 | 51.5k | int res = clock_gettime(pel->clockSourceMonotonic, &ts); |
482 | 51.5k | if(UA_UNLIKELY(res != 0)) |
483 | 0 | return 0; |
484 | | /* Also add the unix epoch for the monotonic clock. So we get a "normal" |
485 | | * output when a "normal" source is configured. */ |
486 | 51.5k | return (ts.tv_sec * UA_DATETIME_SEC) + (ts.tv_nsec / 100) + UA_DATETIME_UNIX_EPOCH; |
487 | | #else |
488 | | return UA_DateTime_nowMonotonic(); |
489 | | #endif |
490 | 51.5k | } |
491 | | |
492 | | static UA_Int64 |
493 | 0 | UA_EventLoopPOSIX_DateTime_localTimeUtcOffset(UA_EventLoop *el) { |
494 | | /* TODO: Fix for custom clock sources */ |
495 | 0 | return UA_DateTime_localTimeUtcOffset(); |
496 | 0 | } |
497 | | |
498 | | /*************************/ |
499 | | /* Initialize and Delete */ |
500 | | /*************************/ |
501 | | |
502 | | static UA_StatusCode |
503 | 13.5k | UA_EventLoopPOSIX_free(UA_EventLoopPOSIX *el) { |
504 | 13.5k | UA_LOCK(&el->elMutex); |
505 | | |
506 | | /* Check if the EventLoop can be deleted */ |
507 | 13.5k | if(el->eventLoop.state != UA_EVENTLOOPSTATE_STOPPED && |
508 | 40 | el->eventLoop.state != UA_EVENTLOOPSTATE_FRESH) { |
509 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
510 | 0 | "Cannot delete a running EventLoop"); |
511 | 0 | UA_UNLOCK(&el->elMutex); |
512 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
513 | 0 | } |
514 | | |
515 | | /* Deregister and delete all the EventSources */ |
516 | 67.7k | while(el->eventLoop.eventSources) { |
517 | 54.1k | UA_EventSource *es = el->eventLoop.eventSources; |
518 | 54.1k | UA_EventLoopPOSIX_deregisterEventSource(el, es); |
519 | 54.1k | es->free(es); |
520 | 54.1k | } |
521 | | |
522 | | /* Remove the repeated timed callbacks */ |
523 | 13.5k | UA_Timer_clear(&el->timer); |
524 | | |
525 | | /* Process remaining delayed callbacks */ |
526 | 13.5k | processDelayed(el); |
527 | | |
528 | | #ifdef UA_ARCHITECTURE_WIN32 |
529 | | /* Stop the Windows networking subsystem */ |
530 | | WSACleanup(); |
531 | | #endif |
532 | | |
533 | 13.5k | UA_KeyValueMap_clear(&el->eventLoop.params); |
534 | | |
535 | | /* Clean up */ |
536 | 13.5k | UA_UNLOCK(&el->elMutex); |
537 | 13.5k | UA_LOCK_DESTROY(&el->elMutex); |
538 | 13.5k | UA_free(el); |
539 | 13.5k | return UA_STATUSCODE_GOOD; |
540 | 13.5k | } |
541 | | |
542 | | static void |
543 | 28.1M | UA_EventLoopPOSIX_lock(UA_EventLoop *public_el) { |
544 | 28.1M | UA_LOCK(&((UA_EventLoopPOSIX*)public_el)->elMutex); |
545 | 28.1M | } |
546 | | static void |
547 | 28.1M | UA_EventLoopPOSIX_unlock(UA_EventLoop *public_el) { |
548 | 28.1M | UA_UNLOCK(&((UA_EventLoopPOSIX*)public_el)->elMutex); |
549 | 28.1M | } |
550 | | |
551 | | UA_EventLoop * |
552 | 13.5k | UA_EventLoop_new_POSIX(const UA_Logger *logger) { |
553 | 13.5k | UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*) |
554 | 13.5k | UA_calloc(1, sizeof(UA_EventLoopPOSIX)); |
555 | 13.5k | if(!el) |
556 | 0 | return NULL; |
557 | | |
558 | 13.5k | UA_LOCK_INIT(&el->elMutex); |
559 | 13.5k | UA_Timer_init(&el->timer); |
560 | | |
561 | | /* Initialize the queue */ |
562 | 13.5k | el->delayedTail = &el->delayedHead1; |
563 | 13.5k | el->delayedHead2 = (UA_DelayedCallback*)0x01; /* sentinel value */ |
564 | | |
565 | | #ifdef UA_ARCHITECTURE_WIN32 |
566 | | /* Start the WSA networking subsystem on Windows */ |
567 | | WSADATA wsaData; |
568 | | WSAStartup(MAKEWORD(2, 2), &wsaData); |
569 | | #endif |
570 | | |
571 | | /* Set the public EventLoop content */ |
572 | 13.5k | el->eventLoop.logger = logger; |
573 | | |
574 | | /* Initialize the clock source to the default */ |
575 | 13.5k | #if defined(UA_ARCHITECTURE_POSIX) |
576 | 13.5k | el->clockSource = CLOCK_REALTIME; |
577 | 13.5k | # ifdef CLOCK_MONOTONIC_RAW |
578 | 13.5k | el->clockSourceMonotonic = CLOCK_MONOTONIC_RAW; |
579 | | # else |
580 | | el->clockSourceMonotonic = CLOCK_MONOTONIC; |
581 | | # endif |
582 | 13.5k | #endif |
583 | | |
584 | | /* Set the method pointers for the interface */ |
585 | 13.5k | el->eventLoop.start = (UA_StatusCode (*)(UA_EventLoop*))UA_EventLoopPOSIX_start; |
586 | 13.5k | el->eventLoop.stop = (void (*)(UA_EventLoop*))UA_EventLoopPOSIX_stop; |
587 | 13.5k | el->eventLoop.free = (UA_StatusCode (*)(UA_EventLoop*))UA_EventLoopPOSIX_free; |
588 | 13.5k | el->eventLoop.run = (UA_StatusCode (*)(UA_EventLoop*, UA_UInt32))UA_EventLoopPOSIX_run; |
589 | 13.5k | el->eventLoop.cancel = (void (*)(UA_EventLoop*))UA_EventLoopPOSIX_cancel; |
590 | | |
591 | 13.5k | el->eventLoop.dateTime_now = UA_EventLoopPOSIX_DateTime_now; |
592 | 13.5k | el->eventLoop.dateTime_nowMonotonic = |
593 | 13.5k | UA_EventLoopPOSIX_DateTime_nowMonotonic; |
594 | 13.5k | el->eventLoop.dateTime_localTimeUtcOffset = |
595 | 13.5k | UA_EventLoopPOSIX_DateTime_localTimeUtcOffset; |
596 | | |
597 | 13.5k | el->eventLoop.nextTimer = UA_EventLoopPOSIX_nextTimer; |
598 | 13.5k | el->eventLoop.addTimer = UA_EventLoopPOSIX_addTimer; |
599 | 13.5k | el->eventLoop.modifyTimer = UA_EventLoopPOSIX_modifyTimer; |
600 | 13.5k | el->eventLoop.removeTimer = UA_EventLoopPOSIX_removeTimer; |
601 | 13.5k | el->eventLoop.addDelayedCallback = UA_EventLoopPOSIX_addDelayedCallback; |
602 | 13.5k | el->eventLoop.removeDelayedCallback = UA_EventLoopPOSIX_removeDelayedCallback; |
603 | | |
604 | 13.5k | el->eventLoop.registerEventSource = |
605 | 13.5k | (UA_StatusCode (*)(UA_EventLoop*, UA_EventSource*)) |
606 | 13.5k | UA_EventLoopPOSIX_registerEventSource; |
607 | 13.5k | el->eventLoop.deregisterEventSource = |
608 | 13.5k | (UA_StatusCode (*)(UA_EventLoop*, UA_EventSource*)) |
609 | 13.5k | UA_EventLoopPOSIX_deregisterEventSource; |
610 | | |
611 | 13.5k | el->eventLoop.lock = UA_EventLoopPOSIX_lock; |
612 | 13.5k | el->eventLoop.unlock = UA_EventLoopPOSIX_unlock; |
613 | | |
614 | 13.5k | return &el->eventLoop; |
615 | 13.5k | } |
616 | | |
617 | | /***************************/ |
618 | | /* Network Buffer Handling */ |
619 | | /***************************/ |
620 | | |
621 | | UA_StatusCode |
622 | | UA_EventLoopPOSIX_allocNetworkBuffer(UA_ConnectionManager *cm, |
623 | | uintptr_t connectionId, |
624 | | UA_ByteString *buf, |
625 | 139 | size_t bufSize) { |
626 | 139 | UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm; |
627 | | /* Reuse the static tx buffer; fall back to allocation for larger messages. */ |
628 | 139 | if(pcm->txBuffer.length < bufSize) |
629 | 0 | return UA_ByteString_allocBuffer(buf, bufSize); |
630 | 139 | *buf = pcm->txBuffer; |
631 | 139 | buf->length = bufSize; |
632 | 139 | return UA_STATUSCODE_GOOD; |
633 | 139 | } |
634 | | |
635 | | void |
636 | | UA_EventLoopPOSIX_freeNetworkBuffer(UA_ConnectionManager *cm, |
637 | | uintptr_t connectionId, |
638 | 139 | UA_ByteString *buf) { |
639 | 139 | UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm; |
640 | 139 | if(pcm->txBuffer.data == buf->data) |
641 | 139 | UA_ByteString_init(buf); |
642 | 0 | else |
643 | 0 | UA_ByteString_clear(buf); |
644 | 139 | } |
645 | | |
646 | | UA_StatusCode |
647 | 15.4k | UA_EventLoopPOSIX_allocateStaticBuffers(UA_POSIXConnectionManager *pcm) { |
648 | 15.4k | UA_StatusCode res = |
649 | 15.4k | UA_EventLoopCommon_allocStaticBuffer(&pcm->cm.eventSource.params, |
650 | 15.4k | UA_QUALIFIEDNAME(0, "recv-bufsize"), |
651 | 15.4k | 1u << 16, /* The default is 64 kb */ |
652 | 15.4k | &pcm->rxBuffer); |
653 | | |
654 | | /* Default the tx buffer to the rx size so a dedicated static send buffer |
655 | | * always exists. This avoids a malloc/free on every send without reusing |
656 | | * the rx buffer (which may still hold unprocessed received data). */ |
657 | 15.4k | res |= UA_EventLoopCommon_allocStaticBuffer(&pcm->cm.eventSource.params, |
658 | 15.4k | UA_QUALIFIEDNAME(0, "send-bufsize"), |
659 | 15.4k | (UA_UInt32)pcm->rxBuffer.length, |
660 | 15.4k | &pcm->txBuffer); |
661 | 15.4k | return res; |
662 | 15.4k | } |
663 | | |
664 | | /******************/ |
665 | | /* Socket Options */ |
666 | | /******************/ |
667 | | |
668 | | enum ZIP_CMP |
669 | 4.73k | cmpFD(const UA_FD *a, const UA_FD *b) { |
670 | 4.73k | if(*a == *b) |
671 | 1.86k | return ZIP_CMP_EQ; |
672 | 2.86k | return (*a < *b) ? ZIP_CMP_LESS : ZIP_CMP_MORE; |
673 | 4.73k | } |
674 | | |
675 | | UA_StatusCode |
676 | 35.1k | UA_EventLoopPOSIX_setNonBlocking(UA_FD sockfd) { |
677 | 35.1k | #ifndef UA_ARCHITECTURE_WIN32 |
678 | 35.1k | int opts = fcntl(sockfd, F_GETFL); |
679 | 35.1k | if(opts < 0 || fcntl(sockfd, F_SETFL, opts | O_NONBLOCK) < 0) |
680 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
681 | | #else |
682 | | u_long iMode = 1; |
683 | | if(ioctlsocket(sockfd, FIONBIO, &iMode) != NO_ERROR) |
684 | | return UA_STATUSCODE_BADINTERNALERROR; |
685 | | #endif |
686 | 35.1k | return UA_STATUSCODE_GOOD; |
687 | 35.1k | } |
688 | | |
689 | | UA_StatusCode |
690 | 35.3k | UA_EventLoopPOSIX_setNoSigPipe(UA_FD sockfd) { |
691 | | #ifdef SO_NOSIGPIPE |
692 | | int val = 1; |
693 | | int res = UA_setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)); |
694 | | if(res < 0) |
695 | | return UA_STATUSCODE_BADINTERNALERROR; |
696 | | #endif |
697 | 35.3k | return UA_STATUSCODE_GOOD; |
698 | 35.3k | } |
699 | | |
700 | | UA_StatusCode |
701 | 2.42k | UA_EventLoopPOSIX_setReusable(UA_FD sockfd) { |
702 | 2.42k | int enableReuseVal = 1; |
703 | 2.42k | #ifndef UA_ARCHITECTURE_WIN32 |
704 | 2.42k | int res = UA_setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, |
705 | 2.42k | (const char*)&enableReuseVal, sizeof(enableReuseVal)); |
706 | 2.42k | res |= UA_setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, |
707 | 2.42k | (const char*)&enableReuseVal, sizeof(enableReuseVal)); |
708 | 2.42k | return (res == 0) ? UA_STATUSCODE_GOOD : UA_STATUSCODE_BADINTERNALERROR; |
709 | | #else |
710 | | int res = UA_setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, |
711 | | (const char*)&enableReuseVal, sizeof(enableReuseVal)); |
712 | | return (res == 0) ? UA_STATUSCODE_GOOD : UA_STATUSCODE_BADINTERNALERROR; |
713 | | #endif |
714 | 2.42k | } |
715 | | |
716 | | /************************/ |
717 | | /* Select / epoll Logic */ |
718 | | /************************/ |
719 | | |
720 | | /* Re-arm the self-pipe socket for the next signal by reading from it */ |
721 | | static void |
722 | 0 | flushSelfPipe(UA_SOCKET s) { |
723 | 0 | char buf[128]; |
724 | | #ifdef UA_ARCHITECTURE_WIN32 |
725 | | recv(s, buf, 128, 0); |
726 | | #else |
727 | 0 | ssize_t i; |
728 | 0 | do { |
729 | 0 | i = read(s, buf, 128); |
730 | 0 | } while(i > 0); |
731 | 0 | #endif |
732 | 0 | } |
733 | | |
734 | | #if !defined(UA_HAVE_EPOLL) |
735 | | |
736 | | UA_StatusCode |
737 | | UA_EventLoopPOSIX_registerFD(UA_EventLoopPOSIX *el, UA_RegisteredFD *rfd) { |
738 | | UA_LOCK_ASSERT(&el->elMutex); |
739 | | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
740 | | "Registering fd: %u", (unsigned)rfd->fd); |
741 | | |
742 | | /* Realloc */ |
743 | | UA_RegisteredFD **fds_tmp = (UA_RegisteredFD**) |
744 | | UA_realloc(el->fds, sizeof(UA_RegisteredFD*) * (el->fdsSize + 1)); |
745 | | if(!fds_tmp) { |
746 | | return UA_STATUSCODE_BADOUTOFMEMORY; |
747 | | } |
748 | | el->fds = fds_tmp; |
749 | | |
750 | | /* Add to the last entry */ |
751 | | el->fds[el->fdsSize] = rfd; |
752 | | el->fdsSize++; |
753 | | return UA_STATUSCODE_GOOD; |
754 | | } |
755 | | |
756 | | UA_StatusCode |
757 | | UA_EventLoopPOSIX_modifyFD(UA_EventLoopPOSIX *el, UA_RegisteredFD *rfd) { |
758 | | /* Do nothing, it is enough if the data was changed in the rfd */ |
759 | | UA_LOCK_ASSERT(&el->elMutex); |
760 | | return UA_STATUSCODE_GOOD; |
761 | | } |
762 | | |
763 | | void |
764 | | UA_EventLoopPOSIX_deregisterFD(UA_EventLoopPOSIX *el, UA_RegisteredFD *rfd) { |
765 | | UA_LOCK_ASSERT(&el->elMutex); |
766 | | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
767 | | "Unregistering fd: %u", (unsigned)rfd->fd); |
768 | | |
769 | | /* Find the entry */ |
770 | | size_t i = 0; |
771 | | for(; i < el->fdsSize; i++) { |
772 | | if(el->fds[i] == rfd) |
773 | | break; |
774 | | } |
775 | | |
776 | | /* Not found? */ |
777 | | if(i == el->fdsSize) |
778 | | return; |
779 | | |
780 | | if(el->fdsSize > 1) { |
781 | | /* Move the last entry in the ith slot and realloc. */ |
782 | | el->fdsSize--; |
783 | | el->fds[i] = el->fds[el->fdsSize]; |
784 | | UA_RegisteredFD **fds_tmp = (UA_RegisteredFD**) |
785 | | UA_realloc(el->fds, sizeof(UA_RegisteredFD*) * el->fdsSize); |
786 | | /* if realloc fails the fds are still in a correct state with |
787 | | * possibly lost memory, so failing silently here is ok */ |
788 | | if(fds_tmp) |
789 | | el->fds = fds_tmp; |
790 | | } else { |
791 | | /* Remove the last entry */ |
792 | | UA_free(el->fds); |
793 | | el->fds = NULL; |
794 | | el->fdsSize = 0; |
795 | | } |
796 | | } |
797 | | |
798 | | static UA_FD |
799 | | setFDSets(UA_EventLoopPOSIX *el, fd_set *readset, fd_set *writeset, fd_set *errset) { |
800 | | UA_LOCK_ASSERT(&el->elMutex); |
801 | | |
802 | | FD_ZERO(readset); |
803 | | FD_ZERO(writeset); |
804 | | FD_ZERO(errset); |
805 | | |
806 | | /* Always listen on the read-end of the pipe */ |
807 | | UA_FD highestfd = el->selfpipe[0]; |
808 | | FD_SET(el->selfpipe[0], readset); |
809 | | |
810 | | for(size_t i = 0; i < el->fdsSize; i++) { |
811 | | UA_FD currentFD = el->fds[i]->fd; |
812 | | |
813 | | /* Add to the fd_sets */ |
814 | | if(el->fds[i]->listenEvents & UA_FDEVENT_IN) |
815 | | FD_SET(currentFD, readset); |
816 | | if(el->fds[i]->listenEvents & UA_FDEVENT_OUT) |
817 | | FD_SET(currentFD, writeset); |
818 | | |
819 | | /* Always return errors */ |
820 | | FD_SET(currentFD, errset); |
821 | | |
822 | | /* Highest fd? */ |
823 | | if(currentFD > highestfd) |
824 | | highestfd = currentFD; |
825 | | } |
826 | | return highestfd; |
827 | | } |
828 | | |
829 | | UA_StatusCode |
830 | | UA_EventLoopPOSIX_pollFDs(UA_EventLoopPOSIX *el, UA_DateTime listenTimeout) { |
831 | | UA_assert(listenTimeout >= 0); |
832 | | UA_LOCK_ASSERT(&el->elMutex); |
833 | | |
834 | | fd_set readset, writeset, errset; |
835 | | UA_FD highestfd = setFDSets(el, &readset, &writeset, &errset); |
836 | | |
837 | | /* Nothing to do? */ |
838 | | if(highestfd == UA_INVALID_FD) { |
839 | | UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
840 | | "No valid FDs for processing"); |
841 | | return UA_STATUSCODE_GOOD; |
842 | | } |
843 | | |
844 | | struct timeval tmptv = { |
845 | | #ifndef UA_ARCHITECTURE_WIN32 |
846 | | (time_t)(listenTimeout / UA_DATETIME_SEC), |
847 | | (suseconds_t)((listenTimeout % UA_DATETIME_SEC) / UA_DATETIME_USEC) |
848 | | #else |
849 | | (long)(listenTimeout / UA_DATETIME_SEC), |
850 | | (long)((listenTimeout % UA_DATETIME_SEC) / UA_DATETIME_USEC) |
851 | | #endif |
852 | | }; |
853 | | |
854 | | UA_UNLOCK(&el->elMutex); |
855 | | int selectStatus = UA_select(highestfd+1, &readset, &writeset, &errset, &tmptv); |
856 | | UA_LOCK(&el->elMutex); |
857 | | if(selectStatus < 0) { |
858 | | /* We will retry, only log the error */ |
859 | | UA_LOG_SOCKET_ERRNO_WRAP( |
860 | | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
861 | | "Error during select: %s", errno_str)); |
862 | | return UA_STATUSCODE_GOOD; |
863 | | } |
864 | | |
865 | | /* The self-pipe has received. Clear the buffer by reading. */ |
866 | | if(UA_UNLIKELY(FD_ISSET(el->selfpipe[0], &readset))) |
867 | | flushSelfPipe(el->selfpipe[0]); |
868 | | |
869 | | /* Loop over all registered FD to see if an event arrived. Yes, this is why |
870 | | * select is slow for many open sockets. */ |
871 | | for(size_t i = 0; i < el->fdsSize; i++) { |
872 | | UA_RegisteredFD *rfd = el->fds[i]; |
873 | | |
874 | | /* The rfd is already registered for removal. Don't process incoming |
875 | | * events any longer. */ |
876 | | if(rfd->dc.callback) |
877 | | continue; |
878 | | |
879 | | /* Event signaled for the fd? */ |
880 | | short event = 0; |
881 | | if(FD_ISSET(rfd->fd, &readset)) { |
882 | | event = UA_FDEVENT_IN; |
883 | | } else if(FD_ISSET(rfd->fd, &writeset)) { |
884 | | event = UA_FDEVENT_OUT; |
885 | | } else if(FD_ISSET(rfd->fd, &errset)) { |
886 | | event = UA_FDEVENT_ERR; |
887 | | } else { |
888 | | continue; |
889 | | } |
890 | | |
891 | | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
892 | | "Processing event %u on fd %u", (unsigned)event, |
893 | | (unsigned)rfd->fd); |
894 | | |
895 | | /* Call the EventSource callback */ |
896 | | rfd->eventSourceCB(rfd->es, rfd, event); |
897 | | |
898 | | /* The fd has removed itself */ |
899 | | if(i == el->fdsSize || rfd != el->fds[i]) |
900 | | i--; |
901 | | } |
902 | | return UA_STATUSCODE_GOOD; |
903 | | } |
904 | | |
905 | | #else /* defined(UA_HAVE_EPOLL) */ |
906 | | |
907 | | UA_StatusCode |
908 | 10.2k | UA_EventLoopPOSIX_registerFD(UA_EventLoopPOSIX *el, UA_RegisteredFD *rfd) { |
909 | 10.2k | struct epoll_event event; |
910 | 10.2k | memset(&event, 0, sizeof(struct epoll_event)); |
911 | 10.2k | event.data.ptr = rfd; |
912 | 10.2k | event.events = 0; |
913 | 10.2k | if(rfd->listenEvents & UA_FDEVENT_IN) |
914 | 9.95k | event.events |= EPOLLIN; |
915 | 10.2k | if(rfd->listenEvents & UA_FDEVENT_OUT) |
916 | 0 | event.events |= EPOLLOUT; |
917 | | |
918 | 10.2k | int err = epoll_ctl(el->epollfd, EPOLL_CTL_ADD, rfd->fd, &event); |
919 | 10.2k | if(err != 0) { |
920 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
921 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
922 | 0 | "TCP %u\t| Could not register for epoll (%s)", |
923 | 0 | rfd->fd, errno_str)); |
924 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
925 | 0 | } |
926 | 10.2k | return UA_STATUSCODE_GOOD; |
927 | 10.2k | } |
928 | | |
929 | | UA_StatusCode |
930 | 0 | UA_EventLoopPOSIX_modifyFD(UA_EventLoopPOSIX *el, UA_RegisteredFD *rfd) { |
931 | 0 | struct epoll_event event; |
932 | 0 | memset(&event, 0, sizeof(struct epoll_event)); |
933 | 0 | event.data.ptr = rfd; |
934 | 0 | event.events = 0; |
935 | 0 | if(rfd->listenEvents & UA_FDEVENT_IN) |
936 | 0 | event.events |= EPOLLIN; |
937 | 0 | if(rfd->listenEvents & UA_FDEVENT_OUT) |
938 | 0 | event.events |= EPOLLOUT; |
939 | |
|
940 | 0 | int err = epoll_ctl(el->epollfd, EPOLL_CTL_MOD, rfd->fd, &event); |
941 | 0 | if(err != 0) { |
942 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
943 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
944 | 0 | "TCP %u\t| Could not modify for epoll (%s)", |
945 | 0 | rfd->fd, errno_str)); |
946 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
947 | 0 | } |
948 | 0 | return UA_STATUSCODE_GOOD; |
949 | 0 | } |
950 | | |
951 | | void |
952 | 10.2k | UA_EventLoopPOSIX_deregisterFD(UA_EventLoopPOSIX *el, UA_RegisteredFD *rfd) { |
953 | 10.2k | int res = epoll_ctl(el->epollfd, EPOLL_CTL_DEL, rfd->fd, NULL); |
954 | 10.2k | if(res != 0) { |
955 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
956 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
957 | 0 | "TCP %u\t| Could not deregister from epoll (%s)", |
958 | 0 | rfd->fd, errno_str)); |
959 | 0 | } |
960 | 10.2k | } |
961 | | |
962 | | UA_StatusCode |
963 | 13.9k | UA_EventLoopPOSIX_pollFDs(UA_EventLoopPOSIX *el, UA_DateTime listenTimeout) { |
964 | 13.9k | UA_assert(listenTimeout >= 0); |
965 | | |
966 | | /* If there is a positive timeout, wait at least one millisecond, the |
967 | | * minimum for blocking epoll_wait. This prevents a busy-loop, as the |
968 | | * open62541 library allows even smaller timeouts, which can result in a |
969 | | * zero timeout due to rounding to an integer here. */ |
970 | 13.9k | int timeout = (int)(listenTimeout / UA_DATETIME_MSEC); |
971 | 13.9k | if(timeout == 0 && listenTimeout > 0) |
972 | 0 | timeout = 1; |
973 | | |
974 | | /* Poll the registered sockets */ |
975 | 13.9k | struct epoll_event epoll_events[64]; |
976 | 13.9k | UA_UNLOCK(&el->elMutex); |
977 | 13.9k | int events = epoll_wait(el->epollfd, epoll_events, 64, timeout); |
978 | 13.9k | UA_LOCK(&el->elMutex); |
979 | | |
980 | | /* TODO: Replace with pwait2 for higher-precision timeouts once this is |
981 | | * available in the standard library. |
982 | | * |
983 | | * struct timespec precisionTimeout = { |
984 | | * (long)(listenTimeout / UA_DATETIME_SEC), |
985 | | * (long)((listenTimeout % UA_DATETIME_SEC) * 100) |
986 | | * }; |
987 | | * int events = epoll_pwait2(epollfd, epoll_events, 64, |
988 | | * precisionTimeout, NULL); */ |
989 | | |
990 | | /* Handle error conditions */ |
991 | 13.9k | if(events == -1) { |
992 | 25 | if(errno == EINTR) { |
993 | | /* We will retry, only log the error */ |
994 | 25 | UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
995 | 25 | "Timeout during poll"); |
996 | 25 | return UA_STATUSCODE_GOOD; |
997 | 25 | } |
998 | 0 | UA_LOG_SOCKET_ERRNO_WRAP( |
999 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, |
1000 | 0 | "TCP\t| Error %s, closing the server socket", |
1001 | 0 | errno_str)); |
1002 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
1003 | 25 | } |
1004 | | |
1005 | | /* Process all received events */ |
1006 | 14.2k | for(int i = 0; i < events; i++) { |
1007 | 394 | UA_RegisteredFD *rfd = (UA_RegisteredFD*)epoll_events[i].data.ptr; |
1008 | | |
1009 | | /* The self-pipe has received */ |
1010 | 394 | if(!rfd) { |
1011 | 0 | flushSelfPipe(el->selfpipe[0]); |
1012 | 0 | continue; |
1013 | 0 | } |
1014 | | |
1015 | | /* The rfd is already registered for removal. Don't process incoming |
1016 | | * events any longer. */ |
1017 | 394 | if(rfd->dc.callback) |
1018 | 0 | continue; |
1019 | | |
1020 | | /* Get the event */ |
1021 | 394 | short revent = 0; |
1022 | 394 | if((epoll_events[i].events & EPOLLIN) == EPOLLIN) { |
1023 | 394 | revent = UA_FDEVENT_IN; |
1024 | 394 | } else if((epoll_events[i].events & EPOLLOUT) == EPOLLOUT) { |
1025 | 0 | revent = UA_FDEVENT_OUT; |
1026 | 0 | } else { |
1027 | 0 | revent = UA_FDEVENT_ERR; |
1028 | 0 | } |
1029 | | |
1030 | | /* Call the EventSource callback */ |
1031 | 394 | rfd->eventSourceCB(rfd->es, rfd, revent); |
1032 | 394 | } |
1033 | 13.9k | return UA_STATUSCODE_GOOD; |
1034 | 13.9k | } |
1035 | | |
1036 | | #endif /* defined(UA_HAVE_EPOLL) */ |
1037 | | |
1038 | | #if defined(UA_ARCHITECTURE_WIN32) || defined(__APPLE__) |
1039 | | int UA_EventLoopPOSIX_pipe(SOCKET fds[2]) { |
1040 | | struct sockaddr_in inaddr; |
1041 | | memset(&inaddr, 0, sizeof(inaddr)); |
1042 | | inaddr.sin_family = AF_INET; |
1043 | | inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
1044 | | inaddr.sin_port = 0; |
1045 | | |
1046 | | SOCKET lst = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
1047 | | bind(lst, (struct sockaddr *)&inaddr, sizeof(inaddr)); |
1048 | | listen(lst, 1); |
1049 | | |
1050 | | struct sockaddr_storage addr; |
1051 | | memset(&addr, 0, sizeof(addr)); |
1052 | | int len = sizeof(addr); |
1053 | | getsockname(lst, (struct sockaddr*)&addr, &len); |
1054 | | |
1055 | | fds[0] = socket(AF_INET, SOCK_STREAM, 0); |
1056 | | int err = connect(fds[0], (struct sockaddr*)&addr, len); |
1057 | | fds[1] = accept(lst, 0, 0); |
1058 | | #ifdef UA_ARCHITECTURE_WIN32 |
1059 | | closesocket(lst); |
1060 | | #endif |
1061 | | #ifdef __APPLE__ |
1062 | | close(lst); |
1063 | | #endif |
1064 | | |
1065 | | UA_EventLoopPOSIX_setNoSigPipe(fds[0]); |
1066 | | UA_EventLoopPOSIX_setReusable(fds[0]); |
1067 | | UA_EventLoopPOSIX_setNonBlocking(fds[0]); |
1068 | | UA_EventLoopPOSIX_setNoSigPipe(fds[1]); |
1069 | | UA_EventLoopPOSIX_setReusable(fds[1]); |
1070 | | UA_EventLoopPOSIX_setNonBlocking(fds[1]); |
1071 | | return err; |
1072 | | } |
1073 | | #elif defined(__QNX__) |
1074 | | int UA_EventLoopPOSIX_pipe(int fds[2]) { |
1075 | | int err = pipe(fds); |
1076 | | if(err == -1) { |
1077 | | return err; |
1078 | | } |
1079 | | |
1080 | | err = fcntl(fds[0], F_SETFL, O_NONBLOCK); |
1081 | | if(err == -1) { |
1082 | | return err; |
1083 | | } |
1084 | | return err; |
1085 | | } |
1086 | | #endif |
1087 | | |
1088 | | void |
1089 | 0 | UA_EventLoopPOSIX_cancel(UA_EventLoopPOSIX *el) { |
1090 | | /* Nothing to do if the EventLoop is not executing */ |
1091 | 0 | if(!el->executing) |
1092 | 0 | return; |
1093 | | |
1094 | | /* Trigger the self-pipe */ |
1095 | | #ifdef UA_ARCHITECTURE_WIN32 |
1096 | | int err = send(el->selfpipe[1], ".", 1, 0); |
1097 | | #else |
1098 | 0 | ssize_t err = write(el->selfpipe[1], ".", 1); |
1099 | 0 | #endif |
1100 | 0 | if(err <= 0) { |
1101 | | UA_LOG_SOCKET_ERRNO_WRAP( |
1102 | 0 | UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, |
1103 | 0 | "Eventloop\t| Error signaling self-pipe (%s)", errno_str)); |
1104 | 0 | } |
1105 | 0 | } |
1106 | | |
1107 | | #endif |