Skip to content
This repository was archived by the owner on Aug 19, 2021. It is now read-only.

Commit 599964f

Browse files
committed
Moved to tick + generation counter to mark in-flight events
Before this commit, the event-id was negated to mark events that became in-flight. Marking each event required a O(n) operation in a critical-section. By using the property that in-flight events must have expired, events do not need to be manually marked, reducing the critical section to a O(1) operation per slot. Unfortunately, relying on the most recent dispatch tick is not sufficient in cases where events post events during the same tick. A simple generation counter was added to avoid this issue. Notable performance impact (make prof): equeue_dispatch_prof: 468 cycles (+20%)
1 parent 02ce85d commit 599964f

File tree

3 files changed

+137
-66
lines changed

3 files changed

+137
-66
lines changed

equeue.c

Lines changed: 82 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ int equeue_create_inplace(equeue_t *q, size_t size, void *buffer) {
3535
q->slab.data = buffer;
3636

3737
q->queue = 0;
38+
q->tick = equeue_tick();
39+
q->generation = 0;
3840
q->breaks = 0;
3941

4042
int err;
@@ -158,8 +160,19 @@ static inline int equeue_tickdiff(unsigned a, unsigned b) {
158160
return (int)(a - b);
159161
}
160162

161-
static void equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned ms) {
163+
static inline void equeue_incid(equeue_t *q, struct equeue_event *e) {
164+
e->id += 1;
165+
if (e->id >> (8*sizeof(int)-1 - q->npw2)) {
166+
e->id = 1;
167+
}
168+
}
169+
170+
static int equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned ms) {
171+
int id = (e->id << q->npw2) | ((unsigned char *)e - q->buffer);
162172
e->target = equeue_tick() + ms;
173+
e->generation = q->generation;
174+
175+
equeue_mutex_lock(&q->queuelock);
163176

164177
struct equeue_event **p = &q->queue;
165178
while (*p && equeue_tickdiff((*p)->target, e->target) < 0) {
@@ -185,9 +198,31 @@ static void equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned ms) {
185198

186199
*p = e;
187200
e->ref = p;
201+
202+
equeue_mutex_unlock(&q->queuelock);
203+
204+
return id;
188205
}
189206

190-
static void equeue_unqueue(equeue_t *q, struct equeue_event *e) {
207+
static struct equeue_event *equeue_unqueue(equeue_t *q, int id) {
208+
struct equeue_event *e = (struct equeue_event *)
209+
&q->buffer[id & ((1 << q->npw2)-1)];
210+
211+
equeue_mutex_lock(&q->queuelock);
212+
if (e->id != id >> q->npw2) {
213+
equeue_mutex_unlock(&q->queuelock);
214+
return 0;
215+
}
216+
217+
e->cb = 0;
218+
e->period = -1;
219+
220+
int diff = equeue_tickdiff(e->target, q->tick);
221+
if (diff < 0 || (diff == 0 && e->generation != q->generation)) {
222+
equeue_mutex_unlock(&q->queuelock);
223+
return 0;
224+
}
225+
191226
if (e->sibling) {
192227
e->sibling->next = e->next;
193228
if (e->sibling->next) {
@@ -202,16 +237,41 @@ static void equeue_unqueue(equeue_t *q, struct equeue_event *e) {
202237
e->next->ref = e->ref;
203238
}
204239
}
240+
241+
equeue_incid(q, e);
242+
equeue_mutex_unlock(&q->queuelock);
243+
244+
return e;
205245
}
206246

207-
static struct equeue_event *equeue_dequeue(equeue_t *q) {
208-
unsigned target = equeue_tick();
209-
struct equeue_event *head = 0;
210-
struct equeue_event **tail = &head;
247+
static struct equeue_event *equeue_dequeue(equeue_t *q, unsigned target) {
248+
equeue_mutex_lock(&q->queuelock);
211249

212-
while (q->queue && equeue_tickdiff(q->queue->target, target) <= 0) {
213-
struct equeue_event *es = q->queue;
214-
q->queue = es->next;
250+
q->generation += 1;
251+
if (equeue_tickdiff(q->tick, target) <= 0) {
252+
q->tick = target;
253+
}
254+
255+
struct equeue_event *head = q->queue;
256+
struct equeue_event **p = &head;
257+
while (*p && equeue_tickdiff((*p)->target, target) <= 0) {
258+
p = &(*p)->next;
259+
}
260+
261+
q->queue = *p;
262+
if (q->queue) {
263+
q->queue->ref = &q->queue;
264+
}
265+
266+
*p = 0;
267+
268+
equeue_mutex_unlock(&q->queuelock);
269+
270+
struct equeue_event **tail = &head;
271+
struct equeue_event *ess = head;
272+
while (ess) {
273+
struct equeue_event *es = ess;
274+
ess = es->next;
215275

216276
struct equeue_event *prev = 0;
217277
for (struct equeue_event *e = es; e; e = e->sibling) {
@@ -223,59 +283,23 @@ static struct equeue_event *equeue_dequeue(equeue_t *q) {
223283
tail = &es->next;
224284
}
225285

226-
if (q->queue) {
227-
q->queue->ref = &q->queue;
228-
}
229-
230286
return head;
231287
}
232288

233-
static inline int equeue_incid(equeue_t *q, int id) {
234-
if ((id+1) >> (8*sizeof(int)-1 - q->npw2)) {
235-
return 1;
236-
}
237-
238-
return id+1;
239-
}
240-
241289
int equeue_post(equeue_t *q, void (*cb)(void*), void *p) {
242290
struct equeue_event *e = (struct equeue_event*)p - 1;
243-
int id = (e->id << q->npw2) | ((unsigned char *)e - q->buffer);
244291
e->cb = cb;
245292

246-
if (e->target < 0) {
247-
equeue_dealloc(q, e+1);
248-
return id;
249-
}
250-
251-
equeue_mutex_lock(&q->queuelock);
252-
equeue_enqueue(q, e, e->target);
253-
equeue_mutex_unlock(&q->queuelock);
254-
293+
int id = equeue_enqueue(q, e, e->target);
255294
equeue_sema_signal(&q->eventsema);
256295
return id;
257296
}
258297

259298
void equeue_cancel(equeue_t *q, int id) {
260-
struct equeue_event *e = (struct equeue_event *)
261-
&q->buffer[id & ((1 << q->npw2)-1)];
262-
263-
equeue_mutex_lock(&q->queuelock);
264-
if (e->id == -id >> q->npw2) {
265-
e->cb = 0;
266-
e->period = -1;
267-
}
268-
269-
if (e->id != id >> q->npw2) {
270-
equeue_mutex_unlock(&q->queuelock);
271-
return;
299+
struct equeue_event *e = equeue_unqueue(q, id);
300+
if (e) {
301+
equeue_dealloc(q, e + 1);
272302
}
273-
274-
equeue_unqueue(q, e);
275-
e->id = equeue_incid(q, e->id);
276-
equeue_mutex_unlock(&q->queuelock);
277-
278-
equeue_dealloc(q, e+1);
279303
}
280304

281305
void equeue_break(equeue_t *q) {
@@ -286,18 +310,12 @@ void equeue_break(equeue_t *q) {
286310
}
287311

288312
void equeue_dispatch(equeue_t *q, int ms) {
289-
unsigned timeout = equeue_tick() + ms;
313+
unsigned tick = equeue_tick();
314+
unsigned timeout = tick + ms;
290315

291316
while (1) {
292317
// collect all the available events and next deadline
293-
equeue_mutex_lock(&q->queuelock);
294-
struct equeue_event *es = equeue_dequeue(q);
295-
296-
// mark events as in-flight
297-
for (struct equeue_event *e = es; e; e = e->next) {
298-
e->id = -e->id;
299-
}
300-
equeue_mutex_unlock(&q->queuelock);
318+
struct equeue_event *es = equeue_dequeue(q, tick);
301319

302320
// dispatch events
303321
while (es) {
@@ -310,20 +328,17 @@ void equeue_dispatch(equeue_t *q, int ms) {
310328
cb(e + 1);
311329
}
312330

313-
// undirty the id and either dealloc or reenqueue periodic events
331+
// reenqueue periodic events or deallocate
314332
if (e->period >= 0) {
315-
equeue_mutex_lock(&q->queuelock);
316-
e->id = -e->id;
317333
equeue_enqueue(q, e, e->period);
318-
equeue_mutex_unlock(&q->queuelock);
319334
} else {
320-
e->id = equeue_incid(q, -e->id);
335+
equeue_incid(q, e);
321336
equeue_dealloc(q, e+1);
322337
}
323338
}
324339

325340
int deadline = -1;
326-
unsigned tick = equeue_tick();
341+
tick = equeue_tick();
327342

328343
// check if we should stop dispatching soon
329344
if (ms >= 0) {
@@ -356,6 +371,9 @@ void equeue_dispatch(equeue_t *q, int ms) {
356371
}
357372
equeue_mutex_unlock(&q->queuelock);
358373
}
374+
375+
// update tick for next iteration
376+
tick = equeue_tick();
359377
}
360378
}
361379

equeue.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ extern "C" {
1717
#include "equeue_sema.h"
1818

1919
#include <stddef.h>
20+
#include <stdint.h>
2021

2122

2223
// Definition of the minimum size of an event
@@ -26,7 +27,9 @@ extern "C" {
2627
// Event/queue structures
2728
struct equeue_event {
2829
unsigned size;
29-
int id;
30+
uint8_t id;
31+
uint8_t generation;
32+
3033
struct equeue_event *next;
3134
struct equeue_event *sibling;
3235
struct equeue_event **ref;
@@ -41,7 +44,9 @@ struct equeue_event {
4144

4245
typedef struct equeue {
4346
struct equeue_event *queue;
44-
int breaks;
47+
unsigned tick;
48+
unsigned breaks;
49+
uint8_t generation;
4550

4651
unsigned char *buffer;
4752
unsigned npw2;

tests/tests.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,16 @@ void fragment_func(void *p) {
9292
test_assert(id);
9393
}
9494

95+
struct cancel {
96+
equeue_t *q;
97+
int id;
98+
};
99+
100+
void cancel_func(void *p) {
101+
struct cancel *cancel = (struct cancel *)p;
102+
equeue_cancel(cancel->q, cancel->id);
103+
}
104+
95105

96106
// Simple call tests
97107
void simple_call_test(void) {
@@ -194,6 +204,8 @@ void destructor_test(void) {
194204
for (int i = 0; i < 3; i++) {
195205
equeue_cancel(&q, ids[i]);
196206
}
207+
208+
equeue_dispatch(&q, 0);
197209
test_assert(touched == 3);
198210

199211
touched = 0;
@@ -251,6 +263,41 @@ void cancel_test(int N) {
251263
equeue_destroy(&q);
252264
}
253265

266+
void cancel_inflight_test(void) {
267+
equeue_t q;
268+
int err = equeue_create(&q, 2048);
269+
test_assert(!err);
270+
271+
bool touched = false;
272+
273+
int id = equeue_call(&q, simple_func, &touched);
274+
equeue_cancel(&q, id);
275+
276+
equeue_dispatch(&q, 0);
277+
test_assert(!touched);
278+
279+
id = equeue_call(&q, simple_func, &touched);
280+
equeue_cancel(&q, id);
281+
282+
equeue_dispatch(&q, 0);
283+
test_assert(!touched);
284+
285+
struct cancel *cancel = equeue_alloc(&q, sizeof(struct cancel));
286+
test_assert(cancel);
287+
cancel->q = &q;
288+
cancel->id = 0;
289+
290+
id = equeue_post(&q, cancel_func, cancel);
291+
test_assert(id);
292+
293+
cancel->id = equeue_call(&q, simple_func, &touched);
294+
295+
equeue_dispatch(&q, 0);
296+
test_assert(!touched);
297+
298+
equeue_destroy(&q);
299+
}
300+
254301
void cancel_unnecessarily_test(void) {
255302
equeue_t q;
256303
int err = equeue_create(&q, 2048);
@@ -431,6 +478,7 @@ int main() {
431478
test_run(destructor_test);
432479
test_run(allocation_failure_test);
433480
test_run(cancel_test, 20);
481+
test_run(cancel_inflight_test);
434482
test_run(cancel_unnecessarily_test);
435483
test_run(loop_protect_test);
436484
test_run(break_test);

0 commit comments

Comments
 (0)