From 5ce534459330c466ec1da3619ac2665d52b7d99a Mon Sep 17 00:00:00 2001 From: chloekek Date: Fri, 26 Apr 2024 00:56:55 +0200 Subject: [PATCH 1/2] Promote `std.process.Config.preExecFunction` to a delegate std.process.Config.preExecFunction is now a delegate instead of a function pointer, and can therefore capture an environment, for example: import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl; import std.process : Config, execute; void runProgram(int pdeathsig) { execute( ["program"], config: Config( preExecFunction: () @trusted => prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1, ), ); } Despite function pointers implicitly converting to delegates, this is a backwards-incompatible change, as user code may rely on the field being a function pointer. For example, code like the following will no longer compile: import std.process : Config; nothrow pure @nogc @safe bool f() { return true; } void example() { auto config = Config(preExecFunction: &f); bool function() g = config.preExecFunction; } --- ...process.Config.preExecFunction-delegate.dd | 38 +++++++++++++++++++ std/process.d | 20 +++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 changelog/std.process.Config.preExecFunction-delegate.dd diff --git a/changelog/std.process.Config.preExecFunction-delegate.dd b/changelog/std.process.Config.preExecFunction-delegate.dd new file mode 100644 index 00000000000..e9b13b3c268 --- /dev/null +++ b/changelog/std.process.Config.preExecFunction-delegate.dd @@ -0,0 +1,38 @@ +Promote `std.process.Config.preExecFunction` to a delegate + +$(LINK2 $(ROOT_DIR)phobos/std_process.html#.Config.preExecFunction, +`std.process.Config.preExecFunction`) is now a delegate instead of a function +pointer, and can therefore capture an environment, for example: + +------- +import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl; +import std.process : Config, execute; + +void runProgram(int pdeathsig) +{ + execute( + ["program"], + config: Config( + preExecFunction: () @trusted => + prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1, + ), + ); +} +------- + +Despite function pointers implicitly converting to delegates, this is a +backwards-incompatible change, as user code may rely on the field being a +function pointer. For example, code like the following will no longer compile: + +------- +import std.process : Config; + +nothrow pure @nogc @safe +bool f() { return true; } + +void example() +{ + auto config = Config(preExecFunction: &f); + bool function() g = config.preExecFunction; +} +------- diff --git a/std/process.d b/std/process.d index 494910f3535..fbdb2e48ea9 100644 --- a/std/process.d +++ b/std/process.d @@ -1271,6 +1271,22 @@ version (Posix) assert(received); } +version (Posix) +@safe unittest +{ + foreach (i; 0 .. 3) + { + auto config = Config( + preExecFunction: delegate() @trusted { + _Exit(i); + return true; + }, + ); + auto pid = spawnProcess(["false"], config: config); + assert(wait(pid) == i); + } +} + /* Implementation of spawnProcess() for Windows. @@ -2188,11 +2204,11 @@ struct Config On Windows, this member is not available. */ - bool function() nothrow @nogc @safe preExecFunction; + bool delegate() nothrow @nogc @safe preExecFunction; } else version (Posix) { - bool function() nothrow @nogc @safe preExecFunction; + bool delegate() nothrow @nogc @safe preExecFunction; } } From 7a280a938e48d96cca5e01bdd5c5ae71bd502114 Mon Sep 17 00:00:00 2001 From: chloekek Date: Fri, 26 Apr 2024 00:56:55 +0200 Subject: [PATCH 2/2] Add std.process.Config.preExecDelegate std.process.Config.preExecDelegate is just like std.process.Config.preExecFunction, but can capture an environment, for example: import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl; import std.process : Config, execute; void runProgram(int pdeathsig) { execute( ["program"], config: Config( preExecDelegate: () @trusted => prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1, ), ); } preExecFunction is retained for backwards compatibility. If both preExecFunction and preExecDelegate are given, both are called. --- .../std.process.Config.preExecDelegate.dd | 25 ++++++++++ ...process.Config.preExecFunction-delegate.dd | 38 --------------- std/process.d | 46 ++++++++++++++++--- 3 files changed, 64 insertions(+), 45 deletions(-) create mode 100644 changelog/std.process.Config.preExecDelegate.dd delete mode 100644 changelog/std.process.Config.preExecFunction-delegate.dd diff --git a/changelog/std.process.Config.preExecDelegate.dd b/changelog/std.process.Config.preExecDelegate.dd new file mode 100644 index 00000000000..393d26cf0b9 --- /dev/null +++ b/changelog/std.process.Config.preExecDelegate.dd @@ -0,0 +1,25 @@ +Add `std.process.Config.preExecDelegate` + +$(LINK2 $(ROOT_DIR)phobos/std_process.html#.Config.preExecDelegate, `std.process.Config.preExecDelegate`) +is just like +$(LINK2 $(ROOT_DIR)phobos/std_process.html#.Config.preExecFunction, `std.process.Config.preExecFunction`), +but can capture an environment, for example: + +------- +import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl; +import std.process : Config, execute; + +void runProgram(int pdeathsig) +{ + execute( + ["program"], + config: Config( + preExecDelegate: () @trusted => + prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1, + ), + ); +} +------- + +`preExecFunction` is retained for backwards compatibility. If both +`preExecFunction` and `preExecDelegate` are given, both are called. diff --git a/changelog/std.process.Config.preExecFunction-delegate.dd b/changelog/std.process.Config.preExecFunction-delegate.dd deleted file mode 100644 index e9b13b3c268..00000000000 --- a/changelog/std.process.Config.preExecFunction-delegate.dd +++ /dev/null @@ -1,38 +0,0 @@ -Promote `std.process.Config.preExecFunction` to a delegate - -$(LINK2 $(ROOT_DIR)phobos/std_process.html#.Config.preExecFunction, -`std.process.Config.preExecFunction`) is now a delegate instead of a function -pointer, and can therefore capture an environment, for example: - -------- -import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl; -import std.process : Config, execute; - -void runProgram(int pdeathsig) -{ - execute( - ["program"], - config: Config( - preExecFunction: () @trusted => - prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1, - ), - ); -} -------- - -Despite function pointers implicitly converting to delegates, this is a -backwards-incompatible change, as user code may rely on the field being a -function pointer. For example, code like the following will no longer compile: - -------- -import std.process : Config; - -nothrow pure @nogc @safe -bool f() { return true; } - -void example() -{ - auto config = Config(preExecFunction: &f); - bool function() g = config.preExecFunction; -} -------- diff --git a/std/process.d b/std/process.d index fbdb2e48ea9..325689ba51d 100644 --- a/std/process.d +++ b/std/process.d @@ -1102,6 +1102,14 @@ private Pid spawnProcessPosix(scope const(char[])[] args, } } + if (config.preExecDelegate !is null) + { + if (config.preExecDelegate() != true) + { + abortOnError(forkPipeOut, InternalError.preExec, .errno); + } + } + // Execute program. core.sys.posix.unistd.execve(argz[0], argz.ptr, envz); @@ -1187,7 +1195,7 @@ private Pid spawnProcessPosix(scope const(char[])[] args, errorMsg = "Failed to allocate memory"; break; case InternalError.preExec: - errorMsg = "Failed to execute preExecFunction"; + errorMsg = "Failed to execute preExecFunction or preExecDelegate"; break; case InternalError.noerror: assert(false); @@ -1272,18 +1280,25 @@ version (Posix) } version (Posix) -@safe unittest +@system unittest { + __gshared int j; foreach (i; 0 .. 3) { auto config = Config( - preExecFunction: delegate() @trusted { - _Exit(i); + preExecFunction: function() @trusted { + j = 1; + return true; + }, + preExecDelegate: delegate() @trusted { + // j should now be 1, as preExecFunction is called before + // preExecDelegate is. + _Exit(i + j); return true; }, ); auto pid = spawnProcess(["false"], config: config); - assert(wait(pid) == i); + assert(wait(pid) == i + 1); } } @@ -2202,13 +2217,30 @@ struct Config Please note that the code in this function must only use async-signal-safe functions.) + If $(LREF preExecDelegate) is also set, it is called last. + + On Windows, this member is not available. + */ + bool function() nothrow @nogc @safe preExecFunction; + + /** + A delegate that is called before `exec` in $(LREF spawnProcess). + It returns `true` if succeeded and otherwise returns `false`. + + $(RED Warning: + Please note that the code in this function must only use + async-signal-safe functions.) + + If $(LREF preExecFunction) is also set, it is called first. + On Windows, this member is not available. */ - bool delegate() nothrow @nogc @safe preExecFunction; + bool delegate() nothrow @nogc @safe preExecDelegate; } else version (Posix) { - bool delegate() nothrow @nogc @safe preExecFunction; + bool function() nothrow @nogc @safe preExecFunction; + bool delegate() nothrow @nogc @safe preExecDelegate; } }