MainLoop.hx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. package haxe;
  2. import haxe.EntryPoint;
  3. #if (target.threaded && !cppia)
  4. import sys.thread.EventLoop;
  5. import sys.thread.Thread;
  6. #end
  7. class MainEvent {
  8. var f:Void->Void;
  9. var prev:MainEvent;
  10. var next:MainEvent;
  11. /**
  12. Tells if the event can lock the process from exiting (default:true)
  13. **/
  14. public var isBlocking:Bool = true;
  15. public var nextRun(default, null):Float;
  16. public var priority(default, null):Int;
  17. function new(f, p) {
  18. this.f = f;
  19. this.priority = p;
  20. nextRun = Math.NEGATIVE_INFINITY;
  21. }
  22. /**
  23. Delay the execution of the event for the given time, in seconds.
  24. If t is null, the event will be run at tick() time.
  25. **/
  26. public function delay(t:Null<Float>) {
  27. nextRun = t == null ? Math.NEGATIVE_INFINITY : haxe.Timer.stamp() + t;
  28. }
  29. /**
  30. Call the event. Will do nothing if the event has been stopped.
  31. **/
  32. public inline function call() {
  33. if (f != null)
  34. f();
  35. }
  36. /**
  37. Stop the event from firing anymore.
  38. **/
  39. public function stop() {
  40. if (f == null)
  41. return;
  42. f = null;
  43. nextRun = Math.NEGATIVE_INFINITY;
  44. if (prev == null)
  45. @:privateAccess MainLoop.pending = next;
  46. else
  47. prev.next = next;
  48. if (next != null)
  49. next.prev = prev;
  50. }
  51. }
  52. @:access(haxe.MainEvent)
  53. class MainLoop {
  54. #if (target.threaded && !cppia)
  55. static var eventLoopHandler:Null<EventHandler>;
  56. static var mutex = new sys.thread.Mutex();
  57. static var mainThread = Thread.current();
  58. #end
  59. static var pending:MainEvent;
  60. public static var threadCount(get, never):Int;
  61. inline static function get_threadCount()
  62. return EntryPoint.threadCount;
  63. public static function hasEvents() {
  64. var p = pending;
  65. while (p != null) {
  66. if (p.isBlocking)
  67. return true;
  68. p = p.next;
  69. }
  70. return false;
  71. }
  72. public static function addThread(f:Void->Void) {
  73. EntryPoint.addThread(f);
  74. }
  75. public static function runInMainThread(f:Void->Void) {
  76. EntryPoint.runInMainThread(f);
  77. }
  78. /**
  79. Add a pending event to be run into the main loop.
  80. **/
  81. public static function add(f:Void->Void, priority = 0):MainEvent@:privateAccess {
  82. if (f == null)
  83. throw "Event function is null";
  84. var e = new MainEvent(f, priority);
  85. var head = pending;
  86. if (head != null)
  87. head.prev = e;
  88. e.next = head;
  89. pending = e;
  90. injectIntoEventLoop(0);
  91. return e;
  92. }
  93. static function injectIntoEventLoop(waitMs:Int) {
  94. #if (target.threaded && !cppia)
  95. mutex.acquire();
  96. if(eventLoopHandler != null)
  97. mainThread.events.cancel(eventLoopHandler);
  98. eventLoopHandler = mainThread.events.repeat(
  99. () -> {
  100. mainThread.events.cancel(eventLoopHandler);
  101. var wait = tick();
  102. if(hasEvents()) {
  103. injectIntoEventLoop(Std.int(wait * 1000));
  104. }
  105. },
  106. waitMs
  107. );
  108. mutex.release();
  109. #end
  110. }
  111. static function sortEvents() {
  112. // pending = haxe.ds.ListSort.sort(pending, function(e1, e2) return e1.nextRun > e2.nextRun ? -1 : 1);
  113. // we can't use directly ListSort because it requires prev/next to be public, which we don't want here
  114. // we do then a manual inline, this also allow use to do a Float comparison of nextRun
  115. var list = pending;
  116. if (list == null)
  117. return;
  118. var insize = 1, nmerges, psize = 0, qsize = 0;
  119. var p, q, e, tail:MainEvent;
  120. while (true) {
  121. p = list;
  122. list = null;
  123. tail = null;
  124. nmerges = 0;
  125. while (p != null) {
  126. nmerges++;
  127. q = p;
  128. psize = 0;
  129. for (i in 0...insize) {
  130. psize++;
  131. q = q.next;
  132. if (q == null)
  133. break;
  134. }
  135. qsize = insize;
  136. while (psize > 0 || (qsize > 0 && q != null)) {
  137. if (psize == 0) {
  138. e = q;
  139. q = q.next;
  140. qsize--;
  141. } else if (qsize == 0
  142. || q == null
  143. || (p.priority > q.priority || (p.priority == q.priority && p.nextRun <= q.nextRun))) {
  144. e = p;
  145. p = p.next;
  146. psize--;
  147. } else {
  148. e = q;
  149. q = q.next;
  150. qsize--;
  151. }
  152. if (tail != null)
  153. tail.next = e;
  154. else
  155. list = e;
  156. e.prev = tail;
  157. tail = e;
  158. }
  159. p = q;
  160. }
  161. tail.next = null;
  162. if (nmerges <= 1)
  163. break;
  164. insize *= 2;
  165. }
  166. list.prev = null; // not cycling
  167. pending = list;
  168. }
  169. /**
  170. Run the pending events. Return the time for next event.
  171. **/
  172. static function tick() {
  173. sortEvents();
  174. var e = pending;
  175. var now = haxe.Timer.stamp();
  176. var wait = 1e9;
  177. while (e != null) {
  178. var next = e.next;
  179. var wt = e.nextRun - now;
  180. if (wt <= 0) {
  181. wait = 0;
  182. e.call();
  183. } else if (wait > wt)
  184. wait = wt;
  185. e = next;
  186. }
  187. return wait;
  188. }
  189. }