Przeglądaj źródła

Eval atomics (#12275)

* [eval] support haxe.atomic

* Improve haxe.atomic docgen
Zeta 1 miesiąc temu
rodzic
commit
b52f17b088

+ 2 - 1
src/context/common.ml

@@ -705,7 +705,8 @@ let get_config com =
 			pf_capture_policy = CPWrapRef;
 			pf_capture_policy = CPWrapRef;
 			pf_exceptions = { default_config.pf_exceptions with
 			pf_exceptions = { default_config.pf_exceptions with
 				ec_avoid_wrapping = false
 				ec_avoid_wrapping = false
-			}
+			};
+			pf_supports_atomics = true;
 		}
 		}
 
 
 let memory_marker = [|Unix.time()|]
 let memory_marker = [|Unix.time()|]

+ 87 - 0
src/macro/eval/evalAtomic.ml

@@ -0,0 +1,87 @@
+open EvalValue
+open EvalEncode
+open EvalDecode
+open EvalExceptions
+open EvalHash
+
+let as_atomic_bool vthis = match vthis with
+	| VInstance {ikind = IAtomicBool i} -> i
+	| _ -> unexpected_value vthis "AtomicBool"
+let as_atomic_int vthis = match vthis with
+	| VInstance {ikind = IAtomicInt i} -> i
+	| _ -> unexpected_value vthis "AtomicInt"
+let as_atomic_object vthis = match vthis with
+	| VInstance {ikind = IAtomicObject i} -> i
+	| _ -> unexpected_value vthis "AtomicObject"
+
+let atomic_object_fields = [
+        "_hx_new", vfun1 (fun v -> encode_instance key_haxe_atomic_AtomicObject ~kind:(IAtomicObject (Atomic.make v)));
+        "load", vfun1 (fun v -> Atomic.get (as_atomic_object v));
+        "store", vfun2 (fun a v -> Atomic.set (as_atomic_object a) v; v);
+        "exchange", vfun2 (fun a v -> Atomic.exchange (as_atomic_object a) v);
+        "compareExchange", vfun3 (fun vthis a b ->
+            let vthis = as_atomic_object vthis in
+            let rec loop () = begin
+                let original = Atomic.get vthis in
+                let real_replacement = if EvalValue.equals original a then b else original in
+                if not (Atomic.compare_and_set vthis original real_replacement) then 
+                    loop ()
+                else
+                    original
+            end
+            in loop ());
+]
+
+let atomic_bool_fields = [
+        "_hx_new", vfun1 (fun v -> encode_instance key_haxe_atomic_AtomicBool ~kind:(IAtomicBool (Atomic.make (decode_bool v))));
+        "load", vfun1 (fun v -> vbool (Atomic.get (as_atomic_bool v)));
+        "store", vfun2 (fun a v -> Atomic.set (as_atomic_bool a) (decode_bool v); v);
+        "exchange", vfun2 (fun a v -> vbool (Atomic.exchange (as_atomic_bool a) (decode_bool v)));
+        "compareExchange", vfun3 (fun vthis a b ->
+            let vthis = as_atomic_bool vthis in
+            let a = decode_bool a and b = decode_bool b in
+            let rec loop () = begin
+                let original = Atomic.get vthis in
+                let real_replacement = if original = a then b else original in
+                if not (Atomic.compare_and_set vthis original real_replacement) then 
+                    loop ()
+                else
+                    vbool original
+            end
+            in loop ());
+]
+
+let fetch_update (this: 'a Atomic.t) (f: 'a -> 'a) =
+    let rec loop () = begin
+        let original = Atomic.get this in
+        let replacement = f original in
+        if not (Atomic.compare_and_set this original replacement) then 
+            loop ()
+        else
+            original
+    end
+    in loop ()
+
+let atomic_int_fields = [
+        "_hx_new", vfun1 (fun v -> encode_instance key_haxe_atomic_AtomicInt ~kind:(IAtomicInt (Atomic.make (decode_int v))));
+        "load", vfun1 (fun v -> vint (Atomic.get (as_atomic_int v)));
+        "store", vfun2 (fun a v -> Atomic.set (as_atomic_int a) (decode_int v); v);
+        "exchange", vfun2 (fun a v -> vint (Atomic.exchange (as_atomic_int a) (decode_int v)));
+        "compareExchange", vfun3 (fun vthis a b ->
+            let vthis = as_atomic_int vthis in
+            let a = decode_int a and b = decode_int b in
+            let rec loop () = begin
+                let original = Atomic.get vthis in
+                let real_replacement = if original = a then b else original in
+                if not (Atomic.compare_and_set vthis original real_replacement) then 
+                    loop ()
+                else
+                    vint original
+            end
+            in loop ());
+        "add", vfun2 (fun a v -> vint (Atomic.fetch_and_add (as_atomic_int a) (decode_int v)));
+        "sub", vfun2 (fun a v -> vint (Atomic.fetch_and_add (as_atomic_int a) (-(decode_int v))));
+        "and", vfun2 (fun a v -> let v = decode_int v in vint (fetch_update (as_atomic_int a) (fun orig -> orig land v)));
+        "or", vfun2 (fun a v -> let v = decode_int v in vint (fetch_update (as_atomic_int a) (fun orig -> orig lor v)));
+        "xor", vfun2 (fun a v -> let v = decode_int v in vint (fetch_update (as_atomic_int a) (fun orig -> orig lxor v)));
+]

+ 3 - 0
src/macro/eval/evalHash.ml

@@ -212,3 +212,6 @@ let key_address = hash "address"
 let key_netmask = hash "netmask"
 let key_netmask = hash "netmask"
 let key_previous = hash "previous"
 let key_previous = hash "previous"
 let key_current = hash "current"
 let key_current = hash "current"
+let key_haxe_atomic_AtomicBool = hash "haxe.atomic.AtomicBool" 
+let key_haxe_atomic_AtomicInt = hash "haxe.atomic.AtomicInt" 
+let key_haxe_atomic_AtomicObject = hash "haxe.atomic.AtomicObject" 

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

@@ -3852,4 +3852,7 @@ let init_standard_library builtins =
 	init_fields builtins (["eval";"luv";"_Prepare"], "Prepare_Impl_") EvalLuv.prepare_fields [];
 	init_fields builtins (["eval";"luv";"_Prepare"], "Prepare_Impl_") EvalLuv.prepare_fields [];
 	init_fields builtins (["eval";"luv";"_Check"], "Check_Impl_") EvalLuv.check_fields [];
 	init_fields builtins (["eval";"luv";"_Check"], "Check_Impl_") EvalLuv.check_fields [];
 	init_fields builtins (["eval";"luv"], "Version") EvalLuv.version_fields [];
 	init_fields builtins (["eval";"luv"], "Version") EvalLuv.version_fields [];
-	EvalSsl.init_fields init_fields builtins
+	EvalSsl.init_fields init_fields builtins;
+	init_fields builtins (["haxe";"atomic"; "_AtomicBool"], "AtomicBool_Impl_") EvalAtomic.atomic_bool_fields [];
+	init_fields builtins (["haxe";"atomic"; "_AtomicInt"], "AtomicInt_Impl_") EvalAtomic.atomic_int_fields [];
+	init_fields builtins (["haxe";"atomic"; "_AtomicObject"], "AtomicObject_Impl_") EvalAtomic.atomic_object_fields [];

+ 3 - 0
src/macro/eval/evalValue.ml

@@ -214,6 +214,9 @@ and vinstance_kind =
 	| IMbedtlsSsl of Mbedtls.mbedtls_ssl_context
 	| IMbedtlsSsl of Mbedtls.mbedtls_ssl_context
 	| IMbedtlsX509Crt of Mbedtls.mbedtls_x509_crt
 	| IMbedtlsX509Crt of Mbedtls.mbedtls_x509_crt
 	| INormal
 	| INormal
+	| IAtomicBool of bool Atomic.t
+	| IAtomicInt of int Atomic.t
+	| IAtomicObject of value Atomic.t
 
 
 and vinstance = {
 and vinstance = {
 	(* The fields of this instance. *)
 	(* The fields of this instance. *)

+ 47 - 0
std/cpp/_std/haxe/atomic/AtomicBool.hx

@@ -0,0 +1,47 @@
+package haxe.atomic;
+
+#if doc_gen
+@:coreApi
+@:coreType
+abstract AtomicBool {
+	public function new(value:Bool):Void;
+
+	public function compareExchange(expected:Bool, replacement:Bool):Bool;
+
+	public function exchange(value:Bool):Bool;
+
+	public function load():Bool;
+
+	public function store(value:Bool):Bool;
+}
+#else
+abstract AtomicBool(AtomicInt) {
+	private inline function toInt(v:Bool):Int {
+		return v ? 1 : 0;
+	}
+
+	private inline function toBool(v:Int):Bool {
+		return v == 1;
+	}
+
+	public inline function new(value:Bool):Void {
+		this = new AtomicInt(toInt(value));
+	}
+
+	public inline function compareExchange(expected:Bool, replacement:Bool):Bool {
+		return toBool(this.compareExchange(toInt(expected), toInt(replacement)));
+	}
+
+	public inline function exchange(value:Bool):Bool {
+		return toBool(this.exchange(toInt(value)));
+	}
+
+	public inline function load():Bool {
+		return toBool(this.load());
+	}
+
+	public inline function store(value:Bool):Bool {
+		return toBool(this.store(toInt(value)));
+	}
+}
+#end

+ 26 - 0
std/cpp/_std/haxe/atomic/AtomicInt.hx

@@ -1,5 +1,30 @@
 package haxe.atomic;
 package haxe.atomic;
 
 
+#if doc_gen
+@:coreApi
+@:coreType
+abstract AtomicInt {
+	public function new(value:Int):Void;
+
+	public function add(b:Int):Int;
+
+	public function sub(b:Int):Int;
+
+	public function and(b:Int):Int;
+
+	public function or(b:Int):Int;
+
+	public function xor(b:Int):Int;
+
+	public function compareExchange(expected:Int, replacement:Int):Int;
+
+	public function exchange(value:Int):Int;
+
+	public function load():Int;
+
+	public function store(value:Int):Int;
+}
+#else
 private final class Data {
 private final class Data {
 	public var value:Int;
 	public var value:Int;
 	public function new(value:Int) {
 	public function new(value:Int) {
@@ -72,3 +97,4 @@ abstract AtomicInt(Data) {
 	}
 	}
 	#end
 	#end
 }
 }
+#end

+ 8 - 27
std/haxe/atomic/AtomicBool.hx

@@ -9,47 +9,28 @@ package haxe.atomic;
 	(js) The Atomics and SharedArrayBuffer objects need to be available. Errors will be thrown if this is not the case.
 	(js) The Atomics and SharedArrayBuffer objects need to be available. Errors will be thrown if this is not the case.
 **/
 **/
 @:coreApi
 @:coreApi
-abstract AtomicBool(AtomicInt) {
-	private inline function toInt(v:Bool):Int {
-		return v ? 1 : 0;
-	}
-
-	private inline function toBool(v:Int):Bool {
-		return v == 1;
-	}
-
-	public inline function new(value:Bool):Void {
-		this = new AtomicInt(toInt(value));
-	}
+@:coreType
+abstract AtomicBool {
+	public function new(value:Bool):Void;
 
 
 	/**
 	/**
 		Atomically compares the value of `a` with `expected` and replaces `a` with `replacement` if they are equal..
 		Atomically compares the value of `a` with `expected` and replaces `a` with `replacement` if they are equal..
 		Returns the original value of `a`.
 		Returns the original value of `a`.
 	**/
 	**/
-	public inline function compareExchange(expected:Bool, replacement:Bool):Bool {
-		return toBool(this.compareExchange(toInt(expected), toInt(replacement)));
-	}
+	public function compareExchange(expected:Bool, replacement:Bool):Bool;
 
 
 	/**
 	/**
 		Atomically exchanges `a` with `value`.
 		Atomically exchanges `a` with `value`.
 		Returns the original value of `a`.
 		Returns the original value of `a`.
 	**/
 	**/
-	public inline function exchange(value:Bool):Bool {
-		return toBool(this.exchange(toInt(value)));
-	}
-
+	public function exchange(value:Bool):Bool;
 	/**
 	/**
 		Atomically fetches the value of `a`.
 		Atomically fetches the value of `a`.
 	**/
 	**/
-	public inline function load():Bool {
-		return toBool(this.load());
-	}
-
+	public function load():Bool;
 	/**
 	/**
 		Atomically stores `value` into `a`.
 		Atomically stores `value` into `a`.
 		Returns the value that has been stored.
 		Returns the value that has been stored.
 	**/
 	**/
-	public inline function store(value:Bool):Bool {
-		return toBool(this.store(toInt(value)));
-	}
-}
+	public function store(value:Bool):Bool;
+}

+ 47 - 0
std/hl/_std/haxe/atomic/AtomicBool.hx

@@ -0,0 +1,47 @@
+package haxe.atomic;
+
+#if doc_gen
+@:coreApi
+@:coreType
+abstract AtomicBool {
+	public function new(value:Bool):Void;
+
+	public function compareExchange(expected:Bool, replacement:Bool):Bool;
+
+	public function exchange(value:Bool):Bool;
+
+	public function load():Bool;
+
+	public function store(value:Bool):Bool;
+}
+#else
+abstract AtomicBool(AtomicInt) {
+	private inline function toInt(v:Bool):Int {
+		return v ? 1 : 0;
+	}
+
+	private inline function toBool(v:Int):Bool {
+		return v == 1;
+	}
+
+	public inline function new(value:Bool):Void {
+		this = new AtomicInt(toInt(value));
+	}
+
+	public inline function compareExchange(expected:Bool, replacement:Bool):Bool {
+		return toBool(this.compareExchange(toInt(expected), toInt(replacement)));
+	}
+
+	public inline function exchange(value:Bool):Bool {
+		return toBool(this.exchange(toInt(value)));
+	}
+
+	public inline function load():Bool {
+		return toBool(this.load());
+	}
+
+	public inline function store(value:Bool):Bool {
+		return toBool(this.store(toInt(value)));
+	}
+}
+#end

+ 26 - 0
std/hl/_std/haxe/atomic/AtomicInt.hx

@@ -5,6 +5,31 @@ package haxe.atomic;
 #end
 #end
 import hl.Atomics;
 import hl.Atomics;
 
 
+#if doc_gen
+@:coreApi
+@:coreType
+abstract AtomicInt {
+	public function new(value:Int):Void;
+
+	public function add(b:Int):Int;
+
+	public function sub(b:Int):Int;
+
+	public function and(b:Int):Int;
+
+	public function or(b:Int):Int;
+
+	public function xor(b:Int):Int;
+
+	public function compareExchange(expected:Int, replacement:Int):Int;
+
+	public function exchange(value:Int):Int;
+
+	public function load():Int;
+
+	public function store(value:Int):Int;
+}
+#else
 abstract AtomicInt(hl.NativeArray<Int>) {
 abstract AtomicInt(hl.NativeArray<Int>) {
 	public inline function new(value:Int):Void {
 	public inline function new(value:Int):Void {
 		this = new hl.NativeArray(1);
 		this = new hl.NativeArray(1);
@@ -47,3 +72,4 @@ abstract AtomicInt(hl.NativeArray<Int>) {
 		return Atomics.store32(this.getRef(), value);
 		return Atomics.store32(this.getRef(), value);
 	}
 	}
 }
 }
+#end

+ 15 - 0
std/hl/_std/haxe/atomic/AtomicObject.hx

@@ -5,6 +5,20 @@ package haxe.atomic;
 #end
 #end
 import hl.Atomics;
 import hl.Atomics;
 
 
+#if doc_gen
+@:coreType
+abstract AtomicObject<T:{}> {
+	public function new(value:T):Void;
+
+	public function compareExchange(expected:T, replacement:T):T;
+
+	public function exchange(value:T):T;
+
+	public function load():T;
+
+	public function store(value:T):T;
+}
+#else
 // use hl.NativeArray<Dynamic> instead of hl.NativeArray<T>
 // use hl.NativeArray<Dynamic> instead of hl.NativeArray<T>
 // so that the compiler doesn't get confused and emit hl.Ref.make(this.getRef())
 // so that the compiler doesn't get confused and emit hl.Ref.make(this.getRef())
 abstract AtomicObject<T:{}>(hl.NativeArray<Dynamic>) {
 abstract AtomicObject<T:{}>(hl.NativeArray<Dynamic>) {
@@ -29,3 +43,4 @@ abstract AtomicObject<T:{}>(hl.NativeArray<Dynamic>) {
 		return Atomics.storePtr(this.getRef(), value);
 		return Atomics.storePtr(this.getRef(), value);
 	}
 	}
 }
 }
+#end

+ 47 - 0
std/js/_std/haxe/atomic/AtomicBool.hx

@@ -0,0 +1,47 @@
+package haxe.atomic;
+
+#if doc_gen
+@:coreApi
+@:coreType
+abstract AtomicBool {
+	public function new(value:Bool):Void;
+
+	public function compareExchange(expected:Bool, replacement:Bool):Bool;
+
+	public function exchange(value:Bool):Bool;
+
+	public function load():Bool;
+
+	public function store(value:Bool):Bool;
+}
+#else
+abstract AtomicBool(AtomicInt) {
+	private inline function toInt(v:Bool):Int {
+		return v ? 1 : 0;
+	}
+
+	private inline function toBool(v:Int):Bool {
+		return v == 1;
+	}
+
+	public inline function new(value:Bool):Void {
+		this = new AtomicInt(toInt(value));
+	}
+
+	public inline function compareExchange(expected:Bool, replacement:Bool):Bool {
+		return toBool(this.compareExchange(toInt(expected), toInt(replacement)));
+	}
+
+	public inline function exchange(value:Bool):Bool {
+		return toBool(this.exchange(toInt(value)));
+	}
+
+	public inline function load():Bool {
+		return toBool(this.load());
+	}
+
+	public inline function store(value:Bool):Bool {
+		return toBool(this.store(toInt(value)));
+	}
+}
+#end

+ 26 - 0
std/js/_std/haxe/atomic/AtomicInt.hx

@@ -2,6 +2,31 @@ package haxe.atomic;
 
 
 import js.lib.Atomics;
 import js.lib.Atomics;
 
 
+#if doc_gen
+@:coreApi
+@:coreType
+abstract AtomicInt {
+	public function new(value:Int):Void;
+
+	public function add(b:Int):Int;
+
+	public function sub(b:Int):Int;
+
+	public function and(b:Int):Int;
+
+	public function or(b:Int):Int;
+
+	public function xor(b:Int):Int;
+
+	public function compareExchange(expected:Int, replacement:Int):Int;
+
+	public function exchange(value:Int):Int;
+
+	public function load():Int;
+
+	public function store(value:Int):Int;
+}
+#else
 abstract AtomicInt(js.lib.Int32Array) {
 abstract AtomicInt(js.lib.Int32Array) {
 	public inline function new(value:Int) {
 	public inline function new(value:Int) {
 		this = new js.lib.Int32Array(new js.lib.SharedArrayBuffer(js.lib.Int32Array.BYTES_PER_ELEMENT));
 		this = new js.lib.Int32Array(new js.lib.SharedArrayBuffer(js.lib.Int32Array.BYTES_PER_ELEMENT));
@@ -48,3 +73,4 @@ abstract AtomicInt(js.lib.Int32Array) {
 		return Atomics.store(asArray(), 0, value);
 		return Atomics.store(asArray(), 0, value);
 	}
 	}
 }
 }
+#end

+ 15 - 0
std/jvm/_std/haxe/atomic/AtomicBool.hx

@@ -1,7 +1,21 @@
 package haxe.atomic;
 package haxe.atomic;
 
 
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicBoolean;
+#if doc_gen
+@:coreApi
+@:coreType
+abstract AtomicBool {
+	public function new(value:Bool):Void;
 
 
+	public function compareExchange(expected:Bool, replacement:Bool):Bool;
+
+	public function exchange(value:Bool):Bool;
+
+	public function load():Bool;
+
+	public function store(value:Bool):Bool;
+}
+#else
 abstract AtomicBool(AtomicBoolean) {
 abstract AtomicBool(AtomicBoolean) {
 	public inline function new(value:Bool) {
 	public inline function new(value:Bool) {
 		this = new AtomicBoolean(value);
 		this = new AtomicBoolean(value);
@@ -32,3 +46,4 @@ abstract AtomicBool(AtomicBoolean) {
 		return value;
 		return value;
 	}
 	}
 }
 }
+#end

+ 26 - 0
std/jvm/_std/haxe/atomic/AtomicInt.hx

@@ -2,6 +2,31 @@ package haxe.atomic;
 
 
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicInteger;
 
 
+#if doc_gen
+@:coreApi
+@:coreType
+abstract AtomicInt {
+	public function new(value:Int):Void;
+
+	public function add(b:Int):Int;
+
+	public function sub(b:Int):Int;
+
+	public function and(b:Int):Int;
+
+	public function or(b:Int):Int;
+
+	public function xor(b:Int):Int;
+
+	public function compareExchange(expected:Int, replacement:Int):Int;
+
+	public function exchange(value:Int):Int;
+
+	public function load():Int;
+
+	public function store(value:Int):Int;
+}
+#else
 abstract AtomicInt(AtomicInteger) {
 abstract AtomicInt(AtomicInteger) {
 	public inline function new(value:Int) {
 	public inline function new(value:Int) {
 		this = new AtomicInteger(value);
 		this = new AtomicInteger(value);
@@ -62,3 +87,4 @@ abstract AtomicInt(AtomicInteger) {
 		return value;
 		return value;
 	}
 	}
 }
 }
+#end

+ 15 - 0
std/jvm/_std/haxe/atomic/AtomicObject.hx

@@ -2,6 +2,20 @@ package haxe.atomic;
 
 
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.atomic.AtomicReference;
 
 
+#if doc_gen
+@:coreType
+abstract AtomicObject<T:{}> {
+	public function new(value:T):Void;
+
+	public function compareExchange(expected:T, replacement:T):T;
+
+	public function exchange(value:T):T;
+
+	public function load():T;
+
+	public function store(value:T):T;
+}
+#else
 abstract AtomicObject<T:{}>(AtomicReference<T>) {
 abstract AtomicObject<T:{}>(AtomicReference<T>) {
 	public inline function new(value:T) {
 	public inline function new(value:T) {
 		this = new AtomicReference(value);
 		this = new AtomicReference(value);
@@ -32,3 +46,4 @@ abstract AtomicObject<T:{}>(AtomicReference<T>) {
 		return value;
 		return value;
 	}
 	}
 }
 }
+#end