Forráskód Böngészése

Refactor event loops (#12388)

Nicolas Cannasse 1 napja
szülő
commit
70820f181c
35 módosított fájl, 924 hozzáadás és 2377 törlés
  1. 1 1
      extra/haxelib_src
  2. 1 12
      src/typing/finalization.ml
  3. 0 189
      std/cpp/_std/sys/thread/Thread.hx
  4. 48 0
      std/cpp/_std/sys/thread/ThreadImpl.hx
  5. 0 203
      std/eval/_std/sys/thread/EventLoop.hx
  6. 0 105
      std/eval/_std/sys/thread/Thread.hx
  7. 56 0
      std/eval/_std/sys/thread/ThreadImpl.hx
  8. 0 4
      std/eval/luv/Loop.hx
  9. 0 2
      std/eval/vm/NativeThread.hx
  10. 33 160
      std/haxe/EntryPoint.hx
  11. 370 0
      std/haxe/EventLoop.hx
  12. 5 177
      std/haxe/MainLoop.hx
  13. 2 19
      std/haxe/Timer.hx
  14. 0 204
      std/hl/_std/sys/thread/Thread.hx
  15. 59 0
      std/hl/_std/sys/thread/ThreadImpl.hx
  16. 0 176
      std/jvm/_std/sys/thread/Thread.hx
  17. 74 0
      std/jvm/_std/sys/thread/ThreadImpl.hx
  18. 0 188
      std/neko/_std/sys/thread/Thread.hx
  19. 50 0
      std/neko/_std/sys/thread/ThreadImpl.hx
  20. 0 164
      std/python/_std/sys/thread/Thread.hx
  21. 16 21
      std/python/_std/sys/thread/ThreadImpl.hx
  22. 0 198
      std/sys/thread/ElasticThreadPool.hx
  23. 0 291
      std/sys/thread/EventLoop.hx
  24. 0 116
      std/sys/thread/FixedThreadPool.hx
  25. 0 9
      std/sys/thread/NoEventLoopException.hx
  26. 157 30
      std/sys/thread/Thread.hx
  27. 43 0
      std/sys/thread/ThreadImpl.hx
  28. 0 6
      std/sys/thread/ThreadPoolException.hx
  29. 2 2
      tests/misc/eventLoop/Main.hx
  30. 2 2
      tests/misc/projects/eventLoop/Main.hx
  31. 1 1
      tests/misc/projects/eventLoop/Main2.hx
  32. 0 32
      tests/threads/src/cases/TestElasticThreadPool.hx
  33. 3 54
      tests/threads/src/cases/TestEvents.hx
  34. 0 10
      tests/threads/src/cases/TestFixedThreadPool.hx
  35. 1 1
      tests/threads/src/cases/TestTimer.hx

+ 1 - 1
extra/haxelib_src

@@ -1 +1 @@
-Subproject commit 659d200a08200b8dbed685e7fa0aa5b02fb44a6b
+Subproject commit b306a2226ace9143dc0fb14541be2ae87a89a744

+ 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();
-	}
-}

+ 56 - 0
std/eval/_std/sys/thread/ThreadImpl.hx

@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+abstract ThreadImpl(NativeThread) {
+
+	public static inline function current():ThreadImpl {
+		return cast NativeThread.self();
+	}
+
+	public static inline function create(job:()->Void):ThreadImpl {
+		return cast new NativeThread(job);
+	}
+
+	public static function getName( t : ThreadImpl ) {
+		return null;
+	}
+
+	public static function setName( t : ThreadImpl, name : String ) {
+	}
+
+	inline function id() {
+		return this.id();
+	}
+
+	@:op(A == B) static inline function eq( a : ThreadImpl, b : ThreadImpl ) {
+		return a.id() == b.id();
+	}
+
+	@:op(A != B) static inline function neq( a : ThreadImpl, b : ThreadImpl ) {
+		return a.id() != b.id();
+	}
+
+}

+ 0 - 4
std/eval/luv/Loop.hx

