[igt-dev] [RFC i-g-t v2 1/4] lib/igt_core: add fork for dynamic tests
Kamil Konieczny
kamil.konieczny at linux.intel.com
Fri Oct 7 18:48:59 UTC 2022
Add new function igt_fork_dyn which can be used from dynamic
subtest in a way which allow children processes to use
igt_fork(). This is now only PoC with many code repetition.
Cc: Anna Karas <anna.karas at intel.com>
Cc: Zbigniew Kempczyński <zbigniew.kempczynski at intel.com>
Cc: Mauro Carvalho Chehab <mauro.chehab at linux.intel.com>
Cc: Petri Latvala <petri.latvala at intel.com>
Signed-off-by: Kamil Konieczny <kamil.konieczny at linux.intel.com>
---
lib/igt_core.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++
lib/igt_core.h | 22 ++++++++
2 files changed, 163 insertions(+)
diff --git a/lib/igt_core.c b/lib/igt_core.c
index 2aee0d08..001466ef 100644
--- a/lib/igt_core.c
+++ b/lib/igt_core.c
@@ -309,6 +309,12 @@ int num_test_children;
int test_children_sz;
bool test_child;
+/* fork dynamic support state */
+pid_t *test_dyn_children;
+int num_test_dyn_children;
+int test_dyn_children_sz;
+bool test_dyn_child;
+
/* For allocator purposes */
pid_t child_pid = -1;
__thread pid_t child_tid = -1;
@@ -2354,6 +2360,61 @@ bool __igt_fork(void)
}
+static void dyn_children_exit_handler(int sig)
+{
+ int status;
+
+ /* The exit handler can be called from a fatal signal, so play safe */
+ while (num_test_dyn_children-- && wait(&status))
+ ;
+}
+
+bool __igt_fork_dyn(void)
+{
+ internal_assert(!test_with_subtests || in_subtest,
+ "forking is only allowed in subtests or igt_simple_main\n");
+ internal_assert(!test_child,
+ "forking is not allowed from already forked children\n");
+ internal_assert(!test_dyn_child,
+ "dynamic forking is not allowed from running dynamic\n");
+
+ igt_install_exit_handler(dyn_children_exit_handler);
+
+ if (num_test_dyn_children >= test_dyn_children_sz) {
+ if (!test_dyn_children_sz)
+ test_dyn_children_sz = 4;
+ else
+ test_dyn_children_sz *= 2;
+
+ test_dyn_children = realloc(test_dyn_children,
+ sizeof(pid_t)*test_dyn_children_sz);
+ igt_assert(test_dyn_children);
+ }
+
+ /* ensure any buffers are flushed before fork */
+ fflush(NULL);
+
+ switch (test_dyn_children[num_test_dyn_children++] = fork()) {
+ case -1:
+ num_test_dyn_children--; /* so we won't kill(-1) during cleanup */
+ igt_assert(0);
+ case 0:
+ test_dyn_child = true;
+ pthread_mutex_init(&print_mutex, NULL);
+ //FIXME child_pid = getpid(); /* for allocator */
+ //FIXME child_tid = -1; /* for allocator */
+ exit_handler_count = 0;
+ reset_helper_process_list();
+ oom_adjust_for_doom();
+ igt_unshare_spins();
+
+ return true;
+ default:
+ return false;
+ }
+
+}
+
int __igt_waitchildren(void)
{
int err = 0;
@@ -2428,6 +2489,86 @@ void igt_waitchildren(void)
igt_fail(err);
}
+static int __igt_waitchildren_dyn(void)
+{
+ int err = 0;
+ int count;
+
+ assert(!test_dyn_child);
+
+ if (num_test_dyn_children > 0 && _igt_dynamic_tests_executed < 0)
+ _igt_dynamic_tests_executed = 0;
+ count = 0;
+ while (count < num_test_dyn_children) {
+ int status = -1;
+ int last = 0;
+ pid_t pid;
+ int c;
+
+ pid = wait(&status);
+ if (pid == -1) {
+ if (errno == EINTR)
+ continue;
+
+ printf("wait(num_dyn_children:%d) failed with %m\n",
+ num_test_dyn_children - count);
+ return IGT_EXIT_FAILURE;
+ }
+
+ for (c = 0; c < num_test_dyn_children; c++)
+ if (pid == test_dyn_children[c])
+ break;
+ if (c == num_test_dyn_children)
+ continue;
+
+ _igt_dynamic_tests_executed++;
+ if (status != 0) {
+ if (WIFEXITED(status)) {
+ printf("dynamic child %i failed with exit status %i\n",
+ c, WEXITSTATUS(status));
+ last = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ printf("dynamic child %i died with signal %i, %s\n",
+ c, WTERMSIG(status),
+ strsignal(WTERMSIG(status)));
+ last = 128 + WTERMSIG(status);
+ } else {
+ printf("Unhandled failure [%d] in dynamic child %i\n", status, c);
+ last = 256;
+ }
+
+ /* we don't want to overwrite error with skip */
+ if (err == 0 || err == IGT_EXIT_SKIP)
+ err = last;
+ //FIXME igt_kill_dyn_children(SIGKILL); // if non-skip happen
+ }
+
+ count++;
+ }
+
+ num_test_dyn_children = 0;
+
+ return err;
+}
+
+/**
+ * igt_waitchildren_dyn:
+ *
+ * Wait for all children forked with igt_fork_dyn.
+ */
+void igt_waitchildren_dyn(void)
+{
+ int err = __igt_waitchildren_dyn();
+
+ igt_debug("%s: err=%d\n", __func__, err);
+ if (err) {
+ if (err == IGT_EXIT_SKIP)
+ igt_skip("__dynamic__"); //FIXME
+ else
+ igt_fail(err);
+ }
+}
+
static void igt_alarm_killchildren(int signal)
{
igt_info("Timed out waiting for children\n");
diff --git a/lib/igt_core.h b/lib/igt_core.h
index f21723de..d124364b 100644
--- a/lib/igt_core.h
+++ b/lib/igt_core.h
@@ -1108,6 +1108,28 @@ void igt_waitchildren(void);
void igt_waitchildren_timeout(int seconds, const char *reason);
void igt_kill_children(int signal);
+bool __igt_fork_dyn(void);
+/**
+ * igt_fork_dyn:
+ * @child: name of the int variable with the child number
+ * @num_children: number of children to fork
+ *
+ * This is a magic control flow block which spawns parallel test with fork()
+ * expecting there will be dynamic subtests run.
+ *
+ * The test children execute in parallel to the main test thread. Joining all
+ * test threads should be done with igt_waitchildren_dyn to ensure that the exit
+ * codes of all children are properly reflected in the test status.
+ *
+ * igt_skip() will be collected from childs.
+ *
+ */
+#define igt_fork_dyn(child, num_children) \
+ for (int child = 0; child < (num_children); child++) \
+ for (; __igt_fork_dyn(); exit(0))
+
+void igt_waitchildren_dyn(void);
+
/**
* igt_helper_process:
* @running: indicates whether the process is currently running
--
2.34.1
More information about the igt-dev
mailing list