diff --git a/gtests/net/packetdrill/code.c b/gtests/net/packetdrill/code.c index e0b89e917813f489a6788162f0a8b864ec9ade9f..e04a9ecba9473ff18c4b72215515518d8fea3d0b 100644 --- a/gtests/net/packetdrill/code.c +++ b/gtests/net/packetdrill/code.c @@ -619,7 +619,7 @@ void run_code_event(struct state *state, struct event *event, error_out: script_path = strdup(state->config->script_path); - state_free(state); + state_free(state, 1); die("%s:%d: runtime error in code: %s\n", script_path, event->line_number, error); free(script_path); diff --git a/gtests/net/packetdrill/run.c b/gtests/net/packetdrill/run.c index f033a3181fbf45caa55c84dbabd6dd5592d7383e..0a7d3751bd667fac6ae65b56133538f363df32ac 100644 --- a/gtests/net/packetdrill/run.c +++ b/gtests/net/packetdrill/run.c @@ -128,12 +128,12 @@ static void close_all_sockets(struct state *state) } } -void state_free(struct state *state) +void state_free(struct state *state, int about_to_die) { /* We have to stop the system call thread first, since it's using * sockets that we want to close and reset. */ - syscalls_free(state, state->syscalls); + syscalls_free(state, state->syscalls, about_to_die); /* Then we close the sockets and reset the connections, while * we still have a netdev for injecting reset packets to free @@ -402,7 +402,7 @@ static void run_local_packet_event(struct state *state, struct event *event, fprintf(stderr, "%s", error); free(error); } else if (result == STATUS_ERR) { - state_free(state); + state_free(state, 1); die("%s\n", error); } } @@ -553,7 +553,7 @@ void run_script(struct config *config, struct script *script) while (1) { if (get_next_event(state, &error)) { - state_free(state); + state_free(state, 1); die("%s\n", error); } event = state->event; @@ -603,14 +603,14 @@ void run_script(struct config *config, struct script *script) if (code_execute(state->code, &error)) { char *script_path = strdup(state->config->script_path); - state_free(state); + state_free(state, 1); die("%s: error executing code: %s\n", script_path, error); free(script_path); free(error); } - state_free(state); + state_free(state, 0); DEBUGP("run_script: done running\n"); } diff --git a/gtests/net/packetdrill/run.h b/gtests/net/packetdrill/run.h index bb186fde590792da8fdea54bf7c8cf97ca2e8af9..981692102cf1cb7bc326bcf7b0793316bba3b123 100644 --- a/gtests/net/packetdrill/run.h +++ b/gtests/net/packetdrill/run.h @@ -110,7 +110,7 @@ extern struct state *state_new(struct config *config, struct netdev *netdev); /* Free all run-time state for a test. */ -void state_free(struct state *state); +void state_free(struct state *state, int about_to_die); /* Grab the global lock for all global state. */ static inline void run_lock(struct state *state) diff --git a/gtests/net/packetdrill/run_command.c b/gtests/net/packetdrill/run_command.c index 7fbaa9730f752d3056d1f44d27dc43fc39b8679f..288ee464d5608bcbe2608dfa308dae94b92dfa62 100644 --- a/gtests/net/packetdrill/run_command.c +++ b/gtests/net/packetdrill/run_command.c @@ -50,7 +50,7 @@ void run_command_event( error_out: script_path = strdup(state->config->script_path); - state_free(state); + state_free(state, 1); die("%s:%d: error executing `%s` command: %s\n", script_path, event->line_number, command->command_line, error); diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c index 93560daa51ef1e333c603c54ce7d49421b071207..60fbd33a11d8ca6534dcd76b915351b3f6f1deaf 100644 --- a/gtests/net/packetdrill/run_system_call.c +++ b/gtests/net/packetdrill/run_system_call.c @@ -6125,7 +6125,7 @@ static void invoke_system_call( error_out: script_path = strdup(state->config->script_path); - state_free(state); + state_free(state, 1); die("%s:%d: runtime error in %s call: %s\n", script_path, event->line_number, syscall->name, error); @@ -6236,7 +6236,7 @@ static void enqueue_system_call( error_out: script_path = strdup(state->config->script_path); - state_free(state); + state_free(state, 1); die("%s:%d: runtime error in %s call: %s\n", script_path, event->line_number, syscall->name, error); @@ -6378,31 +6378,35 @@ struct syscalls *syscalls_new(struct state *state) return syscalls; } -void syscalls_free(struct state *state, struct syscalls *syscalls) +void syscalls_free(struct state *state, struct syscalls *syscalls, int about_to_die) { + int status; + /* Wait a bit for the thread to go idle. */ - if (await_idle_thread(state)) { + status = await_idle_thread(state); + if ((status == STATUS_ERR) && (about_to_die == 0)) { die("%s:%d: runtime error: exiting while " "a blocking system call is in progress\n", state->config->script_path, syscalls->event->line_number); } - /* Send a request to terminate the thread. */ - DEBUGP("main thread: signaling syscall thread to exit\n"); - syscalls->state = SYSCALL_EXITING; - if (pthread_cond_signal(&syscalls->enqueued) != 0) - die_perror("pthread_cond_signal"); - - /* Release the lock briefly and wait for syscall thread to finish. */ - run_unlock(state); - DEBUGP("main thread: unlocking, waiting for syscall thread exit\n"); - void *thread_result = NULL; - if (pthread_join(syscalls->thread, &thread_result) != 0) - die_perror("pthread_cancel"); - DEBUGP("main thread: joined syscall thread; relocking\n"); - run_lock(state); + if (status == STATUS_OK) { + /* Send a request to terminate the thread. */ + DEBUGP("main thread: signaling syscall thread to exit\n"); + syscalls->state = SYSCALL_EXITING; + if (pthread_cond_signal(&syscalls->enqueued) != 0) + die_perror("pthread_cond_signal"); + /* Release the lock briefly and wait for syscall thread to finish. */ + run_unlock(state); + DEBUGP("main thread: unlocking, waiting for syscall thread exit\n"); + void *thread_result = NULL; + if (pthread_join(syscalls->thread, &thread_result) != 0) + die_perror("pthread_cancel"); + DEBUGP("main thread: joined syscall thread; relocking\n"); + run_lock(state); + } if ((pthread_cond_destroy(&syscalls->idle) != 0) || (pthread_cond_destroy(&syscalls->enqueued) != 0) || (pthread_cond_destroy(&syscalls->dequeued) != 0)) { diff --git a/gtests/net/packetdrill/run_system_call.h b/gtests/net/packetdrill/run_system_call.h index 7dbd0887748c3f0dec0114cc8ddfc8f0333a620d..ca30c5bed77ad794e0be11bc9da85e81031199af 100644 --- a/gtests/net/packetdrill/run_system_call.h +++ b/gtests/net/packetdrill/run_system_call.h @@ -88,7 +88,8 @@ extern struct syscalls *syscalls_new(struct state *state); /* Tear down a syscalls and free up the resources it has allocated. */ extern void syscalls_free(struct state *state, - struct syscalls *syscalls); + struct syscalls *syscalls, + int about_to_die); /* Execute the given system call event. The system call may be * expected to block for a while, or it may be expected to return diff --git a/gtests/net/packetdrill/wire_server.c b/gtests/net/packetdrill/wire_server.c index ae144a9f3f932b48ba55917aa6ddfb226b8c7139..25e3d372577ed6149a7e6be9ce586761dd67d8b3 100644 --- a/gtests/net/packetdrill/wire_server.c +++ b/gtests/net/packetdrill/wire_server.c @@ -491,7 +491,7 @@ error_done: fprintf(stderr, "%s\n", error); if (wire_server->state != NULL) - state_free(wire_server->state); + state_free(wire_server->state, 0); DEBUGP("wire_server_thread: connection is done\n"); wire_server_free(wire_server);