1 contributor
261 lines6.2 KB
1#include "asleep.h"
2#include "task.h"
3#include <assert.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <time.h>
7#include <errno.h>
8#include <math.h>
9#include <string.h>
10#include "timespec_utils.h"
11
12
13typedef struct Loop_Timer {
14 struct timespec when;
15 Future *who;
16} Loop_Timer;
17
18typedef struct _ASleep {
19 pthread_mutex_t mutex;
20 unsigned users;
21 Loop_Timer *sleepers;
22 int lo;
23 int hi;
24 int max;
25 pthread_cond_t cond;
26 bool sleeploop_cancel;
27 pthread_t sleeploop;
28 bool started;
29} _ASleep;
30
31static _ASleep asleep;
32
33static void *ASleep_Sleeper(void *param);
34
35static void ASleep_ctor(
36 _ASleep *me
37){
38 int r;
39
40 me->sleepers = NULL;
41 me->lo = 0;
42 me->hi = 0;
43 me->max = 0;
44
45 r = pthread_mutex_init(&me->mutex, NULL);
46 assert(r == 0);
47 r = pthread_cond_init(&me->cond, NULL);
48 assert(r == 0);
49 me->sleeploop_cancel = false;
50 r = pthread_create(&me->sleeploop, NULL, ASleep_Sleeper, me);
51 assert(r == 0);
52 me->started = true;
53}
54
55
56
57static void ASleep_dtor(
58 _ASleep *me
59){
60 int r;
61 me->started = false;
62 me->sleeploop_cancel = true;
63 r = pthread_cond_signal(&me->cond);
64 assert(r == 0);
65 r = pthread_join(me->sleeploop, NULL);
66 assert(r == 0);
67 r = pthread_cond_destroy(&me->cond);
68 assert(r == 0);
69 pthread_mutex_destroy(&me->mutex);
70 assert(r == 0);
71 free(me->sleepers);
72}
73
74
75void ASleep_StartSystem(){
76 ASleep_ctor(&asleep);
77}
78
79
80void ASleep_StopSystem(){
81 ASleep_dtor(&asleep);
82}
83
84
85static void ASleep_AddSleeper(
86 _ASleep *me,
87 struct timespec when,
88 Future *who
89){
90 int r = pthread_mutex_lock(&me->mutex);
91 assert(r == 0);
92
93 int nsleepers;
94 if (me->lo <= me->hi){
95 int nsleepers = me->hi - me->lo;
96 // ensure there's always an empty slot so that lo == hi always means no sleepers
97 if (nsleepers+1 >= me->max) {
98 me->max = (me->max == 0) ? 4 : me->max * 2;
99 me->sleepers = realloc(me->sleepers, me->max * sizeof(Loop_Timer));
100 assert(me->sleepers);
101 }
102 } else {
103 nsleepers = me->hi + me->max - me->lo;
104 // ensure there's always an empty slot so that lo == hi always means no sleepers
105 if (nsleepers+1 >= me->max) {
106 int oldmax = me->max;
107 me->max = (me->max == 0) ? 4 : me->max * 2;
108 me->sleepers = realloc(me->sleepers, me->max * sizeof(Loop_Timer));
109 assert(me->sleepers);
110 memmove(&me->sleepers[me->lo], &me->lo + me->max - oldmax, sizeof(*me->sleepers) * oldmax - me->lo);
111 me->lo += me->max - oldmax;
112 }
113 }
114
115 // insertion sort - we're assuming there's not going to be many sleepers active at once
116 int i;
117 int nexti;
118 for (i = me->hi; i != me->lo; i = nexti){
119 nexti = i-1;
120 if (i < 0){
121 nexti += me->max;
122 }
123 if (timespec_lte(me->sleepers[nexti].when, when)){
124 break;
125 }
126 me->sleepers[i] = me->sleepers[nexti];
127 }
128 me->sleepers[i].when = when;
129 me->sleepers[i].who = who;
130 me->hi += 1;
131 if (me->hi >= me->max){
132 me->hi -= me->max;
133 }
134
135 r = pthread_cond_signal(&me->cond);
136 assert(r == 0);
137
138 r = pthread_mutex_unlock(&me->mutex);
139 assert(r == 0);
140}
141
142
143static void ASleep_RemoveSleeper(
144 _ASleep *me,
145 struct timespec when,
146 Future *who
147){
148 int r = pthread_mutex_lock(&me->mutex);
149 assert(r == 0);
150
151 int i;
152 for (i = me->lo; i != me->hi; ){
153 if (timespec_eq(me->sleepers[i].when, when) && me->sleepers[i].who == who){
154 int previ = i;
155 i += 1;
156 if (i >= me->max){
157 i -= me->max;
158 }
159 for (; i != me->hi; ){
160 me->sleepers[previ] = me->sleepers[i];
161 previ = i;
162 i += 1;
163 if (i >= me->max){
164 i -= me->max;
165 }
166 }
167 me->hi = previ;
168 break;
169 }
170 i += 1;
171 if (i >= me->max){
172 i -= me->max;
173 }
174 }
175
176 r = pthread_cond_signal(&me->cond);
177 assert(r == 0);
178
179 r = pthread_mutex_unlock(&me->mutex);
180 assert(r == 0);
181}
182
183
184static void *ASleep_Sleeper(
185 void *param
186){
187 _ASleep *me = (_ASleep *)param;
188 int r;
189
190 while (!me->sleeploop_cancel){
191 r = pthread_mutex_lock(&me->mutex);
192 assert(r == 0);
193 if (me->lo != me->hi){
194 struct timespec when = me->sleepers[0].when;
195
196 r = pthread_mutex_unlock(&me->mutex);
197 assert(r == 0);
198 r = pthread_cond_timedwait(&me->cond, &me->mutex, &when);
199 assert(r == 0 || r == ETIMEDOUT);
200 } else {
201 r = pthread_mutex_unlock(&me->mutex);
202 assert(r == 0);
203 r = pthread_cond_wait(&me->cond, &me->mutex);
204 assert(r == 0);
205 }
206 // issue any due timers
207 struct timespec now;
208 r = clock_gettime(CLOCK_REALTIME, &now);
209 assert(r == 0);
210 while(me->lo != me->hi && timespec_lte(me->sleepers[me->lo].when, now)) {
211 Future *fut = me->sleepers[me->lo].who;
212 me->lo += 1;
213 if (me->lo >= me->max){
214 me->lo -= me->max;
215 }
216 r = pthread_mutex_unlock(&me->mutex);
217 assert(r == 0);
218 Future_SetResult(fut, false, NULL);
219 r = pthread_mutex_lock(&me->mutex);
220 assert(r == 0);
221 r = clock_gettime(CLOCK_REALTIME, &now);
222 assert(r == 0);
223 }
224 r = pthread_mutex_unlock(&me->mutex);
225 assert(r == 0);
226 }
227
228 return NULL;
229}
230
231
232// value is a pointer to a float delay in seconds
233bool ASleep(
234 float delay,
235 void **value
236){
237 assert(current_task && asleep.started);
238 if (delay < 0){
239 delay = 0;
240 }
241 float secs = floorf(delay);
242 float nsecs = (delay - secs) * 1e9;
243 struct timespec endtime;
244 clock_gettime(CLOCK_REALTIME, &endtime);
245 endtime.tv_nsec += (int)nsecs;
246 if (endtime.tv_nsec > 1000000000){
247 endtime.tv_sec += 1;
248 endtime.tv_nsec -= 1000000000;
249 }
250 endtime.tv_sec += (int)secs;
251
252 Future fut;
253 Future_ctor(&fut);
254 ASleep_AddSleeper(&asleep, endtime, &fut);
255 bool res = Future_Await(&fut, value);
256 ASleep_RemoveSleeper(&asleep, endtime, &fut);
257 Future_dtor(&fut);
258
259 return res;
260}
261