Pārlūkot izejas kodu

rewrite haxe event loop and thread loops support

ncannasse 1 nedēļu atpakaļ
vecāks
revīzija
34cac5076d

+ 1 - 1
extra/haxelib_src

@@ -1 +1 @@
-Subproject commit 659d200a08200b8dbed685e7fa0aa5b02fb44a6b
+Subproject commit 436bd844f90c61100b13a108912c1f7e918aead3

+ 1 - 12
src/typing/finalization.ml

@@ -66,20 +66,9 @@ let get_main ctx main_module types =
 				[main; call_static (["haxe"],"EntryPoint") "run"]
 				[main; call_static (["haxe"],"EntryPoint") "run"]
 			with Not_found ->
 			with Not_found ->
 				[main]
 				[main]
-		(* add calls for event loop *)
-		and add_event_loop main =
-			(try
-				[main; call_static (["sys";"thread";"_Thread"],"Thread_Impl_") "processEvents"]
-			with Not_found ->
-				[main]
-			)
 		in
 		in
 		let main =
 		let main =
-			(* Threaded targets run event loops per thread *)
-			let exprs =
-				if ctx.com.config.pf_supports_threads then add_event_loop main
-				else add_entry_point_run main
-			in
+			let exprs = add_entry_point_run main in
 			match exprs with
 			match exprs with
 			| [e] -> e
 			| [e] -> e
 			| _ -> mk (TBlock exprs) ctx.t.tvoid p
 			| _ -> mk (TBlock exprs) ctx.t.tvoid p

+ 0 - 189
std/cpp/_std/sys/thread/Thread.hx

@@ -1,189 +0,0 @@
-/*
- * Copyright (C)2005-2019 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package sys.thread;
-
-private typedef ThreadImpl = HaxeThread;
-
-abstract Thread(ThreadImpl) from ThreadImpl {
-	public var events(get,never):EventLoop;
-
-	public inline function sendMessage(msg:Dynamic):Void {
-		this.sendMessage(msg);
-	}
-
-	public static inline function current():Thread {
-		return HaxeThread.current();
-	}
-
-	public static inline function create(job:()->Void):Thread {
-		return HaxeThread.create(job, false);
-	}
-
-	public static inline function runWithEventLoop(job:()->Void):Void {
-		HaxeThread.runWithEventLoop(job);
-	}
-
-	public static inline function createWithEventLoop(job:()->Void):Thread {
-		return HaxeThread.create(job, true);
-	}
-
-	public static function readMessage(block:Bool):Dynamic {
-		return HaxeThread.readMessage(block);
-	}
-
-	function get_events():EventLoop {
-		if(this.events == null)
-			throw new NoEventLoopException();
-		return this.events;
-	}
-
-	@:keep
-	static public function processEvents() {
-		HaxeThread.current().events.loop();
-	}
-}
-
-@:callable
-@:coreType
-private abstract NativeThreadHandle {}
-
-private typedef ThreadHandle = NativeThreadHandle;
-
-
-private class HaxeThread {
-	static var threads:Array<{thread:HaxeThread, handle:ThreadHandle}>;
-	static var threadsMutex:Mutex;
-	static var mainThreadHandle:ThreadHandle;
-	static var mainThread:HaxeThread;
-
-	static function __init__() {
-		threads = [];
-		threadsMutex = new Mutex();
-		mainThreadHandle = currentHandle();
-		mainThread = new HaxeThread(currentHandle());		
-		mainThread.events = new EventLoop();
-	}
-
-	public var events(default,null):Null<EventLoop>;
-	public var handle:ThreadHandle;
-	final messages = new Deque<Dynamic>();
-
-	static public function current():HaxeThread {
-		var handle = currentHandle();
-		if(handle == mainThreadHandle) {
-			return mainThread;
-		}
-		threadsMutex.acquire();
-		var thread = null;
-		for(item in threads) {
-			if(item.handle == handle) {
-				thread = item.thread;
-				break;
-			}
-		}
-		if(thread == null) {
-			thread = new HaxeThread(handle);
-			threads.push({thread:thread, handle:handle});
-		}
-		threadsMutex.release();
-		return thread;
-	}
-
-	public static function create(job:()->Void, withEventLoop:Bool):Thread {
-		var item = {handle:null, thread:new HaxeThread(null)};
-		threadsMutex.acquire();
-		var index = threads.push(item);
-		threadsMutex.release();
-		if(withEventLoop)
-			item.thread.events = new EventLoop();
-		item.handle = createHandle(() -> {
-			if(item.thread.handle == null) {
-				item.handle = currentHandle();
-				item.thread.handle = item.handle;
-			}
-			try {
-				job();
-				if(withEventLoop)
-					item.thread.events.loop();
-			} catch(e) {
-				dropThread(item, index);
-				throw e;
-			}
-			dropThread(item, index);
-		});
-		item.thread.handle = item.handle;
-		return item.thread;
-	}
-
-	public static function runWithEventLoop(job:()->Void):Void {
-		var thread = current();
-		if(thread.events == null) {
-			thread.events = new EventLoop();
-			try {
-				job();
-				thread.events.loop();
-				thread.events = null;
-			} catch(e) {
-				thread.events = null;
-				throw e;
-			}
-		} else {
-			job();
-		}
-	}
-
-	static function dropThread(item, probableIndex:Int) {
-		threadsMutex.acquire();
-		if(threads[probableIndex] == item) {
-			threads.splice(probableIndex, 1);
-		} else {
-			for(i => item2 in threads) {
-				if(item2 == item) {
-					threads.splice(i, 1);
-					break;
-				}
-			}
-		}
-		threadsMutex.release();
-	}
-
-	function new(h:ThreadHandle):Void {
-		handle = h;
-	}
-
-	public inline function sendMessage(msg:Dynamic):Void {
-		messages.add(msg);
-	}
-
-	static #if !scriptable inline #end function currentHandle():ThreadHandle {
-		return untyped __global__.__hxcpp_thread_current();
-	}
-
-	static #if !scriptable inline #end function createHandle(callb:Void->Void):ThreadHandle {
-		return untyped __global__.__hxcpp_thread_create(callb);
-	}
-
-	public static #if !scriptable inline #end function readMessage(block:Bool):Dynamic {
-		return current().messages.pop(block);
-	}
-}

+ 48 - 0
std/cpp/_std/sys/thread/ThreadImpl.hx

@@ -0,0 +1,48 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package sys.thread;
+
+@:callable
+@:coreType
+private abstract NativeThreadHandle {}
+
+private typedef ThreadHandle = NativeThreadHandle;
+
+abstract ThreadImpl(ThreadHandle) {
+
+	public static #if !scriptable inline #end function current():ThreadImpl {
+		return untyped __global__.__hxcpp_thread_current();
+	}
+
+	public static #if !scriptable inline #end function create(callb:Void->Void):ThreadImpl {
+		return untyped __global__.__hxcpp_thread_create(callb);
+	}
+
+	public static function setName( t : ThreadImpl, name : String ) {
+	}
+
+	public static function getName( t : ThreadImpl ) {
+		return null;
+	}
+
+}

+ 0 - 203
std/eval/_std/sys/thread/EventLoop.hx

@@ -1,203 +0,0 @@
-package sys.thread;
-
-import eval.luv.Loop;
-import eval.luv.Async;
-import eval.luv.Timer as LuvTimer;
-import haxe.MainLoop;
-
-/**
-	When an event loop has an available event to execute.
-**/
-@:coreApi
-enum NextEventTime {
-	/** There's already an event waiting to be executed */
-	Now;
-	/** No new events are expected. */
-	Never;
-	/**
-		An event is expected to arrive at any time.
-		If `time` is specified, then the event will be ready at that time for sure.
-	*/
-	AnyTime(time:Null<Float>);
-	/** An event is expected to be ready for execution at `time`. */
-	At(time:Float);
-}
-
-abstract EventHandler(RegularEvent) from RegularEvent to RegularEvent {}
-
-private class RegularEvent {
-	public var timer:Null<LuvTimer>;
-	public var event:()->Void;
-
-	public function new(e:()->Void) {
-		event = e;
-	}
-
-	public function run() {
-		event();
-	}
-}
-
-/**
-	An event loop implementation used for `sys.thread.Thread`
-**/
-@:coreApi
-class EventLoop {
-	@:allow(eval.luv.Loop)
-	final handle:Loop;
-
-	final mutex = new Mutex();
-	final wakeup:Async;
-	var promisedEventsCount = 0;
-	var pending:Array<()->Void> = [];
-	var started:Bool = false;
-
-	var isMainThread:Bool;
-	static var CREATED : Bool;
-
-	public function new():Void {
-		isMainThread = !CREATED;
-		CREATED = true;
-		handle = Loop.init().resolve();
-		wakeup = Async.init(handle, consumePending).resolve();
-		wakeup.unref();
-	}
-
-	/**
-		Schedule event for execution every `intervalMs` milliseconds in current loop.
-	**/
-	public function repeat(event:()->Void, intervalMs:Int):EventHandler {
-		var e = new RegularEvent(event);
-		mutex.acquire();
-		e.timer = LuvTimer.init(handle).resolve();
-		e.timer.start(e.run, intervalMs, intervalMs < 1 ? 1 : intervalMs).resolve();
-		mutex.release();
-		wakeup.send();
-		return e;
-	}
-
-	/**
-		Prevent execution of a previously scheduled event in current loop.
-	**/
-	public function cancel(eventHandler:EventHandler):Void {
-		mutex.acquire();
-		(eventHandler:RegularEvent).event = noop;
-		pending.push(() -> {
-			var timer = (eventHandler:RegularEvent).timer;
-			timer.stop().resolve();
-			timer.close(noop);
-		});
-		mutex.release();
-		wakeup.send();
-	}
-	static final noop = function() {}
-
-	/**
-		Notify this loop about an upcoming event.
-		This makes the thread stay alive and wait for as many events as the number of
-		times `.promise()` was called. These events should be added via `.runPromised()`.
-	**/
-	public function promise():Void {
-		mutex.acquire();
-		++promisedEventsCount;
-		pending.push(refUnref);
-		mutex.release();
-		wakeup.send();
-	}
-
-	/**
-		Execute `event` as soon as possible.
-	**/
-	public function run(event:()->Void):Void {
-		mutex.acquire();
-		pending.push(event);
-		mutex.release();
-		wakeup.send();
-	}
-
-	/**
-		Add previously promised `event` for execution.
-	**/
-	public function runPromised(event:()->Void):Void {
-		mutex.acquire();
-		--promisedEventsCount;
-		pending.push(refUnref);
-		pending.push(event);
-		mutex.release();
-		wakeup.send();
-	}
-
-	function refUnref():Void {
-		if (promisedEventsCount > 0 || (isMainThread && haxe.MainLoop.hasEvents())) {
-			wakeup.ref();
-		} else {
-			wakeup.unref();
-		}
-	}
-
-	public function progress():NextEventTime {
-		if (started) throw "Event loop already started";
-
-		if (handle.run(NOWAIT)) {
-			return AnyTime(null);
-		} else {
-			return Never;
-		}
-	}
-
-	/**
-		Blocks until a new event is added or `timeout` (in seconds) to expires.
-
-		Depending on a target platform this method may also automatically execute arriving
-		events while waiting. However if any event is executed it will stop waiting.
-
-		Returns `true` if more events are expected.
-		Returns `false` if no more events expected.
-
-		Depending on a target platform this method may be non-reentrant. It must
-		not be called from event callbacks.
-	**/
-	public function wait(?timeout:Float):Bool {
-		if (started) throw "Event loop already started";
-
-		if(timeout != null) {
-			var timer = LuvTimer.init(handle).resolve();
-			timer.start(() -> {
-				timer.stop().resolve();
-				timer.close(() -> {});
-			}, Std.int(timeout * 1000));
-			return handle.run(ONCE);
-		} else {
-			return handle.run(ONCE);
-		}
-	}
-
-	/**
-		Execute all pending events.
-		Wait and execute as many events as the number of times `promise()` was called.
-		Runs until all repeating events are cancelled and no more events are expected.
-
-		Depending on a target platform this method may be non-reentrant. It must
-		not be called from event callbacks.
-	**/
-	public function loop():Void {
-		if (started) throw "Event loop already started";
-		started = true;
-		consumePending();
-		handle.run(DEFAULT);
-	}
-
-	function consumePending(?_:Async):Void {
-		mutex.acquire();
-		var p = pending;
-		pending = [];
-		mutex.release();
-		for(fn in p) fn();
-
-		if (started && isMainThread) {
-			var next = @:privateAccess MainLoop.tick();
-			if (haxe.MainLoop.hasEvents()) wakeup.send();
-			refUnref();
-		}
-	}
-}

