Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / docs / threading
1
2 Threading in Mono
3 =================
4
5         0. Terminology
6         --------------
7
8                 "Main thread" - The initial OS-native thread that the
9                 application started with.
10
11                 "Helper thread" - A native thread created internally
12                 by the runtime, such as the finalizer thread, or an
13                 asynchronous delegate invocation thread.  These
14                 threads can run managed code.
15  
16                 "Primary CLR thread" - The native thread that called
17                 the Main() method when executing an assembly.
18
19                 "Secondary CLR thread" - A native thread created by a
20                 program that instantiates a System.Threading.Thread object
21                 and calls its Start() method.
22
23         1. Thread exit behaviour in the standalone mono runtime
24         -------------------------------------------------------
25
26                 The correct behaviour of the runtime should be:
27
28                 a) If Main() returns, the runtime should wait for all
29                 foreground secondary CLR threads to finish.  The
30                 wording in the class documentation states: "Once all
31                 foreground threads belonging to a process have
32                 terminated, the common language runtime ends the
33                 process by invoking Abort on any background threads
34                 that are still alive."  Testing seems to indicate that
35                 the background thread can't cancel the Abort by
36                 catching the ThreadAbortException and calling
37                 ResetAbort here. Indeed, not even the finally block
38                 seems to be executed.
39
40                 b) if any of the primary CLR thread, a secondary CLR
41                 thread or a helper thread calls
42                 System.Environment.Exit(), the application should
43                 terminate immediately without waiting for foreground
44                 primary or secondary CLR threads to finish.
45
46                 c) if the primary CLR thread throws an uncaught
47                 exception, the application should terminate
48                 immediately without waiting for secondary CLR threads
49                 to finish.  This might be implemented internally by
50                 pretending that all the running secondary CLR threads
51                 are background threads.
52
53                 d) if a secondary CLR thread throws an uncaught
54                 exception that thread should terminate and all other
55                 threads should continue to execute.
56
57                 e) if a helper thread throws an uncaught exception and
58                 that thread happens to be the GC finalizer thread,
59                 testing seems to indicate that the exception stack
60                 trace is displayed as normal, and the exception is
61                 then ignored (as though there is a try {} catch{}
62                 around all finalizers that just prints the stack
63                 trace.)  Calling Abort() on the GC finalizer thread
64                 also does not cause it to exit: it behaves as though
65                 the ThreadAbortException is caught and ResetAbort is
66                 called.  Asynchronous delegate helper threads should
67                 behave as secondary CLR threads, but uncaught
68                 exceptions should be rethrown on the thread that calls
69                 EndInvoke().
70
71
72                 The difficulties happen with cases b) and c):
73
74                 The current implementation of
75                 System.Environment.Exit() calls exit(2) directly,
76                 which is rather unfriendly: it prevents any runtime
77                 cleanup, statistics gathering, etc. and is pretty
78                 obnoxious to embedded code.
79
80                 The current exception handling code calls ExitThread()
81                 (emulated with pthread_exit() in the io-layer) if an
82                 exception is not caught.
83
84                 When called from the main thread, both POSIX
85                 pthread_exit() and w32 ExitThread() block if there are
86                 other threads still running (in the w32 case, if there
87                 are other foreground threads still running; threads
88                 can set as background.)  If the main thread is also
89                 the primary CLR thread, then the application will
90                 block until all other threads (including helper
91                 threads) terminate.  Some helper threads will not
92                 terminate until specifically told to by the runtime:
93                 for example, the GC finalizer thread needs to run
94                 until all of the primary and secondary CLR threads
95                 have finished.
96
97                 Also, if the main thread is also the primary CLR
98                 thread, the runtime loses the opportunity to do any
99                 cleaning up.  Adding a special case to call exit(2)
100                 instead of ExitThread() in the primary CLR thread
101                 suffers from the same problems as
102                 System.Environment.Exit() calling exit(2).
103
104
105                 The simple solution is to run the primary CLR thread
106                 in a new native thread, leaving the main thread free
107                 for housekeeping duties.  There still needs to be some
108                 special handling for the case where the primary CLR
109                 thread fails to catch an exception: the secondary CLR
110                 threads then need to be terminated.
111
112                 When the primary and secondary CLR threads have all
113                 terminated, the helper threads can be killed off and
114                 the runtime can clean itself up and exit.
115
116
117
118         2. Thread initialisation
119         ------------------------
120
121                 Threads have to undergo some initialisation before
122                 managed code can be executed.  A
123                 System.Threading.Thread object must be created, and
124                 the thread details need to be stored so that the
125                 threads can be managed later.  The JIT needs to record
126                 the last managed frame stack pointer in a TLS slot,
127                 and the current Thread object is also recorded.
128
129                 New threads created by managed calls to
130                 System.Threading.Thread methods will have all needed
131                 initialisation performed.  Threads created by the
132                 runtime with calls to mono_thread_create() will too.
133                 Existing threads can be passed to the runtime; these
134                 must call mono_thread_attach() before any CLR code can
135                 be executed on that thread.
136
137
138         3. Constraints on embedding the Mono runtime
139         --------------------------------------------
140
141                 The discussion above concerning application behaviour
142                 in the event of threads terminating, whether by
143                 returning from the start function, throwing uncaught
144                 exceptions or by calling System.Environment.Exit(),
145                 only really applies to the standalone Mono runtime.
146
147                 An embedding application should specify what behaviour
148                 is required when, for example,
149                 System.Environment.Exit() is called.  The application
150                 is also responsible for its own thread management, and
151                 it should be prepared for any of the primary CLR
152                 thread or secondary CLR threads to terminate at any
153                 time.  The application should also take into account
154                 that the runtime will create helper threads as needed,
155                 as this may cause pthread_exit() or ExitThread() to
156                 block indefinitely, as noted above.