| // 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; |
| } |