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