/home/travis/build/MoarVM/MoarVM/src/io/timers.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* Info we convey about a timer. */ |
4 | | typedef struct { |
5 | | int timeout; |
6 | | int repeat; |
7 | | uv_timer_t *handle; |
8 | | MVMThreadContext *tc; |
9 | | int work_idx; |
10 | | } TimerInfo; |
11 | | |
12 | | /* Frees the timer's handle memory. */ |
13 | 0 | static void free_timer(uv_handle_t *handle) { |
14 | 0 | MVM_free(handle); |
15 | 0 | } |
16 | | |
17 | | /* Timer callback; dispatches schedulee to the queue. */ |
18 | 0 | static void timer_cb(uv_timer_t *handle) { |
19 | 0 | TimerInfo *ti = (TimerInfo *)handle->data; |
20 | 0 | MVMThreadContext *tc = ti->tc; |
21 | 0 | MVMAsyncTask *t = MVM_io_eventloop_get_active_work(tc, ti->work_idx); |
22 | 0 | MVM_repr_push_o(tc, t->body.queue, t->body.schedulee); |
23 | 0 | if (!ti->repeat && ti->work_idx >= 0) { |
24 | 0 | /* The timer will only fire once. Having now fired, stop the callback, |
25 | 0 | * clean up the handle, and remove the active work so that we will not |
26 | 0 | * hold on to the callback and its associated memory. */ |
27 | 0 | uv_timer_stop(ti->handle); |
28 | 0 | uv_close((uv_handle_t *)ti->handle, free_timer); |
29 | 0 | MVM_io_eventloop_remove_active_work(tc, &(ti->work_idx)); |
30 | 0 | } |
31 | 0 | } |
32 | | |
33 | | /* Sets the timer up on the event loop. */ |
34 | 0 | static void setup(MVMThreadContext *tc, uv_loop_t *loop, MVMObject *async_task, void *data) { |
35 | 0 | TimerInfo *ti = (TimerInfo *)data; |
36 | 0 | ti->handle = MVM_malloc(sizeof(uv_timer_t)); |
37 | 0 | uv_timer_init(loop, ti->handle); |
38 | 0 | ti->work_idx = MVM_io_eventloop_add_active_work(tc, async_task); |
39 | 0 | ti->tc = tc; |
40 | 0 | ti->handle->data = ti; |
41 | 0 | uv_timer_start(ti->handle, timer_cb, ti->timeout, ti->repeat); |
42 | 0 | } |
43 | | |
44 | | /* Stops the timer. */ |
45 | 0 | static void cancel(MVMThreadContext *tc, uv_loop_t *loop, MVMObject *async_task, void *data) { |
46 | 0 | TimerInfo *ti = (TimerInfo *)data; |
47 | 0 | if (ti->work_idx >= 0) { |
48 | 0 | uv_timer_stop(ti->handle); |
49 | 0 | uv_close((uv_handle_t *)ti->handle, free_timer); |
50 | 0 | MVM_io_eventloop_send_cancellation_notification(ti->tc, |
51 | 0 | MVM_io_eventloop_get_active_work(tc, ti->work_idx)); |
52 | 0 | MVM_io_eventloop_remove_active_work(tc, &(ti->work_idx)); |
53 | 0 | } |
54 | 0 | } |
55 | | |
56 | | /* Frees data associated with a timer async task. */ |
57 | 0 | static void gc_free(MVMThreadContext *tc, MVMObject *t, void *data) { |
58 | 0 | if (data) |
59 | 0 | MVM_free(data); |
60 | 0 | } |
61 | | |
62 | | /* Operations table for async timer task. */ |
63 | | static const MVMAsyncTaskOps op_table = { |
64 | | setup, |
65 | | NULL, |
66 | | cancel, |
67 | | NULL, |
68 | | gc_free |
69 | | }; |
70 | | |
71 | | /* Creates a new timer. */ |
72 | | MVMObject * MVM_io_timer_create(MVMThreadContext *tc, MVMObject *queue, |
73 | | MVMObject *schedulee, MVMint64 timeout, |
74 | 0 | MVMint64 repeat, MVMObject *async_type) { |
75 | 0 | MVMAsyncTask *task; |
76 | 0 | TimerInfo *timer_info; |
77 | 0 |
|
78 | 0 | /* Validate REPRs. */ |
79 | 0 | if (REPR(queue)->ID != MVM_REPR_ID_ConcBlockingQueue) |
80 | 0 | MVM_exception_throw_adhoc(tc, |
81 | 0 | "timer target queue must have ConcBlockingQueue REPR"); |
82 | 0 | if (REPR(async_type)->ID != MVM_REPR_ID_MVMAsyncTask) |
83 | 0 | MVM_exception_throw_adhoc(tc, |
84 | 0 | "timer result type must have REPR AsyncTask"); |
85 | 0 |
|
86 | 0 | /* Create async task handle. */ |
87 | 0 | MVMROOT2(tc, queue, schedulee, { |
88 | 0 | task = (MVMAsyncTask *)MVM_repr_alloc_init(tc, async_type); |
89 | 0 | }); |
90 | 0 | MVM_ASSIGN_REF(tc, &(task->common.header), task->body.queue, queue); |
91 | 0 | MVM_ASSIGN_REF(tc, &(task->common.header), task->body.schedulee, schedulee); |
92 | 0 | task->body.ops = &op_table; |
93 | 0 | timer_info = MVM_malloc(sizeof(TimerInfo)); |
94 | 0 | timer_info->timeout = timeout; |
95 | 0 | timer_info->repeat = repeat; |
96 | 0 | task->body.data = timer_info; |
97 | 0 |
|
98 | 0 | /* Hand the task off to the event loop, which will set up the timer on the |
99 | 0 | * event loop. */ |
100 | 0 | MVMROOT(tc, task, { |
101 | 0 | MVM_io_eventloop_queue_work(tc, (MVMObject *)task); |
102 | 0 | }); |
103 | 0 |
|
104 | 0 | return (MVMObject *)task; |
105 | 0 | } |