--- old/./README.md	2013-02-06 19:07:00.184241333 +0000
+++ new/./README.md	2013-02-06 19:07:00.189800578 +0000
@@ -559,10 +559,10 @@
 		 * In thread pool context -- do not call any of the
 		 * following functions:
 		 * v8plus_obj_hold()
-		 * v8plus_obj_rele()
+		 * v8plus_obj_rele_direct()
 		 * v8plus_jsfunc_hold()
-		 * v8plus_jsfunc_rele()
-		 * v8plus_call()
+		 * v8plus_jsfunc_rele_direct()
+		 * v8plus_call_direct()
 		 * v8plus_method_call_direct()
 		 * v8plus_defer()
 		 *
@@ -688,7 +688,9 @@
 
 ### void v8plus_obj_rele(const void *op)
 
-Releases a hold placed by `v8plus_obj_hold()`.
+Releases a hold placed by `v8plus_obj_hold()`.  This function may be called
+safely from any thread; releases from threads other than the main event loop
+are non-blocking and will occur some time in the future.
 
 ### void v8plus_jsfunc_hold(v8plus_jsfunc_t f)
 
@@ -702,7 +704,9 @@
 
 ### void v8plus_jsfunc_rele(v8plus_jsfunc_t f)
 
-Releases a hold placed by `v8plus_jsfunc_hold()`.
+Releases a hold placed by `v8plus_jsfunc_hold()`.  This function may be called
+safely from any thread; releases from threads other than the main event loop
+thread are non-blocking and will occur some time in the future.
 
 ### void v8plus_defer(void *op, void *ctx, worker, completion)
 
@@ -719,6 +723,18 @@
 argument and return value encoding match the encodings that are used by C
 functions that provide methods.
 
+As JavaScript functions must be called from the event loop thread,
+`v8plus_call()` contains logic to determine whether we are in the
+correct context or not.  If we are running on some other thread we will
+queue the request and sleep, waiting for the event loop thread to make the
+call.  In the simple case, where we are already in the correct thread, we
+make the call directly.
+
+Note that when passing JavaScript functions around as callbacks, you must use
+first use `v8plus_jsfunc_hold()` from within the main event loop thread.  Once
+finished with the function, you may pass it to `v8plus_jsfunc_rele()` from any
+thread to clean up.
+
 ### nvlist_t *v8plus_method_call(void *op, const char *name, const nvlist_t *ap)
 
 Calls the method named by `name` in the native object `op` with encoded
@@ -727,12 +743,9 @@
 emitter example above.  The effects of using this function to call a native
 method are undefined.
 
-As JavaScript functions must be called from the event loop thread,
-`v8plus_method_call()` contains logic to determine whether we are in the
-correct context or not.  If we are running on some other thread we will
-queue the request and sleep, waiting for the event loop thread to make the
-call.  In the simple case, where we are already in the correct thread, we
-make the call directly.
+When called from threads other than the main event loop thread,
+`v8plus_method_call()` uses the same queue-and-block logic as described above
+in `v8plus_call()`.
 
 ## FAQ
 
--- old/./package.json	2013-02-06 19:07:00.321940837 +0000
+++ new/./package.json	2013-02-06 19:07:00.327442308 +0000
@@ -2,7 +2,10 @@
 	"author": "Keith M Wesolowski <keith.wesolowski@joyent.com>",
 	"name": "v8plus",
 	"description": "utility environment for writing addons in C",
-	"version": "0.0.5",
+	"version": "0.0.6",
+	"contributors": [
+		"Joshua M. Clulow <jmc@joyent.com>"
+	],
 	"repository": {
 		"type": "git",
 		"url": "git://github.com/wesolows/v8plus.git"
--- old/./v8plus_csup.c	2013-02-06 19:07:00.440485145 +0000
+++ new/./v8plus_csup.c	2013-02-06 19:07:00.446017516 +0000
@@ -31,17 +31,42 @@
 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;
 
-	boolean_t vac_run;
-	nvlist_t *vac_return;
-
 	STAILQ_ENTRY(v8plus_async_call) vac_callq_entry;
 } v8plus_async_call_t;
 
@@ -52,7 +77,7 @@
 }
 
 static void
-v8plus_async_callback(uv_async_t *async, int status __UNUSED)
+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");
@@ -78,14 +103,41 @@
 		/*
 		 * Run the queued method:
 		 */
-		if (vac->vac_run == B_TRUE)
+		if (vac->vac_flags & ACF_COMPLETED)
 			v8plus_panic("async call already run");
