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