Daiki Kato / mbed-os-lychee

Dependents:   mbed-os-example-blinky-gr-lychee GR-Boads_Camera_sample GR-Boards_Audio_Recoder GR-Boads_Camera_DisplayApp ... more

Committer:
dkato
Date:
Fri Feb 02 05:42:23 2018 +0000
Revision:
0:f782d9c66c49
mbed-os for GR-LYCHEE

Who changed what in which revision?

UserRevisionLine numberNew contents of line
dkato 0:f782d9c66c49 1 /*
dkato 0:f782d9c66c49 2 * Flexible event queue for dispatching events
dkato 0:f782d9c66c49 3 *
dkato 0:f782d9c66c49 4 * Copyright (c) 2016 Christopher Haster
dkato 0:f782d9c66c49 5 *
dkato 0:f782d9c66c49 6 * Licensed under the Apache License, Version 2.0 (the "License");
dkato 0:f782d9c66c49 7 * you may not use this file except in compliance with the License.
dkato 0:f782d9c66c49 8 * You may obtain a copy of the License at
dkato 0:f782d9c66c49 9 *
dkato 0:f782d9c66c49 10 * http://www.apache.org/licenses/LICENSE-2.0
dkato 0:f782d9c66c49 11 *
dkato 0:f782d9c66c49 12 * Unless required by applicable law or agreed to in writing, software
dkato 0:f782d9c66c49 13 * distributed under the License is distributed on an "AS IS" BASIS,
dkato 0:f782d9c66c49 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
dkato 0:f782d9c66c49 15 * See the License for the specific language governing permissions and
dkato 0:f782d9c66c49 16 * limitations under the License.
dkato 0:f782d9c66c49 17 */
dkato 0:f782d9c66c49 18 #include "equeue/equeue.h"
dkato 0:f782d9c66c49 19
dkato 0:f782d9c66c49 20 #include <stdlib.h>
dkato 0:f782d9c66c49 21 #include <string.h>
dkato 0:f782d9c66c49 22
dkato 0:f782d9c66c49 23
dkato 0:f782d9c66c49 24 // calculate the relative-difference between absolute times while
dkato 0:f782d9c66c49 25 // correctly handling overflow conditions
dkato 0:f782d9c66c49 26 static inline int equeue_tickdiff(unsigned a, unsigned b) {
dkato 0:f782d9c66c49 27 return (int)(unsigned)(a - b);
dkato 0:f782d9c66c49 28 }
dkato 0:f782d9c66c49 29
dkato 0:f782d9c66c49 30 // calculate the relative-difference between absolute times, but
dkato 0:f782d9c66c49 31 // also clamp to zero, resulting in only non-zero values.
dkato 0:f782d9c66c49 32 static inline int equeue_clampdiff(unsigned a, unsigned b) {
dkato 0:f782d9c66c49 33 int diff = equeue_tickdiff(a, b);
dkato 0:f782d9c66c49 34 return ~(diff >> (8*sizeof(int)-1)) & diff;
dkato 0:f782d9c66c49 35 }
dkato 0:f782d9c66c49 36
dkato 0:f782d9c66c49 37 // Increment the unique id in an event, hiding the event from cancel
dkato 0:f782d9c66c49 38 static inline void equeue_incid(equeue_t *q, struct equeue_event *e) {
dkato 0:f782d9c66c49 39 e->id += 1;
dkato 0:f782d9c66c49 40 if (!(e->id << q->npw2)) {
dkato 0:f782d9c66c49 41 e->id = 1;
dkato 0:f782d9c66c49 42 }
dkato 0:f782d9c66c49 43 }
dkato 0:f782d9c66c49 44
dkato 0:f782d9c66c49 45
dkato 0:f782d9c66c49 46 // equeue lifetime management
dkato 0:f782d9c66c49 47 int equeue_create(equeue_t *q, size_t size) {
dkato 0:f782d9c66c49 48 // dynamically allocate the specified buffer
dkato 0:f782d9c66c49 49 void *buffer = malloc(size);
dkato 0:f782d9c66c49 50 if (!buffer) {
dkato 0:f782d9c66c49 51 return -1;
dkato 0:f782d9c66c49 52 }
dkato 0:f782d9c66c49 53
dkato 0:f782d9c66c49 54 int err = equeue_create_inplace(q, size, buffer);
dkato 0:f782d9c66c49 55 q->allocated = buffer;
dkato 0:f782d9c66c49 56 return err;
dkato 0:f782d9c66c49 57 }
dkato 0:f782d9c66c49 58
dkato 0:f782d9c66c49 59 int equeue_create_inplace(equeue_t *q, size_t size, void *buffer) {
dkato 0:f782d9c66c49 60 // setup queue around provided buffer
dkato 0:f782d9c66c49 61 q->buffer = buffer;
dkato 0:f782d9c66c49 62 q->allocated = 0;
dkato 0:f782d9c66c49 63
dkato 0:f782d9c66c49 64 q->npw2 = 0;
dkato 0:f782d9c66c49 65 for (unsigned s = size; s; s >>= 1) {
dkato 0:f782d9c66c49 66 q->npw2++;
dkato 0:f782d9c66c49 67 }
dkato 0:f782d9c66c49 68
dkato 0:f782d9c66c49 69 q->chunks = 0;
dkato 0:f782d9c66c49 70 q->slab.size = size;
dkato 0:f782d9c66c49 71 q->slab.data = buffer;
dkato 0:f782d9c66c49 72
dkato 0:f782d9c66c49 73 q->queue = 0;
dkato 0:f782d9c66c49 74 q->tick = equeue_tick();
dkato 0:f782d9c66c49 75 q->generation = 0;
dkato 0:f782d9c66c49 76 q->breaks = 0;
dkato 0:f782d9c66c49 77
dkato 0:f782d9c66c49 78 q->background.active = false;
dkato 0:f782d9c66c49 79 q->background.update = 0;
dkato 0:f782d9c66c49 80 q->background.timer = 0;
dkato 0:f782d9c66c49 81
dkato 0:f782d9c66c49 82 // initialize platform resources
dkato 0:f782d9c66c49 83 int err;
dkato 0:f782d9c66c49 84 err = equeue_sema_create(&q->eventsema);
dkato 0:f782d9c66c49 85 if (err < 0) {
dkato 0:f782d9c66c49 86 return err;
dkato 0:f782d9c66c49 87 }
dkato 0:f782d9c66c49 88
dkato 0:f782d9c66c49 89 err = equeue_mutex_create(&q->queuelock);
dkato 0:f782d9c66c49 90 if (err < 0) {
dkato 0:f782d9c66c49 91 return err;
dkato 0:f782d9c66c49 92 }
dkato 0:f782d9c66c49 93
dkato 0:f782d9c66c49 94 err = equeue_mutex_create(&q->memlock);
dkato 0:f782d9c66c49 95 if (err < 0) {
dkato 0:f782d9c66c49 96 return err;
dkato 0:f782d9c66c49 97 }
dkato 0:f782d9c66c49 98
dkato 0:f782d9c66c49 99 return 0;
dkato 0:f782d9c66c49 100 }
dkato 0:f782d9c66c49 101
dkato 0:f782d9c66c49 102 void equeue_destroy(equeue_t *q) {
dkato 0:f782d9c66c49 103 // call destructors on pending events
dkato 0:f782d9c66c49 104 for (struct equeue_event *es = q->queue; es; es = es->next) {
dkato 0:f782d9c66c49 105 for (struct equeue_event *e = q->queue; e; e = e->sibling) {
dkato 0:f782d9c66c49 106 if (e->dtor) {
dkato 0:f782d9c66c49 107 e->dtor(e + 1);
dkato 0:f782d9c66c49 108 }
dkato 0:f782d9c66c49 109 }
dkato 0:f782d9c66c49 110 }
dkato 0:f782d9c66c49 111
dkato 0:f782d9c66c49 112 // notify background timer
dkato 0:f782d9c66c49 113 if (q->background.update) {
dkato 0:f782d9c66c49 114 q->background.update(q->background.timer, -1);
dkato 0:f782d9c66c49 115 }
dkato 0:f782d9c66c49 116
dkato 0:f782d9c66c49 117 // clean up platform resources + memory
dkato 0:f782d9c66c49 118 equeue_mutex_destroy(&q->memlock);
dkato 0:f782d9c66c49 119 equeue_mutex_destroy(&q->queuelock);
dkato 0:f782d9c66c49 120 equeue_sema_destroy(&q->eventsema);
dkato 0:f782d9c66c49 121 free(q->allocated);
dkato 0:f782d9c66c49 122 }
dkato 0:f782d9c66c49 123
dkato 0:f782d9c66c49 124
dkato 0:f782d9c66c49 125 // equeue chunk allocation functions
dkato 0:f782d9c66c49 126 static struct equeue_event *equeue_mem_alloc(equeue_t *q, size_t size) {
dkato 0:f782d9c66c49 127 // add event overhead
dkato 0:f782d9c66c49 128 size += sizeof(struct equeue_event);
dkato 0:f782d9c66c49 129 size = (size + sizeof(void*)-1) & ~(sizeof(void*)-1);
dkato 0:f782d9c66c49 130
dkato 0:f782d9c66c49 131 equeue_mutex_lock(&q->memlock);
dkato 0:f782d9c66c49 132
dkato 0:f782d9c66c49 133 // check if a good chunk is available
dkato 0:f782d9c66c49 134 for (struct equeue_event **p = &q->chunks; *p; p = &(*p)->next) {
dkato 0:f782d9c66c49 135 if ((*p)->size >= size) {
dkato 0:f782d9c66c49 136 struct equeue_event *e = *p;
dkato 0:f782d9c66c49 137 if (e->sibling) {
dkato 0:f782d9c66c49 138 *p = e->sibling;
dkato 0:f782d9c66c49 139 (*p)->next = e->next;
dkato 0:f782d9c66c49 140 } else {
dkato 0:f782d9c66c49 141 *p = e->next;
dkato 0:f782d9c66c49 142 }
dkato 0:f782d9c66c49 143
dkato 0:f782d9c66c49 144 equeue_mutex_unlock(&q->memlock);
dkato 0:f782d9c66c49 145 return e;
dkato 0:f782d9c66c49 146 }
dkato 0:f782d9c66c49 147 }
dkato 0:f782d9c66c49 148
dkato 0:f782d9c66c49 149 // otherwise allocate a new chunk out of the slab
dkato 0:f782d9c66c49 150 if (q->slab.size >= size) {
dkato 0:f782d9c66c49 151 struct equeue_event *e = (struct equeue_event *)q->slab.data;
dkato 0:f782d9c66c49 152 q->slab.data += size;
dkato 0:f782d9c66c49 153 q->slab.size -= size;
dkato 0:f782d9c66c49 154 e->size = size;
dkato 0:f782d9c66c49 155 e->id = 1;
dkato 0:f782d9c66c49 156
dkato 0:f782d9c66c49 157 equeue_mutex_unlock(&q->memlock);
dkato 0:f782d9c66c49 158 return e;
dkato 0:f782d9c66c49 159 }
dkato 0:f782d9c66c49 160
dkato 0:f782d9c66c49 161 equeue_mutex_unlock(&q->memlock);
dkato 0:f782d9c66c49 162 return 0;
dkato 0:f782d9c66c49 163 }
dkato 0:f782d9c66c49 164
dkato 0:f782d9c66c49 165 static void equeue_mem_dealloc(equeue_t *q, struct equeue_event *e) {
dkato 0:f782d9c66c49 166 equeue_mutex_lock(&q->memlock);
dkato 0:f782d9c66c49 167
dkato 0:f782d9c66c49 168 // stick chunk into list of chunks
dkato 0:f782d9c66c49 169 struct equeue_event **p = &q->chunks;
dkato 0:f782d9c66c49 170 while (*p && (*p)->size < e->size) {
dkato 0:f782d9c66c49 171 p = &(*p)->next;
dkato 0:f782d9c66c49 172 }
dkato 0:f782d9c66c49 173
dkato 0:f782d9c66c49 174 if (*p && (*p)->size == e->size) {
dkato 0:f782d9c66c49 175 e->sibling = *p;
dkato 0:f782d9c66c49 176 e->next = (*p)->next;
dkato 0:f782d9c66c49 177 } else {
dkato 0:f782d9c66c49 178 e->sibling = 0;
dkato 0:f782d9c66c49 179 e->next = *p;
dkato 0:f782d9c66c49 180 }
dkato 0:f782d9c66c49 181 *p = e;
dkato 0:f782d9c66c49 182
dkato 0:f782d9c66c49 183 equeue_mutex_unlock(&q->memlock);
dkato 0:f782d9c66c49 184 }
dkato 0:f782d9c66c49 185
dkato 0:f782d9c66c49 186 void *equeue_alloc(equeue_t *q, size_t size) {
dkato 0:f782d9c66c49 187 struct equeue_event *e = equeue_mem_alloc(q, size);
dkato 0:f782d9c66c49 188 if (!e) {
dkato 0:f782d9c66c49 189 return 0;
dkato 0:f782d9c66c49 190 }
dkato 0:f782d9c66c49 191
dkato 0:f782d9c66c49 192 e->target = 0;
dkato 0:f782d9c66c49 193 e->period = -1;
dkato 0:f782d9c66c49 194 e->dtor = 0;
dkato 0:f782d9c66c49 195
dkato 0:f782d9c66c49 196 return e + 1;
dkato 0:f782d9c66c49 197 }
dkato 0:f782d9c66c49 198
dkato 0:f782d9c66c49 199 void equeue_dealloc(equeue_t *q, void *p) {
dkato 0:f782d9c66c49 200 struct equeue_event *e = (struct equeue_event*)p - 1;
dkato 0:f782d9c66c49 201
dkato 0:f782d9c66c49 202 if (e->dtor) {
dkato 0:f782d9c66c49 203 e->dtor(e+1);
dkato 0:f782d9c66c49 204 }
dkato 0:f782d9c66c49 205
dkato 0:f782d9c66c49 206 equeue_mem_dealloc(q, e);
dkato 0:f782d9c66c49 207 }
dkato 0:f782d9c66c49 208
dkato 0:f782d9c66c49 209
dkato 0:f782d9c66c49 210 // equeue scheduling functions
dkato 0:f782d9c66c49 211 static int equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned tick) {
dkato 0:f782d9c66c49 212 // setup event and hash local id with buffer offset for unique id
dkato 0:f782d9c66c49 213 int id = (e->id << q->npw2) | ((unsigned char *)e - q->buffer);
dkato 0:f782d9c66c49 214 e->target = tick + equeue_clampdiff(e->target, tick);
dkato 0:f782d9c66c49 215 e->generation = q->generation;
dkato 0:f782d9c66c49 216
dkato 0:f782d9c66c49 217 equeue_mutex_lock(&q->queuelock);
dkato 0:f782d9c66c49 218
dkato 0:f782d9c66c49 219 // find the event slot
dkato 0:f782d9c66c49 220 struct equeue_event **p = &q->queue;
dkato 0:f782d9c66c49 221 while (*p && equeue_tickdiff((*p)->target, e->target) < 0) {
dkato 0:f782d9c66c49 222 p = &(*p)->next;
dkato 0:f782d9c66c49 223 }
dkato 0:f782d9c66c49 224
dkato 0:f782d9c66c49 225 // insert at head in slot
dkato 0:f782d9c66c49 226 if (*p && (*p)->target == e->target) {
dkato 0:f782d9c66c49 227 e->next = (*p)->next;
dkato 0:f782d9c66c49 228 if (e->next) {
dkato 0:f782d9c66c49 229 e->next->ref = &e->next;
dkato 0:f782d9c66c49 230 }
dkato 0:f782d9c66c49 231
dkato 0:f782d9c66c49 232 e->sibling = *p;
dkato 0:f782d9c66c49 233 e->sibling->ref = &e->sibling;
dkato 0:f782d9c66c49 234 } else {
dkato 0:f782d9c66c49 235 e->next = *p;
dkato 0:f782d9c66c49 236 if (e->next) {
dkato 0:f782d9c66c49 237 e->next->ref = &e->next;
dkato 0:f782d9c66c49 238 }
dkato 0:f782d9c66c49 239
dkato 0:f782d9c66c49 240 e->sibling = 0;
dkato 0:f782d9c66c49 241 }
dkato 0:f782d9c66c49 242
dkato 0:f782d9c66c49 243 *p = e;
dkato 0:f782d9c66c49 244 e->ref = p;
dkato 0:f782d9c66c49 245
dkato 0:f782d9c66c49 246 // notify background timer
dkato 0:f782d9c66c49 247 if ((q->background.update && q->background.active) &&
dkato 0:f782d9c66c49 248 (q->queue == e && !e->sibling)) {
dkato 0:f782d9c66c49 249 q->background.update(q->background.timer,
dkato 0:f782d9c66c49 250 equeue_clampdiff(e->target, tick));
dkato 0:f782d9c66c49 251 }
dkato 0:f782d9c66c49 252
dkato 0:f782d9c66c49 253 equeue_mutex_unlock(&q->queuelock);
dkato 0:f782d9c66c49 254
dkato 0:f782d9c66c49 255 return id;
dkato 0:f782d9c66c49 256 }
dkato 0:f782d9c66c49 257
dkato 0:f782d9c66c49 258 static struct equeue_event *equeue_unqueue(equeue_t *q, int id) {
dkato 0:f782d9c66c49 259 // decode event from unique id and check that the local id matches
dkato 0:f782d9c66c49 260 struct equeue_event *e = (struct equeue_event *)
dkato 0:f782d9c66c49 261 &q->buffer[id & ((1 << q->npw2)-1)];
dkato 0:f782d9c66c49 262
dkato 0:f782d9c66c49 263 equeue_mutex_lock(&q->queuelock);
dkato 0:f782d9c66c49 264 if (e->id != id >> q->npw2) {
dkato 0:f782d9c66c49 265 equeue_mutex_unlock(&q->queuelock);
dkato 0:f782d9c66c49 266 return 0;
dkato 0:f782d9c66c49 267 }
dkato 0:f782d9c66c49 268
dkato 0:f782d9c66c49 269 // clear the event and check if already in-flight
dkato 0:f782d9c66c49 270 e->cb = 0;
dkato 0:f782d9c66c49 271 e->period = -1;
dkato 0:f782d9c66c49 272
dkato 0:f782d9c66c49 273 int diff = equeue_tickdiff(e->target, q->tick);
dkato 0:f782d9c66c49 274 if (diff < 0 || (diff == 0 && e->generation != q->generation)) {
dkato 0:f782d9c66c49 275 equeue_mutex_unlock(&q->queuelock);
dkato 0:f782d9c66c49 276 return 0;
dkato 0:f782d9c66c49 277 }
dkato 0:f782d9c66c49 278
dkato 0:f782d9c66c49 279 // disentangle from queue
dkato 0:f782d9c66c49 280 if (e->sibling) {
dkato 0:f782d9c66c49 281 e->sibling->next = e->next;
dkato 0:f782d9c66c49 282 if (e->sibling->next) {
dkato 0:f782d9c66c49 283 e->sibling->next->ref = &e->sibling->next;
dkato 0:f782d9c66c49 284 }
dkato 0:f782d9c66c49 285
dkato 0:f782d9c66c49 286 *e->ref = e->sibling;
dkato 0:f782d9c66c49 287 e->sibling->ref = e->ref;
dkato 0:f782d9c66c49 288 } else {
dkato 0:f782d9c66c49 289 *e->ref = e->next;
dkato 0:f782d9c66c49 290 if (e->next) {
dkato 0:f782d9c66c49 291 e->next->ref = e->ref;
dkato 0:f782d9c66c49 292 }
dkato 0:f782d9c66c49 293 }
dkato 0:f782d9c66c49 294
dkato 0:f782d9c66c49 295 equeue_incid(q, e);
dkato 0:f782d9c66c49 296 equeue_mutex_unlock(&q->queuelock);
dkato 0:f782d9c66c49 297
dkato 0:f782d9c66c49 298 return e;
dkato 0:f782d9c66c49 299 }
dkato 0:f782d9c66c49 300
dkato 0:f782d9c66c49 301 static struct equeue_event *equeue_dequeue(equeue_t *q, unsigned target) {
dkato 0:f782d9c66c49 302 equeue_mutex_lock(&q->queuelock);
dkato 0:f782d9c66c49 303
dkato 0:f782d9c66c49 304 // find all expired events and mark a new generation
dkato 0:f782d9c66c49 305 q->generation += 1;
dkato 0:f782d9c66c49 306 if (equeue_tickdiff(q->tick, target) <= 0) {
dkato 0:f782d9c66c49 307 q->tick = target;
dkato 0:f782d9c66c49 308 }
dkato 0:f782d9c66c49 309
dkato 0:f782d9c66c49 310 struct equeue_event *head = q->queue;
dkato 0:f782d9c66c49 311 struct equeue_event **p = &head;
dkato 0:f782d9c66c49 312 while (*p && equeue_tickdiff((*p)->target, target) <= 0) {
dkato 0:f782d9c66c49 313 p = &(*p)->next;
dkato 0:f782d9c66c49 314 }
dkato 0:f782d9c66c49 315
dkato 0:f782d9c66c49 316 q->queue = *p;
dkato 0:f782d9c66c49 317 if (q->queue) {
dkato 0:f782d9c66c49 318 q->queue->ref = &q->queue;
dkato 0:f782d9c66c49 319 }
dkato 0:f782d9c66c49 320
dkato 0:f782d9c66c49 321 *p = 0;
dkato 0:f782d9c66c49 322
dkato 0:f782d9c66c49 323 equeue_mutex_unlock(&q->queuelock);
dkato 0:f782d9c66c49 324
dkato 0:f782d9c66c49 325 // reverse and flatten each slot to match insertion order
dkato 0:f782d9c66c49 326 struct equeue_event **tail = &head;
dkato 0:f782d9c66c49 327 struct equeue_event *ess = head;
dkato 0:f782d9c66c49 328 while (ess) {
dkato 0:f782d9c66c49 329 struct equeue_event *es = ess;
dkato 0:f782d9c66c49 330 ess = es->next;
dkato 0:f782d9c66c49 331
dkato 0:f782d9c66c49 332 struct equeue_event *prev = 0;
dkato 0:f782d9c66c49 333 for (struct equeue_event *e = es; e; e = e->sibling) {
dkato 0:f782d9c66c49 334 e->next = prev;
dkato 0:f782d9c66c49 335 prev = e;
dkato 0:f782d9c66c49 336 }
dkato 0:f782d9c66c49 337
dkato 0:f782d9c66c49 338 *tail = prev;
dkato 0:f782d9c66c49 339 tail = &es->next;
dkato 0:f782d9c66c49 340 }
dkato 0:f782d9c66c49 341
dkato 0:f782d9c66c49 342 return head;
dkato 0:f782d9c66c49 343 }
dkato 0:f782d9c66c49 344
dkato 0:f782d9c66c49 345 int equeue_post(equeue_t *q, void (*cb)(void*), void *p) {
dkato 0:f782d9c66c49 346 struct equeue_event *e = (struct equeue_event*)p - 1;
dkato 0:f782d9c66c49 347 unsigned tick = equeue_tick();
dkato 0:f782d9c66c49 348 e->cb = cb;
dkato 0:f782d9c66c49 349 e->target = tick + e->target;
dkato 0:f782d9c66c49 350
dkato 0:f782d9c66c49 351 int id = equeue_enqueue(q, e, tick);
dkato 0:f782d9c66c49 352 equeue_sema_signal(&q->eventsema);
dkato 0:f782d9c66c49 353 return id;
dkato 0:f782d9c66c49 354 }
dkato 0:f782d9c66c49 355
dkato 0:f782d9c66c49 356 void equeue_cancel(equeue_t *q, int id) {
dkato 0:f782d9c66c49 357 if (!id) {
dkato 0:f782d9c66c49 358 return;
dkato 0:f782d9c66c49 359 }
dkato 0:f782d9c66c49 360
dkato 0:f782d9c66c49 361 struct equeue_event *e = equeue_unqueue(q, id);
dkato 0:f782d9c66c49 362 if (e) {
dkato 0:f782d9c66c49 363 equeue_dealloc(q, e + 1);
dkato 0:f782d9c66c49 364 }
dkato 0:f782d9c66c49 365 }
dkato 0:f782d9c66c49 366
dkato 0:f782d9c66c49 367 void equeue_break(equeue_t *q) {
dkato 0:f782d9c66c49 368 equeue_mutex_lock(&q->queuelock);
dkato 0:f782d9c66c49 369 q->breaks++;
dkato 0:f782d9c66c49 370 equeue_mutex_unlock(&q->queuelock);
dkato 0:f782d9c66c49 371 equeue_sema_signal(&q->eventsema);
dkato 0:f782d9c66c49 372 }
dkato 0:f782d9c66c49 373
dkato 0:f782d9c66c49 374 void equeue_dispatch(equeue_t *q, int ms) {
dkato 0:f782d9c66c49 375 unsigned tick = equeue_tick();
dkato 0:f782d9c66c49 376 unsigned timeout = tick + ms;
dkato 0:f782d9c66c49 377 q->background.active = false;
dkato 0:f782d9c66c49 378
dkato 0:f782d9c66c49 379 while (1) {
dkato 0:f782d9c66c49 380 // collect all the available events and next deadline
dkato 0:f782d9c66c49 381 struct equeue_event *es = equeue_dequeue(q, tick);
dkato 0:f782d9c66c49 382
dkato 0:f782d9c66c49 383 // dispatch events
dkato 0:f782d9c66c49 384 while (es) {
dkato 0:f782d9c66c49 385 struct equeue_event *e = es;
dkato 0:f782d9c66c49 386 es = e->next;
dkato 0:f782d9c66c49 387
dkato 0:f782d9c66c49 388 // actually dispatch the callbacks
dkato 0:f782d9c66c49 389 void (*cb)(void *) = e->cb;
dkato 0:f782d9c66c49 390 if (cb) {
dkato 0:f782d9c66c49 391 cb(e + 1);
dkato 0:f782d9c66c49 392 }
dkato 0:f782d9c66c49 393
dkato 0:f782d9c66c49 394 // reenqueue periodic events or deallocate
dkato 0:f782d9c66c49 395 if (e->period >= 0) {
dkato 0:f782d9c66c49 396 e->target += e->period;
dkato 0:f782d9c66c49 397 equeue_enqueue(q, e, equeue_tick());
dkato 0:f782d9c66c49 398 } else {
dkato 0:f782d9c66c49 399 equeue_incid(q, e);
dkato 0:f782d9c66c49 400 equeue_dealloc(q, e+1);
dkato 0:f782d9c66c49 401 }
dkato 0:f782d9c66c49 402 }
dkato 0:f782d9c66c49 403
dkato 0:f782d9c66c49 404 int deadline = -1;
dkato 0:f782d9c66c49 405 tick = equeue_tick();
dkato 0:f782d9c66c49 406
dkato 0:f782d9c66c49 407 // check if we should stop dispatching soon
dkato 0:f782d9c66c49 408 if (ms >= 0) {
dkato 0:f782d9c66c49 409 deadline = equeue_tickdiff(timeout, tick);
dkato 0:f782d9c66c49 410 if (deadline <= 0) {
dkato 0:f782d9c66c49 411 // update background timer if necessary
dkato 0:f782d9c66c49 412 if (q->background.update) {
dkato 0:f782d9c66c49 413 equeue_mutex_lock(&q->queuelock);
dkato 0:f782d9c66c49 414 if (q->background.update && q->queue) {
dkato 0:f782d9c66c49 415 q->background.update(q->background.timer,
dkato 0:f782d9c66c49 416 equeue_clampdiff(q->queue->target, tick));
dkato 0:f782d9c66c49 417 }
dkato 0:f782d9c66c49 418 q->background.active = true;
dkato 0:f782d9c66c49 419 equeue_mutex_unlock(&q->queuelock);
dkato 0:f782d9c66c49 420 }
dkato 0:f782d9c66c49 421 return;
dkato 0:f782d9c66c49 422 }
dkato 0:f782d9c66c49 423 }
dkato 0:f782d9c66c49 424
dkato 0:f782d9c66c49 425 // find closest deadline
dkato 0:f782d9c66c49 426 equeue_mutex_lock(&q->queuelock);
dkato 0:f782d9c66c49 427 if (q->queue) {
dkato 0:f782d9c66c49 428 int diff = equeue_clampdiff(q->queue->target, tick);
dkato 0:f782d9c66c49 429 if ((unsigned)diff < (unsigned)deadline) {
dkato 0:f782d9c66c49 430 deadline = diff;
dkato 0:f782d9c66c49 431 }
dkato 0:f782d9c66c49 432 }
dkato 0:f782d9c66c49 433 equeue_mutex_unlock(&q->queuelock);
dkato 0:f782d9c66c49 434
dkato 0:f782d9c66c49 435 // wait for events
dkato 0:f782d9c66c49 436 equeue_sema_wait(&q->eventsema, deadline);
dkato 0:f782d9c66c49 437
dkato 0:f782d9c66c49 438 // check if we were notified to break out of dispatch
dkato 0:f782d9c66c49 439 if (q->breaks) {
dkato 0:f782d9c66c49 440 equeue_mutex_lock(&q->queuelock);
dkato 0:f782d9c66c49 441 if (q->breaks > 0) {
dkato 0:f782d9c66c49 442 q->breaks--;
dkato 0:f782d9c66c49 443 equeue_mutex_unlock(&q->queuelock);
dkato 0:f782d9c66c49 444 return;
dkato 0:f782d9c66c49 445 }
dkato 0:f782d9c66c49 446 equeue_mutex_unlock(&q->queuelock);
dkato 0:f782d9c66c49 447 }
dkato 0:f782d9c66c49 448
dkato 0:f782d9c66c49 449 // update tick for next iteration
dkato 0:f782d9c66c49 450 tick = equeue_tick();
dkato 0:f782d9c66c49 451 }
dkato 0:f782d9c66c49 452 }
dkato 0:f782d9c66c49 453
dkato 0:f782d9c66c49 454
dkato 0:f782d9c66c49 455 // event functions
dkato 0:f782d9c66c49 456 void equeue_event_delay(void *p, int ms) {
dkato 0:f782d9c66c49 457 struct equeue_event *e = (struct equeue_event*)p - 1;
dkato 0:f782d9c66c49 458 e->target = ms;
dkato 0:f782d9c66c49 459 }
dkato 0:f782d9c66c49 460
dkato 0:f782d9c66c49 461 void equeue_event_period(void *p, int ms) {
dkato 0:f782d9c66c49 462 struct equeue_event *e = (struct equeue_event*)p - 1;
dkato 0:f782d9c66c49 463 e->period = ms;
dkato 0:f782d9c66c49 464 }
dkato 0:f782d9c66c49 465
dkato 0:f782d9c66c49 466 void equeue_event_dtor(void *p, void (*dtor)(void *)) {
dkato 0:f782d9c66c49 467 struct equeue_event *e = (struct equeue_event*)p - 1;
dkato 0:f782d9c66c49 468 e->dtor = dtor;
dkato 0:f782d9c66c49 469 }
dkato 0:f782d9c66c49 470
dkato 0:f782d9c66c49 471
dkato 0:f782d9c66c49 472 // simple callbacks
dkato 0:f782d9c66c49 473 struct ecallback {
dkato 0:f782d9c66c49 474 void (*cb)(void*);
dkato 0:f782d9c66c49 475 void *data;
dkato 0:f782d9c66c49 476 };
dkato 0:f782d9c66c49 477
dkato 0:f782d9c66c49 478 static void ecallback_dispatch(void *p) {
dkato 0:f782d9c66c49 479 struct ecallback *e = (struct ecallback*)p;
dkato 0:f782d9c66c49 480 e->cb(e->data);
dkato 0:f782d9c66c49 481 }
dkato 0:f782d9c66c49 482
dkato 0:f782d9c66c49 483 int equeue_call(equeue_t *q, void (*cb)(void*), void *data) {
dkato 0:f782d9c66c49 484 struct ecallback *e = equeue_alloc(q, sizeof(struct ecallback));
dkato 0:f782d9c66c49 485 if (!e) {
dkato 0:f782d9c66c49 486 return 0;
dkato 0:f782d9c66c49 487 }
dkato 0:f782d9c66c49 488
dkato 0:f782d9c66c49 489 e->cb = cb;
dkato 0:f782d9c66c49 490 e->data = data;
dkato 0:f782d9c66c49 491 return equeue_post(q, ecallback_dispatch, e);
dkato 0:f782d9c66c49 492 }
dkato 0:f782d9c66c49 493
dkato 0:f782d9c66c49 494 int equeue_call_in(equeue_t *q, int ms, void (*cb)(void*), void *data) {
dkato 0:f782d9c66c49 495 struct ecallback *e = equeue_alloc(q, sizeof(struct ecallback));
dkato 0:f782d9c66c49 496 if (!e) {
dkato 0:f782d9c66c49 497 return 0;
dkato 0:f782d9c66c49 498 }
dkato 0:f782d9c66c49 499
dkato 0:f782d9c66c49 500 equeue_event_delay(e, ms);
dkato 0:f782d9c66c49 501 e->cb = cb;
dkato 0:f782d9c66c49 502 e->data = data;
dkato 0:f782d9c66c49 503 return equeue_post(q, ecallback_dispatch, e);
dkato 0:f782d9c66c49 504 }
dkato 0:f782d9c66c49 505
dkato 0:f782d9c66c49 506 int equeue_call_every(equeue_t *q, int ms, void (*cb)(void*), void *data) {
dkato 0:f782d9c66c49 507 struct ecallback *e = equeue_alloc(q, sizeof(struct ecallback));
dkato 0:f782d9c66c49 508 if (!e) {
dkato 0:f782d9c66c49 509 return 0;
dkato 0:f782d9c66c49 510 }
dkato 0:f782d9c66c49 511
dkato 0:f782d9c66c49 512 equeue_event_delay(e, ms);
dkato 0:f782d9c66c49 513 equeue_event_period(e, ms);
dkato 0:f782d9c66c49 514 e->cb = cb;
dkato 0:f782d9c66c49 515 e->data = data;
dkato 0:f782d9c66c49 516 return equeue_post(q, ecallback_dispatch, e);
dkato 0:f782d9c66c49 517 }
dkato 0:f782d9c66c49 518
dkato 0:f782d9c66c49 519
dkato 0:f782d9c66c49 520 // backgrounding
dkato 0:f782d9c66c49 521 void equeue_background(equeue_t *q,
dkato 0:f782d9c66c49 522 void (*update)(void *timer, int ms), void *timer) {
dkato 0:f782d9c66c49 523 equeue_mutex_lock(&q->queuelock);
dkato 0:f782d9c66c49 524 if (q->background.update) {
dkato 0:f782d9c66c49 525 q->background.update(q->background.timer, -1);
dkato 0:f782d9c66c49 526 }
dkato 0:f782d9c66c49 527
dkato 0:f782d9c66c49 528 q->background.update = update;
dkato 0:f782d9c66c49 529 q->background.timer = timer;
dkato 0:f782d9c66c49 530
dkato 0:f782d9c66c49 531 if (q->background.update && q->queue) {
dkato 0:f782d9c66c49 532 q->background.update(q->background.timer,
dkato 0:f782d9c66c49 533 equeue_clampdiff(q->queue->target, equeue_tick()));
dkato 0:f782d9c66c49 534 }
dkato 0:f782d9c66c49 535 q->background.active = true;
dkato 0:f782d9c66c49 536 equeue_mutex_unlock(&q->queuelock);
dkato 0:f782d9c66c49 537 }
dkato 0:f782d9c66c49 538
dkato 0:f782d9c66c49 539 struct equeue_chain_context {
dkato 0:f782d9c66c49 540 equeue_t *q;
dkato 0:f782d9c66c49 541 equeue_t *target;
dkato 0:f782d9c66c49 542 int id;
dkato 0:f782d9c66c49 543 };
dkato 0:f782d9c66c49 544
dkato 0:f782d9c66c49 545 static void equeue_chain_dispatch(void *p) {
dkato 0:f782d9c66c49 546 equeue_dispatch((equeue_t *)p, 0);
dkato 0:f782d9c66c49 547 }
dkato 0:f782d9c66c49 548
dkato 0:f782d9c66c49 549 static void equeue_chain_update(void *p, int ms) {
dkato 0:f782d9c66c49 550 struct equeue_chain_context *c = (struct equeue_chain_context *)p;
dkato 0:f782d9c66c49 551 equeue_cancel(c->target, c->id);
dkato 0:f782d9c66c49 552
dkato 0:f782d9c66c49 553 if (ms >= 0) {
dkato 0:f782d9c66c49 554 c->id = equeue_call_in(c->target, ms, equeue_chain_dispatch, c->q);
dkato 0:f782d9c66c49 555 } else {
dkato 0:f782d9c66c49 556 equeue_dealloc(c->target, c);
dkato 0:f782d9c66c49 557 }
dkato 0:f782d9c66c49 558 }
dkato 0:f782d9c66c49 559
dkato 0:f782d9c66c49 560 void equeue_chain(equeue_t *q, equeue_t *target) {
dkato 0:f782d9c66c49 561 if (!target) {
dkato 0:f782d9c66c49 562 equeue_background(q, 0, 0);
dkato 0:f782d9c66c49 563 return;
dkato 0:f782d9c66c49 564 }
dkato 0:f782d9c66c49 565
dkato 0:f782d9c66c49 566 struct equeue_chain_context *c = equeue_alloc(q,
dkato 0:f782d9c66c49 567 sizeof(struct equeue_chain_context));
dkato 0:f782d9c66c49 568
dkato 0:f782d9c66c49 569 c->q = q;
dkato 0:f782d9c66c49 570 c->target = target;
dkato 0:f782d9c66c49 571 c->id = 0;
dkato 0:f782d9c66c49 572
dkato 0:f782d9c66c49 573 equeue_background(q, equeue_chain_update, c);
dkato 0:f782d9c66c49 574 }