+ 0 - 105
std/eval/_std/sys/thread/Thread.hx

@@ -1,105 +0,0 @@
-/*
- * Copyright (C)2005-2019 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package sys.thread;
-
-import eval.vm.NativeThread;
-
-private typedef ThreadImpl = NativeThread;
-
-abstract Thread(ThreadImpl) from ThreadImpl {
-	public var events(get,never):EventLoop;
-
-	static function __init__() {
-		NativeThread.self().events = new EventLoop();
-	}
-
-	inline function new(h:NativeThread):Void {
-		this = h;
-	}
-
-	public inline function sendMessage(msg:Dynamic):Void {
-		this.sendMessage(msg);
-	}
-
-	public static inline function current():Thread {
-		return new Thread(NativeThread.self());
-	}
-
-	public static inline function create(job:()->Void):Thread {
-		return new Thread(new NativeThread(job));
-	}
-
-	public static function runWithEventLoop(job:()->Void):Void {
-		var thread = NativeThread.self();
-		if(thread.events == null) {
-			thread.events = new EventLoop();
-			try {
-				job();
-				thread.events.loop();
-				thread.events = null;
-			} catch(e) {
-				thread.events = null;
-				throw e;
-			}
-		} else {
-			job();
-		}
-	}
-
-	public static inline function createWithEventLoop(job:()->Void):Thread {
-		return new Thread(new NativeThread(() -> {
-			var thread = NativeThread.self();
-			thread.events = new EventLoop();
-			job();
-			thread.events.loop();
-		}));
-	}
-
-	public static inline function readMessage(block:Bool):Dynamic {
-		return NativeThread.readMessage(block);
-	}
-
-	public static inline function yield():Void {
-		NativeThread.yield();
-	}
-
-	@:op(A == B)
-	public inline function equals(other:Thread):Bool {
-		return getHandle().id() == other.getHandle().id();
-	}
-
-	inline function getHandle():NativeThread {
-		return this;
-	}
-
-	function get_events():EventLoop {
-		if(this.events == null)
-			throw new NoEventLoopException();
-		return this.events;
-	}
-
-	@:keep
-	static function processEvents():Void {
-		NativeThread.self().events.loop();
-	}
-}

+ 14 - 21
std/sys/thread/IThreadPool.hx → std/eval/_std/sys/thread/ThreadImpl.hx

@@ -22,30 +22,23 @@
 
 
 package sys.thread;
 package sys.thread;
 
 
-/**
-	A thread pool interface.
-**/
-interface IThreadPool {
+import eval.vm.NativeThread;
 
 
-	/** Amount of alive threads in this pool. */
-	var threadsCount(get,never):Int;
+abstract ThreadImpl(NativeThread) {
 
 
-	/** Indicates if `shutdown` method of this pool has been called. */
-	var isShutdown(get,never):Bool;
+	public static inline function current():ThreadImpl {
+		return cast NativeThread.self();
+	}
 
 
-	/**
-		Submit a task to run in a thread.
+	public static inline function create(job:()->Void):ThreadImpl {
+		return cast new NativeThread(job);
+	}
 
 
-		Throws an exception if the pool is shut down.
-	**/
-	function run(task:()->Void):Void;
+	public static function getName( t : ThreadImpl ) {
+		return null;
+	}
 
 
-	/**
-		Initiates a shutdown.
-		All previously submitted tasks will be executed, but no new tasks will
-		be accepted.
+	public static function setName( t : ThreadImpl, name : String ) {
+	}
 
 
-		Multiple calls to this method have no effect.
-	**/
-	function shutdown():Void;
-}
+}

+ 0 - 2
std/eval/vm/NativeThread.hx

@@ -77,6 +77,4 @@ extern class NativeThread {
 
 
 	function sendMessage<T>(msg:T):Void;
 	function sendMessage<T>(msg:T):Void;
 
 
-	@:allow(sys.thread.Thread)
-	private var events(get,set):Null<sys.thread.EventLoop>;
 }
 }

+ 33 - 160
std/haxe/EntryPoint.hx

@@ -1,182 +1,55 @@
 package haxe;
 package haxe;
 
 
