14 #include <pthread.h>
15 #include "v8plus_glue.h"
16
17 __thread v8plus_errno_t _v8plus_errno;
18 __thread char _v8plus_errmsg[V8PLUS_ERRMSG_LEN];
19
20 typedef struct v8plus_uv_ctx {
21 void *vuc_obj;
22 void *vuc_ctx;
23 void *vuc_result;
24 v8plus_worker_f vuc_worker;
25 v8plus_completion_f vuc_completion;
26 } v8plus_uv_ctx_t;
27
28 static STAILQ_HEAD(v8plus_callq_head, v8plus_async_call) _v8plus_callq =
29 STAILQ_HEAD_INITIALIZER(_v8plus_callq);
30 static pthread_mutex_t _v8plus_callq_mtx;
31 static pthread_t _v8plus_uv_event_thread;
32 static uv_async_t _v8plus_uv_async;
33
34 typedef struct v8plus_async_call {
35 void *vac_cop;
36 const char *vac_name;
37 const nvlist_t *vac_lp;
38
39 pthread_cond_t vac_cv;
40 pthread_mutex_t vac_mtx;
41
42 boolean_t vac_run;
43 nvlist_t *vac_return;
44
45 STAILQ_ENTRY(v8plus_async_call) vac_callq_entry;
46 } v8plus_async_call_t;
47
48 boolean_t
49 v8plus_in_event_thread(void)
50 {
51 return (_v8plus_uv_event_thread == pthread_self() ? B_TRUE : B_FALSE);
52 }
53
54 static void
55 v8plus_async_callback(uv_async_t *async, int status __UNUSED)
56 {
57 if (v8plus_in_event_thread() != B_TRUE)
58 v8plus_panic("async callback called outside of event loop");
59
60 for (;;) {
61 v8plus_async_call_t *vac = NULL;
62
63 /*
64 * Fetch the next queued method:
65 */
66 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
67 v8plus_panic("could not lock async queue mutex");
68 if (!STAILQ_EMPTY(&_v8plus_callq)) {
69 vac = STAILQ_FIRST(&_v8plus_callq);
70 STAILQ_REMOVE_HEAD(&_v8plus_callq, vac_callq_entry);
71 }
72 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
73 v8plus_panic("could not unlock async queue mutex");
74
75 if (vac == NULL)
76 break;
77
78 /*
79 * Run the queued method:
80 */
81 if (vac->vac_run == B_TRUE)
82 v8plus_panic("async call already run");
83 vac->vac_return = v8plus_method_call_direct(vac->vac_cop,
84 vac->vac_name, vac->vac_lp);
85
86 if (pthread_mutex_lock(&vac->vac_mtx) != 0)
87 v8plus_panic("could not lock async call mutex");
88 vac->vac_run = B_TRUE;
89 if (pthread_cond_broadcast(&vac->vac_cv) != 0)
90 v8plus_panic("could not signal async call condvar");
91 if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
92 v8plus_panic("could not unlock async call mutex");
93 }
94 }
95
96 nvlist_t *
97 v8plus_method_call(void *cop, const char *name, const nvlist_t *lp)
98 {
99 v8plus_async_call_t vac;
100
101 if (v8plus_in_event_thread() == B_TRUE) {
102 /*
103 * We're running in the event loop thread, so we can make the
104 * call directly.
105 */
106 return (v8plus_method_call_direct(cop, name, lp));
107 }
108
109 /*
110 * As we cannot manipulate v8plus/V8/Node structures directly from
111 * outside the event loop thread, we push the call arguments onto a
112 * queue and post to the event loop thread. We then sleep on our
113 * condition variable until the event loop thread makes the call
114 * for us and wakes us up.
115 */
116 bzero(&vac, sizeof (vac));
117 vac.vac_cop = cop;
118 vac.vac_name = name;
119 vac.vac_lp = lp;
120 if (pthread_mutex_init(&vac.vac_mtx, NULL) != 0)
121 v8plus_panic("could not init async call mutex");
122 if (pthread_cond_init(&vac.vac_cv, NULL) != 0)
123 v8plus_panic("could not init async call condvar");
124 vac.vac_run = B_FALSE;
125
126 /*
127 * Post request to queue:
128 */
129 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
130 v8plus_panic("could not lock async queue mutex");
131 STAILQ_INSERT_TAIL(&_v8plus_callq, &vac, vac_callq_entry);
132 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
133 v8plus_panic("could not unlock async queue mutex");
134 uv_async_send(&_v8plus_uv_async);
135
136 /*
137 * Wait for our request to be serviced on the event loop thread:
138 */
139 if (pthread_mutex_lock(&vac.vac_mtx) != 0)
140 v8plus_panic("could not lock async call mutex");
141 while (vac.vac_run == B_FALSE) {
142 if (pthread_cond_wait(&vac.vac_cv, &vac.vac_mtx) != 0)
143 v8plus_panic("could not wait on async call condvar");
144 }
145 if (pthread_mutex_unlock(&vac.vac_mtx) != 0)
146 v8plus_panic("could not unlock async call mutex");
147
148 if (pthread_cond_destroy(&vac.vac_cv) != 0)
149 v8plus_panic("could not destroy async call condvar");
150 if (pthread_mutex_destroy(&vac.vac_mtx) != 0)
151 v8plus_panic("could not destroy async call mutex");
152
153 return (vac.vac_return);
154 }
155
156
157 /*
158 * Initialise structures for off-event-loop method calls.
159 *
160 * Note that uv_async_init() must be called inside the libuv event loop, so we
161 * do it here. We also want to record the thread ID of the Event Loop thread
162 * so as to determine what kind of method calls to make later.
163 */
164 void
165 v8plus_crossthread_init(void)
166 {
167 _v8plus_uv_event_thread = pthread_self();
168 if (uv_async_init(uv_default_loop(), &_v8plus_uv_async,
169 v8plus_async_callback) != 0)
170 v8plus_panic("unable to initialise uv_async_t");
171 if (pthread_mutex_init(&_v8plus_callq_mtx, NULL) != 0)
172 v8plus_panic("unable to initialise mutex");
173 }
174
175 nvlist_t *
|
14 #include <pthread.h>
15 #include "v8plus_glue.h"
16
17 __thread v8plus_errno_t _v8plus_errno;
18 __thread char _v8plus_errmsg[V8PLUS_ERRMSG_LEN];
19
20 typedef struct v8plus_uv_ctx {
21 void *vuc_obj;
22 void *vuc_ctx;
23 void *vuc_result;
24 v8plus_worker_f vuc_worker;
25 v8plus_completion_f vuc_completion;
26 } v8plus_uv_ctx_t;
27
28 static STAILQ_HEAD(v8plus_callq_head, v8plus_async_call) _v8plus_callq =
29 STAILQ_HEAD_INITIALIZER(_v8plus_callq);
30 static pthread_mutex_t _v8plus_callq_mtx;
31 static pthread_t _v8plus_uv_event_thread;
32 static uv_async_t _v8plus_uv_async;
33
34 typedef enum v8plus_async_call_type {
35 ACT_OBJECT_CALL = 1,
36 ACT_OBJECT_RELEASE,
37 ACT_JSFUNC_CALL,
38 ACT_JSFUNC_RELEASE,
39 } v8plus_async_call_type_t;
40
41 typedef struct v8plus_async_call {
42 v8plus_async_call_type_t vac_type;
43 boolean_t vac_noreply;
44
45 /*
46 * For ACT_OBJECT_{CALL,RELEASE}:
47 */
48 void *vac_cop;
49 const char *vac_name;
50 /*
51 * For ACT_JSFUNC_{CALL,RELEASE}:
52 */
53 v8plus_jsfunc_t vac_func;
54
55 /*
56 * Common call arguments:
57 */
58 const nvlist_t *vac_lp;
59
60 pthread_cond_t vac_cv;
61 pthread_mutex_t vac_mtx;
62
63 boolean_t vac_run;
64 nvlist_t *vac_return;
65
66 STAILQ_ENTRY(v8plus_async_call) vac_callq_entry;
67 } v8plus_async_call_t;
68
69 boolean_t
70 v8plus_in_event_thread(void)
71 {
72 return (_v8plus_uv_event_thread == pthread_self() ? B_TRUE : B_FALSE);
73 }
74
75 static void
76 v8plus_async_callback(uv_async_t *async __UNUSED, int status __UNUSED)
77 {
78 if (v8plus_in_event_thread() != B_TRUE)
79 v8plus_panic("async callback called outside of event loop");
80
81 for (;;) {
82 v8plus_async_call_t *vac = NULL;
83
84 /*
85 * Fetch the next queued method:
86 */
87 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
88 v8plus_panic("could not lock async queue mutex");
89 if (!STAILQ_EMPTY(&_v8plus_callq)) {
90 vac = STAILQ_FIRST(&_v8plus_callq);
91 STAILQ_REMOVE_HEAD(&_v8plus_callq, vac_callq_entry);
92 }
93 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
94 v8plus_panic("could not unlock async queue mutex");
95
96 if (vac == NULL)
97 break;
98
99 /*
100 * Run the queued method:
101 */
102 if (vac->vac_run == B_TRUE)
103 v8plus_panic("async call already run");
104
105 switch (vac->vac_type) {
106 case ACT_OBJECT_CALL:
107 vac->vac_return = v8plus_method_call_direct(
108 vac->vac_cop, vac->vac_name, vac->vac_lp);
109 break;
110 case ACT_OBJECT_RELEASE:
111 v8plus_obj_rele(vac->vac_cop);
112 break;
113 case ACT_JSFUNC_CALL:
114 vac->vac_return = v8plus_call_direct(
115 vac->vac_func, vac->vac_lp);
116 break;
117 case ACT_JSFUNC_RELEASE:
118 v8plus_jsfunc_rele(vac->vac_func);
119 break;
120 }
121
122 if (vac->vac_noreply == B_TRUE) {
123 /*
124 * The caller posted this event and is not sleeping
125 * on a reply. Just free the call structure and move
126 * on.
127 */
128 free(vac);
129 if (vac->vac_lp != NULL)
130 nvlist_free((nvlist_t *)vac->vac_lp);
131 continue;
132 }
133
134 if (pthread_mutex_lock(&vac->vac_mtx) != 0)
135 v8plus_panic("could not lock async call mutex");
136 vac->vac_run = B_TRUE;
137 if (pthread_cond_broadcast(&vac->vac_cv) != 0)
138 v8plus_panic("could not signal async call condvar");
139 if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
140 v8plus_panic("could not unlock async call mutex");
141 }
142 }
143
144 /*
145 * As we cannot manipulate v8plus/V8/Node structures directly from outside the
146 * event loop thread, we push the call arguments onto a queue and post to the
147 * event loop thread. We then sleep on our condition variable until the event
148 * loop thread makes the call for us and wakes us up.
149 *
150 * This routine implements the parts of this interaction common to all
151 * variants.
152 */
153 static nvlist_t *
154 v8plus_cross_thread_call(v8plus_async_call_t *vac)
155 {
156 /*
157 * Common call structure initialisation:
158 */
159 if (pthread_mutex_init(&vac->vac_mtx, NULL) != 0)
160 v8plus_panic("could not init async call mutex");
161 if (pthread_cond_init(&vac->vac_cv, NULL) != 0)
162 v8plus_panic("could not init async call condvar");
163 vac->vac_run = B_FALSE;
164
165 /*
166 * Post request to queue:
167 */
168 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
169 v8plus_panic("could not lock async queue mutex");
170 STAILQ_INSERT_TAIL(&_v8plus_callq, vac, vac_callq_entry);
171 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
172 v8plus_panic("could not unlock async queue mutex");
173 uv_async_send(&_v8plus_uv_async);
174
175 if (vac->vac_noreply == B_TRUE) {
176 /*
177 * The caller does not care about the reply, and has allocated
178 * the v8plus_async_call_t structure from the heap. The
179 * async callback will free the storage when it completes.
180 */
181 return (NULL);
182 }
183
184 /*
185 * Wait for our request to be serviced on the event loop thread:
186 */
187 if (pthread_mutex_lock(&vac->vac_mtx) != 0)
188 v8plus_panic("could not lock async call mutex");
189 while (vac->vac_run == B_FALSE) {
190 if (pthread_cond_wait(&vac->vac_cv, &vac->vac_mtx) != 0)
191 v8plus_panic("could not wait on async call condvar");
192 }
193 if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
194 v8plus_panic("could not unlock async call mutex");
195
196 if (pthread_cond_destroy(&vac->vac_cv) != 0)
197 v8plus_panic("could not destroy async call condvar");
198 if (pthread_mutex_destroy(&vac->vac_mtx) != 0)
199 v8plus_panic("could not destroy async call mutex");
200
201 return (vac->vac_return);
202 }
203
204 nvlist_t *
205 v8plus_method_call(void *cop, const char *name, const nvlist_t *lp)
206 {
207 v8plus_async_call_t vac;
208
209 if (v8plus_in_event_thread() == B_TRUE) {
210 /*
211 * We're running in the event loop thread, so we can make the
212 * call directly.
213 */
214 return (v8plus_method_call_direct(cop, name, lp));
215 }
216
217 bzero(&vac, sizeof (vac));
218 vac.vac_type = ACT_OBJECT_CALL;
219 vac.vac_noreply = B_FALSE;
220 vac.vac_cop = cop;
221 vac.vac_name = name;
222 vac.vac_lp = lp;
223
224 return (v8plus_cross_thread_call(&vac));
225 }
226
227 nvlist_t *
228 v8plus_call(v8plus_jsfunc_t func, const nvlist_t *lp)
229 {
230 v8plus_async_call_t vac;
231
232 if (v8plus_in_event_thread() == B_TRUE) {
233 /*
234 * We're running in the event loop thread, so we can make the
235 * call directly.
236 */
237 return (v8plus_call_direct(func, lp));
238 }
239
240 bzero(&vac, sizeof (vac));
241 vac.vac_type = ACT_JSFUNC_CALL;
242 vac.vac_noreply = B_FALSE;
243 vac.vac_func = func;
244 vac.vac_lp = lp;
245
246 return (v8plus_cross_thread_call(&vac));
247 }
248
249 void
250 v8plus_obj_rele(const void *cop)
251 {
252 v8plus_async_call_t *vac;
253
254 if (v8plus_in_event_thread() == B_TRUE) {
255 return (v8plus_obj_rele_direct(cop));
256 }
257
258 vac = calloc(1, sizeof (*vac));
259 if (vac == NULL)
260 v8plus_panic("could not allocate async call structure");
261
262 vac->vac_type = ACT_OBJECT_RELEASE;
263 vac->vac_noreply = B_TRUE;
264 vac->vac_cop = (void *)cop;
265
266 (void) v8plus_cross_thread_call(vac);
267 }
268
269 void
270 v8plus_jsfunc_rele(v8plus_jsfunc_t f)
271 {
272 v8plus_async_call_t *vac;
273
274 if (v8plus_in_event_thread() == B_TRUE) {
275 return (v8plus_jsfunc_rele_direct(f));
276 }
277
278 vac = calloc(1, sizeof (*vac));
279 if (vac == NULL)
280 v8plus_panic("could not allocate async call structure");
281
282 vac->vac_type = ACT_JSFUNC_RELEASE;
283 vac->vac_noreply = B_TRUE;
284 vac->vac_func = f;
285
286 (void) v8plus_cross_thread_call(vac);
287 }
288
289 /*
290 * Initialise structures for off-event-loop method calls.
291 *
292 * Note that uv_async_init() must be called inside the libuv event loop, so we
293 * do it here. We also want to record the thread ID of the Event Loop thread
294 * so as to determine what kind of method calls to make later.
295 */
296 void
297 v8plus_crossthread_init(void)
298 {
299 _v8plus_uv_event_thread = pthread_self();
300 if (uv_async_init(uv_default_loop(), &_v8plus_uv_async,
301 v8plus_async_callback) != 0)
302 v8plus_panic("unable to initialise uv_async_t");
303 if (pthread_mutex_init(&_v8plus_callq_mtx, NULL) != 0)
304 v8plus_panic("unable to initialise mutex");
305 }
306
307 nvlist_t *
|