git » sdk » commit 198a3f0

Push gc free call further in

author Stephen Paul Weber
2025-10-23 06:05:24 UTC
committer Stephen Paul Weber
2025-10-23 06:05:24 UTC
parent c8ed3a863d711e1110bd69cbbf249e8fa8d5864e

Push gc free call further in

If we do it ourselves outside the wrapped call, allocations still happen
before we leave for C land, which breaks the rules of gc free and causes
bugs. This way we do everything that must be done haxe-side *before* we
start gc free.

HaxeCBridge.hx +39 -7
borogove/calls/PeerConnection.cpp.hx +2 -6

diff --git a/HaxeCBridge.hx b/HaxeCBridge.hx
index 9fb3046..815a8f0 100644
--- a/HaxeCBridge.hx
+++ b/HaxeCBridge.hx
@@ -182,6 +182,13 @@ class HaxeCBridge {
 			if (field.access.contains(AOverride)) {
 				field.meta.push({name: "HaxeCBridge.noemit", pos: field.pos});
 			}
+			final gcFree = field.meta.filter(m -> m.name == "HaxeCBridge.gcFree").map(m ->
+				switch (m.params[0].expr) {
+					case EConst(CIdent(id)): id;
+					default: null;
+				}
+			);
+			field.meta = field.meta.filter(m -> m.name != "HaxeCBridge.gcFree");
 			if (field.access.contains(APublic) && !field.access.contains(AOverride) && !field.meta.exists((m) -> m.name == "HaxeCBridge.noemit")) {
 				switch field.kind {
 				case FFun(fun):
@@ -208,19 +215,44 @@ class HaxeCBridge {
 							aret = convertSecondaryType(aret).args[0];
 							args.push({name: arg.name, type: TPath({name: "Callable", pack: ["cpp"], params: [TPType(TFunction(Lambda.flatten(aargs.map(aarg -> aarg.args)).concat([TPath({name: "RawPointer", pack: ["cpp"], params: [TPType(TPath({ name: "Void", pack: ["cpp"] }))]})]), aret))]})});
 							args.push({name: arg.name + "__context", type: TPath({name: "RawPointer", pack: ["cpp"], params: [TPType(TPath({ name: "Void", pack: ["cpp"] }))]})});
-							final lambdaargs = Lambda.flatten(aargs.mapi((i, a) ->
+							final lambdaargs = [];
+							final lambdabody = Lambda.flatten(aargs.mapi((i, a) ->
 								if (a.retainType == "Array") {
-									[macro $i{"a" + i}, macro $i{"a" + i}.length];
+									final t = a.args[0];
+									lambdaargs.push(macro $i{"x" + i});
+									lambdaargs.push(macro $i{"xl" + i});
+									final x = "x" + i;
+									final xl = "xl" + i;
+									[macro final $x : $t = $i{"a" + i}].concat([macro final $xl = $i{"a" + i}.length]);
 								} else if (a.retainType == "String") {
-									[macro HaxeCBridge.retainHaxeString($i{"a" + i})];
+									lambdaargs.push(macro $i{"x" + i});
+									final name = "x" + i;
+									[macro final $name = HaxeCBridge.retainHaxeString($i{"a" + i})];
 								} else if (a.retainType == "Object") {
-									[macro HaxeCBridge.retainHaxeObject($i{"a" + i})];
+									lambdaargs.push(macro $i{"x" + i});
+									final name = "x" + i;
+									[macro final $name = HaxeCBridge.retainHaxeObject($i{"a" + i})];
 								} else {
-									[macro $i{"a" + i}];
+									lambdaargs.push(macro $i{"a" + i});
+									[];
 								}
-							)).concat([macro $i{arg.name + "__context"}]);
+							));
+							lambdaargs.push(macro $i{arg.name + "__context"});
+							if (gcFree.contains(arg.name)) lambdabody.push(macro cpp.NativeGc.enterGCFreeZone());
+							switch (aret) {
+								case TPath(_.sub => "Void"):
+									lambdabody.push(macro $i{arg.name}($a{lambdaargs}));
+								default:
+									lambdabody.push(macro final ret = $i{arg.name}($a{lambdaargs}));
+							}
+							if (gcFree.contains(arg.name)) lambdabody.push(macro cpp.NativeGc.exitGCFreeZone());
+							switch (aret) {
+								case TPath(_.sub => "Void"):
+								default:
+								lambdabody.push(macro return ret);
+							}
 							final lambdafargs = aargs.mapi((i, a) ->  {name: "a" + i, meta: null, opt: false, type: null, value: null});
-							passArgs.push({expr: EFunction(null, { args: lambdafargs, expr: macro return $i{arg.name}($a{lambdaargs}) }), pos: field.pos});
+							passArgs.push({expr: EFunction(null, { args: lambdafargs, expr: macro $b{lambdabody} }), pos: field.pos});
 						case TPath(path) if (path.name == "Array"):
 							wrap = true;
 							final isString = switch path.params[0] {
diff --git a/borogove/calls/PeerConnection.cpp.hx b/borogove/calls/PeerConnection.cpp.hx
index a5168b7..0b29c93 100644
--- a/borogove/calls/PeerConnection.cpp.hx
+++ b/borogove/calls/PeerConnection.cpp.hx
@@ -464,6 +464,7 @@ class MediaStreamTrack {
 
 		@param callback takes three arguments, the Signed 16-bit PCM data, the clock rate, and the number of channels
 	**/
+	@HaxeCBridge.gcFree(callback)
 	public function addPCMListener(callback: (Array<cpp.Int16>,Int,Int)->Void) {
 		pcmCallback = callback;
 	}
@@ -501,9 +502,7 @@ class MediaStreamTrack {
 				s16[i] = ULAW_DECODE[cast msg.at(i)];
 			}
 			if (pcmCallback != null) {
-				cpp.vm.Gc.enterGCFreeZone();
 				pcmCallback(s16.toData(), rtp.clockRate, channels);
-				cpp.vm.Gc.exitGCFreeZone();
 			}
 		} else if (format == "opus") {
 			final s16 = new haxe.ds.Vector(5760).toData(); // 5760 is the max size needed for 48khz
@@ -512,9 +511,7 @@ class MediaStreamTrack {
 			final decoded = OpusDecoder.decode(opus, cast msg.data(), msg.size(), cpp.Pointer.ofArray(s16), Std.int(s16.length / channels), false);
 			s16.resize(decoded * channels);
 			if (pcmCallback != null) {
-				cpp.vm.Gc.enterGCFreeZone();
 				pcmCallback(s16, rtp.clockRate, channels);
-				cpp.vm.Gc.exitGCFreeZone();
 			}
 		} else {
 			trace("Ignoring audio frame with format", format);
@@ -527,6 +524,7 @@ class MediaStreamTrack {
 
 		@param callback
 	**/
+	@HaxeCBridge.gcFree(callback)
 	public function addReadyForPCMListener(callback: ()->Void) {
 		readyForPCMCallback = callback;
 		if (untyped __cpp__("track") && track.ref.isOpen()) {
@@ -543,9 +541,7 @@ class MediaStreamTrack {
 					waitForQ = true;
 					mutex.release();
 				} else {
-					cpp.vm.Gc.enterGCFreeZone();
 					readyForPCMCallback();
-					cpp.vm.Gc.exitGCFreeZone();
 				}
 			});
 		}