-#if (target.threaded && !cppia)
-import sys.thread.Lock;
-import sys.thread.Mutex;
-import sys.thread.Thread;
-#elseif sys
-private class Lock {
-	public function new() {}
-
-	public inline function release() {}
-
-	public inline function wait(?t:Float) {}
-}
-
-private class Mutex {
-	public function new() {}
-
-	public inline function acquire() {}
-
-	public inline function release() {}
-}
-
-private class Thread {
-	public static function create(f:Void->Void) {
-		f();
-	}
-}
-#end
-
 /**
 /**
 	If `haxe.MainLoop` is kept from DCE, then we will insert an `haxe.EntryPoint.run()` call just at then end of `main()`.
 	If `haxe.MainLoop` is kept from DCE, then we will insert an `haxe.EntryPoint.run()` call just at then end of `main()`.
 	This class can be redefined by custom frameworks so they can handle their own main loop logic.
 	This class can be redefined by custom frameworks so they can handle their own main loop logic.
 **/
 **/
 class EntryPoint {
 class EntryPoint {
-	#if sys
-		static var mutex = new Mutex();
-		#if (target.threaded && !cppia)
-			static var mainThread:Thread = Thread.current();
-		#else
-			static var sleepLock = new Lock();
-		#end
-	#end
-	static var pending = new Array<Void->Void>();
-	public static var threadCount(default, null):Int = 0;
-
-	/**
-		Wakeup a sleeping `run()`
-	**/
-	public static function wakeup() {
-		#if (sys && !(target.threaded && !cppia))
-		sleepLock.release();
-		#end
-	}
-
-	public static function runInMainThread(f:Void->Void) {
-		#if sys
-			#if (target.threaded && !cppia)
-				mainThread.events.run(f);
-			#else
-				mutex.acquire();
-				pending.push(f);
-				mutex.release();
-				wakeup();
-			#end
-		#else
-		pending.push(f);
-		#end
-	}
 
 
-	public static function addThread(f:Void->Void) {
-		#if sys
-		mutex.acquire();
-		threadCount++;
-		mutex.release();
-		#if (target.threaded && !cppia)
-			mainThread.events.promise();
-		#end
-		Thread.create(function() {
-			f();
-			mutex.acquire();
-			threadCount--;
-			if (threadCount == 0)
-				wakeup();
-			mutex.release();
-			#if (target.threaded && !cppia)
-				mainThread.events.runPromised(() -> {});
-			#end
-		});
-		#else
-		threadCount++;
-		pending.push(function() {
-			f();
-			threadCount--;
-		});
-		#end
-	}
-
-	static function processEvents():Float {
-		#if (target.threaded && !cppia)
-		return -1;
-		#else
-		// flush all pending calls
-		while (true) {
-			#if sys
-			mutex.acquire();
-			var f = pending.shift();
-			mutex.release();
-			#else
-			var f = pending.shift();
-			#end
-			if (f == null)
-				break;
-			f();
-		}
-		var time = @:privateAccess MainLoop.tick();
-		if (!MainLoop.hasEvents() && threadCount == 0)
-			return -1;
-		return time;
-		#end
-	}
-
-	/**
-		Start the main loop. Depending on the platform, this can return immediately or will only return when the application exits.
-	**/
 	@:keep public static function run() @:privateAccess {
 	@:keep public static function run() @:privateAccess {
 		#if js
 		#if js
-		var nextTick = processEvents();
-		inline function setTimeoutNextTick() {
-			if (nextTick >= 0) {
-				(untyped setTimeout)(run, nextTick * 1000);
+			var nextTick = processEvents();
+			inline function setTimeoutNextTick() {
+				if (nextTick >= 0) {
+					(untyped setTimeout)(run, nextTick * 1000);
+				}
 			}
 			}
-		}
-		#if nodejs
-		setTimeoutNextTick();
-		#else
-		if(js.Lib.typeof(js.Browser.window) != 'undefined') {
-			var window:Dynamic = js.Browser.window;
-			var rqf:Dynamic = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
-			if(rqf != null) {
-				rqf(run);
+			#if nodejs
+			setTimeoutNextTick();
+			#else
+			if(js.Lib.typeof(js.Browser.window) != 'undefined') {
+				var window:Dynamic = js.Browser.window;
+				var rqf:Dynamic = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
+				if(rqf != null) {
+					rqf(run);
+				} else {
+					setTimeoutNextTick();
+				}
 			} else {
 			} else {
 				setTimeoutNextTick();
 				setTimeoutNextTick();
 			}
 			}
-		} else {
-			setTimeoutNextTick();
-		}
-		#end
+			#end
+			haxe.EventLoop.main.loopOnce();
 		#elseif flash
 		#elseif flash
-		flash.Lib.current.stage.addEventListener(flash.events.Event.ENTER_FRAME, function(_) processEvents());
-		#elseif (target.threaded && !cppia)
-		//everything is delegated to sys.thread.EventLoop
+			flash.Lib.current.stage.addEventListener(flash.events.Event.ENTER_FRAME, function(_) haxe.EventLoop.main.loopOnce());
 		#elseif lua
 		#elseif lua
-		inline function luvRun(mode:String):Bool
-			return untyped __lua__('_hx_luv.run({0})', mode);
-		while (true) {
-			var nextTick = processEvents();
-			if(untyped __lua__('_hx_luv.loop_alive()')) {
-				if(nextTick < 0)
-					luvRun("once")
-				else
-					luvRun("nowait");
-			} else {
-				if (nextTick < 0)
+			inline function luvRun(mode:String):Bool
+				return untyped __lua__('_hx_luv.run({0})', mode);
+			var events = haxe.EventLoop.main;
+			while (true) {
+				events.loopOnce();
+				if( !events.hasEvents(true) )
 					break;
 					break;
-				if (nextTick > 0)
-					sleepLock.wait(nextTick);
+				var nextTick = events.getNextTick();
+				if(untyped __lua__('_hx_luv.loop_alive()')) {
+					if(nextTick < 0)
+						luvRun("once")
+					else
+						luvRun("nowait");
+				}
 			}
 			}
-		}
-		#elseif sys
-		while (true) {
-			var nextTick = processEvents();
-			if (nextTick < 0)
-				break;
-			if (nextTick > 0)
-				sleepLock.wait(nextTick); // wait until nextTick or wakeup() call
-		}
 		#else
 		#else
-		// no implementation available, let's exit immediately
+			haxe.EventLoop.main.loop();
 		#end
 		#end
 	}
 	}
 }
 }

+ 5 - 177
std/haxe/MainLoop.hx

@@ -1,186 +1,14 @@
 package haxe;
 package haxe;
 
 
-import haxe.EntryPoint;
-#if (target.threaded && !cppia)
-import sys.thread.EventLoop;
-import sys.thread.Thread;
-#end
+typedef MainEvent = haxe.EventLoop.Event;
 
 
-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)
+/**
+	This class exists for backward compatibility. You should use haxe.EventLoop instead.
+**/
 class MainLoop {
 class MainLoop {
 
 
-	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 {
 	public static function add(f:Void->Void, priority = 0) : MainEvent {
-		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;
-		return e;
+		return EventLoop.main.add(f, priority);
 	}
 	}
 
 
-	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;
-	}
 }
 }

+ 2 - 19
std/haxe/Timer.hx

@@ -23,10 +23,6 @@
 package haxe;
 package haxe;
 
 
 import haxe.Int64;
 import haxe.Int64;
