threading 5.9 KB

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