212 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			212 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
| 
								 | 
							
								package haxe;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import haxe.EntryPoint;
							 | 
						||
| 
								 | 
							
								#if (target.threaded && !cppia)
							 | 
						||
| 
								 | 
							
								import sys.thread.EventLoop;
							 | 
						||
| 
								 | 
							
								import sys.thread.Thread;
							 | 
						||
| 
								 | 
							
								#end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class MainEvent {
							 | 
						||
| 
								 | 
							
									var f:Void->Void;
							 | 
						||
| 
								 | 
							
									var prev:MainEvent;
							 | 
						||
| 
								 | 
							
									var next:MainEvent;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Tells if the event can lock the process from exiting (default:true)
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public var isBlocking:Bool = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public var nextRun(default, null):Float;
							 | 
						||
| 
								 | 
							
									public var priority(default, null):Int;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function new(f, p) {
							 | 
						||
| 
								 | 
							
										this.f = f;
							 | 
						||
| 
								 | 
							
										this.priority = p;
							 | 
						||
| 
								 | 
							
										nextRun = Math.NEGATIVE_INFINITY;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Delay the execution of the event for the given time, in seconds.
							 | 
						||
| 
								 | 
							
										If t is null, the event will be run at tick() time.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public function delay(t:Null<Float>) {
							 | 
						||
| 
								 | 
							
										nextRun = t == null ? Math.NEGATIVE_INFINITY : haxe.Timer.stamp() + t;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Call the event. Will do nothing if the event has been stopped.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public inline function call() {
							 | 
						||
| 
								 | 
							
										if (f != null)
							 | 
						||
| 
								 | 
							
											f();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Stop the event from firing anymore.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public function stop() {
							 | 
						||
| 
								 | 
							
										if (f == null)
							 | 
						||
| 
								 | 
							
											return;
							 | 
						||
| 
								 | 
							
										f = null;
							 | 
						||
| 
								 | 
							
										nextRun = Math.NEGATIVE_INFINITY;
							 | 
						||
| 
								 | 
							
										if (prev == null)
							 | 
						||
| 
								 | 
							
											@:privateAccess MainLoop.pending = next;
							 | 
						||
| 
								 | 
							
										else
							 | 
						||
| 
								 | 
							
											prev.next = next;
							 | 
						||
| 
								 | 
							
										if (next != null)
							 | 
						||
| 
								 | 
							
											next.prev = prev;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@:access(haxe.MainEvent)
							 | 
						||
| 
								 | 
							
								class MainLoop {
							 | 
						||
| 
								 | 
							
									#if (target.threaded && !cppia)
							 | 
						||
| 
								 | 
							
									static var eventLoopHandler:Null<EventHandler>;
							 | 
						||
| 
								 | 
							
									static var mutex = new sys.thread.Mutex();
							 | 
						||
| 
								 | 
							
									static var mainThread = Thread.current();
							 | 
						||
| 
								 | 
							
									#end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									static var pending:MainEvent;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public static var threadCount(get, never):Int;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									inline static function get_threadCount()
							 | 
						||
| 
								 | 
							
										return EntryPoint.threadCount;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public static function hasEvents() {
							 | 
						||
| 
								 | 
							
										var p = pending;
							 | 
						||
| 
								 | 
							
										while (p != null) {
							 | 
						||
| 
								 | 
							
											if (p.isBlocking)
							 | 
						||
| 
								 | 
							
												return true;
							 | 
						||
| 
								 | 
							
											p = p.next;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return false;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public static function addThread(f:Void->Void) {
							 | 
						||
| 
								 | 
							
										EntryPoint.addThread(f);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public static function runInMainThread(f:Void->Void) {
							 | 
						||
| 
								 | 
							
										EntryPoint.runInMainThread(f);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Add a pending event to be run into the main loop.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public static function add(f:Void->Void, priority = 0):MainEvent@:privateAccess {
							 | 
						||
| 
								 | 
							
										if (f == null)
							 | 
						||
| 
								 | 
							
											throw "Event function is null";
							 | 
						||
| 
								 | 
							
										var e = new MainEvent(f, priority);
							 | 
						||
| 
								 | 
							
										var head = pending;
							 | 
						||
| 
								 | 
							
										if (head != null)
							 | 
						||
| 
								 | 
							
											head.prev = e;
							 | 
						||
| 
								 | 
							
										e.next = head;
							 | 
						||
| 
								 | 
							
										pending = e;
							 | 
						||
| 
								 | 
							
										injectIntoEventLoop(0);
							 | 
						||
| 
								 | 
							
										return e;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									static function injectIntoEventLoop(waitMs:Int) {
							 | 
						||
| 
								 | 
							
										#if (target.threaded && !cppia)
							 | 
						||
| 
								 | 
							
											mutex.acquire();
							 | 
						||
| 
								 | 
							
											if(eventLoopHandler != null)
							 | 
						||
| 
								 | 
							
												mainThread.events.cancel(eventLoopHandler);
							 | 
						||
| 
								 | 
							
											eventLoopHandler = mainThread.events.repeat(
							 | 
						||
| 
								 | 
							
												() -> {
							 | 
						||
| 
								 | 
							
													mainThread.events.cancel(eventLoopHandler);
							 | 
						||
| 
								 | 
							
													var wait = tick();
							 | 
						||
| 
								 | 
							
													if(hasEvents()) {
							 | 
						||
| 
								 | 
							
														injectIntoEventLoop(Std.int(wait * 1000));
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												},
							 | 
						||
| 
								 | 
							
												waitMs
							 | 
						||
| 
								 | 
							
											);
							 | 
						||
| 
								 | 
							
											mutex.release();
							 | 
						||
| 
								 | 
							
										#end
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									static function sortEvents() {
							 | 
						||
| 
								 | 
							
										// pending = haxe.ds.ListSort.sort(pending, function(e1, e2) return e1.nextRun > e2.nextRun ? -1 : 1);
							 | 
						||
| 
								 | 
							
										// we can't use directly ListSort because it requires prev/next to be public, which we don't want here
							 | 
						||
| 
								 | 
							
										// we do then a manual inline, this also allow use to do a Float comparison of nextRun
							 | 
						||
| 
								 | 
							
										var list = pending;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (list == null)
							 | 
						||
| 
								 | 
							
											return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var insize = 1, nmerges, psize = 0, qsize = 0;
							 | 
						||
| 
								 | 
							
										var p, q, e, tail:MainEvent;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										while (true) {
							 | 
						||
| 
								 | 
							
											p = list;
							 | 
						||
| 
								 | 
							
											list = null;
							 | 
						||
| 
								 | 
							
											tail = null;
							 | 
						||
| 
								 | 
							
											nmerges = 0;
							 | 
						||
| 
								 | 
							
											while (p != null) {
							 | 
						||
| 
								 | 
							
												nmerges++;
							 | 
						||
| 
								 | 
							
												q = p;
							 | 
						||
| 
								 | 
							
												psize = 0;
							 | 
						||
| 
								 | 
							
												for (i in 0...insize) {
							 | 
						||
| 
								 | 
							
													psize++;
							 | 
						||
| 
								 | 
							
													q = q.next;
							 | 
						||
| 
								 | 
							
													if (q == null)
							 | 
						||
| 
								 | 
							
														break;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												qsize = insize;
							 | 
						||
| 
								 | 
							
												while (psize > 0 || (qsize > 0 && q != null)) {
							 | 
						||
| 
								 | 
							
													if (psize == 0) {
							 | 
						||
| 
								 | 
							
														e = q;
							 | 
						||
| 
								 | 
							
														q = q.next;
							 | 
						||
| 
								 | 
							
														qsize--;
							 | 
						||
| 
								 | 
							
													} else if (qsize == 0
							 | 
						||
| 
								 | 
							
														|| q == null
							 | 
						||
| 
								 | 
							
														|| (p.priority > q.priority || (p.priority == q.priority && p.nextRun <= q.nextRun))) {
							 | 
						||
| 
								 | 
							
														e = p;
							 | 
						||
| 
								 | 
							
														p = p.next;
							 | 
						||
| 
								 | 
							
														psize--;
							 | 
						||
| 
								 | 
							
													} else {
							 | 
						||
| 
								 | 
							
														e = q;
							 | 
						||
| 
								 | 
							
														q = q.next;
							 | 
						||
| 
								 | 
							
														qsize--;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
													if (tail != null)
							 | 
						||
| 
								 | 
							
														tail.next = e;
							 | 
						||
| 
								 | 
							
													else
							 | 
						||
| 
								 | 
							
														list = e;
							 | 
						||
| 
								 | 
							
													e.prev = tail;
							 | 
						||
| 
								 | 
							
													tail = e;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												p = q;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											tail.next = null;
							 | 
						||
| 
								 | 
							
											if (nmerges <= 1)
							 | 
						||
| 
								 | 
							
												break;
							 | 
						||
| 
								 | 
							
											insize *= 2;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										list.prev = null; // not cycling
							 | 
						||
| 
								 | 
							
										pending = list;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Run the pending events. Return the time for next event.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									static function tick() {
							 | 
						||
| 
								 | 
							
										sortEvents();
							 | 
						||
| 
								 | 
							
										var e = pending;
							 | 
						||
| 
								 | 
							
										var now = haxe.Timer.stamp();
							 | 
						||
| 
								 | 
							
										var wait = 1e9;
							 | 
						||
| 
								 | 
							
										while (e != null) {
							 | 
						||
| 
								 | 
							
											var next = e.next;
							 | 
						||
| 
								 | 
							
											var wt = e.nextRun - now;
							 | 
						||
| 
								 | 
							
											if (wt <= 0) {
							 | 
						||
| 
								 | 
							
												wait = 0;
							 | 
						||
| 
								 | 
							
												e.call();
							 | 
						||
| 
								 | 
							
											} else if (wait > wt)
							 | 
						||
| 
								 | 
							
												wait = wt;
							 | 
						||
| 
								 | 
							
											e = next;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return wait;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |