| #include "asleep.h" |
| #include "task.h" |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <math.h> |
| #include <string.h> |
| #include "cor_platform.h" |
|
|
| typedef struct Loop_Timer { |
| int64_t when; |
| Future *who; |
| } Loop_Timer; |
|
| typedef struct _ASleep { |
| _Cor_Mutex mutex; |
| unsigned users; |
| Loop_Timer *sleepers; |
| int lo; |
| int hi; |
| int max; |
| _Cor_Semaphore sem; |
| bool sleeploop_cancel; |
| _Cor_Thread sleeploop; |
| bool started; |
| } _ASleep; |
|
| static _ASleep asleep; |
|
| static void *ASleep_Sleeper(void *param); |
|
| static void ASleep_ctor( |
| _ASleep *me |
| ){ |
|
|
| me->sleepers = NULL; |
| me->lo = 0; |
| me->hi = 0; |
| me->max = 0; |
|
| _Cor_Mutex_ctor(&me->mutex); |
| _Cor_Semaphore_ctor(&me->sem); |
| me->sleeploop_cancel = false; |
| _Cor_Thread_ctor(&me->sleeploop, ASleep_Sleeper, me); |
|
| me->started = true; |
| } |
|
|
|
| static void ASleep_dtor( |
| _ASleep *me |
| ){ |
|
| me->started = false; |
| me->sleeploop_cancel = true; |
| _Cor_Semaphore_Signal(&me->sem); |
| _Cor_Thread_Join(&me->sleeploop); |
| _Cor_Thread_dtor(&me->sleeploop); |
| _Cor_Sempahore_dtor(&me->sem); |
| _Cor_Mutex_dtor(&me->mutex); |
| free(me->sleepers); |
| } |
|
|
| void ASleep_StartSystem(){ |
| ASleep_ctor(&asleep); |
| } |
|
|
| void ASleep_StopSystem(){ |
| ASleep_dtor(&asleep); |
| } |
|
|
| static void ASleep_AddSleeper( |
| _ASleep *me, |
| int64_t when, |
| Future *who |
| ){ |
| _Cor_Mutex_Lock(&me->mutex); |
|
| int nsleepers; |
| if (me->lo <= me->hi){ |
| int nsleepers = me->hi - me->lo; |
| // ensure there's always an empty slot so that lo == hi always means no sleepers |
| if (nsleepers+1 >= me->max) { |
| me->max = (me->max == 0) ? 4 : me->max * 2; |
| me->sleepers = realloc(me->sleepers, me->max * sizeof(Loop_Timer)); |
| assert(me->sleepers); |
| } |
| } else { |
| nsleepers = me->hi + me->max - me->lo; |
| // ensure there's always an empty slot so that lo == hi always means no sleepers |
| if (nsleepers+1 >= me->max) { |
| int oldmax = me->max; |
| me->max = (me->max == 0) ? 4 : me->max * 2; |
| me->sleepers = realloc(me->sleepers, me->max * sizeof(Loop_Timer)); |
| assert(me->sleepers); |
| memmove(&me->sleepers[me->lo], &me->lo + me->max - oldmax, sizeof(*me->sleepers) * oldmax - me->lo); |
| me->lo += me->max - oldmax; |
| } |
| } |
|
| // insertion sort - we're assuming there's not going to be many sleepers active at once |
| int i; |
| int nexti; |
| for (i = me->hi; i != me->lo; i = nexti){ |
| nexti = i-1; |
| if (i < 0){ |
| nexti += me->max; |
| } |
| if (me->sleepers[nexti].when <= when){ |
| break; |
| } |
| me->sleepers[i] = me->sleepers[nexti]; |
| } |
| me->sleepers[i].when = when; |
| me->sleepers[i].who = who; |
| me->hi += 1; |
| if (me->hi >= me->max){ |
| me->hi -= me->max; |
| } |
|
| _Cor_Mutex_Unlock(&me->mutex); |
| _Cor_Semaphore_Signal(&me->sem); |
| } |
|
|
| static void ASleep_RemoveSleeper( |
| _ASleep *me, |
| int64_t when, |
| Future *who |
| ){ |
| _Cor_Mutex_Lock(&me->mutex); |
|
| int i; |
| for (i = me->lo; i != me->hi; ){ |
| if (me->sleepers[i].when == when && me->sleepers[i].who == who){ |
| int previ = i; |
| i += 1; |
| if (i >= me->max){ |
| i -= me->max; |
| } |
| for (; i != me->hi; ){ |
| me->sleepers[previ] = me->sleepers[i]; |
| previ = i; |
| i += 1; |
| if (i >= me->max){ |
| i -= me->max; |
| } |
| } |
| me->hi = previ; |
| break; |
| } |
| i += 1; |
| if (i >= me->max){ |
| i -= me->max; |
| } |
| } |
|
| _Cor_Mutex_Unlock(&me->mutex); |
| _Cor_Semaphore_Signal(&me->sem); |
| } |
|
|
| static void *ASleep_Sleeper( |
| void *param |
| ){ |
| _ASleep *me = (_ASleep *)param; |
|
| while (!me->sleeploop_cancel){ |
| _Cor_Mutex_Lock(&me->mutex); |
| bool got_one; |
| if (me->lo != me->hi){ |
| int64_t when = me->sleepers[0].when; |
| _Cor_Mutex_Unlock(&me->mutex); |
| got_one = _Cor_Semaphore_Wait(&me->sem, when); |
| } else { |
| _Cor_Mutex_Unlock(&me->mutex); |
| got_one = _Cor_Semaphore_Wait(&me->sem, -1); |
| } |
| if (!got_one){ |
| // timed out, so... |
| // issue any due timers |
| int64_t now = _Cor_Realtime_Now(); |
| while(me->lo != me->hi && me->sleepers[me->lo].when <= now) { |
| Future *fut = me->sleepers[me->lo].who; |
| me->lo += 1; |
| if (me->lo >= me->max){ |
| me->lo -= me->max; |
| } |
| _Cor_Mutex_Unlock(&me->mutex); |
| Future_SetResult(fut, false, NULL); |
| _Cor_Mutex_Lock(&me->mutex); |
| now = _Cor_Realtime_Now(); |
| } |
| _Cor_Mutex_Unlock(&me->mutex); |
| } |
| } |
|
| return NULL; |
| } |
|
|
| // value is a pointer to a float delay in seconds |
| bool ASleep( |
| float delay, |
| void **value |
| ){ |
| assert(current_task && asleep.started); |
| if (delay < 0){ |
| delay = 0; |
| } |
| int64_t endtime = _Cor_Realtime_Now() + (int64_t)(delay * 1000000000); |
|
| Future fut; |
| Future_ctor(&fut); |
| ASleep_AddSleeper(&asleep, endtime, &fut); |
| bool res = Future_Await(&fut, value); |
| ASleep_RemoveSleeper(&asleep, endtime, &fut); |
| Future_dtor(&fut); |
|
| return res; |
| } |
|