Browse Source

sys.thread.Thread abstracts (#8130)

* sys.thread.Thread abstract on neko

* unit test for #8063 (Thread.current() consistency)

* inline on neko, replace __compare with op == overload

* sys.thread.Thread abstract on eval

* sys.thread.Thread abstract on java

* sys.thread.Thread abstract on cpp

* missing Thread method in eval

* make equals private (although no coreApi)

* rename java ThreadHandle for docgen, make sys.thread.Thread an abstract
Aurel 6 years ago
parent
commit
8358278797

+ 1 - 1
src/macro/eval/evalHash.ml

@@ -116,7 +116,7 @@ let key_socket = hash "socket"
 let key_read = hash "read"
 let key_read = hash "read"
 let key_write = hash "write"
 let key_write = hash "write"
 let key_others = hash "others"
 let key_others = hash "others"
-let key_sys_net_Thread = hash "sys.thread.Thread"
+let key_eval_vm_Thread = hash "eval.vm.NativeThread"
 let key_haxe_zip_Compress = hash "haxe.zip.Compress"
 let key_haxe_zip_Compress = hash "haxe.zip.Compress"
 let key_haxe_zip_Uncompress = hash "haxe.zip.Uncompress"
 let key_haxe_zip_Uncompress = hash "haxe.zip.Uncompress"
 let key_done = hash "done"
 let key_done = hash "done"

+ 4 - 4
src/macro/eval/evalStdLib.ml

@@ -2661,7 +2661,7 @@ module StdThread = struct
 
 
 	let self = vfun0 (fun () ->
 	let self = vfun0 (fun () ->
 		let eval = get_eval (get_ctx()) in
 		let eval = get_eval (get_ctx()) in
-		encode_instance key_sys_net_Thread ~kind:(IThread eval.thread)
+		encode_instance key_eval_vm_Thread ~kind:(IThread eval.thread)
 	)
 	)
 
 
 	let readMessage = vfun1 (fun blocking ->
 	let readMessage = vfun1 (fun blocking ->
@@ -3157,13 +3157,13 @@ let init_constructors builtins =
 				encode_instance key_haxe_zip_Uncompress ~kind:(IZip { z = z; z_flush = Extc.Z_NO_FLUSH })
 				encode_instance key_haxe_zip_Uncompress ~kind:(IZip { z = z; z_flush = Extc.Z_NO_FLUSH })
 			| _ -> assert false
 			| _ -> assert false
 		);
 		);
-	add key_sys_net_Thread
+	add key_eval_vm_Thread
 		(fun vl -> match vl with
 		(fun vl -> match vl with
 			| [f] ->
 			| [f] ->
 				let ctx = get_ctx() in
 				let ctx = get_ctx() in
 				if ctx.is_macro then exc_string "Creating threads in macros is not supported";
 				if ctx.is_macro then exc_string "Creating threads in macros is not supported";
 				let thread = EvalThread.spawn ctx (fun () -> call_value f []) in
 				let thread = EvalThread.spawn ctx (fun () -> call_value f []) in
-				encode_instance key_sys_net_Thread ~kind:(IThread thread)
+				encode_instance key_eval_vm_Thread ~kind:(IThread thread)
 			| _ -> assert false
 			| _ -> assert false
 		);
 		);
 	add key_sys_net_Mutex
 	add key_sys_net_Mutex
@@ -3559,7 +3559,7 @@ let init_standard_library builtins =
 		"systemName",StdSys.systemName;
 		"systemName",StdSys.systemName;
 		"time",StdSys.time;
 		"time",StdSys.time;
 	] [];
 	] [];
