Commit 111d6deb authored by jan.koester's avatar jan.koester
Browse files

deadlock removed

parent 842acb15
Loading
Loading
Loading
Loading
+12 −4
Changes for executor.cpp: 12 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <sys/wait.h>
#include <unistd.h>
#include <spawn.h>
#include <poll.h>

extern char** environ;

@@ -334,19 +335,26 @@ std::vector<std::string> Executor::collect_exports(const Makefile& mf) {
bool Executor::js_acquire(bool& used_free) {
    used_free = false;
    if (!js_active_) return true;
    for (;;) {
        // Prefer this process's implicit free token. Re-checked every loop so a
        // sibling thread releasing it (an atomic store) lets a waiter proceed.
        bool expected = true;
        if (js_free_token_.compare_exchange_strong(expected, false)) {
            used_free = true;
            return true;
        }
    for (;;) {
        // Try to grab a shared token from the (non-blocking) jobserver pipe.
        char tok;
        ssize_t n = ::read(js_read_fd_, &tok, 1);
        if (n == 1) return true;
        if (n < 0 && errno == EINTR) continue;
        // Pipe closed/error: fall back to running without a token rather than
        // deadlocking the build.
        return true;
        if (n == 0) return true;  // write end fully closed: avoid deadlock
        if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
            return true;          // unexpected error: avoid deadlock
        // No token available right now. Wait briefly for the pipe to become
        // readable, but cap the wait so a freed-up free token is noticed.
        struct pollfd pfd{ js_read_fd_, POLLIN, 0 };
        ::poll(&pfd, 1, 50);
    }
}

+11 −0
Changes for executor.h: 11 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <queue>
#include <atomic>
#include <memory>
#include <fcntl.h>

namespace makeplus {

@@ -28,6 +29,16 @@ public:
        js_read_fd_ = read_fd;
        js_write_fd_ = write_fd;
        js_active_ = (read_fd >= 0 && write_fd >= 0);
        if (js_active_) {
            // The whole recursive make tree is makeplus, so we control both ends
            // of the jobserver pipe. Make the read end non-blocking: a worker
            // waiting for a token must be able to wake up when this process's
            // per-process "free" token is released (an atomic, which cannot wake
            // a blocking read()), otherwise a thread that has committed to a
            // target could hang forever once the pipe is empty.
            int fl = ::fcntl(read_fd, F_GETFL);
            if (fl != -1) ::fcntl(read_fd, F_SETFL, fl | O_NONBLOCK);
        }
    }

    int build(const std::vector<std::string>& targets);