Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches

Persistent-child-process line-protocol plumbing shared by the external-tool bridges: Classifier_Extern (TRAIN/TEST label protocol) and Criterion_Extern_Value (VALUE protocol). More...

#include <extern_bridge.hpp>

Public Member Functions

 Extern_Bridge (const char *component)
 component names the owning component (e.g. "Classifier_Extern"), prefixing every diagnostic; does not spawn anything yet
 Extern_Bridge (const Extern_Bridge &)=delete
Extern_Bridge & operator= (const Extern_Bridge &)=delete
void set_command (const std::string &cmd)
 sets the external command line; must be called before the child process is spawned (ensure_process())
const std::string & get_command () const
 returns the configured command line
bool running () const
 true once ensure_process() has successfully spawned a child that has not since been shut down
std::FILE * to_child ()
 for owner-side row streaming (%.17g formatting stays with the owner)
void flush_to_child ()
 flushes buffered writes to the child's stdin (call after a batch of send_line()/owner-side writes, before reading a response)
void die_on_write () const
 shared loud-failure path for owner-side writes
void ensure_process ()
 Lazily spawns the child process if not already running() (a no-op otherwise). Requires get_command() to be non-empty. Spawns are serialized process-wide via extern_spawn_mutex() – without this, a concurrently spawning clone's child (threaded search = one child per criterion/classifier clone) could inherit this clone's pipe ends in the window before they are marked non-inheritable, which would keep a sibling child's stdin open past this instance's shutdown_process() and deadlock the destructor's wait. Prints a one-time process-wide NOTE that the command executes user-supplied code through the shell. Platform-specific pipe setup follows: CreateProcess via "cmd.exe /C <cmd>" on Windows, fork()+"/bin/sh -c <cmd>" on POSIX.
void shutdown_process ()
 Terminates the child, if any, and returns the bridge to a not-running() state. Closing our end of stdin/stdout (fclose) signals EOF to the child, the conventional terminate request; the call then WAITS for the child to actually exit (waitpid() on POSIX – unbounded, so a child that ignores stdin-EOF hangs this call and thus the destructor; WaitForSingleObject with a 2s timeout followed by a forced TerminateProcess on Windows). Idempotent: every handle is individually null-checked, so repeated calls (e.g. destructor after an explicit error-path shutdown) are safe. Called from the destructor and from every "the child misbehaved" error path (a desynced or dead child is dropped rather than reused; the next ensure_process() respawns cleanly).
void send_line (const std::string &line)
 Writes line followed by a newline to the child's stdin. Requires running(). Throws (via die_on_write()) if either write fails, i.e. the child stopped reading. Does NOT flush – callers that need the data to arrive before reading a response must call flush_to_child() themselves (e.g. after a batch of send_line()/row writes).
std::string read_line ()
 Reads one line from the child's stdout, with the trailing "\n" (and a preceding "\r", for CRLF-emitting children) stripped. Requires running(). Throws fst_error if the child produced no response at all (EOF/died mid-read) or if more than 1 MB accumulates without a newline – a runaway/non-protocol child must fail loudly rather than exhaust memory; that case also calls shutdown_process() first so the dead/misbehaving child is not reused.

Private Member Functions

void init_process_handles ()
 Resets the handles/pid to their "not running" sentinel values (nullptr / -1). Called from the constructor to establish that initial state; NOT called by shutdown_process() itself (which null/reset each handle individually as it closes it).

Private Attributes

std::string _component
 owning component name, prefixes every diagnostic
std::string _cmd
 the configured external command line (see set_command())
std::FILE * _to_child
 write end of the pipe to the child's stdin, or nullptr when not running()
std::FILE * _from_child
 read end of the pipe from the child's stdout, or nullptr when not running()
pid_t _child_pid
 POSIX pid of the running child, or -1 when not running().

Detailed Description

Persistent-child-process line-protocol plumbing shared by the external-tool bridges: Classifier_Extern (TRAIN/TEST label protocol) and Criterion_Extern_Value (VALUE protocol).

Owns the child process handles and the raw line I/O; the protocol blocks themselves (headers, data rows, response parsing) live in the owning component. Error messages carry the owning component's name, so a diagnostic always says which bridge failed.

SINGLE OWNER BY DESIGN: exactly one instance per owning component instance, and the class is non-copyable. A copy would duplicate the FILE* pipe ends and the child handle without duplicating the child, so two objects would write into one child's stdin, interleave their protocol blocks, and each try to close and reap the same process in its destructor. Clones of the owners therefore construct FRESH bridges with their own child process - pipes are never shared - which is also what keeps threaded evaluation isolation-safe.

All LOUD CAVEATS of the extern mechanism apply: the command is user-supplied code executed through the system shell, reproducibility guarantees end at the bridge (whatever the external tool does is outside FST4's control), there is no timeout machinery, and see the SIGPIPE note on ensure_process().

Member Function Documentation

◆ ensure_process()

void FST::Extern_Bridge::ensure_process ( )
inline

Lazily spawns the child process if not already running() (a no-op otherwise). Requires get_command() to be non-empty. Spawns are serialized process-wide via extern_spawn_mutex() – without this, a concurrently spawning clone's child (threaded search = one child per criterion/classifier clone) could inherit this clone's pipe ends in the window before they are marked non-inheritable, which would keep a sibling child's stdin open past this instance's shutdown_process() and deadlock the destructor's wait. Prints a one-time process-wide NOTE that the command executes user-supplied code through the shell. Platform-specific pipe setup follows: CreateProcess via "cmd.exe /C <cmd>" on Windows, fork()+"/bin/sh -c <cmd>" on POSIX.

Warning
SIGPIPE side effect on POSIX. A child that stops reading must surface as an fst_error rather than killing fst4, so writes to the pipe must not raise SIGPIPE. On macOS/BSD this is arranged per file descriptor (F_SETNOSIGPIPE) and nothing else is affected. On platforms without that option - Linux among them - the first spawn calls signal(SIGPIPE,SIG_IGN), which changes the disposition for the WHOLE PROCESS and is never restored. Embedders whose own code relies on SIGPIPE terminating the process (or on their own handler) must take that into account before using an external bridge.
Exceptions
fst_erroron any setup failure (pipe/fork/CreateProcess, or a launch failure).

The documentation for this class was generated from the following file:
Copyright © 2000–present ÚTIA, Institute of Information Theory and Automation of the Czech Academy of Sciences · Contacts · This site sets no cookies and uses no analytics.