--- old/./README.md	2013-10-28 19:06:19.810107639 +0000
+++ new/./README.md	2013-10-28 19:06:19.830042721 +0000
@@ -685,12 +685,15 @@
 call but have stashed a reference to the object somewhere and are not
 calling `v8plus_defer()`, you must call this first.  Holds and releases must
 be balanced.  Use of the object within a thread after releasing is a bug.
+This hold includes an implicit event loop hold, as if `v8plus_eventloop_hold()`
+was called.
 
 ### void v8plus_obj_rele(const void *op)
 
 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.
+are non-blocking and will occur some time in the future.  Releases the
+implicit event loop hold obtained by `v8plus_obj_hold()`.
 
 ### void v8plus_jsfunc_hold(v8plus_jsfunc_t f)
 
@@ -700,13 +703,15 @@
 All holds must be balanced with a release.  Because a single hold is placed
 on such objects when passed to you in an argument list (and released for you
 when you return), it is legal to reference and even to invoke such a
-function without first placing an additional hold on it.
+function without first placing an additional hold on it.  This hold includes
+an implicit event loop hold, as if `v8plus_eventloop_hold()` was called.
 
 ### void v8plus_jsfunc_rele(v8plus_jsfunc_t f)
 
 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.
+thread are non-blocking and will occur some time in the future.  Releases
+the implicit event loop hold obtained by `v8plus_jsfunc_hold()`.
 
 ### void v8plus_defer(void *op, void *ctx, worker, completion)
 
@@ -716,6 +721,26 @@
 return value, are passed to `completion` executing in the main event loop
 thread.  See example above.
 
+### void v8plus_eventloop_hold(void)
+
+Places a hold on the V8 event loop.  V8 will terminate when it detects that
+there is no more work to do.  This liveliness check includes things like open
+sockets or file descriptors, but only if they are tracked by the event loop
+itself.  If you are using multiple threads, some of which may blocking waiting
+for input (e.g. a message subscription thread) then you will need to prevent V8
+from terminating prematurely.  This function must be called from within the
+main event loop thread.  Each hold must be balanced with a release.  Note that
+holds on objects or functions obtained via `v8plus_obj_hold()` or
+`v8plus_jsfunc_hold()` will implicitly hold the event loop for you.
+
+### void v8plus_eventloop_rele(void)
+
+Release a hold on the V8 event loop.  If there are no more pending events or
+input sources, then V8 will generally terminate the process shortly afterward.
+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.
+
 ### nvlist_t *v8plus_call(v8plus_jsfunc_t f, const nvlist_t *ap)
 
 Calls the JavaScript function referred to by `f` with encoded arguments
--- old/./v8plus_csup.c	2013-10-28 19:06:20.218436752 +0000
+++ new/./v8plus_csup.c	2013-10-28 19:06:20.230622767 +0000
@@ -31,11 +31,14 @@
 static pthread_t _v8plus_uv_event_thread;
 static uv_async_t _v8plus_uv_async;
 
