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