UPnPsdk 0.1
Universal Plug and Play +, Software Development Kit
 
Loading...
Searching...
No Matches
TimerThread.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 *
3 * Copyright (c) 2000-2003 Intel Corporation
4 * All rights reserved.
5 * Copyright (c) 2012 France Telecom All rights reserved.
6 * Copyright (C) 2021+ GPL 3 and higher by Ingo Höft, Ingo@Hoeft-online.de>
7 * Redistribution only with this Copyright remark. Last modified: 2025-04-28
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * - Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 * - Neither name of Intel Corporation nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
29 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 ******************************************************************************/
34
44#include "TimerThread.hpp"
45
46#include <assert.h>
47
49#define INVALID_EVENT_ID (-10 & 1 << 29)
50
51namespace {
52
67
68
81 TimerThread* timer,
83 ThreadPoolJob* job,
85 Duration persistent,
87 time_t eventTime,
89 int id) {
90 TimerEvent* temp = NULL;
91
92 assert(timer != NULL);
93 assert(job != NULL);
94
95 temp = (TimerEvent*)FreeListAlloc(&timer->freeEvents);
96 if (temp == NULL)
97 return temp;
98 temp->job = (*job);
99 temp->persistent = persistent;
100 temp->eventTime = eventTime;
101 temp->id = id;
102
103 return temp;
104}
105
111 TimerThread* timer,
113 TimerEvent* event) {
114 assert(timer != nullptr);
115
116 FreeListFree(&timer->freeEvents, event);
117}
118
126 void* arg) {
127 TimerThread* timer = (TimerThread*)arg;
128 ListNode* head{nullptr};
129 TimerEvent* nextEvent{nullptr};
130 time_t currentTime = 0;
131 time_t nextEventTime = 0;
132 timespec timeToWait;
133 int tempId;
134
135 assert(timer != nullptr);
136
137 pthread_mutex_lock(&timer->mutex);
138 while (1) {
139 /* mutex should always be locked at top of loop */
140 /* Check for shutdown. */
141 if (timer->shutdown) {
142 timer->shutdown = 0;
143 pthread_cond_signal(&timer->condition);
144 pthread_mutex_unlock(&timer->mutex);
145 return;
146 }
147 nextEvent = NULL;
148 /* Get the next event if possible. */
149 if (timer->eventQ.size > 0) {
150 head = ListHead(&timer->eventQ);
151 if (head == NULL) {
152 pthread_mutex_unlock(&timer->mutex);
153 return;
154 }
155 nextEvent = (TimerEvent*)head->item;
156 nextEventTime = nextEvent->eventTime;
157 }
158 currentTime = time(NULL);
159 /* If time has elapsed, schedule job. */
160 if (nextEvent && currentTime >= nextEventTime) {
161 if (nextEvent->persistent) {
162 if (ThreadPoolAddPersistent(timer->tp, &nextEvent->job,
163 &tempId) != 0) {
164 if (nextEvent->job.arg != NULL &&
165 nextEvent->job.free_func != NULL) {
166 nextEvent->job.free_func(nextEvent->job.arg);
167 }
168 }
169 } else {
170 if (ThreadPoolAdd(timer->tp, &nextEvent->job, &tempId) != 0) {
171 if (nextEvent->job.arg != NULL &&
172 nextEvent->job.free_func != NULL) {
173 nextEvent->job.free_func(nextEvent->job.arg);
174 }
175 }
176 }
177 ListDelNode(&timer->eventQ, head, 0);
178 FreeTimerEvent(timer, nextEvent);
179 continue;
180 }
181 if (nextEvent) {
182 timeToWait.tv_nsec = 0;
183 timeToWait.tv_sec = (long)nextEvent->eventTime;
184 pthread_cond_timedwait(&timer->condition, &timer->mutex,
185 &timeToWait);
186 } else {
187 pthread_cond_wait(&timer->condition, &timer->mutex);
188 }
189 }
190}
191
200 time_t* timeout,
202 TimeoutType type) {
203 time_t now;
204
205 assert(timeout != nullptr);
206
207 switch (type) {
208 case ABS_SEC:
209 return 0;
210 default: /* REL_SEC) */
211 time(&now);
212 (*timeout) += now;
213 return 0;
214 }
215}
216
218} // anonymous namespace
219
220
222
223 int rc = 0;
224
225 ThreadPoolJob timerThreadWorker;
226
227 assert(timer != NULL);
228 assert(tp != NULL);
229
230 if ((timer == NULL) || (tp == NULL)) {
231 return EINVAL;
232 }
233
234 rc += pthread_mutex_init(&timer->mutex, NULL);
235
236 assert(rc == 0);
237
238 rc += pthread_mutex_lock(&timer->mutex);
239 assert(rc == 0);
240
241 rc += pthread_cond_init(&timer->condition, NULL);
242 assert(rc == 0);
243
244 rc += FreeListInit(&timer->freeEvents, sizeof(TimerEvent), 100);
245 assert(rc == 0);
246
247 timer->shutdown = 0;
248 timer->tp = tp;
249 timer->lastEventId = 0;
250 rc += ListInit(&timer->eventQ, NULL, NULL);
251
252 assert(rc == 0);
253
254 if (rc != 0) {
255 rc = EAGAIN;
256 } else {
257
258 TPJobInit(&timerThreadWorker, TimerThreadWorker, timer);
259 TPJobSetPriority(&timerThreadWorker, HIGH_PRIORITY);
260
261 rc = ThreadPoolAddPersistent(tp, &timerThreadWorker, NULL);
262 }
263
264 pthread_mutex_unlock(&timer->mutex);
265
266 if (rc != 0) {
267 pthread_cond_destroy(&timer->condition);
268 pthread_mutex_destroy(&timer->mutex);
270 ListDestroy(&timer->eventQ, 0);
271 }
272
273 return rc;
274}
275
276int TimerThreadSchedule(TimerThread* timer, time_t timeout, TimeoutType type,
277 ThreadPoolJob* job, Duration duration, int* id) {
278 int rc = EOUTOFMEM;
279 int found = 0;
280 int tempId = 0;
281
282 ListNode* tempNode = NULL;
283 TimerEvent* temp = NULL;
284 TimerEvent* newEvent = NULL;
285
286 assert(timer != NULL);
287 assert(job != NULL);
288
289 if ((timer == NULL) || (job == NULL)) {
290 return EINVAL;
291 }
292
293 CalculateEventTime(&timeout, type);
294 pthread_mutex_lock(&timer->mutex);
295
296 if (id == NULL)
297 id = &tempId;
298
299 (*id) = INVALID_EVENT_ID;
300
301 newEvent =
302 CreateTimerEvent(timer, job, duration, timeout, timer->lastEventId);
303
304 if (newEvent == NULL) {
305 pthread_mutex_unlock(&timer->mutex);
306 return rc;
307 }
308
309 tempNode = ListHead(&timer->eventQ);
310 /* add job to Q. Q is ordered by eventTime with the head of the Q being
311 * the next event. */
312 while (tempNode != NULL) {
313 temp = (TimerEvent*)tempNode->item;
314 if (temp->eventTime >= timeout) {
315 if (ListAddBefore(&timer->eventQ, newEvent, tempNode))
316 rc = 0;
317 found = 1;
318 break;
319 }
320 tempNode = ListNext(&timer->eventQ, tempNode);
321 }
322 /* add to the end of Q. */
323 if (!found) {
324 if (ListAddTail(&timer->eventQ, newEvent) != NULL)
325 rc = 0;
326 }
327 /* signal change in Q. */
328 if (rc == 0) {
329 pthread_cond_signal(&timer->condition);
330 } else {
331 FreeTimerEvent(timer, newEvent);
332 }
333 (*id) = timer->lastEventId++;
334 pthread_mutex_unlock(&timer->mutex);
335
336 return rc;
337}
338
339int TimerThreadRemove(TimerThread* timer, int id, ThreadPoolJob* out) {
340 int rc = INVALID_EVENT_ID;
341 ListNode* tempNode = NULL;
342 TimerEvent* temp = NULL;
343
344 assert(timer != NULL);
345
346 if (timer == NULL) {
347 return EINVAL;
348 }
349
350 pthread_mutex_lock(&timer->mutex);
351
352 tempNode = ListHead(&timer->eventQ);
353
354 while (tempNode != NULL) {
355 temp = (TimerEvent*)tempNode->item;
356 if (temp->id == id) {
357
358 ListDelNode(&timer->eventQ, tempNode, 0);
359 if (out != NULL)
360 (*out) = temp->job;
361 FreeTimerEvent(timer, temp);
362 rc = 0;
363 break;
364 }
365 tempNode = ListNext(&timer->eventQ, tempNode);
366 }
367
368 pthread_mutex_unlock(&timer->mutex);
369 return rc;
370}
371
373 ListNode* tempNode2 = NULL;
374 ListNode* tempNode = NULL;
375
376 assert(timer != NULL);
377
378 if (timer == NULL) {
379 return EINVAL;
380 }
381
382 pthread_mutex_lock(&timer->mutex);
383
384 timer->shutdown = 1;
385 tempNode = ListHead(&timer->eventQ);
386
387 /* Delete nodes in Q. Call registered free function on argument. */
388 while (tempNode != NULL) {
389 TimerEvent* temp = (TimerEvent*)tempNode->item;
390
391 tempNode2 = ListNext(&timer->eventQ, tempNode);
392 ListDelNode(&timer->eventQ, tempNode, 0);
393 if (temp->job.free_func) {
394 temp->job.free_func(temp->job.arg);
395 }
396 FreeTimerEvent(timer, temp);
397 tempNode = tempNode2;
398 }
399
400 ListDestroy(&timer->eventQ, 0);
402
403 pthread_cond_broadcast(&timer->condition);
404
405 while (timer->shutdown) {
406 /* wait for timer thread to shutdown. */
407 pthread_cond_wait(&timer->condition, &timer->mutex);
408 }
409 pthread_mutex_unlock(&timer->mutex);
410
411 /* destroy condition. */
412 while (pthread_cond_destroy(&timer->condition) != 0) {
413 }
414 /* destroy mutex. */
415 while (pthread_mutex_destroy(&timer->mutex) != 0) {
416 }
417
418 return 0;
419}
int FreeListInit(FreeList *free_list, size_t elementSize, int maxFreeListLength)
Initializes Free List.
Definition FreeList.cpp:50
int FreeListFree(FreeList *free_list, void *element)
Returns an item to the Free List.
Definition FreeList.cpp:83
void * FreeListAlloc(FreeList *free_list)
Allocates chunk of set size.
Definition FreeList.cpp:64
int FreeListDestroy(FreeList *free_list)
Releases the resources stored with the free list.
Definition FreeList.cpp:103
int ListDestroy(LinkedList *list, int freeItem)
Removes all memory associated with list nodes. Does not free LinkedList *list.
void * ListDelNode(LinkedList *list, ListNode *dnode, int freeItem)
Removes a node from the list. The memory for the node is freed.
ListNode * ListAddBefore(LinkedList *list, void *item, ListNode *anode)
Adds a node before the specified node. Node gets added immediately before anode.
ListNode * ListNext(LinkedList *list, ListNode *node)
Returns the next item in the list.
ListNode * ListHead(LinkedList *list)
Returns the head of the list.
ListNode * ListAddTail(LinkedList *list, void *item)
Adds a node to the tail of the list. Node gets added immediately before list.tail.
int ListInit(LinkedList *list, cmp_routine cmp_func, free_function free_func)
Initializes LinkedList. Must be called first and only once for List.
long size
size of list
#define EOUTOFMEM
Error condition for "out of memory".
Linked list node. Stores generic item and pointers to next and prev.
int ThreadPoolAdd(ThreadPool *tp, ThreadPoolJob *job, int *jobId)
Adds a job to the thread pool.
int TPJobSetPriority(ThreadPoolJob *job, ThreadPriority priority)
Sets the priority of the threadpool job.
int TPJobInit(ThreadPoolJob *job, UPnPsdk::start_routine func, void *arg)
Initializes thread pool job.
int ThreadPoolAddPersistent(ThreadPool *tp, ThreadPoolJob *job, int *jobId)
Adds a persistent job to the thread pool.
Duration
Duration.
A thread pool.
Internal ThreadPool Job.
#define INVALID_EVENT_ID
Invalid event ID.
int TimerThreadShutdown(TimerThread *timer)
Shutdown the timer thread.
int TimerThreadInit(TimerThread *timer, ThreadPool *tp)
Initializes and starts timer thread.
int TimerThreadSchedule(TimerThread *timer, time_t timeout, TimeoutType type, ThreadPoolJob *job, Duration duration, int *id)
Schedules an event to run at a specified time.
int TimerThreadRemove(TimerThread *timer, int id, ThreadPoolJob *out)
Removes an event from the timer Q.
Manage threads that start at a given time (for internal use only).
LinkedList eventQ
[in]
pthread_mutex_t mutex
[in]
pthread_cond_t condition
[in]
int lastEventId
[in]
ThreadPool * tp
[in]
FreeList freeEvents
[in]
int shutdown
[in]
TimeoutType
Timeout Types.
@ ABS_SEC
seconds from Jan 1, 1970.
A timer thread that allows the scheduling of a job to run at a specified time in the future.
void TimerThreadWorker(void *arg)
Implements timer thread.
TimerEvent * CreateTimerEvent(TimerThread *timer, ThreadPoolJob *job, Duration persistent, time_t eventTime, int id)
Creates a Timer Event.
void FreeTimerEvent(TimerThread *timer, TimerEvent *event)
Deallocates a dynamically allocated TimerEvent.
int CalculateEventTime(time_t *timeout, TimeoutType type)
Calculates the appropriate timeout in absolute seconds since Jan 1, 1970.
Structure to contain information for a timer event.