Thread.hx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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. }
  80. public static function readMessage( blocking : Bool ) : Null<Dynamic> {
  81. var t = current();
  82. if( t.messages == null ) {
  83. mutex.acquire();
  84. if( t.messages == null ) t.messages = new Deque();
  85. mutex.release();
  86. }
  87. return t.messages.pop(blocking);
  88. }
  89. static var currentTLS : Tls<Thread>;
  90. /**
  91. Returns the current thread.
  92. 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
  93. a native thread with a `null` EvenLoop and `isNative` set to true. You need to call `disposeNative()` on such value on thread termination.
  94. **/
  95. public static function current():Thread {
  96. var t = currentTLS.value;
  97. if( t != null )
  98. return t;
  99. var impl = ThreadImpl.current();
  100. var t = new Thread(impl);
  101. t.isNative = true;
  102. mutex.acquire();
  103. threads.push(t);
  104. mutex.release();
  105. currentTLS.value = t;
  106. return t;
  107. }
  108. /**
  109. Returns the main thread
  110. **/
  111. public static inline function main() {
  112. return mainThread;
  113. }
  114. /**
  115. Creates a new thread that will execute the `job` function, then exit after all events are processed.
  116. You can specify a custom exception handler `onAbort` or else `Thread.onAbort` will be called.
  117. **/
  118. public static function create(job:()->Void,?onAbort):Thread {
  119. mutex.acquire();
  120. var t = new Thread(null);
  121. t.events = new haxe.EventLoop();
  122. threads.push(t);
  123. mutex.release();
  124. if( onAbort != null )
  125. t.onAbort = onAbort;
  126. t.impl = ThreadImpl.create(function() {
  127. t.impl = ThreadImpl.current();
  128. currentTLS.value = t;
  129. var exception = null;
  130. try {
  131. job();
  132. t.events.loop();
  133. } catch( e ) {
  134. exception = e;
  135. }
  136. t.dispose();
  137. @:privateAccess main().events.wakeup();
  138. if( exception != null )
  139. t.onAbort(exception);
  140. });
  141. return t;
  142. }
  143. /**
  144. Returns a list of all currently running threads.
  145. This excludes native threads which were created without Thread.create and have not been
  146. registered with a call to Thread.current().
  147. **/
  148. public static function getAll() {
  149. mutex.acquire();
  150. var tl = threads.copy();
  151. mutex.release();
  152. return tl;
  153. }
  154. /**
  155. This function is called when an uncaught exception aborted a thread.
  156. The error will be printed to stdout but this function can be redefined.
  157. **/
  158. public dynamic function onAbort(e:haxe.Exception) {
  159. var name = this.name;
  160. if( name == null ) name = "" else name = " "+name;
  161. Sys.println("THREAD"+name+" ABORTED : "+e.message+haxe.CallStack.toString(e.stack));
  162. }
  163. static function hasBlocking() {
  164. // let's check if we have blocking threads running other that our calling thread
  165. var me = current();
  166. mutex.acquire();
  167. for( t in threads )
  168. if( t.impl != me.impl && t.isBlocking ) {
  169. mutex.release();
  170. return true;
  171. }
  172. mutex.release();
  173. return false;
  174. }
  175. static function __init__() {
  176. mutex = new Mutex();
  177. mainThread = new Thread(ThreadImpl.current());
  178. mainThread.name = "Main";
  179. mainThread.events = haxe.EventLoop.main;
  180. threads = [mainThread];
  181. currentTLS = new Tls();
  182. currentTLS.value = mainThread;
  183. }
  184. }