/home/travis/build/MoarVM/MoarVM/src/io/filewatchers.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* Info we convey about a file watcher. */ |
4 | | typedef struct { |
5 | | char *path; |
6 | | uv_fs_event_t handle; |
7 | | MVMThreadContext *tc; |
8 | | int work_idx; |
9 | | } WatchInfo; |
10 | | |
11 | 0 | static void on_changed(uv_fs_event_t *handle, const char *filename, int events, int status) { |
12 | 0 | WatchInfo *wi = (WatchInfo *)handle->data; |
13 | 0 | MVMThreadContext *tc = wi->tc; |
14 | 0 | MVMObject *arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray); |
15 | 0 | MVMAsyncTask *t = MVM_io_eventloop_get_active_work(tc, wi->work_idx); |
16 | 0 | MVM_repr_push_o(tc, arr, t->body.schedulee); |
17 | 0 | MVMROOT2(tc, t, arr, { |
18 | 0 | MVMObject *filename_boxed; |
19 | 0 | MVMObject *rename_boxed; |
20 | 0 | if (filename) { |
21 | 0 | MVMString *filename_str = MVM_string_utf8_c8_decode(tc, |
22 | 0 | tc->instance->VMString, filename, strlen(filename)); |
23 | 0 | filename_boxed = MVM_repr_box_str(tc, |
24 | 0 | tc->instance->boot_types.BOOTStr, |
25 | 0 | filename_str); |
26 | 0 | } |
27 | 0 | else { |
28 | 0 | filename_boxed = tc->instance->boot_types.BOOTStr; |
29 | 0 | } |
30 | 0 | MVM_repr_push_o(tc, arr, filename_boxed); |
31 | 0 | rename_boxed = MVM_repr_box_int(tc, |
32 | 0 | tc->instance->boot_types.BOOTInt, |
33 | 0 | events == UV_RENAME ? 1 : 0); |
34 | 0 | MVM_repr_push_o(tc, arr, rename_boxed); |
35 | 0 | MVM_repr_push_o(tc, arr, tc->instance->boot_types.BOOTStr); |
36 | 0 | }); |
37 | 0 | MVM_repr_push_o(tc, t->body.queue, arr); |
38 | 0 | } |
39 | | |
40 | | /* Sets the signal handler up on the event loop. */ |
41 | 0 | static void setup(MVMThreadContext *tc, uv_loop_t *loop, MVMObject *async_task, void *data) { |
42 | 0 | WatchInfo *wi = (WatchInfo *)data; |
43 | 0 | int r; |
44 | 0 |
|
45 | 0 | /* Add task to active list. */ |
46 | 0 | wi->work_idx = MVM_io_eventloop_add_active_work(tc, async_task); |
47 | 0 | wi->tc = tc; |
48 | 0 | wi->handle.data = wi; |
49 | 0 |
|
50 | 0 | /* Start watching. */ |
51 | 0 | uv_fs_event_init(loop, &wi->handle); |
52 | 0 | if ((r = uv_fs_event_start(&wi->handle, on_changed, wi->path, 0)) != 0) { |
53 | 0 | /* Error; need to notify. */ |
54 | 0 | MVMROOT(tc, async_task, { |
55 | 0 | MVMObject *arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray); |
56 | 0 | MVMAsyncTask *t = (MVMAsyncTask *)async_task; |
57 | 0 | MVM_repr_push_o(tc, arr, t->body.schedulee); |
58 | 0 | MVM_repr_push_o(tc, arr, tc->instance->boot_types.BOOTStr); |
59 | 0 | MVM_repr_push_o(tc, arr, tc->instance->boot_types.BOOTInt); |
60 | 0 | MVMROOT(tc, arr, { |
61 | 0 | MVMString *msg_str = MVM_string_ascii_decode_nt(tc, |
62 | 0 | tc->instance->VMString, uv_strerror(r)); |
63 | 0 | MVMObject *msg_box = MVM_repr_box_str(tc, |
64 | 0 | tc->instance->boot_types.BOOTStr, msg_str); |
65 | 0 | MVM_repr_push_o(tc, arr, msg_box); |
66 | 0 | }); |
67 | 0 | MVM_repr_push_o(tc, t->body.queue, arr); |
68 | 0 | }); |
69 | 0 | } |
70 | 0 | } |
71 | | |
72 | | /* Frees data associated with a file watcher task. */ |
73 | 0 | static void gc_free(MVMThreadContext *tc, MVMObject *t, void *data) { |
74 | 0 | if (data) |
75 | 0 | MVM_free(data); |
76 | 0 | } |
77 | | |
78 | | /* Operations table for a file watcher task. */ |
79 | | static const MVMAsyncTaskOps op_table = { |
80 | | setup, |
81 | | NULL, |
82 | | NULL, |
83 | | NULL, |
84 | | gc_free |
85 | | }; |
86 | | |
87 | | MVMObject * MVM_io_file_watch(MVMThreadContext *tc, MVMObject *queue, |
88 | | MVMObject *schedulee, MVMString *path, |
89 | 0 | MVMObject *async_type) { |
90 | 0 | MVMAsyncTask *task; |
91 | 0 | WatchInfo *watch_info; |
92 | 0 |
|
93 | 0 | /* Encode path. */ |
94 | 0 | char *c_path = MVM_string_utf8_c8_encode_C_string(tc, path); |
95 | 0 |
|
96 | 0 | /* Validate REPRs. */ |
97 | 0 | if (REPR(queue)->ID != MVM_REPR_ID_ConcBlockingQueue) |
98 | 0 | MVM_exception_throw_adhoc(tc, |
99 | 0 | "file watch target queue must have ConcBlockingQueue REPR"); |
100 | 0 | if (REPR(async_type)->ID != MVM_REPR_ID_MVMAsyncTask) |
101 | 0 | MVM_exception_throw_adhoc(tc, |
102 | 0 | "file watch result type must have REPR AsyncTask"); |
103 | 0 |
|
104 | 0 | /* Create async task handle. */ |
105 | 0 | MVMROOT2(tc, queue, schedulee, { |
106 | 0 | task = (MVMAsyncTask *)MVM_repr_alloc_init(tc, async_type); |
107 | 0 | }); |
108 | 0 | MVM_ASSIGN_REF(tc, &(task->common.header), task->body.queue, queue); |
109 | 0 | MVM_ASSIGN_REF(tc, &(task->common.header), task->body.schedulee, schedulee); |
110 | 0 | task->body.ops = &op_table; |
111 | 0 | watch_info = MVM_malloc(sizeof(WatchInfo)); |
112 | 0 | watch_info->path = c_path; |
113 | 0 | task->body.data = watch_info; |
114 | 0 |
|
115 | 0 | /* Hand the task off to the event loop. */ |
116 | 0 | MVMROOT(tc, task, { |
117 | 0 | MVM_io_eventloop_queue_work(tc, (MVMObject *)task); |
118 | 0 | }); |
119 | 0 |
|
120 | 0 | return (MVMObject *)task; |
121 | 0 | } |