ec4220838f2939a4da12e6a91ef51f7cd7964d67
[mono.git] / mono / utils / mono-threads-mach-helper.c
1 /*
2  * mono-threads-mach-helper.c: ObjectiveC hacks to improve our changes with thread shutdown
3  *
4  * Author:
5  *      Rodrigo Kumpera (kumpera@gmail.com)
6  *
7  * (C) 2014 Xamarin Inc
8  */
9
10 #include "config.h"
11
12 #if defined(__MACH__)
13
14 #include <objc/runtime.h>
15 #include <objc/message.h>
16 #include <mono/utils/mono-compiler.h>
17
18 /*
19  * We cannot include mono-threads.h as this includes io-layer internal types
20  * which conflicts with objc.
21  * Hence the hack here.
22 */
23 void mono_threads_init_dead_letter (void) MONO_INTERNAL;
24 void mono_threads_install_dead_letter (void) MONO_INTERNAL;
25 void mono_thread_info_detach (void) MONO_INTERNAL;
26
27 static Class nsobject, nsthread, mono_dead_letter_class;
28 static SEL dealloc, release, currentThread, threadDictionary, init, alloc, objectForKey, setObjectForKey;
29 static id mono_dead_letter_key;
30
31 /*
32  * Our Mach bindings have a problem in that they might need to attach
33  * the runtime after the the user tls keys have been destroyed.
34  *
35  * This happens when a bound object is retained by NSThread, which is
36  * released very late in the TLS cleanup process.
37  *
38  * At that point, attaching the runtime leaves us in no position to
39  * detach it later using TLS destructors as pthread is done with user
40  * keys. This leaves us with a dead thread registered, which can cause
41  * all sorts of terrible problems.
42  *
43  * The tipical crash is when another thread is created at the exact
44  * same address of the previous one, cause thread registration to abort
45  * due to duplicate entries.
46  *
47  * So what do we do here?
48  * 
49  * Experimentation showns that threadDictionary is destroied after the
50  * problematic keys, so we add our dead letter object as an aditional
51  * way to be notified of thread death.
52  */
53 static void
54 mono_dead_letter_dealloc (id self, SEL _cmd)
55 {
56         struct objc_super super;
57         super.receiver = self;
58         super.class = nsobject;
59         objc_msgSendSuper (&super, dealloc);
60
61         mono_thread_info_detach ();
62 }
63
64 void
65 mono_threads_install_dead_letter (void)
66 {
67         id cur, dict;
68
69         cur = objc_msgSend ((id)nsthread, currentThread);
70         if (!cur)
71                 return;
72         dict = objc_msgSend (cur, threadDictionary);
73         if (dict && objc_msgSend (dict, objectForKey, mono_dead_letter_key) == nil) {
74                 id value = objc_msgSend (objc_msgSend ((id)mono_dead_letter_class, alloc), init);
75                 objc_msgSend (dict, setObjectForKey, value, mono_dead_letter_key);
76                 objc_msgSend (value, release);
77         }
78 }
79
80 void
81 mono_threads_init_dead_letter (void)
82 {
83         id nsstring = objc_getClass ("NSString");
84         id nsautoreleasepool = objc_getClass ("NSAutoreleasePool");
85         SEL stringWithUTF8String = sel_registerName ("stringWithUTF8String:");
86         SEL retain = sel_registerName ("retain");
87
88         nsthread = (Class)objc_getClass ("NSThread");
89         nsobject = (Class)objc_getClass ("NSObject");
90
91         init = sel_registerName ("init");
92         alloc = sel_registerName ("alloc");
93         release = sel_registerName ("release");
94         dealloc = sel_registerName ("dealloc");
95         
96
97         currentThread = sel_registerName ("currentThread");
98         threadDictionary = sel_registerName ("threadDictionary");
99         setObjectForKey = sel_registerName ("setObject:forKey:");
100         objectForKey = sel_registerName ("objectForKey:");
101
102         // define the dead letter class
103         mono_dead_letter_class = objc_allocateClassPair (nsobject, "MonoDeadLetter", 0);
104         class_addMethod (mono_dead_letter_class, dealloc, (IMP)mono_dead_letter_dealloc, "v@:");
105         objc_registerClassPair (mono_dead_letter_class);
106
107         // create the dict key
108         id pool = objc_msgSend (objc_msgSend (nsautoreleasepool, alloc), init);
109         mono_dead_letter_key = objc_msgSend (nsstring, stringWithUTF8String, "mono-dead-letter");
110         objc_msgSend (mono_dead_letter_key, retain);
111         objc_msgSend (pool, release);
112 }
113 #endif