public function waitAll(): bool
    {
        if (!$this->pid) {
            throw new BadMethodCallException('waitAll can only be called from the parent of a forked process');
        }

        if (!$this->async) {
            throw new BadMethodCallException('waitAll can only be used with asynchronous forked processes');
        }

        $statuses       = [];
        $this->messages = [];

        // We loop through each of the async channels in turn.
        // Although this means the loop will check each process in
        // the order it was launched, rather than in the order they
        // complete, it allows us to keep track of the PIDs of each
        // of the child processes and receive messages
        foreach ($this->asyncChannels as $pid => $channel) {
            // Wait for a process exit signal for the PID
            pcntl_waitpid($pid, $status);

            // Capture any messages returned by the child process
            if ($msg = $channel->receive()) {
                $this->messages[] = $msg;
            }

            // If the process exited gracefully, report success/failure
            // base on the exit status
            if (pcntl_wifexited($status)) {
                $statuses[$pid] = (pcntl_wexitstatus($status) === 0);
                continue;
            }

            // In all other cases, the process failed for another reason,
            // so we mark it as failed
            $statuses[$pid] = false;
        }

        // Filter the array of statuses, and check whether the count
        // of failed processes is greater than zero
        return count(array_filter($statuses, function (int $status) {
            return $status !== 0;
        })) === 0;
    }