-#if (target.threaded && !cppia)
-import sys.thread.Thread;
-import sys.thread.EventLoop;
-#end
 
 
 /**
 /**
 	The `Timer` class allows you to create asynchronous timers on platforms that
 	The `Timer` class allows you to create asynchronous timers on platforms that
@@ -50,11 +46,8 @@ import sys.thread.EventLoop;
 class Timer {
 class Timer {
 	#if (flash || js)
 	#if (flash || js)
 	private var id:Null<Int>;
 	private var id:Null<Int>;
-	#elseif (target.threaded && !cppia)
-	var thread:Thread;
-	var eventHandler:EventHandler;
 	#else
 	#else
-	private var event:MainLoop.MainEvent;
+	private var event:EventLoop.Event;
 	#end
 	#end
 
 
 	/**
 	/**
@@ -77,16 +70,8 @@ class Timer {
 		#elseif js
 		#elseif js
 		var me = this;
 		var me = this;
 		id = untyped setInterval(function() me.run(), time_ms);
 		id = untyped setInterval(function() me.run(), time_ms);
-		#elseif (target.threaded && !cppia)
-		thread = Thread.current();
-		eventHandler = thread.events.repeat(() -> this.run(), time_ms);
 		#else
 		#else
-		var dt = time_ms / 1000;
-		event = MainLoop.add(function() {
-			@:privateAccess event.nextRun += dt;
-			run();
-		});
-		event.delay(dt);
+		event = EventLoop.current.addTimer(() -> this.run(), time_ms/1000.);
 		#end
 		#end
 	}
 	}
 
 
@@ -108,8 +93,6 @@ class Timer {
 		untyped clearInterval(id);
 		untyped clearInterval(id);
 		#end
 		#end
 		id = null;
 		id = null;
-		#elseif (target.threaded && !cppia)
-		thread.events.cancel(eventHandler);
 		#else
 		#else
 		if (event != null) {
 		if (event != null) {
 			event.stop();
 			event.stop();

+ 0 - 204
std/hl/_std/sys/thread/Thread.hx

@@ -1,204 +0,0 @@
-/*
- * Copyright (C)2005-2019 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package sys.thread;
-
-private typedef ThreadImpl = HaxeThread;
-
-abstract Thread(ThreadImpl) from ThreadImpl {
-	public var events(get,never):EventLoop;
-
-	public inline function sendMessage(msg:Dynamic) {
-		this.sendMessage(msg);
-	}
-
-	public static inline function readMessage(block = true):Dynamic {
-		return HaxeThread.current().readMessage(block);
-	}
-
-	public static inline function create(job:()->Void):Thread {
-		return HaxeThread.create(job, false);
-	}
-
-	public static inline function runWithEventLoop(job:()->Void):Void {
-		HaxeThread.runWithEventLoop(job);
-	}
-
-	public static inline function createWithEventLoop(job:()->Void):Thread {
-		return HaxeThread.create(job, true);
-	}
-
-	public static function current():Thread {
-		return HaxeThread.current();
-	}
-
-
-	public function setName( name : String ) {
-		#if (hl_ver >= version("1.13.0"))
-		set_name(@:privateAccess this.handle, @:privateAccess name.toUtf8());
-		#end
-	}
-
-	public function getName() : Null<String> {
-		#if (hl_ver >= version("1.13.0"))
-		var name = get_name(@:privateAccess this.handle);
-		return name == null ? null : @:privateAccess String.fromUTF8(name);
-		#else
-		return null;
-		#end
-	}
-
-	#if (hl_ver >= version("1.13.0"))
-	@:hlNative("?std", "thread_set_name") static function set_name( t : ThreadHandle, name : hl.Bytes ) {}
-	@:hlNative("?std", "thread_get_name") static function get_name( t : ThreadHandle ) : hl.Bytes { return null; }
-	#end
-
-	function get_events():EventLoop {
-		if(this.events == null)
-			throw new NoEventLoopException();
-		return this.events;
-	}
-
-	@:keep
-	static public function processEvents() {
-		HaxeThread.current().events.loop();
-	}
-}
-
-private typedef ThreadHandle = hl.Abstract<"hl_thread">;
-
-private class HaxeThread {
-
-	static var mainThread:HaxeThread;
-	static var threads:Array<HaxeThread>;
-	static var threadsMutex:Mutex;
-	static var UID = 0;
-
-	static function __init__() {
-		threadsMutex = new Mutex();
-		threads = [];
-		mainThread = new HaxeThread(currentHandle());
-		mainThread.events = new EventLoop();
-	}
-
-	var id = UID++;
-	public var events(default,null):Null<EventLoop>;
-	var handle : ThreadHandle;
-	final messages = new Deque();
-
-	@:hlNative("std", "thread_create")
-	static function createHandle(callb:Void->Void):ThreadHandle {
-		return null;
-	}
-
-	@:hlNative("std", "thread_current")
-	static function currentHandle():ThreadHandle {
-		return null;
-	}
-
-	static public function current():HaxeThread {
-		var handle = currentHandle();
-		if(handle == mainThread.handle) {
-			return mainThread;
-		}
-		threadsMutex.acquire();
-		var thread = null;
-		for(item in threads) {
-			if(item.handle == handle) {
-				thread = item;
-				break;
-			}
-		}
-		if(thread == null) {
-			thread = new HaxeThread(handle);
-			threads.push(thread);
-		}
-		threadsMutex.release();
-		return thread;
-	}
-
-	public static function create(callb:()->Void, withEventLoop:Bool):Thread {
-		var item = new HaxeThread(null);
-		threadsMutex.acquire();
-		threads.push(item);
-		threadsMutex.release();
-		if(withEventLoop)
-			item.events = new EventLoop();
-		item.handle = createHandle(() -> {
-			if(item.handle == null) {
-				item.handle = currentHandle();
-			}
-			try {
-				hl.Api.setErrorHandler(function(_){});
-				callb();
-				if(withEventLoop)
-					item.events.loop();
-			} catch(e) {
-				hl.Api.setErrorHandler(null);
-				dropThread(item);
-				hl.Api.rethrow(e);
-			}
-			dropThread(item);
-		});
-		return item;
-	}
-
-	public static function runWithEventLoop(job:()->Void):Void {
-		var thread = current();
-		if(thread.events == null) {
-			thread.events = new EventLoop();
-			try {
-				job();
-				thread.events.loop();
-				thread.events = null;
-			} catch(e) {
-				thread.events = null;
-				throw e;
-			}
-		} else {
-			job();
-		}
-	}
-
-	static function dropThread(deleteItem) {
-		threadsMutex.acquire();
-		for(i => item in threads) {
-			if(item == deleteItem) {
-				threads.splice(i, 1);
-				break;
-			}
-		}
-		threadsMutex.release();
-	}
-
-	public function readMessage(block:Bool):Dynamic {
-		return messages.pop(block);
-	}
-
-	public function new(h) {
-		handle = h;
-	}
-
-	public function sendMessage(msg:Dynamic) {
-		messages.add(msg);
-	}
-}

+ 59 - 0
std/hl/_std/sys/thread/ThreadImpl.hx

@@ -0,0 +1,59 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package sys.thread;
+
+private typedef ThreadHandle = hl.Abstract<"hl_thread">;
+
+abstract ThreadImpl(ThreadHandle) {
+
+	@:hlNative("std", "thread_create")
+	public static function create(callb:Void->Void):ThreadImpl {
+		return null;
+	}
+
+	@:hlNative("std", "thread_current")
+	public static function current():ThreadImpl {
+		return null;
+	}
+
+	public static function setName( impl : ThreadImpl, name : String ) {
+		#if (hl_ver >= version("1.13.0"))
+		set_name(impl, @:privateAccess name.toUtf8());
+		#end
+	}
+
+	public static function getName( impl : ThreadImpl ) : Null<String> {
+		#if (hl_ver >= version("1.13.0"))
+		var name = get_name(impl);
+		return name == null ? null : @:privateAccess String.fromUTF8(name);
+		#else
+		return null;
+		#end
+	}
+
+	#if (hl_ver >= version("1.13.0"))
+	@:hlNative("?std", "thread_set_name") static function set_name( t : ThreadImpl, name : hl.Bytes ) {}
+	@:hlNative("?std", "thread_get_name") static function get_name( t : ThreadImpl ) : hl.Bytes { return null; }
+	#end
+
+}

+ 0 - 176
std/jvm/_std/sys/thread/Thread.hx

@@ -1,176 +0,0 @@
-/*
- * Copyright (C)2005-2019 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package sys.thread;
-
-import java.lang.Runnable;
-import java.lang.System;
-import java.lang.Thread as JavaThread;
-import java.util.Collections;
-import java.util.WeakHashMap;
-import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.atomic.AtomicInteger;
-import jvm.Int64 as Long;
-
-private typedef ThreadImpl = HaxeThread;
-
-abstract Thread(ThreadImpl) from ThreadImpl {
-	public var events(get, never):EventLoop;
-
-	inline function new(t:HaxeThread) {
-		this = t;
-	}
-
-	public static inline function create(job:() -> Void):Thread {
-		return HaxeThread.create(job, false);
-	}
-
-	public static inline function current():Thread {
-		return HaxeThread.get(JavaThread.currentThread());
-	}
-
-	public static inline function runWithEventLoop(job:() -> Void):Void {
-		HaxeThread.runWithEventLoop(job);
-	}
-
-	public static inline function createWithEventLoop(job:() -> Void):Thread {
-		return HaxeThread.create(job, true);
-	}
-
-	public static inline function readMessage(block:Bool):Dynamic {
-		return current().getHandle().readMessage(block);
-	}
-
-	public inline function sendMessage(msg:Dynamic):Void {
-		this.sendMessage(msg);
-	}
-
-	inline function getHandle():HaxeThread {
-		return this;
-	}
-
-	function get_events():EventLoop {
-		if (this.events == null)
-			throw new NoEventLoopException();
-		return this.events;
-	}
-
-	@:keep // TODO: keep only if events are actually used
-	static function processEvents():Void {
-		current().getHandle().events.loop();
-	}
-}
-
-private class HaxeThread {
-	static var nativeThreads:java.util.Map<JavaThread, HaxeThread>;
-	static var mainJavaThread:JavaThread;
-	static var mainHaxeThread:HaxeThread;
-
-	static function __init__() {
-		nativeThreads = Collections.synchronizedMap(new WeakHashMap<JavaThread, HaxeThread>());
-		mainJavaThread = JavaThread.currentThread();
-		mainHaxeThread = new HaxeThread();
-		mainHaxeThread.events = new EventLoop();
-	}
-
-	public final messages = new LinkedBlockingDeque<Dynamic>();
-
-	public var events(default, null):Null<EventLoop>;
-
-	public static function create(job:() -> Void, withEventLoop:Bool):HaxeThread {
-		var hx = new HaxeThread();
-		if (withEventLoop)
-			hx.events = new EventLoop();
-		var thread = new NativeHaxeThread(hx, job, withEventLoop);
-		thread.setDaemon(true);
-		thread.start();
-		return hx;
-	}
-
-	public static function get(javaThread:JavaThread):HaxeThread {
-		if (javaThread == mainJavaThread) {
-			return mainHaxeThread;
-		} else if (javaThread is NativeHaxeThread) {
-			return (cast javaThread : NativeHaxeThread).haxeThread;
-		} else {
-			switch nativeThreads.get(javaThread) {
-				case null:
-					var hx = new HaxeThread();
-					nativeThreads.put(javaThread, hx);
-					return hx;
-				case hx:
-					return hx;
-			}
-		}
-	}
-
-	public static function runWithEventLoop(job:() -> Void):Void {
-		var thread = get(JavaThread.currentThread());
-		if (thread.events == null) {
-			thread.events = new EventLoop();
-			try {
-				job();
-				thread.events.loop();
-				thread.events = null;
-			} catch (e) {
-				thread.events = null;
-				throw e;
-			}
-		} else {
-			job();
-		}
-	}
-
-	function new() {}
-
-	public function sendMessage(msg:Dynamic):Void {
-		messages.add(msg);
-	}
-
-	public function readMessage(block:Bool):Dynamic {
-		return block ? messages.take() : messages.poll();
-	}
-}
-
-private class NativeHaxeThread extends java.lang.Thread {
-	public final haxeThread:HaxeThread;
-
-	final withEventLoop:Bool;
-
-	public function new(haxeThread:HaxeThread, job:() -> Void, withEventLoop:Bool) {
-		super(new Job(job));
-		this.haxeThread = haxeThread;
-		this.withEventLoop = withEventLoop;
-	}
-
-	override overload public function run() {
-		super.run();
-		if (withEventLoop)
-			haxeThread.events.loop();
-	}
-}
-
-private abstract Job(Runnable) from Runnable to Runnable {
-	public inline function new(job:() -> Void) {
-		this = cast job;
-	}
-}

+ 73 - 0
std/jvm/_std/sys/thread/ThreadImpl.hx

@@ -0,0 +1,73 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package sys.thread;
+
+import java.lang.Runnable;
+import java.lang.System;
+import java.lang.Thread as JavaThread;
+import java.util.Collections;
+import java.util.WeakHashMap;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.atomic.AtomicInteger;
+import jvm.Int64 as Long;
+
+abstract ThreadImpl(JavaThread) {
+
+	function toNative() {
+		return this;
+	}
+
+	public static function current():ThreadImpl {
+		return cast JavaThread.currentThread();
+	}
+
+	public static function create(job:() -> Void):ThreadImpl {
+		return cast new NativeHaxeThread(job);
+	}
+
+	public static function setName( t : ThreadImpl, name : String ) {
+		return t.toNative().setName(name);
+	}
+
+	public static function getName( t : ThreadImpl ) {
+		return t.toNative().getName();
+	}
+
+}
+
+private class NativeHaxeThread extends java.lang.Thread {
+
+	var job : Void -> Void;
+
+	public function new(job) {
+		super();
+		this.job = job;
+		start();
+	}
+
+	public overload override function run() {
+		job();
+	}
+
+}
+

+ 0 - 188
std/neko/_std/sys/thread/Thread.hx

@@ -1,188 +0,0 @@
-/*
- * Copyright (C)2005-2019 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package sys.thread;
-
-private typedef ThreadImpl = HaxeThread;
-
-abstract Thread(ThreadImpl) from ThreadImpl {
-
-	public var events(get,never):EventLoop;
-
-	public inline function sendMessage(msg:Dynamic):Void {
-		this.sendMessage(msg);
-	}
-
-	public static inline function current():Thread {
-		return HaxeThread.current();
-	}
-
-	public static inline function create(job:()->Void):Thread {
-		return HaxeThread.create(job, false);
-	}
-
-	public static inline function runWithEventLoop(job:()->Void):Void {
-		HaxeThread.runWithEventLoop(job);
-	}
-
-	public static inline function createWithEventLoop(job:()->Void):Thread {
-		return HaxeThread.create(job, true);
-	}
-
-	public static inline function readMessage(block:Bool):Dynamic {
-		return HaxeThread.readMessage(block);
-	}
-
-	function get_events():EventLoop {
-		if(this.events == null)
-			throw new NoEventLoopException();
-		return this.events;
-	}
-
-	@:keep
-	static function processEvents() {
-		HaxeThread.current().events.loop();
-	}
-}
-
-@:callable
-@:coreType
-private abstract NativeThreadHandle {}
-
-private typedef ThreadHandle = NativeThreadHandle;
-
-private class HaxeThread {
-	static var thread_create:(callb:(_:Dynamic)->Void, _:Dynamic)->ThreadHandle;
-	static var thread_current:()->ThreadHandle;
-	static var thread_send:(handle:ThreadHandle, msg:Dynamic)->Void;
-	static var thread_read_message:(block:Bool)->Dynamic;
-
-	static var mainThreadHandle:ThreadHandle;
-	static var mainThread:HaxeThread;
-
-	static var threads:Array<{thread:HaxeThread, handle:ThreadHandle}>;
-	static var threadsMutex:Mutex;
-
-	static function __init__() {
-		thread_create = neko.Lib.load("std", "thread_create", 2);
-		thread_current = neko.Lib.load("std", "thread_current", 0);
-		thread_send = neko.Lib.load("std", "thread_send", 2);
-		thread_read_message = neko.Lib.load("std", "thread_read_message", 1);
-
-		mainThreadHandle = thread_current();
-		mainThread = new HaxeThread(mainThreadHandle);
-		mainThread.events = new EventLoop();
-
-		threads = [];
-		threadsMutex = new Mutex();
-	}
-
-	public var events(default,null):Null<EventLoop>;
-	public var handle:ThreadHandle;
-
-	static public function current():HaxeThread {
-		var handle = thread_current();
-		if(handle == mainThreadHandle) {
-			return mainThread;
-		}
-		threadsMutex.acquire();
-		var thread = null;
-		for(item in threads) {
-			if(item.handle == handle) {
-				thread = item.thread;
-				break;
-			}
-		}
-		if(thread == null) {
-			thread = new HaxeThread(handle);
-			threads.push({thread:thread, handle:handle});
-		}
-		threadsMutex.release();
-		return thread;
-	}
-
-	public static function create(callb:()->Void, withEventLoop:Bool):Thread {
-		var item = {handle:null, thread:new HaxeThread(null)};
-		threadsMutex.acquire();
-		threads.push(item);
-		threadsMutex.release();
-		if(withEventLoop)
-			item.thread.events = new EventLoop();
-		item.handle = thread_create(_ -> {
-			if(item.thread.handle == null) {
-				item.handle = thread_current();
-				item.thread.handle = item.handle;
-			}
-			try {
-				callb();
-				if(withEventLoop)
-					item.thread.events.loop();
-			} catch(e) {
-				dropThread(item);
-				throw e;
-			}
-			dropThread(item);
-		}, null);
-		item.thread.handle = item.handle;
-		return item.thread;
-	}
-
-	public static function runWithEventLoop(job:()->Void):Void {
-		var thread = current();
-		if(thread.events == null) {
-			thread.events = new EventLoop();
-			try {
-				job();
-				thread.events.loop();
-				thread.events = null;
-			} catch(e) {
-				thread.events = null;
-				throw e;
-			}
-		} else {
-			job();
-		}
-	}
-
-	static function dropThread(deleteItem) {
-		threadsMutex.acquire();
-		for(i => item in threads) {
-			if(item == deleteItem) {
-				threads.splice(i, 1);
-				break;
-			}
-		}
-		threadsMutex.release();
-	}
-
-	public static inline function readMessage(block:Bool):Dynamic {
-		return thread_read_message(block);
-	}
-
-	public function new(handle:ThreadHandle) {
-		this.handle = handle;
-	}
-
-	public function sendMessage(msg:Dynamic) {
-		thread_send(handle, msg);
-	}
-}

+ 50 - 0
std/neko/_std/sys/thread/ThreadImpl.hx

@@ -0,0 +1,50 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package sys.thread;
+
+abstract ThreadImpl(Dynamic) {
+
+	static var thread_create:(callb:(_:Dynamic)->Void, _:Dynamic)->ThreadImpl;
+	static var thread_current:()->ThreadImpl;
+
+	static function __init__() {
+		thread_create = neko.Lib.load("std", "thread_create", 2);
+		thread_current = neko.Lib.load("std", "thread_current", 0);
+	}
+
+	public static function current() {
+		return thread_current();
+	}
+
+	public static function create( callb ) {
+		return thread_create((_) -> callb(),null);
+	}
+
+	public static function getName( t : ThreadImpl ) {
+		return null;
+	}
+
+	public static function setName( t : ThreadImpl, name : String ) {
+	}
+
+}

+ 0 - 164
std/python/_std/sys/thread/Thread.hx

@@ -1,164 +0,0 @@
-/*
- * Copyright (C)2005-2019 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package sys.thread;
-
-import python.lib.threading.Thread as NativeThread;
-import python.lib.Threading;
-
-import haxe.ds.ObjectMap;
-
-private typedef ThreadImpl = HxThread;
-
-abstract Thread(ThreadImpl) from ThreadImpl {
-	public var events(get,never):EventLoop;
-
-	public static inline function current():Thread {
-		return HxThread.current();
-	}
-
-	public static inline function create(job:Void->Void):Thread {
-		return HxThread.create(job, false);
-	}
-
-	public static inline function runWithEventLoop(job:()->Void):Void {
-		HxThread.runWithEventLoop(job);
-	}
-
-	public static inline function createWithEventLoop(job:()->Void):Thread {
-		return HxThread.create(job, true);
-	}
-
-	public static inline function readMessage(block:Bool):Dynamic {
-		return HxThread.readMessage(block);
-	}
-
-	public inline function sendMessage(msg:Dynamic):Void {
-		this.sendMessage(msg);
-	}
-
-	function get_events():EventLoop {
-		if(this.events == null)
-			throw new NoEventLoopException();
-		return this.events;
-	}
-
-	@:keep
-	static public function processEvents() {
-		HxThread.current().events.loop();
-	}
-}
-
-private class HxThread {
-	public var events(default,null):Null<EventLoop>;
-
-	final nativeThread:NativeThread;
-	final messages = new Deque<Dynamic>();
-
-	static var threads:ObjectMap<NativeThread, HxThread>;
-	static var threadsMutex:Mutex;
-	static var mainThread:HxThread;
-
-	static function __init__() {
-		threads = new ObjectMap();
-		threadsMutex = new Mutex();
-		mainThread = new HxThread(Threading.current_thread());
-		mainThread.events = new EventLoop();
-	}
-
-	private function new(t:NativeThread) {
-		nativeThread = t;
-	}
-
-	public function sendMessage(msg:Dynamic):Void {
-		messages.add(msg);
-	}
-
-	public static function current():HxThread {
-		threadsMutex.acquire();
-		var ct = Threading.current_thread();
-		if (ct == Threading.main_thread()) {
-			threadsMutex.release();
-			return mainThread;
-		}
-		// If the current thread was not created via the haxe API, it can still be wrapped
-		if (!threads.exists(ct)) {
-			threads.set(ct, new HxThread(ct));
-		}
-		var t = threads.get(ct);
-		threadsMutex.release();
-		return t;
-	}
-
-	public static function create(callb:Void->Void, withEventLoop:Bool):HxThread {
-		var nt:NativeThread = null;
-		var t:HxThread = null;
-		// Wrap the callback so it will clear the thread reference once the thread is finished
-		var wrappedCallB = () -> {
-			try {
-				callb();
-				if(withEventLoop)
-					t.events.loop();
-			} catch(e) {
-				dropThread(nt);
-				throw e;
-			}
-			dropThread(nt);
-		}
-		nt = new NativeThread({target:wrappedCallB, daemon: true});
-		t = new HxThread(nt);
-		if(withEventLoop)
-			t.events = new EventLoop();
-		threadsMutex.acquire();
-		threads.set(nt, t);
-		threadsMutex.release();
-		nt.start();
-		return t;
-	}
-
-	public static function runWithEventLoop(job:()->Void):Void {
-		var thread = current();
-		if(thread.events == null) {
-			thread.events = new EventLoop();
-			try {
-				job();
-				thread.events.loop();
-				thread.events = null;
-			} catch(e) {
-				thread.events = null;
-				throw e;
-			}
-		} else {
-			job();
-		}
-	}
-
-	static inline function dropThread(nt:NativeThread) {
-		threadsMutex.acquire();
-		threads.remove(nt);
-		threadsMutex.release();
-	}
-
-	public static function readMessage(block:Bool):Dynamic {
-		return current().messages.pop(block);
-	}
-}

+ 44 - 0
std/python/_std/sys/thread/ThreadImpl.hx

@@ -0,0 +1,44 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package sys.thread;
+
+import python.lib.threading.Thread as NativeThread;
+
+abstract ThreadImpl(NativeThread) {
+
+	public static function current() : ThreadImpl {
+		return cast python.lib.Threading.current_thread();
+	}
+
+	public static function create(callb:Void->Void):ThreadImpl {
+		return cast new NativeThread({target:callb, daemon: true});
+	}
+
+	public static function getName( t : ThreadImpl ) {
+		return null;
+	}
+
+	public static function setName( t : ThreadImpl, name : String ) {
+	}
+
+}

+ 0 - 198
std/sys/thread/ElasticThreadPool.hx

@@ -1,198 +0,0 @@
-/*
- * Copyright (C)2005-2019 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package sys.thread;
-
-#if (!target.threaded)
-#error "This class is not available on this target"
-#end
-
-import haxe.Exception;
-
-/**
-	Thread pool with a varying amount of threads.
-
-	A new thread is spawned every time a task is submitted while all existing
-	threads are busy.
-**/
-@:coreApi
-class ElasticThreadPool implements IThreadPool {
-	/* Amount of alive threads in this pool. */
-	public var threadsCount(get,null):Int;
-	/* Maximum amount of threads in this pool. */
-	public var maxThreadsCount:Int;
-	/** Indicates if `shutdown` method of this pool has been called. */
-	public var isShutdown(get,never):Bool;
-	var _isShutdown = false;
-	function get_isShutdown():Bool return _isShutdown;
-
-	final pool:Array<Worker> = [];
-	final queue = new Deque<()->Void>();
-	final mutex = new Mutex();
-	final threadTimeout:Float;
-
-	/**
-		Create a new thread pool with `threadsCount` threads.
-
-		If a worker thread does not receive a task for `threadTimeout` seconds it
-		is terminated.
-	**/
-	public function new(maxThreadsCount:Int, threadTimeout:Float = 60):Void {
-		if(maxThreadsCount < 1)
-			throw new ThreadPoolException('ElasticThreadPool needs maxThreadsCount to be at least 1.');
-		this.maxThreadsCount = maxThreadsCount;
-		this.threadTimeout = threadTimeout;
-	}
-
-	/**
-		Submit a task to run in a thread.
-
-		Throws an exception if the pool is shut down.
-	**/
-	public function run(task:()->Void):Void {
-		if(_isShutdown)
-			throw new ThreadPoolException('Task is rejected. Thread pool is shut down.');
-		if(task == null)
-			throw new ThreadPoolException('Task to run must not be null.');
-
-		mutex.acquire();
-		var submitted = false;
-		var deadWorker = null;
-		for(worker in pool) {
-			if(deadWorker == null && worker.dead) {
-				deadWorker = worker;
-			}
-			if(worker.task == null) {
-				submitted = true;
-				worker.wakeup(task);
-				break;
-			}
-		}
-		if(!submitted) {
-			if(deadWorker != null) {
-				deadWorker.wakeup(task);
-			} else if(pool.length < maxThreadsCount) {
-				var worker = new Worker(queue, threadTimeout);
-				pool.push(worker);
-				worker.wakeup(task);
-			} else {
-				queue.add(task);
-			}
-		}
-		mutex.release();
-	}
-
-	/**
-		Initiates a shutdown.
-		All previously submitted tasks will be executed, but no new tasks will
-		be accepted.
-
-		Multiple calls to this method have no effect.
-	**/
-	public function shutdown():Void {
-		if(_isShutdown) return;
-		mutex.acquire();
-		_isShutdown = true;
-		for(worker in pool) {
-			worker.shutdown();
-		}
-		mutex.release();
-	}
-
-	function get_threadsCount():Int {
-		var result = 0;
-		for(worker in pool)
-			if(!worker.dead)
-				++result;
-		return result;
-	}
-}
-
-private class Worker {
-	public var task(default,null):Null<()->Void>;
-	public var dead(default,null) = false;
-
-	final deathMutex = new Mutex();
-	final waiter = new Lock();
-	final queue:Deque<()->Void>;
-	final timeout:Float;
-	var thread:Thread;
-	var isShutdown = false;
-
-	public function new(queue:Deque<()->Void>, timeout:Float) {
-		this.queue = queue;
-		this.timeout = timeout;
-		start();
-	}
-
-	public function wakeup(task:()->Void) {
-		deathMutex.acquire();
-		if(dead)
-			start();
-		this.task = task;
-		waiter.release();
-		deathMutex.release();
-	}
-
-	public function shutdown() {
-		isShutdown = true;
-		waiter.release();
-	}
-
-	function start() {
-		dead = false;
-		thread = Thread.create(loop);
-	}
-
-	function loop() {
-		try {
-			while(waiter.wait(timeout)) {
-				switch task {
-					case null:
-						if(isShutdown)
-							break;
-					case fn:
-						fn();
-						//if more tasks were added while all threads were busy
-						while(true) {
-							switch queue.pop(false) {
-								case null: break;
-								case fn: fn();
-							}
-						}
-						task = null;
-				}
-			}
-			deathMutex.acquire();
-			//in case a task was submitted right after the lock timed out
-			if(task != null)
-				start()
-			else
-				dead = true;
-			deathMutex.release();
-		} catch(e) {
-			task = null;
-			start();
-			throw e;
-		}
-	}
-}