-	init_fields builtins (["sys";"thread"],"Thread") [
+	init_fields builtins (["eval";"vm"],"NativeThread") [
 		"delay",StdThread.delay;
 		"delay",StdThread.delay;
 		"exit",StdThread.exit;
 		"exit",StdThread.exit;
 		"join",StdThread.join;
 		"join",StdThread.join;

+ 14 - 12
std/cpp/_std/sys/thread/Thread.hx

@@ -26,23 +26,20 @@ package sys.thread;
 @:coreType
 @:coreType
 private abstract ThreadHandle {}
 private abstract ThreadHandle {}
 
 
-@:coreApi
-class Thread {
-	var handle(default, null):ThreadHandle;
-
-	function new(h:ThreadHandle):Void {
-		handle = h;
+abstract Thread(ThreadHandle) {
+	inline function new(h:ThreadHandle):Void {
+		this = h;
 	}
 	}
 
 
-	public function sendMessage(msg:Dynamic):Void {
-		untyped __global__.__hxcpp_thread_send(handle, msg);
+	public inline function sendMessage(msg:Dynamic):Void {
+		untyped __global__.__hxcpp_thread_send(this, msg);
 	}
 	}
 
 
-	public static function current():Thread {
+	public static inline function current():Thread {
 		return new Thread(untyped __global__.__hxcpp_thread_current());
 		return new Thread(untyped __global__.__hxcpp_thread_current());
 	}
 	}
 
 
-	public static function create(callb:Void->Void):Thread {
+	public static inline function create(callb:Void->Void):Thread {
 		return new Thread(untyped __global__.__hxcpp_thread_create(callb));
 		return new Thread(untyped __global__.__hxcpp_thread_create(callb));
 	}
 	}
 
 
@@ -50,7 +47,12 @@ class Thread {
 		return untyped __global__.__hxcpp_thread_read_message(block);
 		return untyped __global__.__hxcpp_thread_read_message(block);
 	}
 	}
 
 
-	@:keep function __compare(t:Thread):Int {
-		return handle == t.handle ? 0 : 1;
+	@:op(A == B)
+	public inline function equals(other:Thread):Bool {
+		return getHandle() == other.getHandle();
+	}
+
+	private inline function getHandle():ThreadHandle {
+		return this;
 	}
 	}
 }
 }

+ 28 - 54
std/eval/_std/sys/thread/Thread.hx

@@ -22,66 +22,40 @@
 
 
 package sys.thread;
 package sys.thread;
 
 
-/**
-	Experimental Thread API.
+import eval.vm.NativeThread;
 
 
-	Note that the debugger does not properly support threads at the moment.
-**/
-extern class Thread {
-	/**
-		Creates a new thread that executes function `f`.
+abstract Thread(NativeThread) {
+	inline function new(h:NativeThread):Void {
+		this = h;
+	}
 
 
-		Exceptions caused while executing `f` are printed to stderr and are not
-		propagated to the parent thread.
-	**/
-	function new(f:Void->Void):Void;
+	public inline function sendMessage(msg:Dynamic):Void {
+		this.sendMessage(msg);
+	}
 
 
-	/**
-		Return the identifier of the given thread. A thread identifier is an integer
-		that identifies uniquely the thread. It can be used to build data structures
-		indexed by threads.
-	**/
-	function id():Int;
+	public static inline function current():Thread {
+		return new Thread(NativeThread.self());
+	}
 
 
-	/**
-		Terminate prematurely the thread whose handle is given. This functionality is
-		available only with bytecode-level threads.
-	**/
-	function kill():Int;
+	public static inline function create(callb:Void->Void):Thread {
+		return new Thread(new NativeThread(callb));
+	}
 
 
-	/**
-		Suspends the execution of the calling thread for `f` seconds. The other program
-		threads continue to run during this time.
-	**/
-	static function delay(f:Float):Void;
+	public static inline function readMessage(block:Bool):Dynamic {
+		return NativeThread.readMessage(block);
+	}
 
 
-	/**
-		Terminate prematurely the currently executing thread.
-	**/
-	static function exit():Void;
+	public static inline function yield():Void {
+		NativeThread.yield();
+	}
 
 
-	/**
-		Suspends the execution of the calling thread until the thread `thread` has
-		terminated.
-	**/
-	static function join(thread:Thread):Void;
+	@:op(A == B)
+	public inline function equals(other:Thread):Bool {
+		return getHandle().id() == other.getHandle().id();
+	}
 
 
-	/**
-		Return the thread currently executing.
-	**/
-	static function self():Thread;
-
-	/**
-		Re-schedule the calling thread without suspending it. This function can be used
-		to give scheduling hints, telling the scheduler that now is a good time to switch
-		to other threads.
-	**/
-	static function yield():Void;
-	// neko API
-	static function readMessage<T>(block:Bool):T;
-	function sendMessage<T>(msg:T):Void;
-	static inline function create(f:Void->Void):Thread
-		return new Thread(f);
-	static inline function current():Thread
-		return self();
+	private inline function getHandle():NativeThread {
+		return this;
+	}
 }
 }
+

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

