--- old/./README.md	2013-02-04 06:09:41.828501018 +0000
+++ new/./README.md	2013-02-04 06:09:41.833952220 +0000
@@ -220,7 +220,7 @@
 function.  Return an encoded object with a `res` member, or use one of the
 error/exception patterns.
 
-### nvlist_t *v8plus_c_static_method_f(nvlist_t *ap)
+### nvlist_t *v8plus_c_static_method_f(const nvlist_t *ap)
 
 In addition to methods on the native objects returned by your constructor,
 you can also provide a set of functions on the native binding object itself.
@@ -624,7 +624,7 @@
 
 The other asynchronous mechanism is the Node.js `EventEmitter` model.  This
 model requires some assistance from JavaScript code, because v8+ native
-objects no not inherit from `EventEmitter`.  To make this work, you will
+objects do not inherit from `EventEmitter`.  To make this work, you will
 need to create a JavaScript object (the object your consumers actually use)
 that inherits from `EventEmitter`, hang your native object off this object,
 and populate the native object with an appropriate method that will cause
--- old/./v8plus_csup.c	2013-02-04 06:09:41.962501219 +0000
+++ new/./v8plus_csup.c	2013-02-04 06:09:41.967800932 +0000
@@ -4,11 +4,14 @@
 
 #include <sys/ccompile.h>
 #include <sys/debug.h>
+#include <sys/queue.h>
+#include <sys/types.h>
 #include <stdarg.h>
 #include <string.h>
 #include <strings.h>
 #include <errno.h>
 #include <uv.h>
+#include <pthread.h>
 #include "v8plus_glue.h"
 
 __thread v8plus_errno_t _v8plus_errno;
@@ -22,6 +25,150 @@
 	v8plus_completion_f vuc_completion;
 } v8plus_uv_ctx_t;
 
+static STAILQ_HEAD(v8plus_callq_head, v8plus_async_call) _v8plus_callq =
+    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;
+
+nvlist_t *v8plus_method_call_direct(void *, const char *, const nvlist_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, __attribute__((unused)) int status)
+{
+	if (v8plus_in_event_thread() != B_TRUE)
+		v8plus_panic("async callback called outside of event loop");
+
+	for (;;) {
+		v8plus_async_call_t *vac = NULL;
+
+		/*
+		 * Fetch the next queued method:
+		 */
+		if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
+			v8plus_panic("could not lock async queue mutex");
+		if (!STAILQ_EMPTY(&_v8plus_callq)) {
+			vac = STAILQ_FIRST(&_v8plus_callq);
+			STAILQ_REMOVE_HEAD(&_v8plus_callq, vac_callq_entry);
+		}
+		if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
+			v8plus_panic("could not unlock async queue mutex");
+
+		if (vac == NULL)
+			break;
+
+		/*
+		 * Run the queued method:
+		 */
+		if (pthread_mutex_lock(&vac->vac_mtx) != 0)
+			v8plus_panic("could not lock async call mutex");
+
+		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);
+		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.
+	 */
+	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;
+	vac.vac_return = NULL;
+
+	/*
+	 * 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");
+	}
+
+	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
+ * do it here.  We also want to record the thread ID of the Event Loop thread
+ * so as to determine what kind of method calls to make later.
+ */
+void
+v8plus_crossthread_init(void)
+{
+	_v8plus_uv_event_thread = pthread_self();
+	if (uv_async_init(uv_default_loop(), &_v8plus_uv_async,
+	    v8plus_async_callback) != 0)
+		v8plus_panic("unable to initialise uv_async_t");
+	if (pthread_mutex_init(&_v8plus_callq_mtx, NULL) != 0)
+		v8plus_panic("unable to initialise mutex");
+}
+
 nvlist_t *
 v8plus_verror(v8plus_errno_t e, const char *fmt, va_list ap)
 {
--- old/./v8plus_impl.h	2013-02-04 06:09:42.091551364 +0000
+++ new/./v8plus_impl.h	2013-02-04 06:09:42.096921526 +0000
@@ -29,6 +29,9 @@
 #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_objectwrap.cc	2013-02-04 06:09:42.207904713 +0000
+++ new/./v8plus_objectwrap.cc	2013-02-04 06:09:42.213297442 +0000
@@ -100,6 +100,8 @@
 		    v8::FunctionTemplate::New(
 		    v8plus::ObjectWrap::cons)->GetFunction());
 	}
+
+	v8plus_crossthread_init();
 }
 
 v8::Handle<v8::Value>
--- old/./v8plus_subr.cc	2013-02-04 06:09:42.329801358 +0000
+++ new/./v8plus_subr.cc	2013-02-04 06:09:42.335283735 +0000
@@ -453,7 +453,7 @@
 }
 
 extern "C" nvlist_t *
-v8plus_method_call(void *cop, const char *name, const nvlist_t *lp)
+v8plus_method_call_direct(void *cop, const char *name, const nvlist_t *lp)
 {
 	v8plus::ObjectWrap *op = v8plus::ObjectWrap::objlookup(cop);
 	const int max_argc = nvlist_length(lp);
@@ -462,6 +462,9 @@
 	v8::Handle<v8::Value> res;
 	nvlist_t *rp;
 
+	if (v8plus_in_event_thread() != _B_TRUE)
+		v8plus_panic("direct method call outside of event loop");
+
 	argc = max_argc;
 	nvlist_to_v8_argv(lp, &argc, argv);
 
