ua_timer.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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 2017, 2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  6. * Copyright 2017 (c) Stefan Profanter, fortiss GmbH
  7. */
  8. #include "ua_util_internal.h"
  9. #include "ua_timer.h"
  10. struct UA_TimerEntry {
  11. ZIP_ENTRY(UA_TimerEntry) zipfields;
  12. UA_DateTime nextTime; /* The next time when the callback
  13. * is to be executed */
  14. UA_UInt64 interval; /* Interval in 100ns resolution */
  15. UA_Boolean repeated; /* Repeated callback? */
  16. UA_ApplicationCallback callback;
  17. void *application;
  18. void *data;
  19. ZIP_ENTRY(UA_TimerEntry) idZipfields;
  20. UA_UInt64 id; /* Id of the entry */
  21. };
  22. /* There may be several entries with the same nextTime in the tree. We give them
  23. * an absolute order by considering the memory address to break ties. Because of
  24. * this, the nextTime property cannot be used to lookup specific entries. */
  25. static enum ZIP_CMP
  26. cmpDateTime(const UA_DateTime *a, const UA_DateTime *b) {
  27. if(*a < *b)
  28. return ZIP_CMP_LESS;
  29. if(*a > *b)
  30. return ZIP_CMP_MORE;
  31. if(a == b)
  32. return ZIP_CMP_EQ;
  33. if(a < b)
  34. return ZIP_CMP_LESS;
  35. return ZIP_CMP_MORE;
  36. }
  37. ZIP_PROTTYPE(UA_TimerZip, UA_TimerEntry, UA_DateTime)
  38. ZIP_IMPL(UA_TimerZip, UA_TimerEntry, zipfields, UA_DateTime, nextTime, cmpDateTime)
  39. /* The identifiers of entries are unique */
  40. static enum ZIP_CMP
  41. cmpId(const UA_UInt64 *a, const UA_UInt64 *b) {
  42. if(*a < *b)
  43. return ZIP_CMP_LESS;
  44. if(*a == *b)
  45. return ZIP_CMP_EQ;
  46. return ZIP_CMP_MORE;
  47. }
  48. ZIP_PROTTYPE(UA_TimerIdZip, UA_TimerEntry, UA_UInt64)
  49. ZIP_IMPL(UA_TimerIdZip, UA_TimerEntry, idZipfields, UA_UInt64, id, cmpId)
  50. void
  51. UA_Timer_init(UA_Timer *t) {
  52. memset(t, 0, sizeof(UA_Timer));
  53. }
  54. static UA_StatusCode
  55. addCallback(UA_Timer *t, UA_ApplicationCallback callback, void *application, void *data,
  56. UA_DateTime nextTime, UA_UInt64 interval, UA_Boolean repeated,
  57. UA_UInt64 *callbackId) {
  58. /* A callback method needs to be present */
  59. if(!callback)
  60. return UA_STATUSCODE_BADINTERNALERROR;
  61. /* Allocate the repeated callback structure */
  62. UA_TimerEntry *te = (UA_TimerEntry*)UA_malloc(sizeof(UA_TimerEntry));
  63. if(!te)
  64. return UA_STATUSCODE_BADOUTOFMEMORY;
  65. /* Set the repeated callback */
  66. te->interval = (UA_UInt64)interval;
  67. te->id = ++t->idCounter;
  68. te->callback = callback;
  69. te->application = application;
  70. te->data = data;
  71. te->repeated = repeated;
  72. te->nextTime = nextTime;
  73. /* Set the output identifier */
  74. if(callbackId)
  75. *callbackId = te->id;
  76. ZIP_INSERT(UA_TimerZip, &t->root, te, ZIP_FFS32(UA_UInt32_random()));
  77. ZIP_INSERT(UA_TimerIdZip, &t->idRoot, te, ZIP_RANK(te, zipfields));
  78. return UA_STATUSCODE_GOOD;
  79. }
  80. UA_StatusCode
  81. UA_Timer_addTimedCallback(UA_Timer *t, UA_ApplicationCallback callback,
  82. void *application, void *data, UA_DateTime date,
  83. UA_UInt64 *callbackId) {
  84. return addCallback(t, callback, application, data, date, 0, false, callbackId);
  85. }
  86. /* Adding repeated callbacks: Add an entry with the "nextTime" timestamp in the
  87. * future. This will be picked up in the next iteration and inserted at the
  88. * correct place. So that the next execution takes place ät "nextTime". */
  89. UA_StatusCode
  90. UA_Timer_addRepeatedCallback(UA_Timer *t, UA_ApplicationCallback callback,
  91. void *application, void *data, UA_Double interval_ms,
  92. UA_UInt64 *callbackId) {
  93. /* The interval needs to be positive */
  94. if(interval_ms <= 0.0)
  95. return UA_STATUSCODE_BADINTERNALERROR;
  96. UA_UInt64 interval = (UA_UInt64)(interval_ms * UA_DATETIME_MSEC);
  97. UA_DateTime nextTime = UA_DateTime_nowMonotonic() + (UA_DateTime)interval;
  98. return addCallback(t, callback, application, data, nextTime,
  99. interval, true, callbackId);
  100. }
  101. UA_StatusCode
  102. UA_Timer_changeRepeatedCallbackInterval(UA_Timer *t, UA_UInt64 callbackId,
  103. UA_Double interval_ms) {
  104. /* The interval needs to be positive */
  105. if(interval_ms <= 0.0)
  106. return UA_STATUSCODE_BADINTERNALERROR;
  107. /* Remove from the sorted list */
  108. UA_TimerEntry *te = ZIP_FIND(UA_TimerIdZip, &t->idRoot, &callbackId);
  109. if(!te)
  110. return UA_STATUSCODE_BADNOTFOUND;
  111. /* Set the repeated callback */
  112. ZIP_REMOVE(UA_TimerZip, &t->root, te);
  113. te->interval = (UA_UInt64)(interval_ms * UA_DATETIME_MSEC); /* in 100ns resolution */
  114. te->nextTime = UA_DateTime_nowMonotonic() + (UA_DateTime)te->interval;
  115. ZIP_INSERT(UA_TimerZip, &t->root, te, ZIP_RANK(te, zipfields));
  116. return UA_STATUSCODE_GOOD;
  117. }
  118. void
  119. UA_Timer_removeCallback(UA_Timer *t, UA_UInt64 callbackId) {
  120. UA_TimerEntry *te = ZIP_FIND(UA_TimerIdZip, &t->idRoot, &callbackId);
  121. if(!te)
  122. return;
  123. ZIP_REMOVE(UA_TimerZip, &t->root, te);
  124. ZIP_REMOVE(UA_TimerIdZip, &t->idRoot, te);
  125. UA_free(te);
  126. }
  127. UA_DateTime
  128. UA_Timer_process(UA_Timer *t, UA_DateTime nowMonotonic,
  129. UA_TimerExecutionCallback executionCallback,
  130. void *executionApplication) {
  131. UA_TimerEntry *first;
  132. while((first = ZIP_MIN(UA_TimerZip, &t->root)) &&
  133. first->nextTime <= nowMonotonic) {
  134. ZIP_REMOVE(UA_TimerZip, &t->root, first);
  135. /* Reinsert / remove to their new position first. Because the callback
  136. * can interact with the zip tree and expects the same entries in the
  137. * root and idRoot trees. */
  138. if(!first->repeated) {
  139. ZIP_REMOVE(UA_TimerIdZip, &t->idRoot, first);
  140. executionCallback(executionApplication, first->callback,
  141. first->application, first->data);
  142. UA_free(first);
  143. continue;
  144. }
  145. /* Set the time for the next execution. Prevent an infinite loop by
  146. * forcing the next processing into the next iteration. */
  147. first->nextTime += (UA_Int64)first->interval;
  148. if(first->nextTime < nowMonotonic)
  149. first->nextTime = nowMonotonic + 1;
  150. ZIP_INSERT(UA_TimerZip, &t->root, first, ZIP_RANK(first, zipfields));
  151. executionCallback(executionApplication, first->callback,
  152. first->application, first->data);
  153. }
  154. /* Return the timestamp of the earliest next callback */
  155. first = ZIP_MIN(UA_TimerZip, &t->root);
  156. return (first) ? first->nextTime : UA_INT64_MAX;
  157. }
  158. static void
  159. freeEntry(UA_TimerEntry *te, void *data) {
  160. UA_free(te);
  161. }
  162. void
  163. UA_Timer_deleteMembers(UA_Timer *t) {
  164. /* Free all nodes and reset the root */
  165. ZIP_ITER(UA_TimerZip, &t->root, freeEntry, NULL);
  166. ZIP_INIT(&t->root);
  167. }