@@ -0,0 +1,78 @@
+/*
+ * 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 eval.vm;
+
+extern class NativeThread {
+	/**
+		Creates a new thread that executes function `f`.
+
+		Exceptions caused while executing `f` are printed to stderr and are not
+		propagated to the parent thread.
+	**/
+	function new(f:Void->Void):Void;
+
+	/**
+		Return the identifier of the given thread. A thread identifier is an integer
+		that identifies uniquely the thread. It can be used to build data structures
+		indexed by threads.
+	**/
+	function id():Int;
+
+	/**
+		Terminate prematurely the thread whose handle is given. This functionality is
+		available only with bytecode-level threads.
+	**/
+	function kill():Int;
+
+	/**
+		Suspends the execution of the calling thread for `f` seconds. The other program
+		threads continue to run during this time.
+	**/
+	static function delay(f:Float):Void;
+
+	/**
+		Terminate prematurely the currently executing thread.
+	**/
+	static function exit():Void;
+
+	/**
+		Suspends the execution of the calling thread until the thread `thread` has
+		terminated.
+	**/
+	static function join(thread:NativeThread):Void;
+
+	/**
+		Return the thread currently executing.
+	**/
+	static function self():NativeThread;
+
+	/**
+		Re-schedule the calling thread without suspending it. This function can be used
+		to give scheduling hints, telling the scheduler that now is a good time to switch
+		to other threads.
+	**/
+	static function yield():Void;
+
+	static function readMessage<T>(block:Bool):T;
+
+	function sendMessage<T>(msg:T):Void;
+}

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

@@ -22,15 +22,6 @@
 
 
 package sys.thread;
 package sys.thread;
 
 
-#if doc_gen
-@:coreApi
-extern class Thread {
-	public function sendMessage(msg:Dynamic):Void;
-	public static function current():Thread;
-	public static function create(callb:Void->Void):Thread;
-	public static function readMessage(block:Bool):Dynamic;
-}
-#else
 typedef ThreadHandle = hl.Abstract<"hl_thread">;
 typedef ThreadHandle = hl.Abstract<"hl_thread">;
 
 
 abstract Thread(ThreadHandle) {
 abstract Thread(ThreadHandle) {
@@ -75,4 +66,3 @@ abstract Thread(ThreadHandle) {
 		return null;
 		return null;
 	}
 	}
 }
 }
-#end

+ 43 - 30
std/java/_std/sys/thread/Thread.hx

@@ -22,20 +22,51 @@
 package sys.thread;
 package sys.thread;
 import java.Lib;
 import java.Lib;
 
 