@@ -30,10 +30,6 @@ enum abstract LoopOption<T>(Int) {
 	expected.
 	expected.
 **/
 **/
 @:coreType abstract Loop {
 @:coreType abstract Loop {
-	@:from
-	static inline function fromHaxeEventLoop(events:sys.thread.EventLoop):Loop {
-		return events.handle;
-	}
 
 
 	/**
 	/**
 		Returns the default event loop.
 		Returns the default event loop.

+ 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 = haxe.EventLoop.main.getNextTick();
+			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
 	}
 	}
 }
 }

+ 370 - 0
std/haxe/EventLoop.hx

@@ -0,0 +1,370 @@
+package haxe;
+
+import haxe.EntryPoint;
+
+class Event {
+
+	var prev : Event;
+	var next : Event;
+	var events : EventLoop;
+	var callb : Void -> Void;
+	/**
+		The event priority. Events will be executed in order of priority (highest first).
+	**/
+	public var priority : Int;
+	/**
+		Tells if an event is blocking. It means the event loop won't return from `loop()` until this event has been stopped.
+	**/
+	public var isBlocking : Bool = true;
+	var toRemove : Bool;
+	var nextRun : Float = Math.NEGATIVE_INFINITY;
+
+	function new(events, callb, p) {
+		this.events = events;
+		this.callb = callb;
+		this.priority = p;
+	}
+
+	/**
+		Delay the execution of the event for the given time, in seconds.
+		If t is null, the event will be run at next event loop.
+	**/
+	public function delay(t:Null<Float>,fromLastRun=false) {
+		nextRun = t == null ? Math.NEGATIVE_INFINITY : (fromLastRun && Math.isFinite(nextRun) ? nextRun : haxe.Timer.stamp()) + t;
+	}
+
+	/**
+		Stop this event from repeating.
+	**/
+	public function stop() {
+		@:privateAccess events.remove(this);
+	}
+
+}
+
+
+/**
+	Handles async events for all threads
+**/
+@:access(haxe.Event)
+class EventLoop {
+
+	/**
+		This is the main thread event loop.
+	**/
+	public static var main(get,null) : EventLoop;
+
+	/**
+		This is the current thread event loop. For platforms that doesn't support threads
+		it is the same as `main`.
+	**/
+	public static var current(get,never) : EventLoop;
+
+	var events : Event;
+	var inLoop : Bool;
+	var hasPendingRemove : Bool;
+	#if target.threaded
+	var mutex : sys.thread.Mutex;
+	var lockTime : sys.thread.Lock;
+	#end
+
+	public function new() {
+		#if target.threaded
+		mutex = new sys.thread.Mutex();
+		lockTime = new sys.thread.Lock();
+		#end
+	}
+
+	/**
+		Runs until all the blocking events have been stopped.
+		If this is called on the main thread, also wait for all blocking threads to finish.
+	**/
+	public function loop() {
+		while( hasEvents(true) || (this == main && hasRunningThreads()) ) {
+			var time = getNextTick();
+			if( time > 0 ) {
+				wait(time);
+				continue;
+			}
+			loopOnce();
+		}
+	}
+
+	inline function wakeup() {
+		#if target.threaded
+		lockTime.release();
+		#end
+	}
+
+	inline function wait( time : Float ) {
+		#if target.threaded
+		lockTime.wait(time);
+		#elseif sys
+		Sys.sleep(time);
+		#end
+	}
+
+	inline function lock() {
+		#if target.threaded
+		mutex.acquire();
+		#end
+	}
+
+	inline function unlock() {
+		#if target.threaded
+		mutex.release();
+		#end
+	}
+
+	/**
+		Perform an update of pending events.
+	**/
+	public function loopOnce() {
+		lock();
+		sortEvents();
+		var current = events; // protect from further add()
+		inLoop = true;
+		unlock();
+
+		// if inLoop turns false, stop because we had reentrency
+		var time = haxe.Timer.stamp();
+		while( inLoop && current != null ) {
+			var n = current.next;
+			if( current.nextRun <= time && !current.toRemove )
+				current.callb();
+			current = n;
+		}
+
+		lock();
+		inLoop = false;
+		if( hasPendingRemove ) {
+			hasPendingRemove = false;
+			var e = events;
+			while( e != null ) {
+				var n = e.next;
+				if( e.toRemove ) remove(e);
+				e = n;
+			}
+		}
+		unlock();
+	}
+
+	/**
+		Add a callback to be run at each loop of the event loop.
+	**/
+	public function add( callb : Void -> Void, priority = 0 ) : Event {
+		var e = new Event(this,callb,priority);
+		lock();
+		if( events != null )
+			events.prev = e;
+		e.next = events;
+		events = e;
+		wakeup();
+		unlock();
+		return e;
+	}
+
+	/**
+		Add a callback to be run every `delay` seconds until stopped
+	**/
+	public function addTimer( callb : Void -> Void, delay : Float, priority = 0 ) : Event {
+		var e : Event = null;
+		e = new Event(this,function() { e.delay(delay,true); callb(); },priority);
+		e.delay(delay);
+		lock();
+		if( events != null )
+			events.prev = e;
+		e.next = events;
+		events = e;
+		wakeup();
+		unlock();
+		return e;
+	}
+
+	@:deprecated @:noCompletion public function repeat( callb, delay : Int ) {
+		return addTimer(callb,delay/1000);
+	}
+
+	@:deprecated @:noCompletion public function cancel( e : Event ) {
+		e.stop();
+	}
+
+	/**
+		Add a function to be run once at next loop of the event loop.
+	**/
+	public function run( callb : Void -> Void, priority = 0 ) : Event {
+		var e : Event = null;
+		e = add(function() { e.stop(); callb(); }, priority);
+		return e;
+	}
+
+	function remove( e : Event ) {
+		lock();
+		if( inLoop ) {
+			// prevent remove while in loopOnce()
+			e.toRemove = true;
+			hasPendingRemove = true;
+			unlock();
+			return;
+		}
+		if( events == e )
+			events = e.next;
+		else if( e.prev != null )
+			e.prev.next = e.next;
+		if( e.next != null ) {
+			e.next.prev = e.prev;
+			e.next = null;
+		}
+		e.prev = null;
+		wakeup();
+		unlock();
+	}
+
+	function getNextTick() : Float {
+		lock();
+		if( events == null ) {
+			unlock();
+			return 1e9;
+		}
+		var now = haxe.Timer.stamp();
+		var e = events;
+		var next = Math.POSITIVE_INFINITY;
+		while( e != null ) {
+			if( e.nextRun <= now ) {
+				unlock();
+				return -1;
+			}
+			if( e.nextRun < next )
+				next = e.nextRun;
+			e = e.next;
+		}
+		unlock();
+		return next - now;
+	}
+
+	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
+		lock();
+		var list = events;
+
+		if (list == null) {
+			unlock();
+			return;
+		}
+
+		var insize = 1, nmerges, psize = 0, qsize = 0;
+		var p, q, e, tail:Event;
+
+		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
+		events = list;
+		unlock();
+	}
+
+	/**
+		Tells if we currently have blocking unfinished threads.
+	**/
+	public static function hasRunningThreads() {
+		#if !target.threaded
+		return false;
+		#else
+		return @:privateAccess sys.thread.Thread.hasBlocking();
+		#end
+	}
+
+	/**
+		Tells if the event loop has remaining events.
+		If blocking is set to true, only check if it has remaining blocking events.
+	**/
+	public function hasEvents( blocking : Bool = true ) {
+		if( !blocking )
+			return events != null;
+		lock();
+		var e = events;
+		while( e != null ) {
+			if( e.isBlocking ) {
+				unlock();
+				return true;
+			}
+			e = e.next;
+		}
+		unlock();
+		return false;
+	}
+
+	/**
+		Add a task to be run either on another thread or as part of the main event loop if the
+		platform does not support threads.
+	**/
+	public static function addTask( f : Void -> Void, blocking = true ) {
+		#if target.threaded
+		sys.thread.Thread.create(f).isBlocking = blocking;
+		#else
+		main.add(f).isBlocking = blocking;
+		#end
+	}
+
+	static function get_current() {
+		#if target.threaded
+		var events = sys.thread.Thread.current().events;
+		if( events == null ) throw "The current thread doesn't have an event loop.";
+		return events;
+		#else
+		return main;
+		#end
+	}
+
+	static function get_main() {
+		if( main == null ) main = new EventLoop();
+		return main;
+	}
+
+
+}

+ 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;
-	}
-}

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

