root/tools/testing/selftests/powerpc/ptrace/child.h
// SPDX-License-Identifier: GPL-2.0+
/*
 * Helper functions to sync execution between parent and child processes.
 *
 * Copyright 2018, Thiago Jung Bauermann, IBM Corporation.
 */
#include <stdio.h>
#include <stdbool.h>
#include <semaphore.h>

/*
 * Information in a shared memory location for synchronization between child and
 * parent.
 */
struct child_sync {
        /* The parent waits on this semaphore. */
        sem_t sem_parent;

        /* If true, the child should give up as well. */
        bool parent_gave_up;

        /* The child waits on this semaphore. */
        sem_t sem_child;

        /* If true, the parent should give up as well. */
        bool child_gave_up;
};

#define CHILD_FAIL_IF(x, sync)                                          \
        do {                                                            \
                if (x) {                                                \
                        fprintf(stderr,                                 \
                                "[FAIL] Test FAILED on line %d\n", __LINE__); \
                        (sync)->child_gave_up = true;                   \
                        prod_parent(sync);                              \
                        return 1;                                       \
                }                                                       \
        } while (0)

#define PARENT_FAIL_IF(x, sync)                                         \
        do {                                                            \
                if (x) {                                                \
                        fprintf(stderr,                                 \
                                "[FAIL] Test FAILED on line %d\n", __LINE__); \
                        (sync)->parent_gave_up = true;                  \
                        prod_child(sync);                               \
                        return 1;                                       \
                }                                                       \
        } while (0)

#define PARENT_SKIP_IF_UNSUPPORTED(x, sync, msg)                        \
        do {                                                            \
                if ((x) == -1 && (errno == ENODEV || errno == EINVAL)) { \
                        (sync)->parent_gave_up = true;                  \
                        prod_child(sync);                               \
                        SKIP_IF_MSG(1, msg);                            \
                }                                                       \
        } while (0)

int init_child_sync(struct child_sync *sync)
{
        int ret;

        ret = sem_init(&sync->sem_parent, 1, 0);
        if (ret) {
                perror("Semaphore initialization failed");
                return 1;
        }

        ret = sem_init(&sync->sem_child, 1, 0);
        if (ret) {
                perror("Semaphore initialization failed");
                return 1;
        }

        return 0;
}

void destroy_child_sync(struct child_sync *sync)
{
        sem_destroy(&sync->sem_parent);
        sem_destroy(&sync->sem_child);
}

int wait_child(struct child_sync *sync)
{
        int ret;

        /* Wait until the child prods us. */
        ret = sem_wait(&sync->sem_parent);
        if (ret) {
                perror("Error waiting for child");
                return 1;
        }

        return sync->child_gave_up;
}

int prod_child(struct child_sync *sync)
{
        int ret;

        /* Unblock the child now. */
        ret = sem_post(&sync->sem_child);
        if (ret) {
                perror("Error prodding child");
                return 1;
        }

        return 0;
}

int wait_parent(struct child_sync *sync)
{
        int ret;

        /* Wait until the parent prods us. */
        ret = sem_wait(&sync->sem_child);
        if (ret) {
                perror("Error waiting for parent");
                return 1;
        }

        return sync->parent_gave_up;
}

int prod_parent(struct child_sync *sync)
{
        int ret;

        /* Unblock the parent now. */
        ret = sem_post(&sync->sem_parent);
        if (ret) {
                perror("Error prodding parent");
                return 1;
        }

        return 0;
}