Thread.hx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. * Copyright (C)2005-2019 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. */
  22. package sys.thread;
  23. #if (!target.threaded)
  24. #error "This class is not available on this target"
  25. #end
  26. class Thread {
  27. static var threads : Array<Thread>;
  28. static var mutex : Mutex;
  29. static var mainThread : Thread;
  30. var impl : ThreadImpl;
  31. var messages : Deque<Dynamic>;
  32. /**
  33. The events loop of this thread.
  34. If this is a native thread, the events will be null.
  35. **/
  36. public var events(default,null) : Null<haxe.EventLoop>;
  37. /**
  38. Tells if we needs to wait for the thread to terminate before we stop the main loop (default:true).
  39. **/
  40. public var isBlocking : Bool = true;
  41. /**
  42. Allows to query or change the name of the thread. On some platforms this might allow debugger to identify threads.
  43. **/
  44. public var name(default,set) : Null<String>;
  45. /**
  46. Tells if a thread is a native thread that is not managed by Haxe.
  47. See `Thread.current` for details.
  48. **/
  49. public var isNative(default,null) : Bool;
  50. function new(impl) {
  51. this.impl = impl;
  52. if( impl != null ) this.name = ThreadImpl.getName(impl);
  53. }
  54. function set_name(n) {
  55. name = n;
  56. if( impl != null ) ThreadImpl.setName(impl,name == null ? "" : name);
  57. return n;
  58. }
  59. public function toString() {
  60. return "Thread#"+(name ?? Std.string(impl));
  61. }
  62. public function sendMessage( msg : Dynamic ) {
  63. if( messages == null ) {
  64. mutex.acquire();
  65. if( messages == null ) messages = new Deque();
  66. mutex.release();
  67. }
  68. messages.add(msg);
  69. }
  70. public function disposeNative() {
  71. if( !isNative ) return;
  72. dispose();
  73. }
  74. function dispose() {
  75. mutex.acquire();
  76. threads.remove(this);
  77. mutex.release();
  78. currentTLS.value = null;
  79. events.dispose();
  80. }
  81. public static function readMessage( blocking : Bool ) : Null<Dynamic> {
  82. var t = current();
  83. if( t.messages == null ) {
  84. mutex.acquire();
  85. if( t.messages == null ) t.messages = new Deque();
  86. mutex.release();
  87. }
  88. return t.messages.pop(blocking);
  89. }
  90. static var currentTLS : Tls<Thread>;
  91. /**
  92. Returns the current thread.
  93. If you are calling this function from a native thread that is not the main thread and was not created by `Thread.create`, this will return you
  94. a native thread with a `null` EvenLoop and `isNative` set to true. You need to call `disposeNative()` on such value on thread termination.
  95. **/
  96. public static function current():Thread {
  97. var t = currentTLS.value;
  98. if( t != null )
  99. return t;
  100. var impl = ThreadImpl.current();
  101. var t = new Thread(impl);
  102. t.isNative = true;
  103. mutex.acquire();
  104. threads.push(t);
  105. mutex.release();
  106. currentTLS.value = t;
  107. return t;
  108. }
  109. /**
  110. Returns the main thread
  111. **/
  112. public static inline function main() {
  113. return mainThread;
  114. }
  115. /**
  116. Creates a new thread that will execute the `job` function, then exit after all events are processed.
  117. You can specify a custom exception handler `onAbort` or else `Thread.onAbort` will be called.
  118. **/
  119. public static function create(?name:String,job:()->Void,?onAbort):Thread {
  120. mutex.acquire();
  121. var t = new Thread(null);
  122. t.events = new haxe.EventLoop();
  123. t.events.thread = t;
  124. threads.push(t);
  125. mutex.release();
  126. if( onAbort != null )
  127. t.onAbort = onAbort;
  128. t.impl = ThreadImpl.create(function() {
  129. t.impl = ThreadImpl.current();
  130. if( name != null ) t.name = name;
  131. currentTLS.value = t;
  132. var exception = null;
  133. try {
  134. #if hl
  135. hl.Api.setErrorHandler(null);
  136. #end
  137. job();
  138. t.events.loop();
  139. } catch( e ) {
  140. exception = e;
  141. }
  142. t.dispose();
  143. if( exception != null )
  144. t.onAbort(exception);
  145. @:privateAccess main().events.wakeup();
  146. });
  147. if( name != null ) t.name = name;
  148. return t;
  149. }
  150. /**
  151. Returns a list of all currently running threads.
  152. This excludes native threads which were created without Thread.create and have not been
  153. registered with a call to Thread.current().
  154. **/
  155. public static function getAll() {
  156. mutex.acquire();
  157. var tl = threads.copy();
  158. mutex.release();
  159. return tl;
  160. }
  161. /**
  162. This function is called when an uncaught exception aborted a thread.
  163. The error will be printed to stdout but this function can be redefined.
  164. **/
  165. public dynamic function onAbort(e:haxe.Exception) {
  166. var name = this.name;
  167. if( name == null ) name = "" else name = " "+name;
  168. Sys.println("THREAD"+name+" ABORTED : "+e.message+haxe.CallStack.toString(e.stack));
  169. }
  170. static function hasBlocking() {
  171. // let's check if we have blocking threads running other that our calling thread
  172. var me = current();
  173. mutex.acquire();
  174. for( t in threads )
  175. if( t.impl != me.impl && t.isBlocking ) {
  176. mutex.release();
  177. return true;
  178. }
  179. mutex.release();
  180. return false;
  181. }
  182. static function __init__() {
  183. mutex = new Mutex();
  184. mainThread = new Thread(ThreadImpl.current());
  185. mainThread.name = "Main";
  186. mainThread.events = haxe.EventLoop.main;
  187. mainThread.events.thread = mainThread;
  188. threads = [mainThread];
  189. currentTLS = new Tls();
  190. currentTLS.value = mainThread;
  191. }
  192. }