@@ -0,0 +1,74 @@
+/*
+ * 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;
+		setDaemon(true);
+		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);
-	}
-}

+ 16 - 21
std/sys/thread/IThreadPool.hx → std/python/_std/sys/thread/ThreadImpl.hx

@@ -22,30 +22,25 @@
 
 
 package sys.thread;
 package sys.thread;
 
 
-/**
-	A thread pool interface.
-**/
-interface IThreadPool {
+import python.lib.threading.Thread as 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 function current() : ThreadImpl {
+		return cast python.lib.Threading.current_thread();
+	}
 
 
-	/**
-		Submit a task to run in a thread.
+	public static function create(callb:Void->Void):ThreadImpl {
+		var t = new NativeThread({target:callb, daemon: true});
+		t.start();
+		return cast t;
+	}
 
 
-		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 - 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);
-	}
-}

+ 157 - 30
std/sys/thread/Thread.hx

@@ -26,58 +26,185 @@ 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;
+	var messages : Deque<Dynamic>;
 
 
 	/**
 	/**
-		Send a message to the thread queue. This message can be read by using `readMessage`.
+		The events loop of this thread.
+		If this is a native thread, the events will be null.
 	**/
 	**/
-	public function sendMessage(msg:Dynamic):Void;
+	public var events(default,null) : 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.
+		Allows to query or change the name of the thread. On some platforms this might allow debugger to identify threads.
+	**/
+	public var name(default,set) : Null<String>;
 
 
-		This function does not setup an event loop for a new thread.
+	/**
+		Tells if a thread is a native thread that is not managed by Haxe.
+		See `Thread.current` for details.
 	**/
 	**/
-	public static function create(job:()->Void):Thread;
+	public var isNative(default,null) : Bool;
+
+	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;
+	}
+
+	public function toString() {
+		return "Thread#"+(name ?? Std.string(impl));
+	}
+
+	public function sendMessage( msg : Dynamic ) {
+		if( messages == null ) {
+			mutex.acquire();
+			if( messages == null ) messages = new Deque();
+			mutex.release();
+		}
+		messages.add(msg);
+	}
+
+	public function disposeNative() {
+		if( !isNative ) return;
+		dispose();
+	}
+
+
+	function dispose() {
+		mutex.acquire();
+		threads.remove(this);
+		mutex.release();
+		currentTLS.value = null;
+	}
+
+	public static function readMessage( blocking : Bool ) : Null<Dynamic> {
+		var t = current();
+		if( t.messages == null ) {
+			mutex.acquire();
+			if( t.messages == null ) t.messages = new Deque();
+			mutex.release();
+		}
+		return t.messages.pop(blocking);
+	}
+
+	static var currentTLS : Tls<Thread>;
 
 
 	/**
 	/**
-		Simply execute `job` if current thread already has an event loop.
+		Returns the current thread.
+		If you are calling this function from a native thread that is not the main thread and was not created by `Thread.create`, this will return you
+		a native thread with a `null` EvenLoop and `isNative` set to true. You need to call `disposeNative()` on such value on thread termination.
+	**/
+	public static function current():Thread {
+		var t = currentTLS.value;
+		if( t != null )
+			return t;
+		var impl = ThreadImpl.current();
+		var t = new Thread(impl);
+		t.isNative = true;
+		mutex.acquire();
+		threads.push(t);
+		mutex.release();
+		currentTLS.value = t;
+		return t;
+	}
 
 
-		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 main thread
 	**/
 	**/
