diff --git a/private/queue_private.h b/private/queue_private.h index 14d64772d..796ce50d4 100644 --- a/private/queue_private.h +++ b/private/queue_private.h @@ -322,6 +322,20 @@ void 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 42b51f3eb..005d5330f 100644 --- a/src/queue.c +++ b/src/queue.c @@ -861,6 +861,18 @@ gettid(void) 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) { @@ -885,6 +897,11 @@ _libdispatch_tsd_cleanup(void *ctx) _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 b7628c9cf..29e2bf777 100644 --- a/src/swift/Queue.swift +++ b/src/swift/Queue.swift @@ -330,6 +330,15 @@ public extension DispatchQueue { let p = Unmanaged.passRetained(v).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?) {