Re: [PATCH 7/7] t/helper/simple-ipc: convert test-simple-ipc to use start_bg_command
From: Ævar Arnfjörð Bjarmason <hidden>
Date: 2021-09-18 09:12:02
On Fri, Sep 17 2021, Jeff Hostetler wrote:
On 9/16/21 1:06 AM, Taylor Blau wrote:quoted
On Wed, Sep 15, 2021 at 08:36:17PM +0000, Jeff Hostetler via GitGitGadget wrote:quoted
From: Jeff Hostetler <redacted> Convert test helper to use `start_bg_command()` when spawning a server daemon in the background rather than blocks of platform-specific code. Signed-off-by: Jeff Hostetler <redacted> --- t/helper/test-simple-ipc.c | 193 ++++++++----------------------------- 1 file changed, 40 insertions(+), 153 deletions(-)diff --git a/t/helper/test-simple-ipc.c b/t/helper/test-simple-ipc.c index 91345180750..59a950f3b00 100644 --- a/t/helper/test-simple-ipc.c +++ b/t/helper/test-simple-ipc.c@@ -9,6 +9,7 @@ #include "parse-options.h" #include "thread-utils.h" #include "strvec.h" +#include "run-command.h" #ifndef SUPPORTS_SIMPLE_IPC int cmd__simple_ipc(int argc, const char **argv)@@ -274,178 +275,64 @@ static int daemon__run_server(void) return ret; } -#ifndef GIT_WINDOWS_NATIVE -/* - * This is adapted from `daemonize()`. Use `fork()` to directly create and - * run the daemon in a child process. - */ -static int spawn_server(pid_t *pid) -{ - struct ipc_server_opts opts = { - .nr_threads = cl_args.nr_threads, - }; +static start_bg_wait_cb bg_wait_cb;This whole patch is delightful to read, as the new implementation is so much cleaner as a result of the earlier work in this series. Am I correct in assuming that this is to encourage a compiler error if bg_wait_cb does not satisfy the type of start_bg_wait_cb? If so, then I think we are already getting that by trying to pass bg_wait_cb to start_bg_command().I use that trick to get the compiler to give me a compiler error at the point of the function declaration. For example, If I add an arg to the function that doesn't match what's in the prototype definition, I get: t/helper/test-simple-ipc.c:280:12: error: conflicting types for 'bg_wait_cb' static int bg_wait_cb(const struct child_process *cp, void *cb_data, int foo) ^ t/helper/test-simple-ipc.c:278:25: note: previous declaration is here static start_bg_wait_cb bg_wait_cb; ^ 1 error generated. Yes, we may get an error when the function pointer is referenced in start_bg_command() or if we're using it to initialize a vtable or something, but those errors are further away from the actual error (and sometimes they can be a little cryptic). Also, it helps document that this function's signature is predefined for a reason. It's a quirky trick I know, but it has served me well over the years.
I haven't seen this idiom before. I think it's best to avoid patterns
designed to massage messages out of any specific compilers/versions.
It seems inevitable that it'll either be counter-productive or
redundant. Here with clang v11 doing this makes the warning
worse. I.e. without the forward declaration:
t/helper/test-simple-ipc.c:315:31: error: incompatible function
pointer types passing 'int (void *, const struct child_process *,
int)' to parameter of type ' start_bg_wait_cb *' (aka 'int (*)(void
*, const struct child_process *)')
[-Werror,-Wincompatible-function-pointer-types]
sbgr = start_bg_command(&cp, bg_wait_cb, NULL, cl_args.max_wait_sec);
^~~~~~~~~~
./run-command.h:564:29: note: passing argument to parameter 'wait_cb' here
start_bg_wait_cb *wait_cb,
^
1 error generated.
I.e. we get the specific warning category for this type of error
(-Werror,-Wincompatible-function-pointer-types), and we're pointed at
the caller in question (which to be fair, it seems you don't prefer),
but also a reference to the run-command.h definition.
Most importantly, we get quoted what the type is/should be, which is
missing with the forward declaration. It's the equivalent of saying "you
did bad!" instead of "you did bad X, do Y instead!".
quoted
E.g., applying this (intentionally broken) diff on top:--- 8< --- diff --git a/t/helper/test-simple-ipc.c b/t/helper/test-simple-ipc.c index 59a950f3b0..3aed787206 100644 --- a/t/helper/test-simple-ipc.c +++ b/t/helper/test-simple-ipc.c@@ -275,9 +275,7 @@ static int daemon__run_server(void) return ret; } -static start_bg_wait_cb bg_wait_cb; - -static int bg_wait_cb(void *cb_data, const struct child_process *cp) +static int bg_wait_cb(const void *cb_data, const struct child_process *cp) { int s = ipc_get_active_state(cl_args.path); --- >8 ---and then compiling still warns of a mismatched type when calling start_bg_command().quoted
- *pid = fork(); - - switch (*pid) { - case 0: - if (setsid() == -1) - error_errno(_("setsid failed")); - close(0); - close(1); - close(2); - sanitize_stdfds(); +static int bg_wait_cb(void *cb_data, const struct child_process *cp) +{ + int s = ipc_get_active_state(cl_args.path); - return ipc_server_run(cl_args.path, &opts, test_app_cb, - (void*)&my_app_data); + switch (s) { + case IPC_STATE__LISTENING: + /* child is "ready" */ + return 0; - case -1: - return error_errno(_("could not spawn daemon in the background")); + case IPC_STATE__NOT_LISTENING: + case IPC_STATE__PATH_NOT_FOUND: + /* give child more time */ + return 1; default:I'm always a little hesitant to have default cases when switch over enum types, since it suppresses the warning when there's a new value of that type. But we already have a similar default in client__probe_server().Do all compilers now handle switching over an enum and detect unhandled cases? Once upon a time that wasn't the case IIRC.
I don't think so, but the ones we widely use do, i.e. clang and gcc at least. For this sort of thing it really doesn't matter if *all* compilers support it, since we'll only need to catch such "missing enum arm" issues with one of them. E.g. in my 338abb0f045 (builtins + test helpers: use return instead of exit() in cmd_*, 2021-06-08) I fixed something that I've only gotten Oracle SunCC to emit (gcc and clang don't detect it), but as long as that one compiler does & someone checks it regularly... By having a "default" case you're hiding that detection from the compilers capable of detecting a logic error in this code, whereas if the compiler can't do that it'll just ignore it.