child_process#StdioOptions TypeScript Examples

The following examples show how to use child_process#StdioOptions. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From AIPerf with MIT License 7 votes vote down vote up
/**
 * run command as ChildProcess
 */
function getTunerProc(command: string, stdio: StdioOptions, newCwd: string, newEnv: any): ChildProcess {
    let cmd: string = command;
    let arg: string[] = [];
    let newShell: boolean = true;
    if(process.platform === "win32"){
        cmd = command.split(" ", 1)[0];
        arg = command.substr(cmd.length+1).split(" ");
        newShell = false;
    }
    const tunerProc: ChildProcess = spawn(cmd, arg, {
        stdio,
        cwd: newCwd,
        env: newEnv,
        shell: newShell
    });
    return tunerProc;
}
Example #2
Source File: nnimanager.ts    From AIPerf with MIT License 6 votes vote down vote up
private setupTuner(command: string, cwd: string | undefined, mode: 'start' | 'resume', dataDirectory: string): void {
        if (this.dispatcher !== undefined) {
            return;
        }
        const stdio: StdioOptions = ['ignore', process.stdout, process.stderr, 'pipe', 'pipe'];
        let newCwd: string;
        if (cwd === undefined || cwd === '') {
            newCwd = getLogDir();
        } else {
            newCwd = cwd;
        }
        // TO DO: add CUDA_VISIBLE_DEVICES
        let includeIntermediateResultsEnv: boolean | undefined = false;
        if (this.experimentProfile.params.tuner !== undefined) {
            includeIntermediateResultsEnv = this.experimentProfile.params.tuner.includeIntermediateResults;
        }

        const nniEnv = {
            SDK_PROCESS: 'dispatcher',
            NNI_MODE: mode,
            NNI_CHECKPOINT_DIRECTORY: dataDirectory,
            NNI_LOG_DIRECTORY: getLogDir(),
            NNI_LOG_LEVEL: getLogLevel(),
            NNI_INCLUDE_INTERMEDIATE_RESULTS: includeIntermediateResultsEnv,
            CUDA_VISIBLE_DEVICES: this.getGpuEnvvarValue()
        };
        const newEnv = Object.assign({}, process.env, nniEnv);
        const tunerProc: ChildProcess = getTunerProc(command,stdio,newCwd,newEnv);
        this.dispatcherPid = tunerProc.pid;
        this.dispatcher = createDispatcherInterface(tunerProc);

        return;
    }
Example #3
Source File: ipcInterface.test.ts    From AIPerf with MIT License 5 votes vote down vote up
function runProcess(): Promise<Error | null> {
    // the process is intended to throw error, do not reject
    const deferred: Deferred<Error | null> = new Deferred<Error | null>();

    // create fake assessor process
    const stdio: StdioOptions = ['ignore', 'pipe', process.stderr, 'pipe', 'pipe'];
    const command: string = getCmdPy() + ' assessor.py';
    const proc: ChildProcess = getTunerProc(command, stdio,  'core/test', process.env);
    // record its sent/received commands on exit
    proc.on('error', (error: Error): void => { deferred.resolve(error); });
    proc.on('exit', (code: number): void => {
        if (code !== 0) {
            deferred.resolve(new Error(`return code: ${code}`));
        } else {
            let str = proc.stdout.read().toString();
            if(str.search("\r\n")!=-1){
                sentCommands = str.split("\r\n");
            }
            else{
                sentCommands = str.split('\n');
            }
            deferred.resolve(null);
        }
    });

    // create IPC interface
    const dispatcher: IpcInterface = createDispatcherInterface(proc);
    dispatcher.onCommand((commandType: string, content: string): void => {
        receivedCommands.push({ commandType, content });
    });

    // Command #1: ok
    dispatcher.sendCommand('IN');

    // Command #2: ok
    dispatcher.sendCommand('ME', '123');

    // Command #3: too long
    try {
        dispatcher.sendCommand('ME', 'x'.repeat(1_000_000));
    } catch (error) {
        commandTooLong = error;
    }

    // Command #4: FE is not tuner/assessor command, test the exception type of send non-valid command
    try {
        dispatcher.sendCommand('FE', '1');
    } catch (error) {
        rejectCommandType = error;
    }

    return deferred.promise;
}
Example #4
Source File: ipcInterfaceTerminate.test.ts    From AIPerf with MIT License 5 votes vote down vote up
function startProcess(): void {
    // create fake assessor process
    const stdio: StdioOptions = ['ignore', 'pipe', process.stderr, 'pipe', 'pipe'];

    const dispatcherCmd: string = getMsgDispatcherCommand(
        // Mock tuner config
        {
            experimentName: 'exp1',
            maxExecDuration: 3600,
            searchSpace: '',
            trainingServicePlatform: 'local',
            authorName: '',
            trialConcurrency: 1,
            maxTrialNum: 5,
            tuner: {
                className: 'DummyTuner',
                codeDir: './',
                classFileName: 'dummy_tuner.py',
                checkpointDir: './'
            },
            assessor: {
                className: 'DummyAssessor',
                codeDir: './',
                classFileName: 'dummy_assessor.py',
                checkpointDir: './'
            }
        }
    );
    const proc: ChildProcess = getTunerProc(dispatcherCmd, stdio,  'core/test', process.env);
    proc.on('error', (error: Error): void => {
        procExit = true;
        procError = true;
    });
    proc.on('exit', (code: number): void => {
        procExit = true;
        procError = (code !== 0);
    });

    // create IPC interface
    dispatcher = createDispatcherInterface(proc);
    (<IpcInterface>dispatcher).onCommand((commandType: string, content: string): void => {
        console.log(commandType, content);
    });
}
Example #5
Source File: service.ts    From cardano-launcher with Apache License 2.0 4 votes vote down vote up
/**
 * Initialise a [[Service]] which can control the lifetime of a
 * backend process.
 *
 * This does not start the process. Use [[Service.start]] for that.
 *
 * @param cfgPromise - a promise which will return the command to run.
 * @param logger - logging object.
 * @param childProcessLogWriteStream - WriteStream for writing the child process data events from stdout and stderr.
 * @return A handle on the [[Service]].
 */
