Class: Stockshark::Process
- Inherits:
-
Object
- Object
- Stockshark::Process
- Defined in:
- lib/stockshark/process.rb
Overview
Raw OS process lifecycle for a UCI-speaking engine — spawns it, writes lines to its stdin, and continuously drains stdout and stderr from dedicated reader threads so neither pipe can ever fill up and block the child. Draining stderr matters here specifically because the engine path is operator-configurable: nothing guarantees the binary behind it is a "pure" build that never writes to stderr, and if it did while nobody was reading that pipe, the child would block writing to it and every read on stdout would hang right along with it.
Zero chess/UCI knowledge on purpose — Engine owns the protocol, this only owns "a subprocess and its pipes."
Instance Method Summary collapse
- #alive? ⇒ Boolean
-
#initialize(path) ⇒ Process
constructor
A new instance of Process.
-
#read_line(timeout:) ⇒ Object
Pops the next available line from either stream, tagged with which one it came from, waiting at most
timeoutseconds. -
#stop ⇒ Object
Safe to call more than once, and safe to call after the process has already died on its own.
- #write_line(line) ⇒ Object
Constructor Details
#initialize(path) ⇒ Process
Returns a new instance of Process.
16 17 18 19 20 21 22 |
# File 'lib/stockshark/process.rb', line 16 def initialize(path) @stdin, stdout, stderr, @wait_thread = Open3.popen3(path) @queue = Queue.new @readers = [ reader_thread(stdout, :stdout), reader_thread(stderr, :stderr) ] rescue Errno::ENOENT, Errno::EACCES => e raise Stockshark::EngineNotFoundError, "could not start engine at #{path.inspect}: #{e.}" end |
Instance Method Details
#alive? ⇒ Boolean
42 43 44 |
# File 'lib/stockshark/process.rb', line 42 def alive? @wait_thread.alive? end |
#read_line(timeout:) ⇒ Object
Pops the next available line from either stream, tagged with which
one it came from, waiting at most timeout seconds. Returns nil on
timeout — never raises Timeout::Error, deliberately: cancellation
here is just "give up waiting on the queue," not "interrupt whatever
is actually blocking," which is what makes Ruby's Timeout.timeout
unsafe to wrap around real IO.
38 39 40 |
# File 'lib/stockshark/process.rb', line 38 def read_line(timeout:) @queue.pop(timeout: timeout) end |
#stop ⇒ Object
Safe to call more than once, and safe to call after the process has already died on its own.
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/stockshark/process.rb', line 48 def stop return unless alive? begin @stdin.puts("quit") rescue Errno::EPIPE, IOError # already gone — nothing left to say goodbye to. end unless @wait_thread.join(0.5) ::Process.kill("KILL", @wait_thread.pid) @wait_thread.join end ensure @readers.each(&:kill) @stdin.close unless @stdin.closed? end |
#write_line(line) ⇒ Object
24 25 26 27 28 29 30 |
# File 'lib/stockshark/process.rb', line 24 def write_line(line) raise Stockshark::CrashedError, "engine process is not running" unless alive? @stdin.puts(line) rescue Errno::EPIPE, IOError => e raise Stockshark::CrashedError, "could not write to engine: #{e.}" end |