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 }