Browse Source

[jvm] fixed `sys.thread.Thread.current()` for non-haxe threads (closes #9863)

Aleksandr Kuzmenko 5 years ago
parent
commit
93bce4deca
2 changed files with 121 additions and 12 deletions
  1. 55 12
      std/jvm/_std/sys/thread/Thread.hx
  2. 66 0
      tests/threads/src/cases/Issue9863.hx

+ 55 - 12
std/jvm/_std/sys/thread/Thread.hx

@@ -24,41 +24,84 @@ package sys.thread;
 
 import java.Lib;
 import java.lang.Runnable;
+import java.util.WeakHashMap;
+import java.util.Collections;
+import java.lang.Thread as JavaThread;
 
-abstract Thread(HaxeThread) from HaxeThread to java.lang.Thread {
+abstract Thread(HaxeThread) from HaxeThread {
 	inline function new(t:HaxeThread) {
 		this = t;
 	}
 
-	public static function create(callb:Void->Void):Thread {
-		var ret = new HaxeThread((cast callb : Runnable));
-		ret.setDaemon(true);
-		ret.start();
-		return new Thread(ret);
+	public static inline function create(callb:()->Void):Thread {
+		return HaxeThread.create(callb);
 	}
 
-	public static function current():Thread {
-		return new Thread(Std.downcast(java.lang.Thread.currentThread(), HaxeThread));
+	public static inline function current():Thread {
+		return HaxeThread.get(JavaThread.currentThread());
 	}
 
-	public static function readMessage(block:Bool):Dynamic {
-		return current().getHandle().messages.pop(block);
+	public static inline function readMessage(block:Bool):Dynamic {
+		return current().getHandle().readMessage(block);
 	}
 
 	public inline function sendMessage(msg:Dynamic):Void {
 		this.sendMessage(msg);
 	}
 
-	private inline function getHandle():HaxeThread {
+	inline function getHandle():HaxeThread {
 		return this;
 	}
 }
 
-class HaxeThread extends java.lang.Thread {
+private class HaxeThread {
+	static final nativeThreads = Collections.synchronizedMap(new WeakHashMap<JavaThread,HaxeThread>());
+	static final mainJavaThread = JavaThread.currentThread();
+	static final mainHaxeThread = new HaxeThread();
 
 	public final messages = new Deque<Dynamic>();
 
+	public static function create(callb:()->Void):HaxeThread {
+		var hx = new HaxeThread();
+		var thread = new NativeHaxeThread(hx, callb);
+		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;
+			}
+		}
+	}
+
+	function new() {}
+
 	public inline function sendMessage(msg:Dynamic):Void {
 		messages.add(msg);
 	}
+
+	public inline function readMessage(block:Bool):Dynamic {
+		return messages.pop(block);
+	}
+}
+
+private class NativeHaxeThread extends java.lang.Thread {
+	public final haxeThread:HaxeThread;
+
+	public function new(haxeThread:HaxeThread, callb:()->Void) {
+		super((cast callb:Runnable));
+		this.haxeThread = haxeThread;
+	}
 }

+ 66 - 0
tests/threads/src/cases/Issue9863.hx

@@ -0,0 +1,66 @@
+package cases;
+
+import utest.Assert;
+import sys.thread.Thread;
+
+#if cpp
+class NativeThreads extends utest.Test {
+	public function test() {
+		Assert.pass();
+	}
+}
+#else
+
+class Issue9863 extends utest.Test {
+	static function runNative(job:()->Void) {
+		#if java
+			var t = new java.lang.Thread(new Task(job));
+			t.start();
+		#elseif cs
+			var t = new cs.system.threading.Thread(job);
+			t.Start();
+		#else
+			#error "Issue9863 is not implemented for this target"
+		#end
+	}
+
+	public function test() {
+		var childThread:Null<Thread> = null;
+		function getJob(mainThread:Thread) {
+			return () -> {
+				childThread = Thread.current();
+				mainThread.sendMessage('childThread ready');
+
+				var msg = Thread.readMessage(true);
+				Assert.equals('from main to child', msg);
+
+				mainThread.sendMessage('done');
+			}
+		}
+		runNative(getJob(Thread.current()));
+
+		var msg = Thread.readMessage(true);
+		Assert.equals('childThread ready', msg);
+
+		childThread.sendMessage('from main to child');
+
+		var msg = Thread.readMessage(true);
+		Assert.equals('done', msg);
+	}
+}
+
+#end
+
+#if java
+private class Task implements java.lang.Runnable {
+	final job:()->Void;
+
+	public function new(job:()->Void) {
+		this.job = job;
+	}
+
+	public function run() {
+		job();
+	}
+}
+#end