123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- #include "ua_util.h"
- #include "ua_timer.h"
- struct UA_RepeatedJob {
- SLIST_ENTRY(UA_RepeatedJob) next;
- UA_DateTime nextTime;
- UA_UInt64 interval;
- UA_Guid id;
- UA_Job job;
- };
- void
- UA_RepeatedJobsList_init(UA_RepeatedJobsList *rjl,
- UA_RepeatedJobsListProcessCallback processCallback,
- void *processContext) {
- SLIST_INIT(&rjl->repeatedJobs);
- rjl->changes_head = (UA_RepeatedJob*)&rjl->changes_stub;
- rjl->changes_tail = (UA_RepeatedJob*)&rjl->changes_stub;
- rjl->changes_stub = NULL;
- rjl->processCallback = processCallback;
- rjl->processContext = processContext;
- }
- static void
- enqueueChange(UA_RepeatedJobsList *rjl, UA_RepeatedJob *rj) {
- rj->next.sle_next = NULL;
- UA_RepeatedJob *prev = UA_atomic_xchg((void* volatile *)&rjl->changes_head, rj);
-
- prev->next.sle_next = rj;
- }
- static UA_RepeatedJob *
- dequeueChange(UA_RepeatedJobsList *rjl) {
- UA_RepeatedJob *tail = rjl->changes_tail;
- UA_RepeatedJob *next = tail->next.sle_next;
- if(tail == (UA_RepeatedJob*)&rjl->changes_stub) {
- if(!next)
- return NULL;
- rjl->changes_tail = next;
- tail = next;
- next = next->next.sle_next;
- }
- if(next) {
- rjl->changes_tail = next;
- return tail;
- }
- UA_RepeatedJob* head = rjl->changes_head;
- if(tail != head)
- return NULL;
- enqueueChange(rjl, (UA_RepeatedJob*)&rjl->changes_stub);
- next = tail->next.sle_next;
- if(next) {
- rjl->changes_tail = next;
- return tail;
- }
- return NULL;
- }
- UA_StatusCode
- UA_RepeatedJobsList_addRepeatedJob(UA_RepeatedJobsList *rjl, const UA_Job job,
- const UA_UInt32 interval, UA_Guid *jobId) {
-
- if(interval < 5)
- return UA_STATUSCODE_BADINTERNALERROR;
-
- UA_RepeatedJob *rj = (UA_RepeatedJob*)UA_malloc(sizeof(UA_RepeatedJob));
- if(!rj)
- return UA_STATUSCODE_BADOUTOFMEMORY;
-
- rj->interval = (UA_UInt64)interval * (UA_UInt64)UA_MSEC_TO_DATETIME;
- rj->id = UA_Guid_random();
- rj->job = job;
- rj->nextTime = UA_DateTime_nowMonotonic() + (UA_DateTime)rj->interval;
-
- if(jobId)
- *jobId = rj->id;
-
- enqueueChange(rjl, rj);
- return UA_STATUSCODE_GOOD;
- }
- static void
- addRepeatedJob(UA_RepeatedJobsList *rjl,
- UA_RepeatedJob * UA_RESTRICT rj,
- UA_DateTime nowMonotonic) {
-
- rj->nextTime = nowMonotonic + (UA_Int64)rj->interval;
-
- UA_RepeatedJob *tmpRj, *afterRj = NULL;
- SLIST_FOREACH(tmpRj, &rjl->repeatedJobs, next) {
- if(tmpRj->nextTime >= rj->nextTime)
- break;
- afterRj = tmpRj;
-
- if(tmpRj->interval == rj->interval &&
- tmpRj->nextTime > (rj->nextTime - UA_SEC_TO_DATETIME))
- rj->nextTime = tmpRj->nextTime;
- }
-
- if(afterRj)
- SLIST_INSERT_AFTER(afterRj, rj, next);
- else
- SLIST_INSERT_HEAD(&rjl->repeatedJobs, rj, next);
- }
- UA_StatusCode
- UA_RepeatedJobsList_removeRepeatedJob(UA_RepeatedJobsList *rjl, const UA_Guid jobId) {
-
- UA_RepeatedJob *rj = (UA_RepeatedJob*)UA_malloc(sizeof(UA_RepeatedJob));
- if(!rj)
- return UA_STATUSCODE_BADOUTOFMEMORY;
-
- rj->id = jobId;
- rj->nextTime = UA_INT64_MAX;
-
- enqueueChange(rjl, rj);
- return UA_STATUSCODE_GOOD;
- }
- static void
- removeRepeatedJob(UA_RepeatedJobsList *rjl, const UA_Guid *jobId) {
- UA_RepeatedJob *rj, *prev = NULL;
- SLIST_FOREACH(rj, &rjl->repeatedJobs, next) {
- if(UA_Guid_equal(jobId, &rj->id)) {
- if(prev)
- SLIST_REMOVE_AFTER(prev, next);
- else
- SLIST_REMOVE_HEAD(&rjl->repeatedJobs, next);
- UA_free(rj);
- break;
- }
- prev = rj;
- }
- }
- static void
- processChanges(UA_RepeatedJobsList *rjl, UA_DateTime nowMonotonic) {
- UA_RepeatedJob *change;
- while((change = dequeueChange(rjl))) {
- if(change->nextTime < UA_INT64_MAX) {
- addRepeatedJob(rjl, change, nowMonotonic);
- } else {
- removeRepeatedJob(rjl, &change->id);
- UA_free(change);
- }
- }
- }
- UA_DateTime
- UA_RepeatedJobsList_process(UA_RepeatedJobsList *rjl,
- UA_DateTime nowMonotonic,
- UA_Boolean *dispatched) {
-
- processChanges(rjl, nowMonotonic);
-
- UA_RepeatedJob *firstAfter, *lastNow = NULL;
- SLIST_FOREACH(firstAfter, &rjl->repeatedJobs, next) {
- if(firstAfter->nextTime > nowMonotonic)
- break;
- lastNow = firstAfter;
- }
-
- if(!lastNow) {
- if(firstAfter)
- return firstAfter->nextTime;
- return UA_INT64_MAX;
- }
-
- struct memberstruct(UA_RepeatedJobsList,RepeatedJobsSList) executedNowList;
- executedNowList.slh_first = SLIST_FIRST(&rjl->repeatedJobs);
- lastNow->next.sle_next = NULL;
-
- UA_RepeatedJob tmp_first;
- tmp_first.nextTime = nowMonotonic - 1;
- tmp_first.next.sle_next = firstAfter;
- UA_RepeatedJob *last_dispatched = &tmp_first;
-
- UA_RepeatedJob *rj;
- while((rj = SLIST_FIRST(&executedNowList))) {
-
- SLIST_REMOVE_HEAD(&executedNowList, next);
-
- rjl->processCallback(rjl->processContext, &rj->job);
- *dispatched = true;
-
- rj->nextTime += (UA_Int64)rj->interval;
- if(rj->nextTime < nowMonotonic)
- rj->nextTime = nowMonotonic + 1;
-
- UA_RepeatedJob *prev_rj;
- if(last_dispatched->nextTime == rj->nextTime) {
-
- UA_assert(last_dispatched != &tmp_first);
- prev_rj = last_dispatched;
- } else {
-
- prev_rj = &tmp_first;
- while(true) {
- UA_RepeatedJob *n = SLIST_NEXT(prev_rj, next);
- if(!n || n->nextTime >= rj->nextTime)
- break;
- prev_rj = n;
- }
-
- last_dispatched = rj;
- }
-
- SLIST_INSERT_AFTER(prev_rj, rj, next);
- }
-
- rjl->repeatedJobs.slh_first = tmp_first.next.sle_next;
-
- processChanges(rjl, nowMonotonic);
-
- return SLIST_FIRST(&rjl->repeatedJobs)->nextTime;
- }
- void
- UA_RepeatedJobsList_deleteMembers(UA_RepeatedJobsList *rjl) {
-
- processChanges(rjl, 0);
-
- UA_RepeatedJob *current;
- while((current = SLIST_FIRST(&rjl->repeatedJobs))) {
- SLIST_REMOVE_HEAD(&rjl->repeatedJobs, next);
- UA_free(current);
- }
- }
|