-		vac->vac_return = v8plus_method_call_direct(vac->vac_cop,
-		    vac->vac_name, vac->vac_lp);
+
+		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_run = B_TRUE;
+		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)
@@ -93,66 +145,148 @@
 	}
 }
 
-nvlist_t *
-v8plus_method_call(void *cop, const char *name, const nvlist_t *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.
+ *
+ * This routine implements the parts of this interaction common to all
+ * variants.
+ */
+static nvlist_t *
+v8plus_cross_thread_call(v8plus_async_call_t *vac)
 {
-	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.
+	 * Common call structure initialisation:
 	 */
-	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)
+	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)
+	if (pthread_cond_init(&vac->vac_cv, NULL) != 0)
 		v8plus_panic("could not init async call condvar");
-	vac.vac_run = B_FALSE;
+	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);
+	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)
+	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)
+	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)
+	if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
 		v8plus_panic("could not unlock async call mutex");
 
-	if (pthread_cond_destroy(&vac.vac_cv) != 0)
+	if (pthread_cond_destroy(&vac->vac_cv) != 0)
 		v8plus_panic("could not destroy async call condvar");
-	if (pthread_mutex_destroy(&vac.vac_mtx) != 0)
+	if (pthread_mutex_destroy(&vac->vac_mtx) != 0)
 		v8plus_panic("could not destroy async call mutex");
 
-	return (vac.vac_return);
+	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.
--- old/./v8plus_glue.h	2013-02-06 19:07:00.578608530 +0000
+++ new/./v8plus_glue.h	2013-02-06 19:07:00.584103522 +0000
@@ -112,6 +112,7 @@
 extern int nvpair_value_v8plus_jsfunc(const nvpair_t *, v8plus_jsfunc_t *);
 extern void v8plus_jsfunc_hold(v8plus_jsfunc_t);
 extern void v8plus_jsfunc_rele(v8plus_jsfunc_t);
+extern void v8plus_jsfunc_rele_direct(v8plus_jsfunc_t);
 
 /*
  * Place or release a hold on the V8 representation of the specified C object.
@@ -124,6 +125,7 @@
  */
 extern void v8plus_obj_hold(const void *);
 extern void v8plus_obj_rele(const void *);
+extern void v8plus_obj_rele_direct(const void *);
 
 /*
  * Convenience functions for dealing with JS arguments.
@@ -154,6 +156,7 @@
  * possible for the JS code to modify the function represented by the cookie.
  */
 extern nvlist_t *v8plus_call(v8plus_jsfunc_t, const nvlist_t *);
+extern nvlist_t *v8plus_call_direct(v8plus_jsfunc_t, const nvlist_t *);
 
 /*
  * Call the named JavaScript function in the context of the JS object
--- old/./v8plus_impl.h	2013-02-06 19:07:00.702757347 +0000
+++ new/./v8plus_impl.h	2013-02-06 19:07:00.708269439 +0000
@@ -29,9 +29,6 @@
 #define	NODE_MAKECALLBACK_RETURN
 #endif
 
-extern "C" boolean_t v8plus_in_event_thread(void);
-extern "C" void v8plus_crossthread_init(void);
-
 namespace v8plus {
 
 class ObjectWrap;
--- old/./v8plus_subr.cc	2013-02-06 19:07:00.822629915 +0000
+++ new/./v8plus_subr.cc	2013-02-06 19:07:00.828209591 +0000
@@ -416,7 +416,7 @@
 }
 
 extern "C" nvlist_t *
-v8plus_call(v8plus_jsfunc_t f, const nvlist_t *lp)
+v8plus_call_direct(v8plus_jsfunc_t f, const nvlist_t *lp)
 {
 	std::unordered_map<uint64_t, cb_hdl_t>::iterator it;
 	const int max_argc = nvlist_length(lp);
@@ -526,7 +526,7 @@
 }
 
 extern "C" void
-v8plus_jsfunc_rele(v8plus_jsfunc_t f)
+v8plus_jsfunc_rele_direct(v8plus_jsfunc_t f)
 {
 	v8::Local<v8::Function> lfh;
 	std::unordered_map<uint64_t, cb_hdl_t>::iterator it;
@@ -640,7 +640,7 @@
 }
 
 extern "C" void
-v8plus_obj_rele(const void *cop)
+v8plus_obj_rele_direct(const void *cop)
 {
 	v8plus::ObjectWrap *op = v8plus::ObjectWrap::objlookup(cop);
 	op->public_Unref();
