瀏覽代碼

Update timer implementation to not change order of callbacks (#1885)

* Update timer implementation to not change order of callbacks

If there are a set of timer callbacks in the timer list that are
executed in the same timer process cycle the order of them were
inversed every time after they had been executed, except the first
callback. Also, if a set of timer callbacks with same timer interval
are added within 1 second of each other, they are synchronized to
expire at the same time and added in front of each other, except the
first one which will keep the first position. If four timer callbacks
with the same time interval are added after each other they are put
in the timer list in the following order: #1, #4, #3, #2.

This change fixes so callback timers that will be executed with
the same time interval and that are put together in a "block" in the
timer list are not inversed within this block after each execution of
them. It also fixes so all timers within a "block" are added in a
deterministic order. As described above they where added in reversed
order to the timer list except the first one. Now all timers that can
be put together in a "block" are added in reversed order within this
block. One advantage of putting the callback timers in reversed order
is if a subscription with a set of monitored items are created with
the same publish/sampling interval. In that case the monitored items
that are created after the subscription has been created will be
executed by the timer process before the subscription's publish
function is called. This execution order will cause minimum delay
of data notifications to be published.

* Updated timer issue change with descriptive comments
Jonas Green 6 年之前
父節點
當前提交
970389260c
共有 2 個文件被更改,包括 25 次插入7 次删除
  1. 17 6
      src/ua_timer.c
  2. 8 1
      src/ua_timer.h

+ 17 - 6
src/ua_timer.c

@@ -128,16 +128,25 @@ addTimerCallbackEntry(UA_Timer *t, UA_TimerCallbackEntry * UA_RESTRICT tc) {
     SLIST_FOREACH(tmpTc, &t->repeatedCallbacks, next) {
         if(tmpTc->nextTime >= tc->nextTime)
             break;
-        afterTc = tmpTc;
 
         /* The goal is to have many repeated callbacks with the same repetition
          * interval in a "block" in order to reduce linear search for re-entry
          * to the sorted list after processing. Allow the first execution to lie
          * between "nextTime - 1s" and "nextTime" if this adjustment groups
-         * callbacks with the same repetition interval. */
+         * callbacks with the same repetition interval.
+         * Callbacks of a block are added in reversed order. This design allows
+         * the monitored items of a subscription (if created in a sequence with the
+         * same publish/sample interval) to be executed before the subscription
+         * publish the notifications */
         if(tmpTc->interval == tc->interval &&
-           tmpTc->nextTime > (tc->nextTime - UA_DATETIME_SEC))
+           tmpTc->nextTime > (tc->nextTime - UA_DATETIME_SEC)) {
             tc->nextTime = tmpTc->nextTime;
+            break;
+        }
+
+        /* tc is neither in the same interval nor supposed to be executed sooner
+         * than tmpTc. Update afterTc to push tc further back in the timer list. */
+        afterTc = tmpTc;
     }
 
     /* Add the repeated callback */
@@ -324,11 +333,13 @@ UA_Timer_process(UA_Timer *t, UA_DateTime nowMonotonic,
                     break;
                 prev_tc = n;
             }
-
-            /* Update last_dispatched */
-            last_dispatched = tc;
         }
 
+        /* Update last_dispatched to make sure batched callbacks are added in the
+         * same sequence as before they were executed and to save some iterations
+         * of the linear search for callbacks to be added further back in the list. */
+        last_dispatched = tc;
+
         /* Add entry to the new position in the sorted list */
         SLIST_INSERT_AFTER(prev_tc, tc, next);
     }

+ 8 - 1
src/ua_timer.h

@@ -18,7 +18,14 @@ extern "C" {
 /* An (event) timer triggers callbacks with a recurring interval. Adding,
  * removing and changing repeated callbacks can be done from independent
  * threads. Processing the changes and dispatching callbacks must be done by a
- * single "mainloop" process. */
+ * single "mainloop" process.
+ * Timer callbacks with the same recurring interval are batched into blocks in
+ * order to reduce linear search for re-entry to the sorted list after processing.
+ * Callbacks are inserted in reversed order (last callback are put first in the block)
+ * to allow the monitored items of a subscription (if created in a sequence with the
+ * same publish/sample interval) to be executed before the subscription publish the
+ * notifications. When callbacks are entered to the timer list after execution they
+ * are added in the same order as before execution. */
 
 /* Forward declaration */
 struct UA_TimerCallbackEntry;