+ 0 - 291
std/sys/thread/EventLoop.hx

@@ -1,291 +0,0 @@
-package sys.thread;
-
-/**
-	When an event loop has an available event to execute.
-**/
-enum NextEventTime {
-	/** There's already an event waiting to be executed */
-	Now;
-	/** No new events are expected. */
-	Never;
-	/**
-		An event is expected to arrive at any time.
-		If `time` is specified, then the event will be ready at that time for sure.
-	*/
-	AnyTime(time:Null<Float>);
-	/** An event is expected to be ready for execution at `time`. */
-	At(time:Float);
-}
-
-/**
-	An event loop implementation used for `sys.thread.Thread`
-**/
-@:coreApi
-class EventLoop {
-	final mutex = new Mutex();
-	final oneTimeEvents = new Array<Null<()->Void>>();
-	var oneTimeEventsIdx = 0;
-	final waitLock = new Lock();
-	var promisedEventsCount = 0;
-	var regularEvents:Null<RegularEvent>;
-	var isMainThread:Bool;
-	static var CREATED : Bool;
-
-	public function new():Void {
-		isMainThread = !CREATED;
-		CREATED = true;
-	}
-
-	/**
-		Schedule event for execution every `intervalMs` milliseconds in current loop.
-	**/
-	public function repeat(event:()->Void, intervalMs:Int):EventHandler {
-		mutex.acquire();
-		var interval = 0.001 * intervalMs;
-		var event = new RegularEvent(event, Sys.time() + interval, interval);
-		inline insertEventByTime(event);
-		waitLock.release();
-		mutex.release();
-		return event;
-	}
-
-	function insertEventByTime(event:RegularEvent):Void {
-		switch regularEvents {
-			case null:
-				regularEvents = event;
-			case current:
-				var previous = null;
-				while(true) {
-					if(current == null) {
-						previous.next = event;
-						event.previous = previous;
-						break;
-					} else if(event.nextRunTime < current.nextRunTime) {
-						event.next = current;
-						current.previous = event;
-						switch previous {
-							case null:
-								regularEvents = event;
-								case _:
-								event.previous = previous;
-								previous.next = event;
-								current.previous = event;
-						}
-						break;
-					} else {
-						previous = current;
-						current = current.next;
-					}
-				}
-		}
-	}
-
-	/**
-		Prevent execution of a previously scheduled event in current loop.
-	**/
-	public function cancel(eventHandler:EventHandler):Void {
-		mutex.acquire();
-		var event:RegularEvent = eventHandler;
-		event.cancelled = true;
-		if(regularEvents == event) {
-			regularEvents = event.next;
-		}
-		switch event.next {
-			case null:
-			case e: e.previous = event.previous;
-		}
-		switch event.previous {
-			case null:
-			case e: e.next = event.next;
-		}
-		event.next = event.previous = null;
-		mutex.release();
-	}
-
-	/**
-		Notify this loop about an upcoming event.
-		This makes the thread stay alive and wait for as many events as the number of
-		times `.promise()` was called. These events should be added via `.runPromised()`.
-	**/
-	public function promise():Void {
-		mutex.acquire();
-		++promisedEventsCount;
-		mutex.release();
-	}
-
-	/**
-		Execute `event` as soon as possible.
-	**/
-	public function run(event:()->Void):Void {
-		mutex.acquire();
-		oneTimeEvents[oneTimeEventsIdx++] = event;
-		waitLock.release();
-		mutex.release();
-	}
-
-	/**
-		Add previously promised `event` for execution.
-	**/
-	public function runPromised(event:()->Void):Void {
-		mutex.acquire();
-		oneTimeEvents[oneTimeEventsIdx++] = event;
-		--promisedEventsCount;
-		waitLock.release();
-		mutex.release();
-	}
-
-	/**
-		Executes all pending events.
-
-		The returned time stamps can be used with `Sys.time()` for calculations.
-
-		Depending on a target platform this method may be non-reentrant. It must
-		not be called from event callbacks.
-	**/
-	public function progress():NextEventTime {
-		return switch __progress(Sys.time(), [], []) {
-			case {nextEventAt:-2}: Now;
-			case {nextEventAt:-1, anyTime:false}: Never;
-			case {nextEventAt:-1, anyTime:true}: AnyTime(null);
-			case {nextEventAt:time, anyTime:true}: AnyTime(time);
-			case {nextEventAt:time, anyTime:false}: At(time);
-		}
-	}
-
-	/**
-		Blocks until a new event is added or `timeout` (in seconds) to expires.
-
-		Depending on a target platform this method may also automatically execute arriving
-		events while waiting. However if any event is executed it will stop waiting.
-
-		Returns `true` if more events are expected.
-		Returns `false` if no more events expected.
-
-		Depending on a target platform this method may be non-reentrant. It must
-		not be called from event callbacks.
-	**/
-	public function wait(?timeout:Float):Bool {
-		return waitLock.wait(timeout);
-	}
-
-	/**
-		Execute all pending events.
-		Wait and execute as many events as the number of times `promise()` was called.
-		Runs until all repeating events are cancelled and no more events are expected.
-
-		Depending on a target platform this method may be non-reentrant. It must
-		not be called from event callbacks.
-	**/
-	public function loop():Void {
-		var recycleRegular = [];
-		var recycleOneTimers = [];
-		while(true) {
-			var r = __progress(Sys.time(), recycleRegular, recycleOneTimers);
-			switch r {
-				case {nextEventAt:-2}:
-				case {nextEventAt:-1, anyTime:false}:
-					break;
-				case {nextEventAt:-1, anyTime:true}:
-					waitLock.wait();
-				case {nextEventAt:time}:
-					var timeout = time - Sys.time();
-					waitLock.wait(Math.max(0, timeout));
-			}
-		}
-	}
-
-	/**
-		`.progress` implementation with a reusable array for internal usage.
-		The `nextEventAt` field of the return value denotes when the next event
-		is expected to run:
-		* -1 - never
-		* -2 - now
-		* other values - at specified time
-	**/
-	inline function __progress(now:Float, recycleRegular:Array<RegularEvent>, recycleOneTimers:Array<()->Void>):{nextEventAt:Float, anyTime:Bool} {
-		var regularsToRun = recycleRegular;
-		var eventsToRunIdx = 0;
-		// When the next event is expected to run
-		var nextEventAt:Float = -1;
-
-		mutex.acquire();
-		//reset waitLock
-		while(waitLock.wait(0.0)) {}
-		// Collect regular events to run
-		var current = regularEvents;
-		while(current != null) {
-			if(current.nextRunTime <= now) {
-				regularsToRun[eventsToRunIdx++] = current;
-				current.nextRunTime += current.interval;
-				nextEventAt = -2;
-			} else if(nextEventAt == -1 || current.nextRunTime < nextEventAt) {
-				nextEventAt = current.nextRunTime;
-			}
-			current = current.next;
-		}
-		mutex.release();
-
-		// Run regular events
-		for(i in 0...eventsToRunIdx) {
-			if(!regularsToRun[i].cancelled)
-				regularsToRun[i].run();
-			regularsToRun[i] = null;
-		}
-		eventsToRunIdx = 0;
-
-		var oneTimersToRun = recycleOneTimers;
-		// Collect pending one-time events
-		mutex.acquire();
-		for(i => event in oneTimeEvents) {
-			switch event {
-				case null:
-					break;
-				case _:
-					oneTimersToRun[eventsToRunIdx++] = event;
-					oneTimeEvents[i] = null;
-			}
-		}
-		oneTimeEventsIdx = 0;
-		var hasPromisedEvents = promisedEventsCount > 0;
-		mutex.release();
-
-		//run events
-		for(i in 0...eventsToRunIdx) {
-			oneTimersToRun[i]();
-			oneTimersToRun[i] = null;
-		}
-
-		// run main events
-		if( isMainThread ) {
-			var next = @:privateAccess haxe.MainLoop.tick();
-			if( haxe.MainLoop.hasEvents() ) {
-				eventsToRunIdx++;
-				if( nextEventAt > next )
-					nextEventAt = next;
-			}
-		}
-
-		// Some events were executed. They could add new events to run.
-		if(eventsToRunIdx > 0) {
-			nextEventAt = -2;
-		}
-		return {nextEventAt:nextEventAt, anyTime:hasPromisedEvents}
-	}
-}
-
-abstract EventHandler(RegularEvent) from RegularEvent to RegularEvent {}
-
-private class RegularEvent {
-	public var nextRunTime:Float;
-	public final interval:Float;
-	public final run:()->Void;
-	public var next:Null<RegularEvent>;
-	public var previous:Null<RegularEvent>;
-	public var cancelled:Bool = false;
-
-	public function new(run:()->Void, nextRunTime:Float, interval:Float) {
-		this.run = run;
-		this.nextRunTime = nextRunTime;
-		this.interval = interval;
-	}
-}

