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 nvlist_t *v8plus_method_call_direct(void *, const char *, const nvlist_t *);
49
50 boolean_t
51 v8plus_in_event_thread(void)
52 {
53 return (_v8plus_uv_event_thread == pthread_self() ? B_TRUE : B_FALSE);
54 }
55
56 static void
57 v8plus_async_callback(uv_async_t *async, __attribute__((unused)) int status)
58 {
59 if (v8plus_in_event_thread() != B_TRUE)
60 v8plus_panic("async callback called outside of event loop");
61
62 for (;;) {
63 v8plus_async_call_t *vac = NULL;
64
65 /*
66 * Fetch the next queued method:
67 */
68 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
69 v8plus_panic("could not lock async queue mutex");
70 if (!STAILQ_EMPTY(&_v8plus_callq)) {
71 vac = STAILQ_FIRST(&_v8plus_callq);
72 STAILQ_REMOVE_HEAD(&_v8plus_callq, vac_callq_entry);
73 }
74 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
75 v8plus_panic("could not unlock async queue mutex");
76
77 if (vac == NULL)
78 break;
79
80 /*
81 * Run the queued method:
82 */
83 if (pthread_mutex_lock(&vac->vac_mtx) != 0)
84 v8plus_panic("could not lock async call mutex");
85
86 if (vac->vac_run == B_TRUE)
87 v8plus_panic("async call already run");
88
89 vac->vac_return = v8plus_method_call_direct(vac->vac_cop,
90 vac->vac_name, vac->vac_lp);
91 vac->vac_run = B_TRUE;
92
93 if (pthread_cond_broadcast(&vac->vac_cv) != 0)
94 v8plus_panic("could not signal async call condvar");
95 if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
96 v8plus_panic("could not unlock async call mutex");
97 }
98 }
99
100 nvlist_t *
101 v8plus_method_call(void *cop, const char *name, const nvlist_t *lp)
102 {
103 v8plus_async_call_t vac;
104
105 if (v8plus_in_event_thread() == B_TRUE) {
106 /*
107 * We're running in the event loop thread, so we can make the
108 * call directly.
109 */
110 return (v8plus_method_call_direct(cop, name, lp));
111 }
112
113 /*
114 * As we cannot manipulate v8plus/V8/Node structures directly from
115 * outside the event loop thread, we push the call arguments onto a
116 * queue and post to the event loop thread. We then sleep on our
117 * condition variable until the event loop thread makes the call
118 * for us and wakes us up.
119 */
120 vac.vac_cop = cop;
121 vac.vac_name = name;
122 vac.vac_lp = lp;
123 if (pthread_mutex_init(&vac.vac_mtx, NULL) != 0)
124 v8plus_panic("could not init async call mutex");
125 if (pthread_cond_init(&vac.vac_cv, NULL) != 0)
126 v8plus_panic("could not init async call condvar");
127 vac.vac_run = B_FALSE;
128 vac.vac_return = NULL;
129
130 /*
131 * Post request to queue:
132 */
133 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
134 v8plus_panic("could not lock async queue mutex");
135 STAILQ_INSERT_TAIL(&_v8plus_callq, &vac, vac_callq_entry);
136 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
137 v8plus_panic("could not unlock async queue mutex");
138 uv_async_send(&_v8plus_uv_async);
139
140 /*
141 * Wait for our request to be serviced on the Event Loop thread:
142 */
143 if (pthread_mutex_lock(&vac.vac_mtx) != 0)
144 v8plus_panic("could not lock async call mutex");
145 while (vac.vac_run == B_FALSE) {
146 if (pthread_cond_wait(&vac.vac_cv, &vac.vac_mtx) != 0)
147 v8plus_panic("could not wait on async call condvar");
148 }
149
150 return (vac.vac_return);
151 }
152
153
154 /*
155 * Initialise structures for off-event-loop method calls.
156 *
157 * Note that uv_async_init() must be called inside the libuv Event Loop, so we
158 * do it here. We also want to record the thread ID of the Event Loop thread
159 * so as to determine what kind of method calls to make later.
160 */
161 void
162 v8plus_crossthread_init(void)
163 {
164 _v8plus_uv_event_thread = pthread_self();
165 if (uv_async_init(uv_default_loop(), &_v8plus_uv_async,
166 v8plus_async_callback) != 0)
167 v8plus_panic("unable to initialise uv_async_t");
168 if (pthread_mutex_init(&_v8plus_callq_mtx, NULL) != 0)
169 v8plus_panic("unable to initialise mutex");
170 }
171
172 nvlist_t *
173 v8plus_verror(v8plus_errno_t e, const char *fmt, va_list ap)
174 {
175 if (fmt == NULL) {
176 if (e == V8PLUSERR_NOERROR) {
177 *_v8plus_errmsg = '\0';
178 } else {
179 (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN,
180 "%s", v8plus_strerror(e));
181 }
182 } else {
183 (void) vsnprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN, fmt, ap);
184 }
185 _v8plus_errno = e;
186
187 return (NULL);
188 }
189
190 nvlist_t *
191 v8plus_error(v8plus_errno_t e, const char *fmt, ...)
192 {
193 va_list ap;
194
195 va_start(ap, fmt);
196 (void) v8plus_verror(e, fmt, ap);
197 va_end(ap);
198
199 return (NULL);
200 }
201
202 static void __NORETURN
203 v8plus_vpanic(const char *fmt, va_list ap)
204 {
205 (void) vfprintf(stderr, fmt, ap);
206 (void) fflush(stderr);
207 abort();
208 }
209
210 void
211 v8plus_panic(const char *fmt, ...)
212 {
213 va_list ap;
214
215 va_start(ap, fmt);
216 v8plus_vpanic(fmt, ap);
217 va_end(ap);
218 }
219
220 nvlist_t *
221 v8plus_nverr(int nverr, const char *member)
222 {
223 (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN,
224 "nvlist manipulation error on member %s: %s",
225 member == NULL ? "<none>" : member, strerror(nverr));
226
227 switch (nverr) {
228 case ENOMEM:
229 _v8plus_errno = V8PLUSERR_NOMEM;
230 break;
231 case EINVAL:
232 _v8plus_errno = V8PLUSERR_YOUSUCK;
233 break;
234 default:
235 _v8plus_errno = V8PLUSERR_UNKNOWN;
236 break;
237 }
238
239 return (NULL);
240 }
241
242 nvlist_t *
243 v8plus_syserr(int syserr, const char *fmt, ...)
244 {
245 v8plus_errno_t e;
246 va_list ap;
247
248 switch (syserr) {
249 case ENOMEM:
250 e = V8PLUSERR_NOMEM;
251 break;
252 case EBADF:
253 e = V8PLUSERR_BADF;
254 break;
255 default:
256 e = V8PLUSERR_UNKNOWN;
257 break;
258 }
259
260 va_start(ap, fmt);
261 (void) v8plus_verror(e, fmt, ap);
262 va_end(ap);
263
264 return (NULL);
265 }
266
267 /*
268 * The NULL nvlist with V8PLUSERR_NOERROR means we are returning void.
269 */
270 nvlist_t *
271 v8plus_void(void)
272 {
273 return (v8plus_error(V8PLUSERR_NOERROR, NULL));
274 }
275
276 v8plus_type_t
277 v8plus_typeof(const nvpair_t *pp)
278 {
279 data_type_t t = nvpair_type((nvpair_t *)pp);
280
281 switch (t) {
282 case DATA_TYPE_DOUBLE:
283 return (V8PLUS_TYPE_NUMBER);
284 case DATA_TYPE_STRING:
285 return (V8PLUS_TYPE_STRING);
286 case DATA_TYPE_NVLIST:
287 return (V8PLUS_TYPE_OBJECT);
288 case DATA_TYPE_BOOLEAN_VALUE:
289 return (V8PLUS_TYPE_BOOLEAN);
290 case DATA_TYPE_BOOLEAN:
291 return (V8PLUS_TYPE_UNDEFINED);
292 case DATA_TYPE_BYTE:
293 {
294 uchar_t v;
295 if (nvpair_value_byte((nvpair_t *)pp, &v) != 0 || v != 0)
296 return (V8PLUS_TYPE_INVALID);
297 return (V8PLUS_TYPE_NULL);
298 }
299 case DATA_TYPE_UINT64_ARRAY:
300 {
301 uint64_t *vp;
302 uint_t nv;
303 if (nvpair_value_uint64_array((nvpair_t *)pp, &vp, &nv) != 0 ||
304 nv != 1) {
305 return (V8PLUS_TYPE_INVALID);
306 }
307 return (V8PLUS_TYPE_JSFUNC);
308 }
309 default:
310 return (V8PLUS_TYPE_INVALID);
311 }
312 }
313
314 static int
315 v8plus_arg_value(v8plus_type_t t, const nvpair_t *pp, void *vp)
316 {
317 data_type_t dt = nvpair_type((nvpair_t *)pp);
318
319 switch (t) {
320 case V8PLUS_TYPE_NONE:
321 return (-1);
322 case V8PLUS_TYPE_STRING:
323 if (dt == DATA_TYPE_STRING) {
324 if (vp != NULL) {
325 (void) nvpair_value_string((nvpair_t *)pp,
326 (char **)vp);
327 }
328 return (0);
329 }
330 return (-1);
331 case V8PLUS_TYPE_NUMBER:
332 if (dt == DATA_TYPE_DOUBLE) {
333 if (vp != NULL) {
334 (void) nvpair_value_double((nvpair_t *)pp,
335 (double *)vp);
336 }
337 return (0);
338 }
339 return (-1);
340 case V8PLUS_TYPE_BOOLEAN:
341 if (dt == DATA_TYPE_BOOLEAN_VALUE) {
342 if (vp != NULL) {
343 (void) nvpair_value_boolean_value(
344 (nvpair_t *)pp, (boolean_t *)vp);
345 }
346 return (0);
347 }
348 return (-1);
349 case V8PLUS_TYPE_JSFUNC:
350 if (dt == DATA_TYPE_UINT64_ARRAY) {
351 uint_t nv;
352 uint64_t *vpp;
353
354 if (nvpair_value_uint64_array((nvpair_t *)pp,
355 &vpp, &nv) == 0 && nv == 1) {
356 if (vp != NULL)
357 *(v8plus_jsfunc_t *)vp = vpp[0];
358 return (0);
359 }
360 }
361 return (-1);
362 case V8PLUS_TYPE_OBJECT:
363 if (dt == DATA_TYPE_NVLIST) {
364 if (vp != NULL) {
365 (void) nvpair_value_nvlist((nvpair_t *)pp,
366 (nvlist_t **)vp);
367 }
368 return (0);
369 }
370 return (-1);
371 case V8PLUS_TYPE_NULL:
372 if (dt == DATA_TYPE_BYTE) {
373 uchar_t v;
374
375 if (nvpair_value_byte((nvpair_t *)pp, &v) == 0 &&
376 v == 0)
377 return (0);
378 }
379 return (-1);
380 case V8PLUS_TYPE_UNDEFINED:
381 return (dt == DATA_TYPE_BOOLEAN ? 0 : -1);
382 case V8PLUS_TYPE_ANY:
383 if (vp != NULL)
384 *(const nvpair_t **)vp = pp;
385 return (0);
386 case V8PLUS_TYPE_INVALID:
387 if (vp != NULL)
388 *(data_type_t *)vp = dt;
389 return (0);
390 case V8PLUS_TYPE_STRNUMBER64:
391 if (dt == DATA_TYPE_STRING) {
392 char *s;
393 uint64_t v;
394
395 (void) nvpair_value_string((nvpair_t *)pp, &s);
396 errno = 0;
397 v = (uint64_t)strtoull(s, NULL, 0);
398 if (errno != 0)
399 return (-1);
400 if (vp != NULL)
401 *(uint64_t *)vp = v;
402 return (0);
403 }
404 return (-1);
405 default:
406 return (-1);
407 }
408 }
409
410 int
411 v8plus_args(const nvlist_t *lp, uint_t flags, v8plus_type_t t, ...)
412 {
413 v8plus_type_t nt;
414 nvpair_t *pp;
415 void *vp;
416 va_list ap;
417 uint_t i;
418 char buf[32];
419
420 va_start(ap, t);
421
422 for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) {
423 switch (nt) {
424 case V8PLUS_TYPE_UNDEFINED:
425 case V8PLUS_TYPE_NULL:
426 break;
427 default:
428 (void) va_arg(ap, void *);
429 }
430
431 (void) snprintf(buf, sizeof (buf), "%u", i);
432 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) != 0) {
433 (void) v8plus_error(V8PLUSERR_MISSINGARG,
434 "argument %u is required", i);
435 return (-1);
436 }
437
438 if (v8plus_arg_value(nt, pp, NULL) != 0) {
439 (void) v8plus_error(V8PLUSERR_BADARG,
440 "argument %u is of incorrect type", i);
441 return (-1);
442 }
443
444 nt = va_arg(ap, data_type_t);
445 }
446
447 va_end(ap);
448
449 if (flags & V8PLUS_ARG_F_NOEXTRA) {
450 (void) snprintf(buf, sizeof (buf), "%u", i);
451 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0) {
452 (void) v8plus_error(V8PLUSERR_EXTRAARG,
453 "superfluous extra argument(s) detected");
454 return (-1);
455 }
456 }
457
458 va_start(ap, t);
459
460 for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) {
461 switch (nt) {
462 case V8PLUS_TYPE_UNDEFINED:
463 case V8PLUS_TYPE_NULL:
464 vp = NULL;
465 break;
466 default:
467 vp = va_arg(ap, void *);
468 }
469
470 (void) snprintf(buf, sizeof (buf), "%u", i);
471 VERIFY(nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0);
472 VERIFY(v8plus_arg_value(nt, pp, vp) == 0);
473
474 nt = va_arg(ap, data_type_t);
475 }
476
477 va_end(ap);
478
479 return (0);
480 }
481
482 static int
483 v8plus_obj_vsetprops(nvlist_t *lp, v8plus_type_t t, va_list *ap)
484 {
485 v8plus_type_t nt = t;
486 char *name;
487 int err;
488
489 /*
490 * Do not call va_start() or va_end() in this function! We are limited
491 * to a single traversal of the arguments so that we can recurse to
492 * handle embedded object definitions.
493 */
494
495 while (nt != V8PLUS_TYPE_NONE) {
496 name = va_arg(*ap, char *);
497
498 switch (nt) {
499 case V8PLUS_TYPE_STRING:
500 {
501 char *s = va_arg(*ap, char *);
502 if ((err = nvlist_add_string(lp, name, s)) != 0) {
503 (void) v8plus_nverr(err, name);
504 return (-1);
505 }
506 break;
507 }
508 case V8PLUS_TYPE_NUMBER:
509 {
510 double d = va_arg(*ap, double);
511 if ((err = nvlist_add_double(lp, name, d)) != 0) {
512 (void) v8plus_nverr(err, name);
513 return (-1);
514 }
515 break;
516 }
517 case V8PLUS_TYPE_BOOLEAN:
518 {
519 boolean_t b = va_arg(*ap, boolean_t);
520 if ((err = nvlist_add_boolean_value(lp,
521 name, b)) != 0) {
522 (void) v8plus_nverr(err, name);
523 return (-1);
524 }
525 break;
526 }
527 case V8PLUS_TYPE_JSFUNC:
528 {
529 v8plus_jsfunc_t j = va_arg(*ap, v8plus_jsfunc_t);
530 if ((err = nvlist_add_uint64_array(lp,
531 name, &j, 1)) != 0) {
532 (void) v8plus_nverr(err, name);
533 return (-1);
534 }
535 if ((err = nvlist_add_string_array(lp,
536 V8PLUS_JSF_COOKIE, NULL, 0)) != 0) {
537 (void) v8plus_nverr(err, V8PLUS_JSF_COOKIE);
538 return (-1);
539 }
540 v8plus_jsfunc_hold(j);
541 break;
542 }
543 case V8PLUS_TYPE_OBJECT:
544 {
545 const nvlist_t *op = va_arg(*ap, const nvlist_t *);
546 if ((err = nvlist_add_nvlist(lp, name,
547 (nvlist_t *)op)) != 0) {
548 (void) v8plus_nverr(err, name);
549 return (-1);
550 }
551 break;
552 }
553 case V8PLUS_TYPE_NULL:
554 if ((err = nvlist_add_byte(lp, name, 0)) != 0) {
555 (void) v8plus_nverr(err, name);
556 return (-1);
557 }
558 break;
559 case V8PLUS_TYPE_UNDEFINED:
560 if ((err = nvlist_add_boolean(lp, name)) != 0) {
561 (void) v8plus_nverr(err, name);
562 return (-1);
563 }
564 break;
565 case V8PLUS_TYPE_ANY:
566 {
567 nvpair_t *pp = va_arg(*ap, nvpair_t *);
568 if ((err = nvlist_add_nvpair(lp, pp)) != 0) {
569 (void) v8plus_nverr(err, name);
570 return (-1);
571 }
572 break;
573 }
574 case V8PLUS_TYPE_STRNUMBER64:
575 {
576 uint64_t v = va_arg(*ap, uint64_t);
577 char s[32];
578 (void) snprintf(s, sizeof (s), "%" PRIu64, v);
579 if ((err = nvlist_add_string(lp, name, s)) != 0) {
580 (void) v8plus_nverr(err, name);
581 return (-1);
582 }
583 break;
584 }
585 case V8PLUS_TYPE_INL_OBJECT:
586 {
587 nvlist_t *slp;
588
589 nt = va_arg(*ap, v8plus_type_t);
590 err = nvlist_alloc(&slp, NV_UNIQUE_NAME, 0);
591 if (err != 0) {
592 (void) v8plus_nverr(err, name);
593 return (-1);
594 }
595 if (v8plus_obj_vsetprops(slp, nt, ap) != 0)
596 return (-1);
597
598 err = nvlist_add_nvlist(lp, name, slp);
599 nvlist_free(slp);
600 if (err != 0) {
601 (void) v8plus_nverr(err, name);
602 return (-1);
603 }
604 break;
605 }
606 case V8PLUS_TYPE_INVALID:
607 default:
608 (void) v8plus_error(V8PLUSERR_YOUSUCK,
609 "invalid property type %d", nt);
610 return (-1);
611 }
612
613 nt = va_arg(*ap, v8plus_type_t);
614 }
615
616 return (0);
617 }
618
619 nvlist_t *
620 v8plus_obj(v8plus_type_t t, ...)
621 {
622 nvlist_t *rp;
623 va_list ap;
624 int err;
625
626 if ((err = nvlist_alloc(&rp, NV_UNIQUE_NAME, 0)) != 0)
627 return (v8plus_nverr(err, NULL));
628
629 va_start(ap, t);
630 err = v8plus_obj_vsetprops(rp, t, &ap);
631 va_end(ap);
632
633 if (err != 0) {
634 nvlist_free(rp);
635 rp = NULL;
636 }
637
638 return (rp);
639 }
640
641 int
642 v8plus_obj_setprops(nvlist_t *lp, v8plus_type_t t, ...)
643 {
644 va_list ap;
645 int err;
646
647 va_start(ap, t);
648 err = v8plus_obj_vsetprops(lp, t, &ap);
649 va_end(ap);
650
651 return (err);
652 }
653
654 static void
655 v8plus_uv_worker(uv_work_t *wp)
656 {
657 v8plus_uv_ctx_t *cp = wp->data;
658
659 cp->vuc_result = cp->vuc_worker(cp->vuc_obj, cp->vuc_ctx);
660 }
661
662 static void
663 v8plus_uv_completion(uv_work_t *wp)
664 {
665 v8plus_uv_ctx_t *cp = wp->data;
666
667 cp->vuc_completion(cp->vuc_obj, cp->vuc_ctx, cp->vuc_result);
668 v8plus_obj_rele(cp->vuc_obj);
669 free(cp);
670 free(wp);
671 }
672
673 void
674 v8plus_defer(void *cop, void *ctxp, v8plus_worker_f worker,
675 v8plus_completion_f completion)
676 {
677 uv_work_t *wp = malloc(sizeof (uv_work_t));
678 v8plus_uv_ctx_t *cp = malloc(sizeof (v8plus_uv_ctx_t));
679
680 bzero(wp, sizeof (uv_work_t));
681 bzero(cp, sizeof (v8plus_uv_ctx_t));
682
683 v8plus_obj_hold(cop);
684 cp->vuc_obj = cop;
685 cp->vuc_ctx = ctxp;
686 cp->vuc_worker = worker;
687 cp->vuc_completion = completion;
688 wp->data = cp;
689
690 uv_queue_work(uv_default_loop(), wp, v8plus_uv_worker,
691 v8plus_uv_completion);
692 }