瀏覽代碼

Eval atomics (#12275)

* [eval] support haxe.atomic

* Improve haxe.atomic docgen
Zeta 1 月之前
父節點
當前提交
b52f17b088

+ 2 - 1
src/context/common.ml

@@ -705,7 +705,8 @@ let get_config com =
 			pf_capture_policy = CPWrapRef;
 			pf_exceptions = { default_config.pf_exceptions with
 				ec_avoid_wrapping = false
-			}
+			};
+			pf_supports_atomics = true;
 		}
 
 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_previous = hash "previous"
 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";"_Check"], "Check_Impl_") EvalLuv.check_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
 	| IMbedtlsX509Crt of Mbedtls.mbedtls_x509_crt
 	| INormal
+	| IAtomicBool of bool Atomic.t
+	| IAtomicInt of int Atomic.t
+	| IAtomicObject of value Atomic.t
 
 and vinstance = {
 	(* 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;
 
+#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 {
 	public var value:Int;
 	public function new(value:Int) {
@@ -72,3 +97,4 @@ abstract AtomicInt(Data) {
 	}
 	#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.
 **/
 @: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..
 		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`.
 		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`.
 	**/
-	public inline function load():Bool {
-		return toBool(this.load());
-	}
-
+	public function load():Bool;
 	/**
 		Atomically stores `value` into `a`.
 		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
 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>) {
 	public inline function new(value:Int):Void {
 		this = new hl.NativeArray(1);
@@ -47,3 +72,4 @@ abstract AtomicInt(hl.NativeArray<Int>) {
 		return Atomics.store32(this.getRef(), value);
 	}
 }
+#end

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

@@ -5,6 +5,20 @@ package haxe.atomic;
 #end
 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>
 // so that the compiler doesn't get confused and emit hl.Ref.make(this.getRef())
 abstract AtomicObject<T:{}>(hl.NativeArray<Dynamic>) {
@@ -29,3 +43,4 @@ abstract AtomicObject<T:{}>(hl.NativeArray<Dynamic>) {
 		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;
 
+#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) {
 	public inline function new(value:Int) {
 		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);
 	}
 }
+#end

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

@@ -1,7 +1,21 @@
 package haxe.atomic;
 
 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) {
 	public inline function new(value:Bool) {
 		this = new AtomicBoolean(value);
@@ -32,3 +46,4 @@ abstract AtomicBool(AtomicBoolean) {
 		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;
 
+#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) {
 	public inline function new(value:Int) {
 		this = new AtomicInteger(value);
@@ -62,3 +87,4 @@ abstract AtomicInt(AtomicInteger) {
 		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;
 
+#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>) {
 	public inline function new(value:T) {
 		this = new AtomicReference(value);
@@ -32,3 +46,4 @@ abstract AtomicObject<T:{}>(AtomicReference<T>) {
 		return value;
 	}
 }
+#end