diff options
Diffstat (limited to 'docs')
-rw-r--r-- | docs/threading | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/docs/threading b/docs/threading new file mode 100644 index 00000000000..30f439fa4e0 --- /dev/null +++ b/docs/threading @@ -0,0 +1,156 @@ + +Threading in Mono +================= + + 0. Terminology + -------------- + + "Main thread" - The initial OS-native thread that the + application started with. + + "Helper thread" - A native thread created internally + by the runtime, such as the finalizer thread, or an + asynchronous delegate invocation thread. These + threads can run managed code. + + "Primary CLR thread" - The native thread that called + the Main() method when executing an assembly. + + "Secondary CLR thread" - A native thread created by a + program that instantiates a System.Threading.Thread object + and calls its Start() method. + + 1. Thread exit behaviour in the standalone mono runtime + ------------------------------------------------------- + + The correct behaviour of the runtime should be: + + a) If Main() returns, the runtime should wait for all + foreground secondary CLR threads to finish. The + wording in the class documentation states: "Once all + foreground threads belonging to a process have + terminated, the common language runtime ends the + process by invoking Abort on any background threads + that are still alive." Testing seems to indicate that + the background thread can't cancel the Abort by + catching the ThreadAbortException and calling + ResetAbort here. Indeed, not even the finally block + seems to be executed. + + b) if any of the primary CLR thread, a secondary CLR + thread or a helper thread calls + System.Environment.Exit(), the application should + terminate immediately without waiting for foreground + primary or secondary CLR threads to finish. + + c) if the primary CLR thread throws an uncaught + exception, the application should terminate + immediately without waiting for secondary CLR threads + to finish. This might be implemented internally by + pretending that all the running secondary CLR threads + are background threads. + + d) if a secondary CLR thread throws an uncaught + exception that thread should terminate and all other + threads should continue to execute. + + e) if a helper thread throws an uncaught exception and + that thread happens to be the GC finalizer thread, + testing seems to indicate that the exception stack + trace is displayed as normal, and the exception is + then ignored (as though there is a try {} catch{} + around all finalizers that just prints the stack + trace.) Calling Abort() on the GC finalizer thread + also does not cause it to exit: it behaves as though + the ThreadAbortException is caught and ResetAbort is + called. Asynchronous delegate helper threads should + behave as secondary CLR threads, but uncaught + exceptions should be rethrown on the thread that calls + EndInvoke(). + + + The difficulties happen with cases b) and c): + + The current implementation of + System.Environment.Exit() calls exit(2) directly, + which is rather unfriendly: it prevents any runtime + cleanup, statistics gathering, etc. and is pretty + obnoxious to embedded code. + + The current exception handling code calls ExitThread() + (emulated with pthread_exit() in the io-layer) if an + exception is not caught. + + When called from the main thread, both POSIX + pthread_exit() and w32 ExitThread() block if there are + other threads still running (in the w32 case, if there + are other foreground threads still running; threads + can set as background.) If the main thread is also + the primary CLR thread, then the application will + block until all other threads (including helper + threads) terminate. Some helper threads will not + terminate until specifically told to by the runtime: + for example, the GC finalizer thread needs to run + until all of the primary and secondary CLR threads + have finished. + + Also, if the main thread is also the primary CLR + thread, the runtime loses the opportunity to do any + cleaning up. Adding a special case to call exit(2) + instead of ExitThread() in the primary CLR thread + suffers from the same problems as + System.Environment.Exit() calling exit(2). + + + The simple solution is to run the primary CLR thread + in a new native thread, leaving the main thread free + for housekeeping duties. There still needs to be some + special handling for the case where the primary CLR + thread fails to catch an exception: the secondary CLR + threads then need to be terminated. + + When the primary and secondary CLR threads have all + terminated, the helper threads can be killed off and + the runtime can clean itself up and exit. + + + + 2. Thread initialisation + ------------------------ + + Threads have to undergo some initialisation before + managed code can be executed. A + System.Threading.Thread object must be created, and + the thread details need to be stored so that the + threads can be managed later. The JIT needs to record + the last managed frame stack pointer in a TLS slot, + and the current Thread object is also recorded. + + New threads created by managed calls to + System.Threading.Thread methods will have all needed + initialisation performed. Threads created by the + runtime with calls to mono_thread_create() will too. + Existing threads can be passed to the runtime; these + must call mono_thread_attach() before any CLR code can + be executed on that thread. + + + 3. Constraints on embedding the Mono runtime + -------------------------------------------- + + The discussion above concerning application behaviour + in the event of threads terminating, whether by + returning from the start function, throwing uncaught + exceptions or by calling System.Environment.Exit(), + only really applies to the standalone Mono runtime. + + An embedding application should specify what behaviour + is required when, for example, + System.Environment.Exit() is called. The application + is also responsible for its own thread management, and + it should be prepared for any of the primary CLR + thread or secondary CLR threads to terminate at any + time. The application should also take into account + that the runtime will create helper threads as needed, + as this may cause pthread_exit() or ExitThread() to + block indefinitely, as noted above. |