export function setupService(
  cfgPromise: Promise<StartService>,
  logger: Logger = console,
  childProcessLogWriteStream?: WriteStream
): Service {
  const events = new ServiceEvents();
  // What the current state is.
  let status = ServiceStatus.NotStarted;
  // Fulfilled promise of service command-line.
  // This will always be defined if status > Starting.
  let cfg: StartService;
  // NodeJS child process object, or null if not running.
  let proc: ChildProcess | null = null;
  // Pipe file descriptor for clean shutdown, or null if not yet running.
  let shutdownFD: number | null;
  // When the service started (milliseconds since epoch)
  let startTimeMs = 0;
  // How the child process exited, or null if it hasn't yet exited.
  let exitStatus: ServiceExitStatus | null;
  // For cancelling the kill timeout.
  let killTimer: NodeJS.Timeout | null = null;
  let startPromise: Promise<Pid>;

  const setStatus = (newStatus: ServiceStatus): void => {
    logger.debug(
      `setStatus ${ServiceStatus[status]} -> ${ServiceStatus[newStatus]}`
    );
    status = newStatus;
    if (status === ServiceStatus.Started) {
      startTimeMs = Date.now();
    }
    events.statusChanged(status);
  };

  const onStopped = (
    code: number | null = null,
    signal: string | null = null,
    err: Error | null = null
  ): void => {
    exitStatus = { exe: cfg.command, code, signal, err };
    logger.debug(`Service onStopped`, exitStatus);
    if (killTimer) {
      clearTimeout(killTimer);
      killTimer = null;
    }
    proc = null;
    setStatus(ServiceStatus.Stopped);
  };

  const doStart = async (): Promise<Pid> => {
    const envStr = _.map(
      cfg.extraEnv,
      (value, name) => `${name}=${value} `
    ).join('');
    const commandStr = `${envStr}${cfg.command} ${cfg.args.join(' ')}`;
    logger.info(`Service.start: trying to start ${commandStr}`, cfg);
    const stdOuts = childProcessLogWriteStream ? 'pipe' : 'inherit';
    const stdio = [
      cfg.shutdownMethod === ShutdownMethod.CloseStdin ? 'pipe' : 'ignore',
      stdOuts,
      stdOuts,
    ].concat(
      cfg.shutdownMethod === ShutdownMethod.CloseFD ? ['pipe'] : []
    ) as StdioOptions;
    const cwd = cfg.cwd ? { cwd: cfg.cwd } : {};
    const env = cfg.extraEnv
      ? Object.assign({}, process.env, cfg.extraEnv)
      : process.env;
    const options = Object.assign({ stdio }, cwd, { env });
    try {
      proc = spawn(cfg.command, cfg.args, options);
    } catch (err) {
      logger.error(`Service.start: child_process.spawn() failed: ${err}`);
      logger.error(
        `Service.start: child_process.spawn(${cfg.command}, ${cfg.args.join(
          ' '
        )}, ...)`,
        options
      );
      throw err;
    }
    if (cfg.shutdownMethod === ShutdownMethod.CloseStdin) {
      // corresponds to first element of `stdio` above
      shutdownFD = 0;
    } else if (cfg.shutdownMethod === ShutdownMethod.CloseFD) {
      // corresponds to last element of `stdio` above
      shutdownFD = cleanShutdownFD;
    }
    setStatus(ServiceStatus.Started);
    proc.on('exit', (code, signal) => {
      onStopped(code, signal);
    });
    proc.on('error', err => {
      logger.error(`Service.start: child_process failed: ${err}`);
      onStopped(null, null, err);
    });
    if (proc.stdout && proc.stderr && childProcessLogWriteStream) {
      proc.stdout.on('data', data => {
        childProcessLogWriteStream.write(data);
      });
      proc.stderr.on('data', data => {
        childProcessLogWriteStream.write(data);
      });
    }
    return proc.pid as number;
  };

  const doStop = (timeoutSeconds: number): void => {
    logger.info(`Service.stop: trying to stop ${cfg.command}`, cfg);
    setStatus(ServiceStatus.Stopping);
    if (proc) {
      if (cfg.shutdownMethod === ShutdownMethod.Signal) {
        proc.kill('SIGTERM');
      } else if (shutdownFD !== null && proc.stdio[shutdownFD]) {
        const stream = proc.stdio[shutdownFD] as Writable;
        const closeFD = (): void => {
          stream.end();
        };

        // Allow the service one second after startup to begin reading from its
        // shutdownFD, before closing the shutdown FD.
        const shutdownFDGracePeriodMs = 1000;
        const grace = startTimeMs - Date.now() + shutdownFDGracePeriodMs;
        if (grace > 0) {
          setTimeout(closeFD, grace);
        } else {
          closeFD();
        }
      }
    }
    killTimer = setTimeout(() => {
      if (proc) {
        logger.info(
          `Service.stop: timed out after ${timeoutSeconds} seconds. Killing process ${proc.pid}.`
        );
        proc.kill('SIGKILL');
      }
    }, timeoutSeconds * 1000);
  };

  const waitForStop = (): Promise<ServiceExitStatus> =>
    new Promise(resolve => {
      logger.debug(`Service.stop: waiting for ServiceStatus.Stopped`);
      events.on('statusChanged', status => {
        if (status === ServiceStatus.Stopped && exitStatus) {
          resolve(exitStatus);
        }
      });
    });

  const waitForExit = (): Promise<ServiceExitStatus> => {
    const defaultExitStatus = {
      exe: cfg ? cfg.command : '',
      code: null,
      signal: null,
      err: null,
    };
    switch (status) {
      case ServiceStatus.NotStarted:
      case ServiceStatus.Starting:
        return new Promise(resolve => {
          status = ServiceStatus.Stopped;
          exitStatus = defaultExitStatus;
          resolve(exitStatus);
        });
      case ServiceStatus.Started:
        return waitForStop();
      case ServiceStatus.Stopping:
        return waitForStop();
      case ServiceStatus.Stopped:
        return new Promise(resolve => resolve(exitStatus || defaultExitStatus));
    }
  };

  return {
    start: async (): Promise<Pid> => {
      switch (status) {
        case ServiceStatus.NotStarted:
          setStatus(ServiceStatus.Starting);
          startPromise = cfgPromise.then(theCfg => {
            cfg = theCfg;
            return doStart();
          });
          return startPromise;
        case ServiceStatus.Starting:
          logger.info(`Service.start: already starting`);
          return startPromise;
        case ServiceStatus.Started:
          logger.info(`Service.start: already started`);
          return proc?.pid || -1;
        case ServiceStatus.Stopping:
          logger.info(`Service.start: cannot start - already stopping`);
          return -1;
        case ServiceStatus.Stopped:
          logger.info(`Service.start: cannot start - already stopped`);
          return -1;
      }
    },
    stop: async (
      timeoutSeconds = defaultTimeoutSeconds
    ): Promise<ServiceExitStatus> => {
      switch (status) {
        case ServiceStatus.NotStarted:
        case ServiceStatus.Starting:
          logger.info(`Service.stop: cannot stop - never started`);
          break;
        case ServiceStatus.Started:
          doStop(timeoutSeconds);
          break;
        case ServiceStatus.Stopping:
          if (timeoutSeconds === 0 && proc) {
            logger.info(
              `Service.stop: was already stopping, but will now kill process ${proc.pid} immediately`
            );
            proc.kill('SIGKILL');
          } else {
            logger.info(`Service.stop: already stopping`);
          }
          break;
        case ServiceStatus.Stopped:
          logger.info(`Service.stop: already stopped`);
          break;
      }
      return waitForExit();
    },
    waitForExit,
    getStatus: (): ServiceStatus => status,
    getProcess: (): ChildProcess | null => proc,
    getConfig: (): StartService | null => cfg,
    events,
  };
}