123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * Copyright 2017, 2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
- * Copyright 2017 (c) Stefan Profanter, fortiss GmbH
- */
- #include "ua_util_internal.h"
- #include "ua_timer.h"
- struct UA_TimerEntry {
- ZIP_ENTRY(UA_TimerEntry) zipfields;
- UA_DateTime nextTime; /* The next time when the callback
- * is to be executed */
- UA_UInt64 interval; /* Interval in 100ns resolution */
- UA_Boolean repeated; /* Repeated callback? */
- UA_ApplicationCallback callback;
- void *application;
- void *data;
- ZIP_ENTRY(UA_TimerEntry) idZipfields;
- UA_UInt64 id; /* Id of the entry */
- };
- /* There may be several entries with the same nextTime in the tree. We give them
- * an absolute order by considering the memory address to break ties. Because of
- * this, the nextTime property cannot be used to lookup specific entries. */
- static enum ZIP_CMP
- cmpDateTime(const UA_DateTime *a, const UA_DateTime *b) {
- if(*a < *b)
- return ZIP_CMP_LESS;
- if(*a > *b)
- return ZIP_CMP_MORE;
- if(a == b)
- return ZIP_CMP_EQ;
- if(a < b)
- return ZIP_CMP_LESS;
- return ZIP_CMP_MORE;
- }
- ZIP_PROTTYPE(UA_TimerZip, UA_TimerEntry, UA_DateTime)
- ZIP_IMPL(UA_TimerZip, UA_TimerEntry, zipfields, UA_DateTime, nextTime, cmpDateTime)
- /* The identifiers of entries are unique */
- static enum ZIP_CMP
- cmpId(const UA_UInt64 *a, const UA_UInt64 *b) {
- if(*a < *b)
- return ZIP_CMP_LESS;
- if(*a == *b)
- return ZIP_CMP_EQ;
- return ZIP_CMP_MORE;
- }
- ZIP_PROTTYPE(UA_TimerIdZip, UA_TimerEntry, UA_UInt64)
- ZIP_IMPL(UA_TimerIdZip, UA_TimerEntry, idZipfields, UA_UInt64, id, cmpId)
- void
- UA_Timer_init(UA_Timer *t) {
- memset(t, 0, sizeof(UA_Timer));
- }
- static UA_StatusCode
- addCallback(UA_Timer *t, UA_ApplicationCallback callback, void *application, void *data,
- UA_DateTime nextTime, UA_UInt64 interval, UA_Boolean repeated,
- UA_UInt64 *callbackId) {
- /* A callback method needs to be present */
- if(!callback)
- return UA_STATUSCODE_BADINTERNALERROR;
- /* Allocate the repeated callback structure */
- UA_TimerEntry *te = (UA_TimerEntry*)UA_malloc(sizeof(UA_TimerEntry));
- if(!te)
- return UA_STATUSCODE_BADOUTOFMEMORY;
- /* Set the repeated callback */
- te->interval = (UA_UInt64)interval;
- te->id = ++t->idCounter;
- te->callback = callback;
- te->application = application;
- te->data = data;
- te->repeated = repeated;
- te->nextTime = nextTime;
- /* Set the output identifier */
- if(callbackId)
- *callbackId = te->id;
- ZIP_INSERT(UA_TimerZip, &t->root, te, ZIP_FFS32(UA_UInt32_random()));
- ZIP_INSERT(UA_TimerIdZip, &t->idRoot, te, ZIP_RANK(te, zipfields));
- return UA_STATUSCODE_GOOD;
- }
- UA_StatusCode
- UA_Timer_addTimedCallback(UA_Timer *t, UA_ApplicationCallback callback,
- void *application, void *data, UA_DateTime date,
- UA_UInt64 *callbackId) {
- return addCallback(t, callback, application, data, date, 0, false, callbackId);
- }
- /* Adding repeated callbacks: Add an entry with the "nextTime" timestamp in the
- * future. This will be picked up in the next iteration and inserted at the
- * correct place. So that the next execution takes place ät "nextTime". */
- UA_StatusCode
- UA_Timer_addRepeatedCallback(UA_Timer *t, UA_ApplicationCallback callback,
- void *application, void *data, UA_Double interval_ms,
- UA_UInt64 *callbackId) {
- /* The interval needs to be positive */
- if(interval_ms <= 0.0)
- return UA_STATUSCODE_BADINTERNALERROR;
- UA_UInt64 interval = (UA_UInt64)(interval_ms * UA_DATETIME_MSEC);
- UA_DateTime nextTime = UA_DateTime_nowMonotonic() + (UA_DateTime)interval;
- return addCallback(t, callback, application, data, nextTime,
- interval, true, callbackId);
- }
- UA_StatusCode
- UA_Timer_changeRepeatedCallbackInterval(UA_Timer *t, UA_UInt64 callbackId,
- UA_Double interval_ms) {
- /* The interval needs to be positive */
- if(interval_ms <= 0.0)
- return UA_STATUSCODE_BADINTERNALERROR;
- /* Remove from the sorted list */
- UA_TimerEntry *te = ZIP_FIND(UA_TimerIdZip, &t->idRoot, &callbackId);
- if(!te)
- return UA_STATUSCODE_BADNOTFOUND;
- /* Set the repeated callback */
- ZIP_REMOVE(UA_TimerZip, &t->root, te);
- te->interval = (UA_UInt64)(interval_ms * UA_DATETIME_MSEC); /* in 100ns resolution */
- te->nextTime = UA_DateTime_nowMonotonic() + (UA_DateTime)te->interval;
- ZIP_INSERT(UA_TimerZip, &t->root, te, ZIP_RANK(te, zipfields));
- return UA_STATUSCODE_GOOD;
- }
- void
- UA_Timer_removeCallback(UA_Timer *t, UA_UInt64 callbackId) {
- UA_TimerEntry *te = ZIP_FIND(UA_TimerIdZip, &t->idRoot, &callbackId);
- if(!te)
- return;
- ZIP_REMOVE(UA_TimerZip, &t->root, te);
- ZIP_REMOVE(UA_TimerIdZip, &t->idRoot, te);
- UA_free(te);
- }
- UA_DateTime
- UA_Timer_process(UA_Timer *t, UA_DateTime nowMonotonic,
- UA_TimerExecutionCallback executionCallback,
- void *executionApplication) {
- UA_TimerEntry *first;
- while((first = ZIP_MIN(UA_TimerZip, &t->root)) &&
- first->nextTime <= nowMonotonic) {
- ZIP_REMOVE(UA_TimerZip, &t->root, first);
- /* Reinsert / remove to their new position first. Because the callback
- * can interact with the zip tree and expects the same entries in the
- * root and idRoot trees. */
- if(!first->repeated) {
- ZIP_REMOVE(UA_TimerIdZip, &t->idRoot, first);
- executionCallback(executionApplication, first->callback,
- first->application, first->data);
- UA_free(first);
- continue;
- }
- /* Set the time for the next execution. Prevent an infinite loop by
- * forcing the next processing into the next iteration. */
- first->nextTime += (UA_Int64)first->interval;
- if(first->nextTime < nowMonotonic)
- first->nextTime = nowMonotonic + 1;
- ZIP_INSERT(UA_TimerZip, &t->root, first, ZIP_RANK(first, zipfields));
- executionCallback(executionApplication, first->callback,
- first->application, first->data);
- }
- /* Return the timestamp of the earliest next callback */
- first = ZIP_MIN(UA_TimerZip, &t->root);
- return (first) ? first->nextTime : UA_INT64_MAX;
- }
- static void
- freeEntry(UA_TimerEntry *te, void *data) {
- UA_free(te);
- }
- void
- UA_Timer_deleteMembers(UA_Timer *t) {
- /* Free all nodes and reset the root */
- ZIP_ITER(UA_TimerZip, &t->root, freeEntry, NULL);
- ZIP_INIT(&t->root);
- }
|