-@:coreApi
-@:native('haxe.java.vm.Thread') class Thread
-{
+abstract Thread(NativeThread) {
+	inline function new(t:NativeThread)
+	{
+		this = t;
+	}
+
+	public static function create(callb:Void->Void):Thread
+	{
+		var ret = new NativeThread();
+		var t = new HaxeThread(ret, callb);
+		t.start();
+		return new Thread(ret);
+	}
+
+	public static function current():Thread
+	{
+		return new Thread(NativeThread.getThread( java.lang.Thread.currentThread() ));
+	}
+
+	public static function readMessage(block : Bool) : Dynamic
+	{
+		return current().getHandle().messages.pop(block);
+	}
+
+	public inline function sendMessage(msg:Dynamic):Void
+	{
+		this.sendMessage(msg);
+	}
 
 
-	@:private static var javaThreadToHaxe = new haxe.ds.WeakMap<java.lang.Thread, sys.thread.Thread>();
+	private inline function getHandle():NativeThread {
+		return this;
+	}
+}
+
+@:native('haxe.java.vm.Thread') private class NativeThread
+{
+	@:private static var javaThreadToHaxe = new haxe.ds.WeakMap<java.lang.Thread, NativeThread>();
 	@:private static var mainJavaThread = java.lang.Thread.currentThread();
 	@:private static var mainJavaThread = java.lang.Thread.currentThread();
 	@:private static var mainHaxeThread = {
 	@:private static var mainHaxeThread = {
-		var ret = new Thread();
+		var ret = new NativeThread();
 		javaThreadToHaxe.set(mainJavaThread, ret);
 		javaThreadToHaxe.set(mainJavaThread, ret);
 		ret;
 		ret;
 	};
 	};
 
 
-
-	private static function getThread(jt:java.lang.Thread):Thread
+	public static function getThread(jt:java.lang.Thread):NativeThread
 	{
 	{
 		if (Std.is(jt, HaxeThread))
 		if (Std.is(jt, HaxeThread))
 		{
 		{
@@ -53,7 +84,7 @@ import java.Lib;
 				ret = javaThreadToHaxe.get(jt);
 				ret = javaThreadToHaxe.get(jt);
 				if (ret == null)
 				if (ret == null)
 				{
 				{
-					ret = new Thread();
+					ret = new NativeThread();
 					javaThreadToHaxe.set(jt, ret);
 					javaThreadToHaxe.set(jt, ret);
 				}
 				}
 
 
@@ -62,9 +93,9 @@ import java.Lib;
 		}
 		}
 	}
 	}
 
 
-	private var messages:Deque<Dynamic>;
+	public var messages:Deque<Dynamic>;
 
 
-  function new()
+	public function new()
 	{
 	{
 		this.messages = new Deque();
 		this.messages = new Deque();
 	}
 	}
@@ -73,36 +104,18 @@ import java.Lib;
 	{
 	{
 		messages.add(msg);
 		messages.add(msg);
 	}
 	}
-
-	public static function current():Thread
-	{
-		return getThread( java.lang.Thread.currentThread() );
-	}
-
-	public static function readMessage(block : Bool) : Dynamic
-	{
-		return current().messages.pop(block);
-	}
-
-	public static function create(callb:Void->Void):Thread
-	{
-		var ret = new Thread();
-		var t = new HaxeThread(ret, callb);
-		t.start();
-		return ret;
-	}
 }
 }
 
 
 @:native('haxe.java.vm.HaxeThread')
 @:native('haxe.java.vm.HaxeThread')
 private class HaxeThread extends java.lang.Thread
 private class HaxeThread extends java.lang.Thread
 {
 {
-	public var threadObject(default, null):Thread;
+	public var threadObject(default, null):NativeThread;
 	private var runFunction:Void->Void;
 	private var runFunction:Void->Void;
 	@:overload override public function run():Void
 	@:overload override public function run():Void
 	{
 	{
 		runFunction();
 		runFunction();
 	}
 	}
-	public function new(hxThread:Thread, run:Void->Void)
+	public function new(hxThread:NativeThread, run:Void->Void)
 	{
 	{
 		super();
 		super();
 		threadObject = hxThread;
 		threadObject = hxThread;

+ 15 - 13
std/neko/_std/sys/thread/Thread.hx

@@ -26,34 +26,36 @@ package sys.thread;
 @:coreType
 @:coreType
 private abstract ThreadHandle {}
 private abstract ThreadHandle {}
 
 
-@:coreApi
-class Thread {
-	var handle:ThreadHandle;
-
-	function new(h:ThreadHandle):Void {
-		handle = h;
+abstract Thread(ThreadHandle) {
+	inline function new(h:ThreadHandle):Void {
+		this = h;
 	}
 	}
 
 
-	public function sendMessage(msg:Dynamic):Void {
-		thread_send(handle, msg);
+	public inline function sendMessage(msg:Dynamic):Void {
+		thread_send(this, msg);
 	}
 	}
 
 
-	public static function current():Thread {
+	public static inline function current():Thread {
 		return new Thread(thread_current());
 		return new Thread(thread_current());
 	}
 	}
 
 
-	public static function create(callb:Void->Void):Thread {
+	public static inline function create(callb:Void->Void):Thread {
 		return new Thread(thread_create(function(_) {
 		return new Thread(thread_create(function(_) {
 			return callb();
 			return callb();
 		}, null));
 		}, null));
 	}
 	}
 
 
-	public static function readMessage(block:Bool):Dynamic {
+	public static inline function readMessage(block:Bool):Dynamic {
 		return thread_read_message(block);
 		return thread_read_message(block);
 	}
 	}
 
 
-	@:keep function __compare(t:Dynamic):Int {
-		return untyped __dollar__compare(handle, t.handle);
+	@:op(A == B)
+	inline function equals(other:Thread):Bool {
+		return getHandle() == other.getHandle();
+	}
+
+	private inline function getHandle():ThreadHandle {
+		return this;
 	}
 	}
 
 
 	/**
 	/**

+ 1 - 1
std/sys/thread/Thread.hx

@@ -25,7 +25,7 @@ package sys.thread;
 #if (!target.threaded)
 #if (!target.threaded)
 #error "This class is not available on this target"
 #error "This class is not available on this target"
 #end
 #end
-extern class Thread {
+extern abstract Thread({}) {
 	/**
 	/**
 		Send a message to the thread queue. This message can be read by using `readMessage`.
 		Send a message to the thread queue. This message can be read by using `readMessage`.
 	**/
 	**/

+ 19 - 0
tests/threads/src/cases/Issue8063.hx

@@ -0,0 +1,19 @@
+package cases;
+
+import utest.Async;
+import utest.Assert;
+import utest.ITest;
+
+class Issue8063 implements ITest {
+	public function new() { }
+
+	@:timeout(5000)
+	function test(async:Async) {
+		Sys.println("Running Issue8063");
+		Assert.isTrue(Thread.current() == Thread.current());
+		Thread.create(() -> {
+			Assert.isTrue(Thread.current() == Thread.current());
+			async.done();
+		});
+	}
+}