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 }