Coverage Report

Created: 2018-07-03 15:31

/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
}