1 /*
2 * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 */
4
5 #include <sys/ccompile.h>
6 #include <sys/debug.h>
7 #include <sys/queue.h>
8 #include <sys/types.h>
9 #include <stdarg.h>
10 #include <string.h>
11 #include <strings.h>
12 #include <errno.h>
13 #include <uv.h>
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 *
176 v8plus_verror(v8plus_errno_t e, const char *fmt, va_list ap)
177 {
178 if (fmt == NULL) {
179 if (e == V8PLUSERR_NOERROR) {
180 *_v8plus_errmsg = '\0';
181 } else {
182 (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN,
183 "%s", v8plus_strerror(e));
184 }
185 } else {
186 (void) vsnprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN, fmt, ap);
187 }
188 _v8plus_errno = e;
189
190 return (NULL);
191 }
192
193 nvlist_t *
194 v8plus_error(v8plus_errno_t e, const char *fmt, ...)
195 {
196 va_list ap;
197
198 va_start(ap, fmt);
199 (void) v8plus_verror(e, fmt, ap);
200 va_end(ap);
201
202 return (NULL);
203 }
204
205 static void __NORETURN
206 v8plus_vpanic(const char *fmt, va_list ap)
207 {
208 (void) vfprintf(stderr, fmt, ap);
209 (void) fflush(stderr);
210 abort();
211 }
212
213 void
214 v8plus_panic(const char *fmt, ...)
215 {
216 va_list ap;
217
218 va_start(ap, fmt);
219 v8plus_vpanic(fmt, ap);
220 va_end(ap);
221 }
222
223 nvlist_t *
224 v8plus_nverr(int nverr, const char *member)
225 {
226 (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN,
227 "nvlist manipulation error on member %s: %s",
228 member == NULL ? "<none>" : member, strerror(nverr));
229
230 switch (nverr) {
231 case ENOMEM:
232 _v8plus_errno = V8PLUSERR_NOMEM;
233 break;
234 case EINVAL:
235 _v8plus_errno = V8PLUSERR_YOUSUCK;
236 break;
237 default:
238 _v8plus_errno = V8PLUSERR_UNKNOWN;
239 break;
240 }
241
242 return (NULL);
243 }
244
245 nvlist_t *
246 v8plus_syserr(int syserr, const char *fmt, ...)
247 {
248 v8plus_errno_t e;
249 va_list ap;
250
251 switch (syserr) {
252 case ENOMEM:
253 e = V8PLUSERR_NOMEM;
254 break;
255 case EBADF:
256 e = V8PLUSERR_BADF;
257 break;
258 default:
259 e = V8PLUSERR_UNKNOWN;
260 break;
261 }
262
263 va_start(ap, fmt);
264 (void) v8plus_verror(e, fmt, ap);
265 va_end(ap);
266
267 return (NULL);
268 }
269
270 /*
271 * The NULL nvlist with V8PLUSERR_NOERROR means we are returning void.
272 */
273 nvlist_t *
274 v8plus_void(void)
275 {
276 return (v8plus_error(V8PLUSERR_NOERROR, NULL));
277 }
278
279 v8plus_type_t
280 v8plus_typeof(const nvpair_t *pp)
281 {
282 data_type_t t = nvpair_type((nvpair_t *)pp);
283
284 switch (t) {
285 case DATA_TYPE_DOUBLE:
286 return (V8PLUS_TYPE_NUMBER);
287 case DATA_TYPE_STRING:
288 return (V8PLUS_TYPE_STRING);
289 case DATA_TYPE_NVLIST:
290 return (V8PLUS_TYPE_OBJECT);
291 case DATA_TYPE_BOOLEAN_VALUE:
292 return (V8PLUS_TYPE_BOOLEAN);
293 case DATA_TYPE_BOOLEAN:
294 return (V8PLUS_TYPE_UNDEFINED);
295 case DATA_TYPE_BYTE:
296 {
297 uchar_t v;
298 if (nvpair_value_byte((nvpair_t *)pp, &v) != 0 || v != 0)
299 return (V8PLUS_TYPE_INVALID);
300 return (V8PLUS_TYPE_NULL);
301 }
302 case DATA_TYPE_UINT64_ARRAY:
303 {
304 uint64_t *vp;
305 uint_t nv;
306 if (nvpair_value_uint64_array((nvpair_t *)pp, &vp, &nv) != 0 ||
307 nv != 1) {
308 return (V8PLUS_TYPE_INVALID);
309 }
310 return (V8PLUS_TYPE_JSFUNC);
311 }
312 default:
313 return (V8PLUS_TYPE_INVALID);
314 }
315 }
316
317 static int
318 v8plus_arg_value(v8plus_type_t t, const nvpair_t *pp, void *vp)
319 {
320 data_type_t dt = nvpair_type((nvpair_t *)pp);
321
322 switch (t) {
323 case V8PLUS_TYPE_NONE:
324 return (-1);
325 case V8PLUS_TYPE_STRING:
326 if (dt == DATA_TYPE_STRING) {
327 if (vp != NULL) {
328 (void) nvpair_value_string((nvpair_t *)pp,
329 (char **)vp);
330 }
331 return (0);
332 }
333 return (-1);
334 case V8PLUS_TYPE_NUMBER:
335 if (dt == DATA_TYPE_DOUBLE) {
336 if (vp != NULL) {
337 (void) nvpair_value_double((nvpair_t *)pp,
338 (double *)vp);
339 }
340 return (0);
341 }
342 return (-1);
343 case V8PLUS_TYPE_BOOLEAN:
344 if (dt == DATA_TYPE_BOOLEAN_VALUE) {
345 if (vp != NULL) {
346 (void) nvpair_value_boolean_value(
347 (nvpair_t *)pp, (boolean_t *)vp);
348 }
349 return (0);
350 }
351 return (-1);
352 case V8PLUS_TYPE_JSFUNC:
353 if (dt == DATA_TYPE_UINT64_ARRAY) {
354 uint_t nv;
355 uint64_t *vpp;
356
357 if (nvpair_value_uint64_array((nvpair_t *)pp,
358 &vpp, &nv) == 0 && nv == 1) {
359 if (vp != NULL)
360 *(v8plus_jsfunc_t *)vp = vpp[0];
361 return (0);
362 }
363 }
364 return (-1);
365 case V8PLUS_TYPE_OBJECT:
366 if (dt == DATA_TYPE_NVLIST) {
367 if (vp != NULL) {
368 (void) nvpair_value_nvlist((nvpair_t *)pp,
369 (nvlist_t **)vp);
370 }
371 return (0);
372 }
373 return (-1);
374 case V8PLUS_TYPE_NULL:
375 if (dt == DATA_TYPE_BYTE) {
376 uchar_t v;
377
378 if (nvpair_value_byte((nvpair_t *)pp, &v) == 0 &&
379 v == 0)
380 return (0);
381 }
382 return (-1);
383 case V8PLUS_TYPE_UNDEFINED:
384 return (dt == DATA_TYPE_BOOLEAN ? 0 : -1);
385 case V8PLUS_TYPE_ANY:
386 if (vp != NULL)
387 *(const nvpair_t **)vp = pp;
388 return (0);
389 case V8PLUS_TYPE_INVALID:
390 if (vp != NULL)
391 *(data_type_t *)vp = dt;
392 return (0);
393 case V8PLUS_TYPE_STRNUMBER64:
394 if (dt == DATA_TYPE_STRING) {
395 char *s;
396 uint64_t v;
397
398 (void) nvpair_value_string((nvpair_t *)pp, &s);
399 errno = 0;
400 v = (uint64_t)strtoull(s, NULL, 0);
401 if (errno != 0)
402 return (-1);
403 if (vp != NULL)
404 *(uint64_t *)vp = v;
405 return (0);
406 }
407 return (-1);
408 default:
409 return (-1);
410 }
411 }
412
413 int
414 v8plus_args(const nvlist_t *lp, uint_t flags, v8plus_type_t t, ...)
415 {
416 v8plus_type_t nt;
417 nvpair_t *pp;
418 void *vp;
419 va_list ap;
420 uint_t i;
421 char buf[32];
422
423 va_start(ap, t);
424
425 for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) {
426 switch (nt) {
427 case V8PLUS_TYPE_UNDEFINED:
428 case V8PLUS_TYPE_NULL:
429 break;
430 default:
431 (void) va_arg(ap, void *);
432 }
433
434 (void) snprintf(buf, sizeof (buf), "%u", i);
435 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) != 0) {
436 (void) v8plus_error(V8PLUSERR_MISSINGARG,
437 "argument %u is required", i);
438 return (-1);
439 }
440
441 if (v8plus_arg_value(nt, pp, NULL) != 0) {
442 (void) v8plus_error(V8PLUSERR_BADARG,
443 "argument %u is of incorrect type", i);
444 return (-1);
445 }
446
447 nt = va_arg(ap, data_type_t);
448 }
449
450 va_end(ap);
451
452 if (flags & V8PLUS_ARG_F_NOEXTRA) {
453 (void) snprintf(buf, sizeof (buf), "%u", i);
454 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0) {
455 (void) v8plus_error(V8PLUSERR_EXTRAARG,
456 "superfluous extra argument(s) detected");
457 return (-1);
458 }
459 }
460
461 va_start(ap, t);
462
463 for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) {
464 switch (nt) {
465 case V8PLUS_TYPE_UNDEFINED:
466 case V8PLUS_TYPE_NULL:
467 vp = NULL;
468 break;
469 default:
470 vp = va_arg(ap, void *);
471 }
472
473 (void) snprintf(buf, sizeof (buf), "%u", i);
474 VERIFY(nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0);
475 VERIFY(v8plus_arg_value(nt, pp, vp) == 0);
476
477 nt = va_arg(ap, data_type_t);
478 }
479
480 va_end(ap);
481
482 return (0);
483 }
484
485 static int
486 v8plus_obj_vsetprops(nvlist_t *lp, v8plus_type_t t, va_list *ap)
487 {
488 v8plus_type_t nt = t;
489 char *name;
490 int err;
491
492 /*
493 * Do not call va_start() or va_end() in this function! We are limited
494 * to a single traversal of the arguments so that we can recurse to
495 * handle embedded object definitions.
496 */
497
498 while (nt != V8PLUS_TYPE_NONE) {
499 name = va_arg(*ap, char *);
500
501 switch (nt) {
502 case V8PLUS_TYPE_STRING:
503 {
504 char *s = va_arg(*ap, char *);
505 if ((err = nvlist_add_string(lp, name, s)) != 0) {
506 (void) v8plus_nverr(err, name);
507 return (-1);
508 }
509 break;
510 }
511 case V8PLUS_TYPE_NUMBER:
512 {
513 double d = va_arg(*ap, double);
514 if ((err = nvlist_add_double(lp, name, d)) != 0) {
515 (void) v8plus_nverr(err, name);
516 return (-1);
517 }
518 break;
519 }
520 case V8PLUS_TYPE_BOOLEAN:
521 {
522 boolean_t b = va_arg(*ap, boolean_t);
523 if ((err = nvlist_add_boolean_value(lp,
524 name, b)) != 0) {
525 (void) v8plus_nverr(err, name);
526 return (-1);
527 }
528 break;
529 }
530 case V8PLUS_TYPE_JSFUNC:
531 {
532 v8plus_jsfunc_t j = va_arg(*ap, v8plus_jsfunc_t);
533 if ((err = nvlist_add_uint64_array(lp,
534 name, &j, 1)) != 0) {
535 (void) v8plus_nverr(err, name);
536 return (-1);
537 }
538 if ((err = nvlist_add_string_array(lp,
539 V8PLUS_JSF_COOKIE, NULL, 0)) != 0) {
540 (void) v8plus_nverr(err, V8PLUS_JSF_COOKIE);
541 return (-1);
542 }
543 v8plus_jsfunc_hold(j);
544 break;
545 }
546 case V8PLUS_TYPE_OBJECT:
547 {
548 const nvlist_t *op = va_arg(*ap, const nvlist_t *);
549 if ((err = nvlist_add_nvlist(lp, name,
550 (nvlist_t *)op)) != 0) {
551 (void) v8plus_nverr(err, name);
552 return (-1);
553 }
554 break;
555 }
556 case V8PLUS_TYPE_NULL:
557 if ((err = nvlist_add_byte(lp, name, 0)) != 0) {
558 (void) v8plus_nverr(err, name);
559 return (-1);
560 }
561 break;
562 case V8PLUS_TYPE_UNDEFINED:
563 if ((err = nvlist_add_boolean(lp, name)) != 0) {
564 (void) v8plus_nverr(err, name);
565 return (-1);
566 }
567 break;
568 case V8PLUS_TYPE_ANY:
569 {
570 nvpair_t *pp = va_arg(*ap, nvpair_t *);
571 if ((err = nvlist_add_nvpair(lp, pp)) != 0) {
572 (void) v8plus_nverr(err, name);
573 return (-1);
574 }
575 break;
576 }
577 case V8PLUS_TYPE_STRNUMBER64:
578 {
579 uint64_t v = va_arg(*ap, uint64_t);
580 char s[32];
581 (void) snprintf(s, sizeof (s), "%" PRIu64, v);
582 if ((err = nvlist_add_string(lp, name, s)) != 0) {
583 (void) v8plus_nverr(err, name);
584 return (-1);
585 }
586 break;
587 }
588 case V8PLUS_TYPE_INL_OBJECT:
589 {
590 nvlist_t *slp;
591
592 nt = va_arg(*ap, v8plus_type_t);
593 err = nvlist_alloc(&slp, NV_UNIQUE_NAME, 0);
594 if (err != 0) {
595 (void) v8plus_nverr(err, name);
596 return (-1);
597 }
598 if (v8plus_obj_vsetprops(slp, nt, ap) != 0)
599 return (-1);
600
601 err = nvlist_add_nvlist(lp, name, slp);
602 nvlist_free(slp);
603 if (err != 0) {
604 (void) v8plus_nverr(err, name);
605 return (-1);
606 }
607 break;
608 }
609 case V8PLUS_TYPE_INVALID:
610 default:
611 (void) v8plus_error(V8PLUSERR_YOUSUCK,
612 "invalid property type %d", nt);
613 return (-1);
614 }
615
616 nt = va_arg(*ap, v8plus_type_t);
617 }
618
619 return (0);
620 }
621
622 nvlist_t *
623 v8plus_obj(v8plus_type_t t, ...)
624 {
625 nvlist_t *rp;
626 va_list ap;
627 int err;
628
629 if ((err = nvlist_alloc(&rp, NV_UNIQUE_NAME, 0)) != 0)
630 return (v8plus_nverr(err, NULL));
631
632 va_start(ap, t);
633 err = v8plus_obj_vsetprops(rp, t, &ap);
634 va_end(ap);
635
636 if (err != 0) {
637 nvlist_free(rp);
638 rp = NULL;
639 }
640
641 return (rp);
642 }
643
644 int
645 v8plus_obj_setprops(nvlist_t *lp, v8plus_type_t t, ...)
646 {
647 va_list ap;
648 int err;
649
650 va_start(ap, t);
651 err = v8plus_obj_vsetprops(lp, t, &ap);
652 va_end(ap);
653
654 return (err);
655 }
656
657 static void
658 v8plus_uv_worker(uv_work_t *wp)
659 {
660 v8plus_uv_ctx_t *cp = wp->data;
661
662 cp->vuc_result = cp->vuc_worker(cp->vuc_obj, cp->vuc_ctx);
663 }
664
665 static void
666 v8plus_uv_completion(uv_work_t *wp)
667 {
668 v8plus_uv_ctx_t *cp = wp->data;
669
670 cp->vuc_completion(cp->vuc_obj, cp->vuc_ctx, cp->vuc_result);
671 v8plus_obj_rele(cp->vuc_obj);
672 free(cp);
673 free(wp);
674 }
675
676 void
677 v8plus_defer(void *cop, void *ctxp, v8plus_worker_f worker,
678 v8plus_completion_f completion)
679 {
680 uv_work_t *wp = malloc(sizeof (uv_work_t));
681 v8plus_uv_ctx_t *cp = malloc(sizeof (v8plus_uv_ctx_t));
682
683 bzero(wp, sizeof (uv_work_t));
684 bzero(cp, sizeof (v8plus_uv_ctx_t));
685
686 v8plus_obj_hold(cop);
687 cp->vuc_obj = cop;
688 cp->vuc_ctx = ctxp;
689 cp->vuc_worker = worker;
690 cp->vuc_completion = completion;
691 wp->data = cp;
692
693 uv_queue_work(uv_default_loop(), wp, v8plus_uv_worker,
694 v8plus_uv_completion);
695 }