/
mono-threads-mach-helper.c
130 lines (108 loc) · 4.14 KB
/
mono-threads-mach-helper.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
* mono-threads-mach-helper.c: 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 <stdio.h>
#include <objc/runtime.h>
#include <objc/message.h>
#include <mono/utils/mono-compiler.h>
/*
* 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) MONO_INTERNAL;
void mono_threads_install_dead_letter (void) MONO_INTERNAL;
void mono_thread_info_detach (void) MONO_INTERNAL;
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