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