forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			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; | ||
|  | 	} | ||
|  | } |