public function run(Context $context, array $args, ?callable $stdin = null, ?callable $stdout = null, ?callable $stderr = null): Promise
    {
        static $discard;
        static $closer;
        
        if ($discard === null) {
            $discard = static function (Context $context, ReadableStream $stream) {
                yield $stream->discard($context);
            };
        }
        
        if ($closer === null) {
            $closer = static function (Context $context, ?callable $callback, $pipe) {
                $result = null;
                
                try {
                    if ($callback) {
                        $result = yield from Coroutine::generate($callback, $context, $pipe);
                    }
                } finally {
                    $pipe->close();
                }
                
                return $result;
            };
        }
        
        return $context->task(function (Context $context) use ($args, $stdin, $stdout, $stderr, $closer, $discard) {
            $process = yield $this->start($context, $args);
            
            try {
                $context = $context->cancellable($cancel = $context->cancellationHandler());
                
                yield $context->all([
                    $closer($context, $stdin, $process->getStdin()),
                    $closer($context, $stdout ?? $discard, $process->getStdout()),
                    $closer($context, $stderr ?? $discard, $process->getStderr())
                ], $cancel);
                
                $code = yield $process->exitCode($context);
            } finally {
                $process->close();
            }
            
            return $code;
        });
    }