2
0
Nicolas Cannasse 1 өдөр өмнө
parent
commit
70820f181c
35 өөрчлөгдсөн 924 нэмэгдсэн , 2377 устгасан
  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"]
 			with Not_found ->
 				[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
 		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
 			| [e] -> e
 			| _ -> 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.
 **/
 @:coreType abstract Loop {
-	@:from
-	static inline function fromHaxeEventLoop(events:sys.thread.EventLoop):Loop {
-		return events.handle;
-	}
 
 	/**
 		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;
 
-	@: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;
 
-#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()`.
 	This class can be redefined by custom frameworks so they can handle their own main loop logic.
 **/
 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 {
 		#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 {
 				setTimeoutNextTick();
 			}
-		} else {
-			setTimeoutNextTick();
-		}
-		#end
+			#end
+			haxe.EventLoop.main.loopOnce();
 		#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
-		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;
-				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
-		// no implementation available, let's exit immediately
+			haxe.EventLoop.main.loop();
 		#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;
 
-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 {
 
-	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 {
-		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;
 
 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
@@ -50,11 +46,8 @@ import sys.thread.EventLoop;
 class Timer {
 	#if (flash || js)
 	private var id:Null<Int>;
-	#elseif (target.threaded && !cppia)
-	var thread:Thread;
-	var eventHandler:EventHandler;
 	#else
-	private var event:MainLoop.MainEvent;
+	private var event:EventLoop.Event;
 	#end
 
 	/**
@@ -77,16 +70,8 @@ class Timer {
 		#elseif js
 		var me = this;
 		id = untyped setInterval(function() me.run(), time_ms);
-		#elseif (target.threaded && !cppia)
-		thread = Thread.current();
-		eventHandler = thread.events.repeat(() -> this.run(), time_ms);
 		#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
 	}
 
@@ -108,8 +93,6 @@ class Timer {
 		untyped clearInterval(id);
 		#end
 		id = null;
-		#elseif (target.threaded && !cppia)
-		thread.events.cancel(eventHandler);
 		#else
 		if (event != null) {
 			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;
 
-/**
-	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"
 #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++);
 			if( count == 10 ) {
 				event.stop();
-				trace(haxe.MainLoop.hasEvents());
+				trace(haxe.EventLoop.main.hasEvents());
 			}
 		});
 
@@ -41,7 +41,7 @@ class Main {
 		Sys.sleep(0.3);
 		#end
 
-		haxe.MainLoop.addThread(function() {
+		haxe.EventLoop.addTask(function() {
 			var event : haxe.MainLoop.MainEvent = null;
 			var count = 0;
 			event = haxe.MainLoop.add(function() {

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

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

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

@@ -6,7 +6,7 @@ class Main2 {
 			trace(count++);
 			if( count == 10 ) {
 				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);
 	}
 
-	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) {
 		var mainThread = Thread.current();
-		Thread.createWithEventLoop(() -> {
+		Thread.create(() -> {
 			var childThread = Thread.current();
 			isTrue(mainThread != childThread);
 			mainThread.events.run(() -> {
@@ -82,47 +66,12 @@ class TestEvents extends utest.Test {
 		//test in main thread
 		test(mainThread, () -> {
 			//now test in a child thread
-			Thread.createWithEventLoop(() -> {
+			Thread.create(() -> {
 				var childThread = Thread.current();
 				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) {
-			Thread.createWithEventLoop(work.bind(n));
+			Thread.create(work.bind(n));
 		}
 
 		//expect two messages with different types per thread