Merge pull request #259 from johnno1962a/master

Thread detach hook for Java JNI on Android
diff --git a/private/queue_private.h b/private/queue_private.h
index 98c7f5e..b9356e2 100644
--- a/private/queue_private.h
+++ b/private/queue_private.h
@@ -323,6 +323,20 @@
 dispatch_async_enforce_qos_class_f(dispatch_queue_t queue,
 	void *_Nullable context, dispatch_function_t work);
 
+#ifdef __ANDROID__
+/*!
+ * @function _dispatch_install_thread_detach_callback
+ *
+ * @param callback
+ * Function to be called before each worker thread exits to detach JVM.
+ *
+ * Hook to be able to detach threads from the Java JVM before they exit.
+ * If JNI has been used on a thread on Android it needs to have been
+ * "detached" before the thread exits or the application will crash.
+ */
+DISPATCH_EXPORT
+void _dispatch_install_thread_detach_callback(dispatch_function_t cb);
+#endif
 
 __END_DECLS
 
diff --git a/src/queue.c b/src/queue.c
index 4d506ef..2406e7e 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -885,6 +885,18 @@
 		if ((f) && tsd->k) ((void(*)(void*))(f))(tsd->k); \
 	} while (0)
 
+#ifdef __ANDROID__
+static void (*_dispatch_thread_detach_callback)(void);
+
+void
+_dispatch_install_thread_detach_callback(dispatch_function_t cb)
+{
+    if (os_atomic_xchg(&_dispatch_thread_detach_callback, cb, relaxed)) {
+        DISPATCH_CLIENT_CRASH(0, "Installing a thread detach callback twice");
+    }
+}
+#endif
+
 void
 _libdispatch_tsd_cleanup(void *ctx)
 {
@@ -909,6 +921,11 @@
 	_tsd_call_cleanup(dispatch_voucher_key, _voucher_thread_cleanup);
 	_tsd_call_cleanup(dispatch_deferred_items_key,
 			_dispatch_deferred_items_cleanup);
+#ifdef __ANDROID__
+	if (_dispatch_thread_detach_callback) {
+		_dispatch_thread_detach_callback();
+	}
+#endif
 	tsd->tid = 0;
 }
 
diff --git a/src/swift/Queue.swift b/src/swift/Queue.swift
index 1808f9b..b946a80 100644
--- a/src/swift/Queue.swift
+++ b/src/swift/Queue.swift
@@ -330,6 +330,15 @@
 		let p = v.flatMap { Unmanaged.passRetained($0).toOpaque() }
 		dispatch_queue_set_specific(self.__wrapped, k, p, _destructDispatchSpecificValue)
 	}
+
+	#if os(Android)
+	@_silgen_name("_dispatch_install_thread_detach_callback")
+	private static func _dispatch_install_thread_detach_callback(_ cb: @escaping @convention(c) () -> Void)
+
+	public static func setThreadDetachCallback(_ cb: @escaping @convention(c) () -> Void) {
+		_dispatch_install_thread_detach_callback(cb)
+	}
+	#endif
 }
 
 private func _destructDispatchSpecificValue(ptr: UnsafeMutableRawPointer?) {