Implementing exec task as daemon

Hello,

I am attempting to implement a task that follows the ExecSpec, but rather than wait for the process to exit it should wait for a ready condition and proceed with build once ready. Ideally this process would remain active with the gradle daemon, baring that should cleanly exit at the end of the build.

I am most of the way there with this:

class DaemonExec extends AbstractExecTask<DaemonExec> implements ExecSpec {

    private final DaemonExecActionFactory execActionFactory = objectFactory.newInstance(DaemonExecActionFactory)

    private final DaemonExecAction execAction = execActionFactory.newDecoratedDaemonExecAction()

    private final Property<ExecResult> execResult = objectFactory.property(ExecResult)

    public DaemonExec() {
        super(DaemonExec)

        setExecAction(execAction)
    }

    @Inject
    protected ObjectFactory getObjectFactory() {
        throw new UnsupportedOperationException()
    }
    
    void processReadyWhen(Closure<Boolean> closure) {
        execAction.readyWhen(closure)
    }

    @TaskAction
    protected void exec() {
        execAction.setExecutable(this.getExecutable())
        execAction.setArgs(this.getArgs())
        execAction.setWorkingDir(this.getWorkingDir())
        execAction.setEnvironment(this.getEnvironment())
        
        execResult.set(execAction.execute())
    }
}
class DefaultDaemonExecAction extends DefaultExecHandleBuilder implements ExecAction, DaemonExecAction {

    private static ExecResult waitForReady(final ExecHandle execHandle, final Closure<Boolean> readyCondition = null) {
        if(readyCondition) {
            final ReentrantLock lock = new ReentrantLock()
            final Condition stateChanged = lock.newCondition()

            lock.lock()
            try {
                //TODO stream stdout to readyCondition, unlock when returns true
                while (!execHandle.getState().isTerminal()) {
                    try {
                        stateChanged.await()
                    } catch (InterruptedException e) {
                        execHandle.abort()
                        throw UncheckedException.throwAsUncheckedException(e)
                    }
                }
            } finally {
                lock.unlock()
            }

            //TODO we need to figure out how to return a valid 
            //     ExecResult which has not yet exited
        }

        return execHandle.start().waitForFinish()
    }

    private Closure<Boolean> readyCondition = null

    @Inject
    DefaultDaemonExecAction(PathToFileResolver fileResolver, Executor executor, BuildCancellationToken buildCancellationToken) {
        super(fileResolver, executor, buildCancellationToken)
    }

    @Override
    public ExecResult execute() {
        final ExecHandle execHandle = build()
        final ExecResult execResult = waitForReady(execHandle, readyCondition)
        // TODO defer assertion until process exits
        if (!isIgnoreExitValue()) {
            execResult.assertNormalExitValue()
        }
        return execResult
    }

    @Override
    void readyWhen(Closure<Boolean> closure) {
        readyCondition = closure
    }
}

So far so good, this works just like Exec when no readyCondition is used. The remaining implementation is uninteresting, but can be found at [1] if interested.

What is unclear to me is how I should go about returning an ExecResult from waitForReady, which has not yet exited. Is this even possible? I was hoping that by implementing the task in the same fashion as Exec I could piggyback off the gradle daemon to handle the process thread.

Any ideas how to achieve this, or am I just opening up a bunch of footguns?

Cheers, and thanks for your time!

~Aaron

[1] - Full Source