+ 0 - 116
std/sys/thread/FixedThreadPool.hx

@@ -1,116 +0,0 @@
-/*
- * Copyright (C)2005-2019 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-package sys.thread;
-
-#if (!target.threaded)
-#error "This class is not available on this target"
-#end
-
-import haxe.Exception;
-
-/**
-	Thread pool with a constant amount of threads.
-	Threads in the pool will exist until the pool is explicitly shut down.
-**/
-@:coreApi
-class FixedThreadPool implements IThreadPool {
-	/* Amount of threads in this pool. */
-	public var threadsCount(get,null):Int;
-	function get_threadsCount():Int return threadsCount;
-
-	/** Indicates if `shutdown` method of this pool has been called. */
-	public var isShutdown(get,never):Bool;
-	var _isShutdown = false;
-	function get_isShutdown():Bool return _isShutdown;
-
-	final pool:Array<Worker>;
-	final poolMutex = new Mutex();
-	final queue = new Deque<()->Void>();
-
-	/**
-		Create a new thread pool with `threadsCount` threads.
-	**/
-	public function new(threadsCount:Int):Void {
-		if(threadsCount < 1)
-			throw new ThreadPoolException('FixedThreadPool needs threadsCount to be at least 1.');
-		this.threadsCount = threadsCount;
-		pool = [for(i in 0...threadsCount) new Worker(queue)];
-	}
-
-	/**
-		Submit a task to run in a thread.
-
-		Throws an exception if the pool is shut down.
-	**/
-	public function run(task:()->Void):Void {
-		if(_isShutdown)
-			throw new ThreadPoolException('Task is rejected. Thread pool is shut down.');
-		if(task == null)
-			throw new ThreadPoolException('Task to run must not be null.');
-		queue.add(task);
-	}
-
-	/**
-		Initiates a shutdown.
-		All previously submitted tasks will be executed, but no new tasks will
-		be accepted.
-
-		Multiple calls to this method have no effect.
-	**/
-	public function shutdown():Void {
-		if(_isShutdown) return;
-		_isShutdown = true;
-		for(_ in pool) {
-			queue.add(shutdownTask);
-		}
-	}
-
-	static function shutdownTask():Void {
-		throw new ShutdownException('');
-	}
-}
-
-private class ShutdownException extends Exception {}
-
-private class Worker {
-	var thread:Thread;
-	final queue:Deque<Null<()->Void>>;
-
-	public function new(queue:Deque<Null<()->Void>>) {
-		this.queue = queue;
-		thread = Thread.create(loop);
-	}
-
-	function loop() {
-		try {
-			while(true) {
-				var task = queue.pop(true);
-				task();
-			}
-		} catch(_:ShutdownException) {
-		} catch(e) {
-			thread = Thread.create(loop);
-			throw e;
-		}
-	}
-}