+static int _v8plus_eventloop_refcount;
+
 typedef enum v8plus_async_call_type {
 	ACT_OBJECT_CALL = 1,
 	ACT_OBJECT_RELEASE,
 	ACT_JSFUNC_CALL,
 	ACT_JSFUNC_RELEASE,
+	ACT_EVENTLOOP_RELEASE
 } v8plus_async_call_type_t;
 
 typedef enum v8plus_async_call_flags {
@@ -121,6 +124,9 @@
 		case ACT_JSFUNC_RELEASE:
 			v8plus_jsfunc_rele_direct(vac->vac_func);
 			break;
+		case ACT_EVENTLOOP_RELEASE:
+			v8plus_eventloop_rele_direct();
+			break;
 		}
 
 		if (vac->vac_flags & ACF_NOREPLY) {
@@ -304,6 +310,49 @@
 		v8plus_panic("unable to initialise uv_async_t");
 	if (pthread_mutex_init(&_v8plus_callq_mtx, NULL) != 0)
 		v8plus_panic("unable to initialise mutex");
+
+	/*
+	 * If we do not unreference the async handle, then its mere
+	 * existence will keep the event loop open forever.  If the consumer
+	 * _wants_ this behaviour, they may call v8plus_eventloop_hold()
+	 * from the event loop thread.
+	 */
+	uv_unref((uv_handle_t *)&_v8plus_uv_async);
+}
+
+void
+v8plus_eventloop_hold(void)
+{
+	++_v8plus_eventloop_refcount;
+	uv_ref((uv_handle_t *)&_v8plus_uv_async);
+}
+
+void
+v8plus_eventloop_rele_direct(void)
+{
+	if (--_v8plus_eventloop_refcount < 1) {
+		_v8plus_eventloop_refcount = 0;
+		uv_unref((uv_handle_t *)&_v8plus_uv_async);
+	}
+}
+
+void
+v8plus_eventloop_rele(void)
+{
+	v8plus_async_call_t *vac;
+
+	if (v8plus_in_event_thread() == B_TRUE) {
+		return (v8plus_eventloop_rele_direct());
+	}
+
+	vac = calloc(1, sizeof (*vac));
+	if (vac == NULL)
+		v8plus_panic("could not allocate async call structure");
+
+	vac->vac_type = ACT_EVENTLOOP_RELEASE;
+	vac->vac_flags = ACF_NOREPLY;
+
+	(void) v8plus_cross_thread_call(vac);
 }
 
 nvlist_t *
--- old/./v8plus_glue.h	2013-10-28 19:06:20.547178031 +0000
+++ new/./v8plus_glue.h	2013-10-28 19:06:20.559908756 +0000
@@ -184,6 +184,22 @@
     const nvlist_t *);
 
 /*
+ * These functions allow the consumer to hold the V8 event loop open for
+ * potential input from other threads.  If your process blocks in another
+ * thread, e.g. an event subscription thread, you must signal to v8plus
+ * that the event loop should remain active.  Calls to v8plus_eventloop_hold()
+ * and v8plus_eventloop_rele() should be balanced.  It is safe to call
+ * v8plus_eventloop_rele() from outside the event loop thread.
+ *
+ * Note: Holds obtained via v8plus_obj_hold() and v8plus_jsfunc_hold() will
+ * also automatically hold the event loop, removing the need to use this
+ * interface explicitly.
+ */
+extern void v8plus_eventloop_hold(void);
+extern void v8plus_eventloop_rele(void);
+extern void v8plus_eventloop_rele_direct(void);
+
+/*
  * These methods are analogous to strerror(3c) and similar functions; they
  * translate among error names, codes, and default messages.  There is
  * normally little need for these functions in C methods, as everything
--- old/./v8plus_subr.cc	2013-10-28 19:06:20.852735786 +0000
+++ new/./v8plus_subr.cc	2013-10-28 19:06:20.865944845 +0000
@@ -523,6 +523,12 @@
 		it->second.ch_persist = _B_TRUE;
 	}
 	++it->second.ch_refs;
+
+	/*
+	 * If the consumer puts a hold on a callback, we should also put a hold
+	 * on the V8 event loop to prevent it dematerialising beneath us.
+	 */
+	v8plus_eventloop_hold();
 }
 
 extern "C" void
@@ -544,6 +550,11 @@
 		}
 		cbhash.erase(it);
 	}
+
+	/*
+	 * Release the event loop hold we took in v8plus_jsfunc_hold():
+	 */
+	v8plus_eventloop_rele_direct();
 }
 
 static size_t
@@ -637,6 +648,12 @@
 {
 	v8plus::ObjectWrap *op = v8plus::ObjectWrap::objlookup(cop);
 	op->public_Ref();
+
+	/*
+	 * If the consumer puts a hold on an object, we should also put a hold
+	 * on the V8 event loop to prevent it dematerialising beneath us.
+	 */
+	v8plus_eventloop_hold();
 }
 
 extern "C" void
@@ -644,4 +661,9 @@
 {
 	v8plus::ObjectWrap *op = v8plus::ObjectWrap::objlookup(cop);
 	op->public_Unref();
+
+	/*
+	 * Release the event loop hold we took in v8plus_obj_hold():
+	 */
+	v8plus_eventloop_rele_direct();
 }
