Class: Cuboid::MCP::CoreTools::KillInstance

Inherits:
MCP::Tool
  • Object
show all
Defined in:
lib/cuboid/mcp/core_tools.rb

Constant Summary collapse

REAP_GRACE_SECONDS =

Send TERM, give the engine up to ~10s to drain its browser cluster + at_exit chain (which is what unlinks per-engine temp dirs), then SIGKILL if anything’s still alive. The earlier 2s grace was generally too short for engines with an active browser pool — they’d get SIGKILL’d mid-shutdown and leak ‘/tmp/<App>Engine<…>/` directories. Daemonised processes have no parent to wait() on, so we can’t reap; we just verify exit by ESRCH on ‘kill 0`. All branches are best-effort — a missing PID, ESRCH, or EPERM are all silently ignored.

10.0

Class Method Summary collapse

Class Method Details

.call(instance_id:) ⇒ Object



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/cuboid/mcp/core_tools.rb', line 250

def self.call( instance_id:, ** )
    CoreTools.instrumented_call do
        instance = CoreTools.instances[instance_id]
        raise "unknown instance: #{instance_id}" if !instance

        # `instance.shutdown` is an RPC call asking the engine
        # to clean up gracefully. The daemonised subprocess
        # *should* exit on its own afterwards, but in practice
        # it sometimes doesn't — leaking the ruby PID plus its
        # whole chromedriver / browser pool subtree (each
        # engine spawns ~7 chromes). Reap the PID directly
        # too: TERM, brief grace, then KILL if still around.
        pid = instance.pid rescue nil
        (instance.shutdown rescue nil)
        CoreTools.instances.delete( instance_id ).close
        # Drop any live-event registration so future engine
        # pushes 410 instead of being silently relayed.
        ::Cuboid::MCP::Live.unregister( instance_id )

        if pid && pid > 0
            reap_engine_pid( pid )
        end

        { killed: instance_id }
    end
end

.reap_engine_pid(pid) ⇒ Object



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/cuboid/mcp/core_tools.rb', line 289

def self.reap_engine_pid( pid )
    Process.kill( 'TERM', pid ) rescue nil

    deadline = Process.clock_gettime( Process::CLOCK_MONOTONIC ) +
        REAP_GRACE_SECONDS
    while Process.clock_gettime( Process::CLOCK_MONOTONIC ) < deadline
        begin
            Process.kill( 0, pid )
            sleep 0.1
        rescue Errno::ESRCH
            return
        rescue Errno::EPERM
            return
        end
    end

    Process.kill( 'KILL', pid ) rescue nil
end