+ 0 - 9
std/sys/thread/NoEventLoopException.hx

@@ -1,9 +0,0 @@
-package sys.thread;
-
-import haxe.Exception;
-
-class NoEventLoopException extends Exception {
-	public function new(msg:String = 'Event loop is not available. Refer to sys.thread.Thread.runWithEventLoop.', ?previous:Exception) {
-		super(msg, previous);
-	}
-}

+ 98 - 32
std/sys/thread/Thread.hx

@@ -26,58 +26,124 @@ package sys.thread;
 #error "This class is not available on this target"
 #error "This class is not available on this target"
 #end
 #end
 
 
-private typedef ThreadImpl = {};
+class Thread {
 
 
-extern abstract Thread(ThreadImpl) from ThreadImpl {
-	/**
-		Event loop of this thread (if available).
+	static var threads : Array<Thread>;
+	static var mutex : Mutex;
+	static var mainThread : Thread;
 
 
-		Note that by default event loop is only available in the main thread.
-		To setup an event loop in other threads use `sys.thread.Thread.runWithEventLoop`
-		or create new threads with built-in event loops using `sys.thread.Thread.createWithEventLoop`
-	**/
-	public var events(get,never):EventLoop;
+	var impl : ThreadImpl;
 
 
 	/**
 	/**
-		Send a message to the thread queue. This message can be read by using `readMessage`.
+		Event loop of this thread.
 	**/
 	**/
-	public function sendMessage(msg:Dynamic):Void;
+	public var events(default,null) : haxe.EventLoop;
 
 
 	/**
 	/**
-		Returns the current thread.
+		Tells if we needs to wait for the thread to terminate before we stop the main loop (default:true).
 	**/
 	**/
-	public static function current():Thread;
+	public var isBlocking : Bool = true;
 
 
 	/**
 	/**
-		Creates a new thread that will execute the `job` function, then exit.
-
-		This function does not setup an event loop for a new thread.
+		Allows to query or change the name of the thread. On some platforms this might allow debugger to identify threads.
 	**/
 	**/
-	public static function create(job:()->Void):Thread;
+	public var name(default,set) : Null<String>;
 
 
-	/**
-		Simply execute `job` if current thread already has an event loop.
+	function new(impl) {
+		this.impl = impl;
+		if( impl != null ) this.name = ThreadImpl.getName(impl);
+	}
+
+	function set_name(n) {
+		name = n;
+		if( impl != null ) ThreadImpl.setName(impl,name == null ? "" : name);
+		return n;
+	}
 
 
-		But if current thread does not have an event loop: setup event loop,
-		run `job` and then destroy event loop. And in this case this function
-		does not return until no more events left to run.
+	/**
+		Returns the current thread.
 	**/
 	**/
-	public static function runWithEventLoop(job:()->Void):Void;
+	public static function current():Thread {
+		var impl = ThreadImpl.current();
+		if( impl == mainThread.impl )
+			return mainThread;
+		mutex.acquire();
+		for( t in threads )
+			if( t.impl == impl ) {
+				mutex.release();
+				return t;
+			}
+		var t = new Thread(impl);
+		// keep t.events = null because this is an unkown thread (not main and not created with create())
+		threads.push(t);
+		mutex.release();
+		return t;
+	}
 
 
 	/**
 	/**
-		This is logically equal to `Thread.create(() -> Thread.runWithEventLoop(job));`
+		Returns the main thread
 	**/
 	**/
-	public static function createWithEventLoop(job:()->Void):Thread;
+	public static inline function main() {
+		return mainThread;
+	}
 
 
 	/**
 	/**
-		Reads a message from the thread queue. If `block` is true, the function
-		blocks until a message is available. If `block` is false, the function
-		returns `null` if no message is available.
+		Creates a new thread that will execute the `job` function, then exit after all events are processed.
+		You can specify a custom exception handler `onAbort` or else `Thread.onAbort` will be called.
 	**/
 	**/
-	public static function readMessage(block:Bool):Dynamic;
+	public static function create(job:()->Void,?onAbort):Thread {
+		mutex.acquire();
+		var t = new Thread(null);
+		t.events = new haxe.EventLoop();
+		threads.push(t);
+		mutex.release();
+		if( onAbort != null )
+			t.onAbort = onAbort;
+		t.impl = ThreadImpl.create(function() {
+			t.impl = ThreadImpl.current();
+			var exception = null;
+			try {
+				job();
+				t.events.loop();
+			} catch( e ) {
+				exception = e;
+			}
+			mutex.acquire();
+			threads.remove(t);
+			mutex.release();
+			if( exception != null )
+				t.onAbort(exception);
+		});
+		return t;
+	}
 
 
 	/**
 	/**
-		Run event loop of the current thread
+		This function is called when an uncaught exception aborted a thread.
+		The error will be printed to stdout but this function can be redefined.
 	**/
 	**/
-	private static function processEvents():Void;
-}
+	public dynamic function onAbort(e:haxe.Exception) {
+		var name = this.name;
+		if( name == null ) name = "" else name = " "+name;
+		Sys.println("THREAD"+name+" ABORTED : "+e.message+haxe.CallStack.toString(e.stack));
+	}
+
+	static function hasBlocking() {
+		// let's check if we have blocking threads running
+		mutex.acquire();
+		for( t in threads )
+			if( t.isBlocking ) {
+				mutex.release();
+				return true;
+			}
+		mutex.release();
+		return false;
+	}
+
+	static function __init__() {
+		mutex = new Mutex();
+		threads = [];
+		mainThread = new Thread(ThreadImpl.current());
+		mainThread.events = haxe.EventLoop.main;
+	}
+
+}

+ 0 - 6
std/sys/thread/ThreadPoolException.hx

@@ -1,6 +0,0 @@
-package sys.thread;
-
-import haxe.Exception;
-
-class ThreadPoolException extends Exception {
-}