| |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <poll.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <sys/eventfd.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "barrier.h" |
| #include "fd-util.h" |
| #include "macro.h" |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int barrier_create(Barrier *b) { |
| _cleanup_(barrier_destroyp) Barrier *staging = b; |
| int r; |
| |
| assert(b); |
| |
| b->me = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); |
| if (b->me < 0) |
| return -errno; |
| |
| b->them = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); |
| if (b->them < 0) |
| return -errno; |
| |
| r = pipe2(b->pipe, O_CLOEXEC | O_NONBLOCK); |
| if (r < 0) |
| return -errno; |
| |
| staging = NULL; |
| return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void barrier_destroy(Barrier *b) { |
| if (!b) |
| return; |
| |
| b->me = safe_close(b->me); |
| b->them = safe_close(b->them); |
| safe_close_pair(b->pipe); |
| b->barriers = 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void barrier_set_role(Barrier *b, unsigned int role) { |
| int fd; |
| |
| assert(b); |
| assert(IN_SET(role, BARRIER_PARENT, BARRIER_CHILD)); |
| |
| assert(b->pipe[0] >= 0 && b->pipe[1] >= 0); |
| |
| if (role == BARRIER_PARENT) |
| b->pipe[1] = safe_close(b->pipe[1]); |
| else { |
| b->pipe[0] = safe_close(b->pipe[0]); |
| |
| |
| fd = b->me; |
| b->me = b->them; |
| b->them = fd; |
| } |
| } |
| |
| |
| static bool barrier_write(Barrier *b, uint64_t buf) { |
| ssize_t len; |
| |
| |
| if (barrier_i_aborted(b)) |
| return false; |
| |
| assert(b->me >= 0); |
| do { |
| len = write(b->me, &buf, sizeof(buf)); |
| } while (len < 0 && IN_SET(errno, EAGAIN, EINTR)); |
| |
| if (len != sizeof(buf)) |
| goto error; |
| |
| |
| if (buf >= (uint64_t)BARRIER_ABORTION) { |
| if (barrier_they_aborted(b)) |
| b->barriers = BARRIER_WE_ABORTED; |
| else |
| b->barriers = BARRIER_I_ABORTED; |
| } else if (!barrier_is_aborted(b)) |
| b->barriers += buf; |
| |
| return !barrier_i_aborted(b); |
| |
| error: |
| |
| |
| |
| |
| |
| safe_close_pair(b->pipe); |
| b->barriers = BARRIER_WE_ABORTED; |
| return false; |
| } |
| |
| |
| static bool barrier_read(Barrier *b, int64_t comp) { |
| if (barrier_they_aborted(b)) |
| return false; |
| |
| while (b->barriers > comp) { |
| struct pollfd pfd[2] = { |
| { .fd = b->pipe[0] >= 0 ? b->pipe[0] : b->pipe[1], |
| .events = POLLHUP }, |
| { .fd = b->them, |
| .events = POLLIN }}; |
| uint64_t buf; |
| int r; |
| |
| r = poll(pfd, 2, -1); |
| if (r < 0 && IN_SET(errno, EAGAIN, EINTR)) |
| continue; |
| else if (r < 0) |
| goto error; |
| |
| if (pfd[1].revents) { |
| ssize_t len; |
| |
| |
| len = read(b->them, &buf, sizeof(buf)); |
| if (len < 0 && IN_SET(errno, EAGAIN, EINTR)) |
| continue; |
| |
| if (len != sizeof(buf)) |
| goto error; |
| } else if (pfd[0].revents & (POLLHUP | POLLERR | POLLNVAL)) |
| |
| |
| |
| |
| |
| buf = BARRIER_ABORTION; |
| else |
| continue; |
| |
| |
| if (buf >= (uint64_t)BARRIER_ABORTION) { |
| if (barrier_i_aborted(b)) |
| b->barriers = BARRIER_WE_ABORTED; |
| else |
| b->barriers = BARRIER_THEY_ABORTED; |
| } else if (!barrier_is_aborted(b)) |
| b->barriers -= buf; |
| } |
| |
| return !barrier_they_aborted(b); |
| |
| error: |
| |
| |
| |
| |
| |
| safe_close_pair(b->pipe); |
| b->barriers = BARRIER_WE_ABORTED; |
| return false; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool barrier_place(Barrier *b) { |
| assert(b); |
| |
| if (barrier_is_aborted(b)) |
| return false; |
| |
| barrier_write(b, BARRIER_SINGLE); |
| return true; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool barrier_abort(Barrier *b) { |
| assert(b); |
| |
| barrier_write(b, BARRIER_ABORTION); |
| return !barrier_they_aborted(b); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool barrier_wait_next(Barrier *b) { |
| assert(b); |
| |
| if (barrier_is_aborted(b)) |
| return false; |
| |
| barrier_read(b, b->barriers - 1); |
| return !barrier_is_aborted(b); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool barrier_wait_abortion(Barrier *b) { |
| assert(b); |
| |
| barrier_read(b, BARRIER_THEY_ABORTED); |
| return !barrier_i_aborted(b); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool barrier_sync_next(Barrier *b) { |
| assert(b); |
| |
| if (barrier_is_aborted(b)) |
| return false; |
| |
| barrier_read(b, MAX((int64_t)0, b->barriers - 1)); |
| return !barrier_is_aborted(b); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool barrier_sync(Barrier *b) { |
| assert(b); |
| |
| if (barrier_is_aborted(b)) |
| return false; |
| |
| barrier_read(b, 0); |
| return !barrier_is_aborted(b); |
| } |