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