Print this page
enable v8plus_call to be used in any thread

*** 29,60 **** STAILQ_HEAD_INITIALIZER(_v8plus_callq); static pthread_mutex_t _v8plus_callq_mtx; static pthread_t _v8plus_uv_event_thread; static uv_async_t _v8plus_uv_async; typedef struct v8plus_async_call { void *vac_cop; const char *vac_name; const nvlist_t *vac_lp; pthread_cond_t vac_cv; pthread_mutex_t vac_mtx; - boolean_t vac_run; - nvlist_t *vac_return; - STAILQ_ENTRY(v8plus_async_call) vac_callq_entry; } v8plus_async_call_t; boolean_t v8plus_in_event_thread(void) { return (_v8plus_uv_event_thread == pthread_self() ? B_TRUE : B_FALSE); } static void ! v8plus_async_callback(uv_async_t *async, int status __UNUSED) { if (v8plus_in_event_thread() != B_TRUE) v8plus_panic("async callback called outside of event loop"); for (;;) { --- 29,85 ---- STAILQ_HEAD_INITIALIZER(_v8plus_callq); static pthread_mutex_t _v8plus_callq_mtx; static pthread_t _v8plus_uv_event_thread; static uv_async_t _v8plus_uv_async; + typedef enum v8plus_async_call_type { + ACT_OBJECT_CALL = 1, + ACT_OBJECT_RELEASE, + ACT_JSFUNC_CALL, + ACT_JSFUNC_RELEASE, + } v8plus_async_call_type_t; + + typedef enum v8plus_async_call_flags { + ACF_COMPLETED = 0x01, + ACF_NOREPLY = 0x02 + } v8plus_async_call_flags_t; + + typedef struct v8plus_async_call { + v8plus_async_call_type_t vac_type; + v8plus_async_call_flags_t vac_flags; + + /* + * For ACT_OBJECT_{CALL,RELEASE}: + */ void *vac_cop; const char *vac_name; + /* + * For ACT_JSFUNC_{CALL,RELEASE}: + */ + v8plus_jsfunc_t vac_func; + + /* + * Common call arguments: + */ const nvlist_t *vac_lp; + nvlist_t *vac_return; pthread_cond_t vac_cv; pthread_mutex_t vac_mtx; STAILQ_ENTRY(v8plus_async_call) vac_callq_entry; } v8plus_async_call_t; boolean_t v8plus_in_event_thread(void) { return (_v8plus_uv_event_thread == pthread_self() ? B_TRUE : B_FALSE); } static void ! v8plus_async_callback(uv_async_t *async __UNUSED, int status __UNUSED) { if (v8plus_in_event_thread() != B_TRUE) v8plus_panic("async callback called outside of event loop"); for (;;) {
*** 76,160 **** break; /* * Run the queued method: */ ! if (vac->vac_run == B_TRUE) v8plus_panic("async call already run"); ! vac->vac_return = v8plus_method_call_direct(vac->vac_cop, ! vac->vac_name, vac->vac_lp); if (pthread_mutex_lock(&vac->vac_mtx) != 0) v8plus_panic("could not lock async call mutex"); ! vac->vac_run = B_TRUE; if (pthread_cond_broadcast(&vac->vac_cv) != 0) v8plus_panic("could not signal async call condvar"); if (pthread_mutex_unlock(&vac->vac_mtx) != 0) v8plus_panic("could not unlock async call mutex"); } } ! nvlist_t * ! v8plus_method_call(void *cop, const char *name, const nvlist_t *lp) ! { ! v8plus_async_call_t vac; ! ! if (v8plus_in_event_thread() == B_TRUE) { ! /* ! * We're running in the event loop thread, so we can make the ! * call directly. */ ! return (v8plus_method_call_direct(cop, name, lp)); ! } ! /* ! * As we cannot manipulate v8plus/V8/Node structures directly from ! * outside the event loop thread, we push the call arguments onto a ! * queue and post to the event loop thread. We then sleep on our ! * condition variable until the event loop thread makes the call ! * for us and wakes us up. */ ! bzero(&vac, sizeof (vac)); ! vac.vac_cop = cop; ! vac.vac_name = name; ! vac.vac_lp = lp; ! if (pthread_mutex_init(&vac.vac_mtx, NULL) != 0) v8plus_panic("could not init async call mutex"); ! if (pthread_cond_init(&vac.vac_cv, NULL) != 0) v8plus_panic("could not init async call condvar"); ! vac.vac_run = B_FALSE; /* * Post request to queue: */ if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0) v8plus_panic("could not lock async queue mutex"); ! STAILQ_INSERT_TAIL(&_v8plus_callq, &vac, vac_callq_entry); if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0) v8plus_panic("could not unlock async queue mutex"); uv_async_send(&_v8plus_uv_async); /* * Wait for our request to be serviced on the event loop thread: */ ! if (pthread_mutex_lock(&vac.vac_mtx) != 0) v8plus_panic("could not lock async call mutex"); ! while (vac.vac_run == B_FALSE) { ! if (pthread_cond_wait(&vac.vac_cv, &vac.vac_mtx) != 0) v8plus_panic("could not wait on async call condvar"); } ! if (pthread_mutex_unlock(&vac.vac_mtx) != 0) v8plus_panic("could not unlock async call mutex"); ! if (pthread_cond_destroy(&vac.vac_cv) != 0) v8plus_panic("could not destroy async call condvar"); ! if (pthread_mutex_destroy(&vac.vac_mtx) != 0) v8plus_panic("could not destroy async call mutex"); ! return (vac.vac_return); } /* * Initialise structures for off-event-loop method calls. * * Note that uv_async_init() must be called inside the libuv event loop, so we --- 101,294 ---- break; /* * Run the queued method: */ ! if (vac->vac_flags & ACF_COMPLETED) v8plus_panic("async call already run"); ! ! switch (vac->vac_type) { ! case ACT_OBJECT_CALL: ! vac->vac_return = v8plus_method_call_direct( ! vac->vac_cop, vac->vac_name, vac->vac_lp); ! break; ! case ACT_OBJECT_RELEASE: ! v8plus_obj_rele_direct(vac->vac_cop); ! break; ! case ACT_JSFUNC_CALL: ! vac->vac_return = v8plus_call_direct( ! vac->vac_func, vac->vac_lp); ! break; ! case ACT_JSFUNC_RELEASE: ! v8plus_jsfunc_rele_direct(vac->vac_func); ! break; ! } ! ! if (vac->vac_flags & ACF_NOREPLY) { ! /* ! * The caller posted this event and is not sleeping ! * on a reply. Just free the call structure and move ! * on. ! */ ! free(vac); ! if (vac->vac_lp != NULL) ! nvlist_free((nvlist_t *)vac->vac_lp); ! continue; ! } if (pthread_mutex_lock(&vac->vac_mtx) != 0) v8plus_panic("could not lock async call mutex"); ! vac->vac_flags |= ACF_COMPLETED; if (pthread_cond_broadcast(&vac->vac_cv) != 0) v8plus_panic("could not signal async call condvar"); if (pthread_mutex_unlock(&vac->vac_mtx) != 0) v8plus_panic("could not unlock async call mutex"); } } ! /* ! * As we cannot manipulate v8plus/V8/Node structures directly from outside the ! * event loop thread, we push the call arguments onto a queue and post to the ! * event loop thread. We then sleep on our condition variable until the event ! * loop thread makes the call for us and wakes us up. ! * ! * This routine implements the parts of this interaction common to all ! * variants. */ ! static nvlist_t * ! v8plus_cross_thread_call(v8plus_async_call_t *vac) ! { /* ! * Common call structure initialisation: */ ! if (pthread_mutex_init(&vac->vac_mtx, NULL) != 0) v8plus_panic("could not init async call mutex"); ! if (pthread_cond_init(&vac->vac_cv, NULL) != 0) v8plus_panic("could not init async call condvar"); ! vac->vac_flags &= ~(ACF_COMPLETED); /* * Post request to queue: */ if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0) v8plus_panic("could not lock async queue mutex"); ! STAILQ_INSERT_TAIL(&_v8plus_callq, vac, vac_callq_entry); if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0) v8plus_panic("could not unlock async queue mutex"); uv_async_send(&_v8plus_uv_async); + if (vac->vac_flags & ACF_NOREPLY) { + /* + * The caller does not care about the reply, and has allocated + * the v8plus_async_call_t structure from the heap. The + * async callback will free the storage when it completes. + */ + return (NULL); + } + /* * Wait for our request to be serviced on the event loop thread: */ ! if (pthread_mutex_lock(&vac->vac_mtx) != 0) v8plus_panic("could not lock async call mutex"); ! while (!(vac->vac_flags & ACF_COMPLETED)) { ! if (pthread_cond_wait(&vac->vac_cv, &vac->vac_mtx) != 0) v8plus_panic("could not wait on async call condvar"); } ! if (pthread_mutex_unlock(&vac->vac_mtx) != 0) v8plus_panic("could not unlock async call mutex"); ! if (pthread_cond_destroy(&vac->vac_cv) != 0) v8plus_panic("could not destroy async call condvar"); ! if (pthread_mutex_destroy(&vac->vac_mtx) != 0) v8plus_panic("could not destroy async call mutex"); ! return (vac->vac_return); ! } ! ! nvlist_t * ! v8plus_method_call(void *cop, const char *name, const nvlist_t *lp) ! { ! v8plus_async_call_t vac; ! ! if (v8plus_in_event_thread() == B_TRUE) { ! /* ! * We're running in the event loop thread, so we can make the ! * call directly. ! */ ! return (v8plus_method_call_direct(cop, name, lp)); ! } ! ! bzero(&vac, sizeof (vac)); ! vac.vac_type = ACT_OBJECT_CALL; ! vac.vac_cop = cop; ! vac.vac_name = name; ! vac.vac_lp = lp; ! ! return (v8plus_cross_thread_call(&vac)); } + nvlist_t * + v8plus_call(v8plus_jsfunc_t func, const nvlist_t *lp) + { + v8plus_async_call_t vac; + + if (v8plus_in_event_thread() == B_TRUE) { + /* + * We're running in the event loop thread, so we can make the + * call directly. + */ + return (v8plus_call_direct(func, lp)); + } + + bzero(&vac, sizeof (vac)); + vac.vac_type = ACT_JSFUNC_CALL; + vac.vac_func = func; + vac.vac_lp = lp; + + return (v8plus_cross_thread_call(&vac)); + } + + void + v8plus_obj_rele(const void *cop) + { + v8plus_async_call_t *vac; + + if (v8plus_in_event_thread() == B_TRUE) { + return (v8plus_obj_rele_direct(cop)); + } + + vac = calloc(1, sizeof (*vac)); + if (vac == NULL) + v8plus_panic("could not allocate async call structure"); + + vac->vac_type = ACT_OBJECT_RELEASE; + vac->vac_flags = ACF_NOREPLY; + vac->vac_cop = (void *)cop; + + (void) v8plus_cross_thread_call(vac); + } + + void + v8plus_jsfunc_rele(v8plus_jsfunc_t f) + { + v8plus_async_call_t *vac; + + if (v8plus_in_event_thread() == B_TRUE) { + return (v8plus_jsfunc_rele_direct(f)); + } + + vac = calloc(1, sizeof (*vac)); + if (vac == NULL) + v8plus_panic("could not allocate async call structure"); + + vac->vac_type = ACT_JSFUNC_RELEASE; + vac->vac_flags = ACF_NOREPLY; + vac->vac_func = f; + + (void) v8plus_cross_thread_call(vac); + } /* * Initialise structures for off-event-loop method calls. * * Note that uv_async_init() must be called inside the libuv event loop, so we