Class: SecurityBox::Pool
- Inherits:
-
Object
- Object
- SecurityBox::Pool
- Defined in:
- lib/security_box/pool.rb
Overview
Bounded pool of :oneshot Sandboxes.
What it gives you:
- a hard cap on concurrent sandbox evaluations (
size); - cheap checkout/checkin of pre-built Sandbox objects;
- basic metrics (evals, total/average wall time).
What it does NOT give you (measured in stage 4, docs/plan/stages/stage_4.md):
parallelism. invoke holds the GVL for the whole guest lifetime, so
concurrent evals on threads serialize. For real parallelism use
RactorPool, which runs workers on Ractors with a shared Engine+Module.
Usage:
pool = SecurityBox::Pool.new(:lean, size: 4)
pool.eval("1 + 1") # => Result (checks out a sandbox)
pool.checkout { |sandbox| ... } # manual checkout block
pool.shutdown
Instance Attribute Summary collapse
-
#size ⇒ Object
readonly
Returns the value of attribute size.
Instance Method Summary collapse
-
#checkout ⇒ Object
Checks a sandbox out of the pool for the duration of the block.
- #closed? ⇒ Boolean
-
#eval(code, **overrides) ⇒ Object
Checks a sandbox out and runs
codeon it. -
#initialize(profile = nil, size: 4, **options) ⇒ Pool
constructor
A new instance of Pool.
-
#metrics ⇒ Object
Snapshot of the counters: created:, evals:, total_ms:, avg_ms:.
-
#shutdown ⇒ Object
Closes the pool.
Constructor Details
#initialize(profile = nil, size: 4, **options) ⇒ Pool
Returns a new instance of Pool.
24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/security_box/pool.rb', line 24 def initialize(profile = nil, size: 4, **) @size = Integer(size) raise ArgumentError, "size must be >= 1" if @size < 1 @config = resolve_config(profile, ) @sandboxes = Queue.new @mutex = Mutex.new @created = 0 @closed = false @metrics = { evals: 0, total_ms: 0.0 } end |
Instance Attribute Details
#size ⇒ Object (readonly)
Returns the value of attribute size.
22 23 24 |
# File 'lib/security_box/pool.rb', line 22 def size @size end |
Instance Method Details
#checkout ⇒ Object
Checks a sandbox out of the pool for the duration of the block.
Blocks while all size sandboxes are busy.
38 39 40 41 42 43 44 45 46 47 |
# File 'lib/security_box/pool.rb', line 38 def checkout raise PoolClosed, "pool is closed" if closed? sandbox = acquire begin yield sandbox ensure release(sandbox) end end |
#closed? ⇒ Boolean
69 70 71 |
# File 'lib/security_box/pool.rb', line 69 def closed? @mutex.synchronize { @closed } end |
#eval(code, **overrides) ⇒ Object
Checks a sandbox out and runs code on it. Per-call overrides are
forwarded to Sandbox#eval.
51 52 53 54 55 56 |
# File 'lib/security_box/pool.rb', line 51 def eval(code, **overrides) t0 = monotonic_ms result = checkout { |sandbox| sandbox.eval(code, **overrides) } record(monotonic_ms - t0) result end |
#metrics ⇒ Object
Snapshot of the counters: created:, evals:, total_ms:, avg_ms:.
60 61 62 63 64 65 66 67 |
# File 'lib/security_box/pool.rb', line 60 def metrics @mutex.synchronize do evals = @metrics[:evals] { size: @size, created: @created, evals: evals, total_ms: @metrics[:total_ms].round(2), avg_ms: evals.zero? ? 0.0 : (@metrics[:total_ms] / evals).round(2) } end end |
#shutdown ⇒ Object
Closes the pool. Sandboxes currently checked out keep working until released; threads blocked on an empty pool keep waiting (callers must ensure they do not shut down while checkouts are still being awaited).
76 77 78 |
# File 'lib/security_box/pool.rb', line 76 def shutdown @mutex.synchronize { @closed = true } end |