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 static int _v8plus_eventloop_refcount;
35
36 typedef enum v8plus_async_call_type {
37 ACT_OBJECT_CALL = 1,
38 ACT_OBJECT_RELEASE,
39 ACT_JSFUNC_CALL,
40 ACT_JSFUNC_RELEASE,
41 ACT_EVENTLOOP_RELEASE
42 } v8plus_async_call_type_t;
43
44 typedef enum v8plus_async_call_flags {
45 ACF_COMPLETED = 0x01,
46 ACF_NOREPLY = 0x02
47 } v8plus_async_call_flags_t;
48
49
50 typedef struct v8plus_async_call {
51 v8plus_async_call_type_t vac_type;
52 v8plus_async_call_flags_t vac_flags;
53
54 /*
55 * For ACT_OBJECT_{CALL,RELEASE}:
56 */
57 void *vac_cop;
58 const char *vac_name;
59 /*
60 * For ACT_JSFUNC_{CALL,RELEASE}:
61 */
62 v8plus_jsfunc_t vac_func;
63
64 /*
65 * Common call arguments:
66 */
67 const nvlist_t *vac_lp;
68 nvlist_t *vac_return;
69
70 pthread_cond_t vac_cv;
71 pthread_mutex_t vac_mtx;
72
73 STAILQ_ENTRY(v8plus_async_call) vac_callq_entry;
74 } v8plus_async_call_t;
75
76 boolean_t
77 v8plus_in_event_thread(void)
78 {
79 return (_v8plus_uv_event_thread == pthread_self() ? B_TRUE : B_FALSE);
80 }
81
82 static void
83 v8plus_async_callback(uv_async_t *async __UNUSED, int status __UNUSED)
84 {
85 if (v8plus_in_event_thread() != B_TRUE)
86 v8plus_panic("async callback called outside of event loop");
87
88 for (;;) {
89 v8plus_async_call_t *vac = NULL;
90
91 /*
92 * Fetch the next queued method:
93 */
94 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
95 v8plus_panic("could not lock async queue mutex");
96 if (!STAILQ_EMPTY(&_v8plus_callq)) {
97 vac = STAILQ_FIRST(&_v8plus_callq);
98 STAILQ_REMOVE_HEAD(&_v8plus_callq, vac_callq_entry);
99 }
100 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
101 v8plus_panic("could not unlock async queue mutex");
102
103 if (vac == NULL)
104 break;
105
106 /*
107 * Run the queued method:
108 */
109 if (vac->vac_flags & ACF_COMPLETED)
110 v8plus_panic("async call already run");
111
112 switch (vac->vac_type) {
113 case ACT_OBJECT_CALL:
114 vac->vac_return = v8plus_method_call_direct(
115 vac->vac_cop, vac->vac_name, vac->vac_lp);
116 break;
117 case ACT_OBJECT_RELEASE:
118 v8plus_obj_rele_direct(vac->vac_cop);
119 break;
120 case ACT_JSFUNC_CALL:
121 vac->vac_return = v8plus_call_direct(
122 vac->vac_func, vac->vac_lp);
123 break;
124 case ACT_JSFUNC_RELEASE:
125 v8plus_jsfunc_rele_direct(vac->vac_func);
126 break;
127 case ACT_EVENTLOOP_RELEASE:
128 v8plus_eventloop_rele_direct();
129 break;
130 }
131
132 if (vac->vac_flags & ACF_NOREPLY) {
133 /*
134 * The caller posted this event and is not sleeping
135 * on a reply. Just free the call structure and move
136 * on.
137 */
138 free(vac);
139 if (vac->vac_lp != NULL)
140 nvlist_free((nvlist_t *)vac->vac_lp);
141 continue;
142 }
143
144 if (pthread_mutex_lock(&vac->vac_mtx) != 0)
145 v8plus_panic("could not lock async call mutex");
146 vac->vac_flags |= ACF_COMPLETED;
147 if (pthread_cond_broadcast(&vac->vac_cv) != 0)
148 v8plus_panic("could not signal async call condvar");
149 if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
150 v8plus_panic("could not unlock async call mutex");
151 }
152 }
153
154 /*
155 * As we cannot manipulate v8plus/V8/Node structures directly from outside the
156 * event loop thread, we push the call arguments onto a queue and post to the
157 * event loop thread. We then sleep on our condition variable until the event
158 * loop thread makes the call for us and wakes us up.
159 *
160 * This routine implements the parts of this interaction common to all
161 * variants.
162 */
163 static nvlist_t *
164 v8plus_cross_thread_call(v8plus_async_call_t *vac)
165 {
166 /*
167 * Common call structure initialisation:
168 */
169 if (pthread_mutex_init(&vac->vac_mtx, NULL) != 0)
170 v8plus_panic("could not init async call mutex");
171 if (pthread_cond_init(&vac->vac_cv, NULL) != 0)
172 v8plus_panic("could not init async call condvar");
173 vac->vac_flags &= ~(ACF_COMPLETED);
174
175 /*
176 * Post request to queue:
177 */
178 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
179 v8plus_panic("could not lock async queue mutex");
180 STAILQ_INSERT_TAIL(&_v8plus_callq, vac, vac_callq_entry);
181 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
182 v8plus_panic("could not unlock async queue mutex");
183 uv_async_send(&_v8plus_uv_async);
184
185 if (vac->vac_flags & ACF_NOREPLY) {
186 /*
187 * The caller does not care about the reply, and has allocated
188 * the v8plus_async_call_t structure from the heap. The
189 * async callback will free the storage when it completes.
190 */
191 return (NULL);
192 }
193
194 /*
195 * Wait for our request to be serviced on the event loop thread:
196 */
197 if (pthread_mutex_lock(&vac->vac_mtx) != 0)
198 v8plus_panic("could not lock async call mutex");
199 while (!(vac->vac_flags & ACF_COMPLETED)) {
200 if (pthread_cond_wait(&vac->vac_cv, &vac->vac_mtx) != 0)
201 v8plus_panic("could not wait on async call condvar");
202 }
203 if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
204 v8plus_panic("could not unlock async call mutex");
205
206 if (pthread_cond_destroy(&vac->vac_cv) != 0)
207 v8plus_panic("could not destroy async call condvar");
208 if (pthread_mutex_destroy(&vac->vac_mtx) != 0)
209 v8plus_panic("could not destroy async call mutex");
210
211 return (vac->vac_return);
212 }
213
214 nvlist_t *
215 v8plus_method_call(void *cop, const char *name, const nvlist_t *lp)
216 {
217 v8plus_async_call_t vac;
218
219 if (v8plus_in_event_thread() == B_TRUE) {
220 /*
221 * We're running in the event loop thread, so we can make the
222 * call directly.
223 */
224 return (v8plus_method_call_direct(cop, name, lp));
225 }
226
227 bzero(&vac, sizeof (vac));
228 vac.vac_type = ACT_OBJECT_CALL;
229 vac.vac_cop = cop;
230 vac.vac_name = name;
231 vac.vac_lp = lp;
232
233 return (v8plus_cross_thread_call(&vac));
234 }
235
236 nvlist_t *
237 v8plus_call(v8plus_jsfunc_t func, const nvlist_t *lp)
238 {
239 v8plus_async_call_t vac;
240
241 if (v8plus_in_event_thread() == B_TRUE) {
242 /*
243 * We're running in the event loop thread, so we can make the
244 * call directly.
245 */
246 return (v8plus_call_direct(func, lp));
247 }
248
249 bzero(&vac, sizeof (vac));
250 vac.vac_type = ACT_JSFUNC_CALL;
251 vac.vac_func = func;
252 vac.vac_lp = lp;
253
254 return (v8plus_cross_thread_call(&vac));
255 }
256
257 void
258 v8plus_obj_rele(const void *cop)
259 {
260 v8plus_async_call_t *vac;
261
262 if (v8plus_in_event_thread() == B_TRUE) {
263 return (v8plus_obj_rele_direct(cop));
264 }
265
266 vac = calloc(1, sizeof (*vac));
267 if (vac == NULL)
268 v8plus_panic("could not allocate async call structure");
269
270 vac->vac_type = ACT_OBJECT_RELEASE;
271 vac->vac_flags = ACF_NOREPLY;
272 vac->vac_cop = (void *)cop;
273
274 (void) v8plus_cross_thread_call(vac);
275 }
276
277 void
278 v8plus_jsfunc_rele(v8plus_jsfunc_t f)
279 {
280 v8plus_async_call_t *vac;
281
282 if (v8plus_in_event_thread() == B_TRUE) {
283 return (v8plus_jsfunc_rele_direct(f));
284 }
285
286 vac = calloc(1, sizeof (*vac));
287 if (vac == NULL)
288 v8plus_panic("could not allocate async call structure");
289
290 vac->vac_type = ACT_JSFUNC_RELEASE;
291 vac->vac_flags = ACF_NOREPLY;
292 vac->vac_func = f;
293
294 (void) v8plus_cross_thread_call(vac);
295 }
296
297 /*
298 * Initialise structures for off-event-loop method calls.
299 *
300 * Note that uv_async_init() must be called inside the libuv event loop, so we
301 * do it here. We also want to record the thread ID of the Event Loop thread
302 * so as to determine what kind of method calls to make later.
303 */
304 void
305 v8plus_crossthread_init(void)
306 {
307 _v8plus_uv_event_thread = pthread_self();
308 if (uv_async_init(uv_default_loop(), &_v8plus_uv_async,
309 v8plus_async_callback) != 0)
310 v8plus_panic("unable to initialise uv_async_t");
311 if (pthread_mutex_init(&_v8plus_callq_mtx, NULL) != 0)
312 v8plus_panic("unable to initialise mutex");
313
314 /*
315 * If we do not unreference the async handle, then its mere
316 * existence will keep the event loop open forever. If the consumer
317 * _wants_ this behaviour, they may call v8plus_eventloop_hold()
318 * from the event loop thread.
319 */
320 uv_unref((uv_handle_t *)&_v8plus_uv_async);
321 }
322
323 void
324 v8plus_eventloop_hold(void)
325 {
326 ++_v8plus_eventloop_refcount;
327 uv_ref((uv_handle_t *)&_v8plus_uv_async);
328 }
329
330 void
331 v8plus_eventloop_rele_direct(void)
332 {
333 if (--_v8plus_eventloop_refcount < 1) {
334 _v8plus_eventloop_refcount = 0;
335 uv_unref((uv_handle_t *)&_v8plus_uv_async);
336 }
337 }
338
339 void
340 v8plus_eventloop_rele(void)
341 {
342 v8plus_async_call_t *vac;
343
344 if (v8plus_in_event_thread() == B_TRUE) {
345 return (v8plus_eventloop_rele_direct());
346 }
347
348 vac = calloc(1, sizeof (*vac));
349 if (vac == NULL)
350 v8plus_panic("could not allocate async call structure");
351
352 vac->vac_type = ACT_EVENTLOOP_RELEASE;
353 vac->vac_flags = ACF_NOREPLY;
354
355 (void) v8plus_cross_thread_call(vac);
356 }
357
358 nvlist_t *
359 v8plus_verror(v8plus_errno_t e, const char *fmt, va_list ap)
360 {
361 if (fmt == NULL) {
362 if (e == V8PLUSERR_NOERROR) {
363 *_v8plus_errmsg = '\0';
364 } else {
365 (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN,
366 "%s", v8plus_strerror(e));
367 }
368 } else {
369 (void) vsnprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN, fmt, ap);
370 }
371 _v8plus_errno = e;
372
373 return (NULL);
374 }
375
376 nvlist_t *
377 v8plus_error(v8plus_errno_t e, const char *fmt, ...)
378 {
379 va_list ap;
380
381 va_start(ap, fmt);
382 (void) v8plus_verror(e, fmt, ap);
383 va_end(ap);
384
385 return (NULL);
386 }
387
388 static void __NORETURN
389 v8plus_vpanic(const char *fmt, va_list ap)
390 {
391 (void) vfprintf(stderr, fmt, ap);
392 (void) fflush(stderr);
393 abort();
394 }
395
396 void
397 v8plus_panic(const char *fmt, ...)
398 {
399 va_list ap;
400
401 va_start(ap, fmt);
402 v8plus_vpanic(fmt, ap);
403 va_end(ap);
404 }
405
406 nvlist_t *
407 v8plus_nverr(int nverr, const char *member)
408 {
409 (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN,
410 "nvlist manipulation error on member %s: %s",
411 member == NULL ? "<none>" : member, strerror(nverr));
412
413 switch (nverr) {
414 case ENOMEM:
415 _v8plus_errno = V8PLUSERR_NOMEM;
416 break;
417 case EINVAL:
418 _v8plus_errno = V8PLUSERR_YOUSUCK;
419 break;
420 default:
421 _v8plus_errno = V8PLUSERR_UNKNOWN;
422 break;
423 }
424
425 return (NULL);
426 }
427
428 nvlist_t *
429 v8plus_syserr(int syserr, const char *fmt, ...)
430 {
431 v8plus_errno_t e;
432 va_list ap;
433
434 switch (syserr) {
435 case ENOMEM:
436 e = V8PLUSERR_NOMEM;
437 break;
438 case EBADF:
439 e = V8PLUSERR_BADF;
440 break;
441 default:
442 e = V8PLUSERR_UNKNOWN;
443 break;
444 }
445
446 va_start(ap, fmt);
447 (void) v8plus_verror(e, fmt, ap);
448 va_end(ap);
449
450 return (NULL);
451 }
452
453 /*
454 * The NULL nvlist with V8PLUSERR_NOERROR means we are returning void.
455 */
456 nvlist_t *
457 v8plus_void(void)
458 {
459 return (v8plus_error(V8PLUSERR_NOERROR, NULL));
460 }
461
462 v8plus_type_t
463 v8plus_typeof(const nvpair_t *pp)
464 {
465 data_type_t t = nvpair_type((nvpair_t *)pp);
466
467 switch (t) {
468 case DATA_TYPE_DOUBLE:
469 return (V8PLUS_TYPE_NUMBER);
470 case DATA_TYPE_STRING:
471 return (V8PLUS_TYPE_STRING);
472 case DATA_TYPE_NVLIST:
473 return (V8PLUS_TYPE_OBJECT);
474 case DATA_TYPE_BOOLEAN_VALUE:
475 return (V8PLUS_TYPE_BOOLEAN);
476 case DATA_TYPE_BOOLEAN:
477 return (V8PLUS_TYPE_UNDEFINED);
478 case DATA_TYPE_BYTE:
479 {
480 uchar_t v;
481 if (nvpair_value_byte((nvpair_t *)pp, &v) != 0 || v != 0)
482 return (V8PLUS_TYPE_INVALID);
483 return (V8PLUS_TYPE_NULL);
484 }
485 case DATA_TYPE_UINT64_ARRAY:
486 {
487 uint64_t *vp;
488 uint_t nv;
489 if (nvpair_value_uint64_array((nvpair_t *)pp, &vp, &nv) != 0 ||
490 nv != 1) {
491 return (V8PLUS_TYPE_INVALID);
492 }
493 return (V8PLUS_TYPE_JSFUNC);
494 }
495 default:
496 return (V8PLUS_TYPE_INVALID);
497 }
498 }
499
500 static int
501 v8plus_arg_value(v8plus_type_t t, const nvpair_t *pp, void *vp)
502 {
503 data_type_t dt = nvpair_type((nvpair_t *)pp);
504
505 switch (t) {
506 case V8PLUS_TYPE_NONE:
507 return (-1);
508 case V8PLUS_TYPE_STRING:
509 if (dt == DATA_TYPE_STRING) {
510 if (vp != NULL) {
511 (void) nvpair_value_string((nvpair_t *)pp,
512 (char **)vp);
513 }
514 return (0);
515 }
516 return (-1);
517 case V8PLUS_TYPE_NUMBER:
518 if (dt == DATA_TYPE_DOUBLE) {
519 if (vp != NULL) {
520 (void) nvpair_value_double((nvpair_t *)pp,
521 (double *)vp);
522 }
523 return (0);
524 }
525 return (-1);
526 case V8PLUS_TYPE_BOOLEAN:
527 if (dt == DATA_TYPE_BOOLEAN_VALUE) {
528 if (vp != NULL) {
529 (void) nvpair_value_boolean_value(
530 (nvpair_t *)pp, (boolean_t *)vp);
531 }
532 return (0);
533 }
534 return (-1);
535 case V8PLUS_TYPE_JSFUNC:
536 if (dt == DATA_TYPE_UINT64_ARRAY) {
537 uint_t nv;
538 uint64_t *vpp;
539
540 if (nvpair_value_uint64_array((nvpair_t *)pp,
541 &vpp, &nv) == 0 && nv == 1) {
542 if (vp != NULL)
543 *(v8plus_jsfunc_t *)vp = vpp[0];
544 return (0);
545 }
546 }
547 return (-1);
548 case V8PLUS_TYPE_OBJECT:
549 if (dt == DATA_TYPE_NVLIST) {
550 if (vp != NULL) {
551 (void) nvpair_value_nvlist((nvpair_t *)pp,
552 (nvlist_t **)vp);
553 }
554 return (0);
555 }
556 return (-1);
557 case V8PLUS_TYPE_NULL:
558 if (dt == DATA_TYPE_BYTE) {
559 uchar_t v;
560
561 if (nvpair_value_byte((nvpair_t *)pp, &v) == 0 &&
562 v == 0)
563 return (0);
564 }
565 return (-1);
566 case V8PLUS_TYPE_UNDEFINED:
567 return (dt == DATA_TYPE_BOOLEAN ? 0 : -1);
568 case V8PLUS_TYPE_ANY:
569 if (vp != NULL)
570 *(const nvpair_t **)vp = pp;
571 return (0);
572 case V8PLUS_TYPE_INVALID:
573 if (vp != NULL)
574 *(data_type_t *)vp = dt;
575 return (0);
576 case V8PLUS_TYPE_STRNUMBER64:
577 if (dt == DATA_TYPE_STRING) {
578 char *s;
579 uint64_t v;
580
581 (void) nvpair_value_string((nvpair_t *)pp, &s);
582 errno = 0;
583 v = (uint64_t)strtoull(s, NULL, 0);
584 if (errno != 0)
585 return (-1);
586 if (vp != NULL)
587 *(uint64_t *)vp = v;
588 return (0);
589 }
590 return (-1);
591 default:
592 return (-1);
593 }
594 }
595
596 int
597 v8plus_args(const nvlist_t *lp, uint_t flags, v8plus_type_t t, ...)
598 {
599 v8plus_type_t nt;
600 nvpair_t *pp;
601 void *vp;
602 va_list ap;
603 uint_t i;
604 char buf[32];
605
606 va_start(ap, t);
607
608 for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) {
609 switch (nt) {
610 case V8PLUS_TYPE_UNDEFINED:
611 case V8PLUS_TYPE_NULL:
612 break;
613 default:
614 (void) va_arg(ap, void *);
615 }
616
617 (void) snprintf(buf, sizeof (buf), "%u", i);
618 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) != 0) {
619 (void) v8plus_error(V8PLUSERR_MISSINGARG,
620 "argument %u is required", i);
621 return (-1);
622 }
623
624 if (v8plus_arg_value(nt, pp, NULL) != 0) {
625 (void) v8plus_error(V8PLUSERR_BADARG,
626 "argument %u is of incorrect type", i);
627 return (-1);
628 }
629
630 nt = va_arg(ap, data_type_t);
631 }
632
633 va_end(ap);
634
635 if (flags & V8PLUS_ARG_F_NOEXTRA) {
636 (void) snprintf(buf, sizeof (buf), "%u", i);
637 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0) {
638 (void) v8plus_error(V8PLUSERR_EXTRAARG,
639 "superfluous extra argument(s) detected");
640 return (-1);
641 }
642 }
643
644 va_start(ap, t);
645
646 for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) {
647 switch (nt) {
648 case V8PLUS_TYPE_UNDEFINED:
649 case V8PLUS_TYPE_NULL:
650 vp = NULL;
651 break;
652 default:
653 vp = va_arg(ap, void *);
654 }
655
656 (void) snprintf(buf, sizeof (buf), "%u", i);
657 VERIFY(nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0);
658 VERIFY(v8plus_arg_value(nt, pp, vp) == 0);
659
660 nt = va_arg(ap, data_type_t);
661 }
662
663 va_end(ap);
664
665 return (0);
666 }
667
668 static int
669 v8plus_obj_vsetprops(nvlist_t *lp, v8plus_type_t t, va_list *ap)
670 {
671 v8plus_type_t nt = t;
672 char *name;
673 int err;
674
675 /*
676 * Do not call va_start() or va_end() in this function! We are limited
677 * to a single traversal of the arguments so that we can recurse to
678 * handle embedded object definitions.
679 */
680
681 while (nt != V8PLUS_TYPE_NONE) {
682 name = va_arg(*ap, char *);
683
684 switch (nt) {
685 case V8PLUS_TYPE_STRING:
686 {
687 char *s = va_arg(*ap, char *);
688 if ((err = nvlist_add_string(lp, name, s)) != 0) {
689 (void) v8plus_nverr(err, name);
690 return (-1);
691 }
692 break;
693 }
694 case V8PLUS_TYPE_NUMBER:
695 {
696 double d = va_arg(*ap, double);
697 if ((err = nvlist_add_double(lp, name, d)) != 0) {
698 (void) v8plus_nverr(err, name);
699 return (-1);
700 }
701 break;
702 }
703 case V8PLUS_TYPE_BOOLEAN:
704 {
705 boolean_t b = va_arg(*ap, boolean_t);
706 if ((err = nvlist_add_boolean_value(lp,
707 name, b)) != 0) {
708 (void) v8plus_nverr(err, name);
709 return (-1);
710 }
711 break;
712 }
713 case V8PLUS_TYPE_JSFUNC:
714 {
715 v8plus_jsfunc_t j = va_arg(*ap, v8plus_jsfunc_t);
716 if ((err = nvlist_add_uint64_array(lp,
717 name, &j, 1)) != 0) {
718 (void) v8plus_nverr(err, name);
719 return (-1);
720 }
721 if ((err = nvlist_add_string_array(lp,
722 V8PLUS_JSF_COOKIE, NULL, 0)) != 0) {
723 (void) v8plus_nverr(err, V8PLUS_JSF_COOKIE);
724 return (-1);
725 }
726 v8plus_jsfunc_hold(j);
727 break;
728 }
729 case V8PLUS_TYPE_OBJECT:
730 {
731 const nvlist_t *op = va_arg(*ap, const nvlist_t *);
732 if ((err = nvlist_add_nvlist(lp, name,
733 (nvlist_t *)op)) != 0) {
734 (void) v8plus_nverr(err, name);
735 return (-1);
736 }
737 break;
738 }
739 case V8PLUS_TYPE_NULL:
740 if ((err = nvlist_add_byte(lp, name, 0)) != 0) {
741 (void) v8plus_nverr(err, name);
742 return (-1);
743 }
744 break;
745 case V8PLUS_TYPE_UNDEFINED:
746 if ((err = nvlist_add_boolean(lp, name)) != 0) {
747 (void) v8plus_nverr(err, name);
748 return (-1);
749 }
750 break;
751 case V8PLUS_TYPE_ANY:
752 {
753 nvpair_t *pp = va_arg(*ap, nvpair_t *);
754 if ((err = nvlist_add_nvpair(lp, pp)) != 0) {
755 (void) v8plus_nverr(err, name);
756 return (-1);
757 }
758 break;
759 }
760 case V8PLUS_TYPE_STRNUMBER64:
761 {
762 uint64_t v = va_arg(*ap, uint64_t);
763 char s[32];
764 (void) snprintf(s, sizeof (s), "%" PRIu64, v);
765 if ((err = nvlist_add_string(lp, name, s)) != 0) {
766 (void) v8plus_nverr(err, name);
767 return (-1);
768 }
769 break;
770 }
771 case V8PLUS_TYPE_INL_OBJECT:
772 {
773 nvlist_t *slp;
774
775 nt = va_arg(*ap, v8plus_type_t);
776 err = nvlist_alloc(&slp, NV_UNIQUE_NAME, 0);
777 if (err != 0) {
778 (void) v8plus_nverr(err, name);
779 return (-1);
780 }
781 if (v8plus_obj_vsetprops(slp, nt, ap) != 0)
782 return (-1);
783
784 err = nvlist_add_nvlist(lp, name, slp);
785 nvlist_free(slp);
786 if (err != 0) {
787 (void) v8plus_nverr(err, name);
788 return (-1);
789 }
790 break;
791 }
792 case V8PLUS_TYPE_INVALID:
793 default:
794 (void) v8plus_error(V8PLUSERR_YOUSUCK,
795 "invalid property type %d", nt);
796 return (-1);
797 }
798
799 nt = va_arg(*ap, v8plus_type_t);
800 }
801
802 return (0);
803 }
804
805 nvlist_t *
806 v8plus_obj(v8plus_type_t t, ...)
807 {
808 nvlist_t *rp;
809 va_list ap;
810 int err;
811
812 if ((err = nvlist_alloc(&rp, NV_UNIQUE_NAME, 0)) != 0)
813 return (v8plus_nverr(err, NULL));
814
815 va_start(ap, t);
816 err = v8plus_obj_vsetprops(rp, t, &ap);
817 va_end(ap);
818
819 if (err != 0) {
820 nvlist_free(rp);
821 rp = NULL;
822 }
823
824 return (rp);
825 }
826
827 int
828 v8plus_obj_setprops(nvlist_t *lp, v8plus_type_t t, ...)
829 {
830 va_list ap;
831 int err;
832
833 va_start(ap, t);
834 err = v8plus_obj_vsetprops(lp, t, &ap);
835 va_end(ap);
836
837 return (err);
838 }
839
840 static void
841 v8plus_uv_worker(uv_work_t *wp)
842 {
843 v8plus_uv_ctx_t *cp = wp->data;
844
845 cp->vuc_result = cp->vuc_worker(cp->vuc_obj, cp->vuc_ctx);
846 }
847
848 static void
849 v8plus_uv_completion(uv_work_t *wp)
850 {
851 v8plus_uv_ctx_t *cp = wp->data;
852
853 cp->vuc_completion(cp->vuc_obj, cp->vuc_ctx, cp->vuc_result);
854 v8plus_obj_rele(cp->vuc_obj);
855 free(cp);
856 free(wp);
857 }
858
859 void
860 v8plus_defer(void *cop, void *ctxp, v8plus_worker_f worker,
861 v8plus_completion_f completion)
862 {
863 uv_work_t *wp = malloc(sizeof (uv_work_t));
864 v8plus_uv_ctx_t *cp = malloc(sizeof (v8plus_uv_ctx_t));
865
866 bzero(wp, sizeof (uv_work_t));
867 bzero(cp, sizeof (v8plus_uv_ctx_t));
868
869 v8plus_obj_hold(cop);
870 cp->vuc_obj = cop;
871 cp->vuc_ctx = ctxp;
872 cp->vuc_worker = worker;
873 cp->vuc_completion = completion;
874 wp->data = cp;
875
876 uv_queue_work(uv_default_loop(), wp, v8plus_uv_worker,
877 v8plus_uv_completion);
878 }