-	public static function runWithEventLoop(job:()->Void):Void;
+	public static inline function main() {
+		return mainThread;
+	}
 
 
 	/**
 	/**
-		This is logically equal to `Thread.create(() -> Thread.runWithEventLoop(job));`
+		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 createWithEventLoop(job:()->Void):Thread;
+	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();
+			currentTLS.value = t;
+			var exception = null;
+			try {
+				job();
+				t.events.loop();
+			} catch( e ) {
+				exception = e;
+			}
+			t.dispose();
+			@:privateAccess main().events.wakeup();
+			if( exception != null )
+				t.onAbort(exception);
+		});
+		return t;
+	}
 
 
 	/**
 	/**
-		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.
+		Returns a list of all currently running threads.
+		This excludes native threads which were created without Thread.create and have not been
+		registered with a call to Thread.current().
 	**/
 	**/
-	public static function readMessage(block:Bool):Dynamic;
+	public static function getAll() {
+		mutex.acquire();
+		var tl = threads.copy();
+		mutex.release();
+		return tl;
+	}
 
 
 	/**
 	/**
-		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 other that our calling thread
+		var me = current();
+		mutex.acquire();
+		for( t in threads )
+			if( t.impl != me.impl && t.isBlocking ) {
+				mutex.release();
+				return true;
+			}
+		mutex.release();
+		return false;
+	}
+
+	static function __init__() {
+		mutex = new Mutex();
+		mainThread = new Thread(ThreadImpl.current());
+		mainThread.name = "Main";
+		mainThread.events = haxe.EventLoop.main;
+		threads = [mainThread];
+		currentTLS = new Tls();
+		currentTLS.value = mainThread;
+	}
+
+}

+ 43 - 0
std/sys/thread/ThreadImpl.hx

@@ -0,0 +1,43 @@
+/*
+ * 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) {
+
+	public static function create( f : Void -> Void ) : ThreadImpl {
+		throw "Not implemented for this platform";
+	}
+
+	public static function current() : ThreadImpl {
+		throw "Not implemented for this platform";
+	}
+
+	public static function getName( t : ThreadImpl ) : Null<String> {
+		throw "Not implemented for this platform";
+	}
+
+	public static function setName( t : ThreadImpl, name : String ) {
+		throw "Not implemented for this platform";
+	}
+
+}

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

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

+ 2 - 2
tests/misc/eventLoop/Main.hx

@@ -7,7 +7,7 @@ class Main {
 			trace(count++);
 			trace(count++);
 			if( count == 10 ) {
 			if( count == 10 ) {
 				event.stop();
 				event.stop();
-				trace(haxe.MainLoop.hasEvents());
+				trace(haxe.EventLoop.main.hasEvents());
 			}
 			}
 		});
 		});
 
 
@@ -41,7 +41,7 @@ class Main {
 		Sys.sleep(0.3);
 		Sys.sleep(0.3);
 		#end
 		#end
 
 
-		haxe.MainLoop.addThread(function() {
+		haxe.EventLoop.addTask(function() {
 			var event : haxe.MainLoop.MainEvent = null;
 			var event : haxe.MainLoop.MainEvent = null;
 			var count = 0;
 			var count = 0;
 			event = haxe.MainLoop.add(function() {
 			event = haxe.MainLoop.add(function() {

+ 2 - 2
tests/misc/projects/eventLoop/Main.hx

@@ -7,7 +7,7 @@ class Main {
 			trace(count++);
 			trace(count++);
 			if( count == 10 ) {
 			if( count == 10 ) {
 				event.stop();
 				event.stop();
-				trace(haxe.MainLoop.hasEvents());
+				trace(haxe.EventLoop.main.hasEvents());
 			}
 			}
 		});
 		});
 
 
@@ -41,7 +41,7 @@ class Main {
 		Sys.sleep(0.3);
 		Sys.sleep(0.3);
 		#end
 		#end
 
 
-		haxe.MainLoop.addThread(function() {
+		haxe.EventLoop.addTask(function() {
 			var event : haxe.MainLoop.MainEvent = null;
 			var event : haxe.MainLoop.MainEvent = null;
 			var count = 0;
 			var count = 0;
 			event = haxe.MainLoop.add(function() {
 			event = haxe.MainLoop.add(function() {

+ 1 - 1
tests/misc/projects/eventLoop/Main2.hx

@@ -6,7 +6,7 @@ class Main2 {
 			trace(count++);
 			trace(count++);
 			if( count == 10 ) {
 			if( count == 10 ) {
 				event.stop();
 				event.stop();
-				trace(haxe.MainLoop.hasEvents());
+				trace(haxe.EventLoop.main.hasEvents());
 			}
 			}
 		});
 		});
 	}
 	}

+ 0 - 32
tests/threads/src/cases/TestElasticThreadPool.hx

@@ -1,32 +0,0 @@
-package cases;
-
-import sys.thread.IThreadPool;
-import sys.thread.ElasticThreadPool;
-
-class TestElasticThreadPool extends misc.TestThreadPoolBase {
-	function createThreadPool(count:Int):IThreadPool {
-		return new ElasticThreadPool(count);
-	}
-
-	function testThreadTimeout() {
-		var timeout = 0.1;
-		var pool = new ElasticThreadPool(3, timeout);
-
-		for(_ in 0...3)
-			pool.run(() -> Sys.sleep(0.2));
-
-		//by the end of this sleep all threads should be terminated as timed out
-		Sys.sleep(1);
-
-		equals(0, pool.threadsCount);
-
-		//check we still can run tasks after all threads were terminated
-		var lock = new Lock();
-		for(_ in 0...3)
-			pool.run(() -> {
-				Sys.sleep(0.2);
-				lock.release();
-			});
-		assertReleased(lock, 3);
-	}
-}

+ 3 - 54
tests/threads/src/cases/TestEvents.hx

@@ -27,25 +27,9 @@ class TestEvents extends utest.Test {
 		}, 100);
 		}, 100);
 	}
 	}
 
 
-	function testThreadRunWithEventLoop() {
-		var eventExecuted = false;
-		var lock = new sys.thread.Lock();
-		Thread.create(() -> {
-			var thread = Thread.current();
-			raises(
-				() -> thread.events.run(() -> {}),
-				sys.thread.NoEventLoopException
-			);
-			Thread.runWithEventLoop(() -> {
-				thread.events.run(lock.release);
-			});
-		});
-		isTrue(lock.wait(1.0));
-	}
-
 	function testRun(async:Async) {
 	function testRun(async:Async) {
 		var mainThread = Thread.current();
 		var mainThread = Thread.current();
-		Thread.createWithEventLoop(() -> {
+		Thread.create(() -> {
 			var childThread = Thread.current();
 			var childThread = Thread.current();
 			isTrue(mainThread != childThread);
 			isTrue(mainThread != childThread);
 			mainThread.events.run(() -> {
 			mainThread.events.run(() -> {
@@ -82,47 +66,12 @@ class TestEvents extends utest.Test {
 		//test in main thread
 		//test in main thread
 		test(mainThread, () -> {
 		test(mainThread, () -> {
 			//now test in a child thread
 			//now test in a child thread
-			Thread.createWithEventLoop(() -> {
+			Thread.create(() -> {
 				var childThread = Thread.current();
 				var childThread = Thread.current();
 				isTrue(childThread != mainThread);
 				isTrue(childThread != mainThread);
-				test(childThread, mainThread.events.run.bind(() -> async.done()));
+				test(childThread, mainThread.events.run.bind(() -> async.done(),0));
 			});
 			});
 		});
 		});
 	}
 	}
 
 
-	@:depends(testRun)
-	function testPromisedEvents(async:Async) {
-		var mainThread = Thread.current();
-		mainThread.events.promise();
-		// this thread is expected to wait for promised events
-		Thread.createWithEventLoop(() -> {
-			var eventsExecuted = 0;
-			var testThread = Thread.current();
-			testThread.events.promise(); // 1 promised event
-			// this thread will deliver promised events to the testThread
-			Thread.createWithEventLoop(() -> {
-				Sys.sleep(0.2);
-				testThread.events.promise(); // 2 promised events
-				testThread.events.runPromised(() -> {
-					++eventsExecuted;
-					isTrue(testThread == Thread.current());
-				});
-				testThread.events.promise(); // 3 promised events
-				Sys.sleep(0.2);
-				testThread.events.runPromised(() -> {
-					++eventsExecuted;
-					isTrue(testThread == Thread.current());
-				});
-				Sys.sleep(0.2);
-				testThread.events.runPromised(() -> {
-					++eventsExecuted;
-					isTrue(testThread == Thread.current());
-					mainThread.events.runPromised(() -> {
-						equals(3, eventsExecuted);
-						async.done();
-					});
-				});
-			});
-		});
-	}
 }
 }

+ 0 - 10
tests/threads/src/cases/TestFixedThreadPool.hx

@@ -1,10 +0,0 @@
-package cases;
-
-import sys.thread.IThreadPool;
-import sys.thread.FixedThreadPool;
-
-class TestFixedThreadPool extends misc.TestThreadPoolBase {
-	function createThreadPool(count:Int):IThreadPool {
-		return new FixedThreadPool(count);
-	}
-}

+ 1 - 1
tests/threads/src/cases/TestTimer.hx

@@ -58,7 +58,7 @@ class TestTimer extends utest.Test {
 		}
 		}
 
 
 		for(n in 0...10) {
 		for(n in 0...10) {
-			Thread.createWithEventLoop(work.bind(n));
+			Thread.create(work.bind(n));
 		}
 		}
 
 
 		//expect two messages with different types per thread
 		//expect two messages with different types per thread