/** * \file * ObjectiveC hacks to improve our changes with thread shutdown * * Author: * Rodrigo Kumpera (kumpera@gmail.com) * * (C) 2014 Xamarin Inc */ #include "config.h" #if defined(__MACH__) #include #include #include #include /* * We cannot include mono-threads.h as this includes io-layer internal types * which conflicts with objc. * Hence the hack here. */ void mono_threads_init_dead_letter (void); void mono_threads_install_dead_letter (void); void mono_thread_info_detach (void); static Class nsobject, nsthread, mono_dead_letter_class; static SEL dealloc, release, currentThread, threadDictionary, init, alloc, objectForKey, setObjectForKey; static id mono_dead_letter_key; /* * Our Mach bindings have a problem in that they might need to attach * the runtime after the the user tls keys have been destroyed. * * This happens when a bound object is retained by NSThread, which is * released very late in the TLS cleanup process. * * At that point, attaching the runtime leaves us in no position to * detach it later using TLS destructors as pthread is done with user * keys. This leaves us with a dead thread registered, which can cause * all sorts of terrible problems. * * The tipical crash is when another thread is created at the exact * same address of the previous one, cause thread registration to abort * due to duplicate entries. * * So what do we do here? * * Experimentation showns that threadDictionary is destroied after the * problematic keys, so we add our dead letter object as an aditional * way to be notified of thread death. */ static void mono_dead_letter_dealloc (id self, SEL _cmd) { struct objc_super super; super.receiver = self; super.class = nsobject; objc_msgSendSuper (&super, dealloc); mono_thread_info_detach (); } void mono_threads_install_dead_letter (void) { id cur, dict; /* * See the 'Dispatch Objective-C Messages Using the Method Function’s Prototype' section in * the '64-Bit Transition Guide for Cocoa Touch' as to why this is required. * * It doesn't hurt on other architectures either, so no need to #ifdef it only for ARM64. */ id (*id_objc_msgSend_id)(id, SEL, id) = (id (*)(id, SEL, id)) objc_msgSend; void (*objc_msgSend_id_id)(id, SEL, id, id) = (void (*)(id, SEL, id, id)) objc_msgSend; cur = objc_msgSend ((id)nsthread, currentThread); if (!cur) return; dict = objc_msgSend (cur, threadDictionary); if (dict && id_objc_msgSend_id (dict, objectForKey, mono_dead_letter_key) == nil) { id value = objc_msgSend (objc_msgSend ((id)mono_dead_letter_class, alloc), init); objc_msgSend_id_id (dict, setObjectForKey, value, mono_dead_letter_key); objc_msgSend (value, release); } } void mono_threads_init_dead_letter (void) { id nsstring = (id) objc_getClass ("NSString"); id nsautoreleasepool = (id) objc_getClass ("NSAutoreleasePool"); SEL stringWithUTF8String = sel_registerName ("stringWithUTF8String:"); SEL retain = sel_registerName ("retain"); id pool; nsthread = (Class)objc_getClass ("NSThread"); nsobject = (Class)objc_getClass ("NSObject"); init = sel_registerName ("init"); alloc = sel_registerName ("alloc"); release = sel_registerName ("release"); dealloc = sel_registerName ("dealloc"); currentThread = sel_registerName ("currentThread"); threadDictionary = sel_registerName ("threadDictionary"); setObjectForKey = sel_registerName ("setObject:forKey:"); objectForKey = sel_registerName ("objectForKey:"); // define the dead letter class mono_dead_letter_class = objc_allocateClassPair (nsobject, "MonoDeadLetter", 0); class_addMethod (mono_dead_letter_class, dealloc, (IMP)mono_dead_letter_dealloc, "v@:"); objc_registerClassPair (mono_dead_letter_class); // create the dict key pool = objc_msgSend (objc_msgSend (nsautoreleasepool, alloc), init); id (*objc_msgSend_char)(id, SEL, const char*) = (id (*)(id, SEL, const char*)) objc_msgSend; mono_dead_letter_key = objc_msgSend_char (nsstring, stringWithUTF8String, "mono-dead-letter"); objc_msgSend (mono_dead_letter_key, retain); objc_msgSend (pool, release); } #endif