kobako 0.9.2 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +25 -0
- data/Cargo.lock +3 -1
- data/README.md +35 -14
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +11 -1
- data/ext/kobako/src/runtime/cache.rs +168 -4
- data/ext/kobako/src/runtime/dispatch.rs +2 -2
- data/ext/kobako/src/runtime/exports.rs +32 -21
- data/ext/kobako/src/runtime/instance_pre.rs +97 -0
- data/ext/kobako/src/runtime/invocation.rs +12 -69
- data/ext/kobako/src/runtime.rs +371 -384
- data/lib/kobako/catalog/handles.rb +9 -13
- data/lib/kobako/catalog/namespaces.rb +22 -3
- data/lib/kobako/catalog/snippets.rb +9 -1
- data/lib/kobako/codec/decoder.rb +5 -1
- data/lib/kobako/errors.rb +11 -1
- data/lib/kobako/namespace.rb +16 -2
- data/lib/kobako/pool.rb +182 -0
- data/lib/kobako/sandbox.rb +16 -14
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +1 -0
- data/release-please-config.json +16 -1
- data/sig/kobako/catalog/handles.rbs +0 -2
- data/sig/kobako/errors.rbs +3 -0
- data/sig/kobako/namespace.rbs +2 -0
- data/sig/kobako/pool.rbs +44 -0
- data/sig/kobako/sandbox.rbs +2 -2
- metadata +4 -1
|
@@ -30,13 +30,6 @@ module Kobako
|
|
|
30
30
|
# +0x7fff_ffff+ (2³¹ − 1). Allocation beyond the cap raises
|
|
31
31
|
# immediately — no silent truncation, no wrap, no ID reuse.
|
|
32
32
|
class Handles
|
|
33
|
-
# Reflective gadget types that are never minted as a Capability Handle
|
|
34
|
-
# ({docs/behavior.md B-43}[link:../../../docs/behavior.md]): wrapping a
|
|
35
|
-
# +Binding+ / +Method+ / +UnboundMethod+ would hand the guest a callable
|
|
36
|
-
# proxy onto host reflection (a returned +Binding+ reaches +Binding#eval+).
|
|
37
|
-
UNWRAPPABLE_TYPES = [Binding, Method, UnboundMethod].freeze
|
|
38
|
-
private_constant :UNWRAPPABLE_TYPES
|
|
39
|
-
|
|
40
33
|
# Build a fresh, empty table. +next_id+ is an internal seam that
|
|
41
34
|
# sets the starting value of the monotonic counter (defaults to 1 per
|
|
42
35
|
# B-15); tests pass a value near +Kobako::Handle::MAX_ID+ to exercise
|
|
@@ -97,13 +90,16 @@ module Kobako
|
|
|
97
90
|
private
|
|
98
91
|
|
|
99
92
|
# Refuse to mint a Capability Handle for a reflective gadget
|
|
100
|
-
# ({
|
|
101
|
-
#
|
|
102
|
-
#
|
|
93
|
+
# ({docs/behavior.md B-43}[link:../../../docs/behavior.md]): a +Binding+ /
|
|
94
|
+
# +Method+ / +UnboundMethod+ would hand the guest a callable proxy onto
|
|
95
|
+
# host reflection (a returned +Binding+ reaches +Binding#eval+). Raising
|
|
96
|
+
# here keeps the rule at the single mint point, so it holds on both the
|
|
97
|
+
# Service-return (B-14) and the +#run+ host→guest auto-wrap (B-34) paths.
|
|
103
98
|
def reject_unwrappable!(object)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
99
|
+
case object
|
|
100
|
+
when Binding, Method, UnboundMethod
|
|
101
|
+
raise SandboxError, "a #{object.class} cannot cross as a Capability Handle"
|
|
102
|
+
end
|
|
107
103
|
end
|
|
108
104
|
|
|
109
105
|
# Guard {#alloc} against issuing an ID past the B-21 cap. Returns +nil+
|
|
@@ -37,6 +37,7 @@ module Kobako
|
|
|
37
37
|
@namespaces = {} # : Hash[String, Kobako::Namespace]
|
|
38
38
|
@handler = handler
|
|
39
39
|
@sealed = false
|
|
40
|
+
@encoded = nil # : String?
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
# Declare or retrieve the Namespace named +name+ (idempotent —
|
|
@@ -80,14 +81,32 @@ module Kobako
|
|
|
80
81
|
# Arrays, so none of the kobako ext types actually fire. Structure:
|
|
81
82
|
# +[["Namespace", ["MemberA", "MemberB"]], ...]+. Returns a binary
|
|
82
83
|
# +String+ of msgpack bytes.
|
|
84
|
+
#
|
|
85
|
+
# Once sealed, the bytes are computed once and reused for every
|
|
86
|
+
# subsequent invocation: B-33 seals Service registration (B-07 /
|
|
87
|
+
# B-08) at the first invocation, so the preamble is exactly the
|
|
88
|
+
# bindings that existed at that moment — a bind reaching a
|
|
89
|
+
# +Kobako::Namespace+ after the seal raises +ArgumentError+ (E-45)
|
|
90
|
+
# and never alters Frame 1.
|
|
83
91
|
def encode
|
|
84
|
-
|
|
92
|
+
return @encoded if @encoded
|
|
93
|
+
|
|
94
|
+
bytes = Codec::Encoder.encode(@namespaces.values.map(&:to_preamble)).freeze
|
|
95
|
+
@encoded = bytes if @sealed
|
|
96
|
+
bytes
|
|
85
97
|
end
|
|
86
98
|
|
|
87
|
-
# Mark the registry as sealed
|
|
88
|
-
#
|
|
99
|
+
# Mark the registry as sealed and propagate the seal to every
|
|
100
|
+
# declared +Kobako::Namespace+
|
|
101
|
+
# ({docs/behavior.md B-33}[link:../../../docs/behavior.md]). Called
|
|
102
|
+
# by +Sandbox+ on the first invocation. After sealing, #define
|
|
103
|
+
# raises ArgumentError (E-18) and +Namespace#bind+ raises
|
|
104
|
+
# ArgumentError (E-45). Idempotent.
|
|
89
105
|
def seal!
|
|
106
|
+
return self if @sealed
|
|
107
|
+
|
|
90
108
|
@sealed = true
|
|
109
|
+
@namespaces.each_value(&:seal!)
|
|
91
110
|
self
|
|
92
111
|
end
|
|
93
112
|
|
|
@@ -31,6 +31,7 @@ module Kobako
|
|
|
31
31
|
|
|
32
32
|
def initialize
|
|
33
33
|
@entries = [] # : Array[Kobako::Snippet::Source | Kobako::Snippet::Binary]
|
|
34
|
+
@encoded = nil # : String?
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
# Serialize the registered snippets to wire bytes. Each entry
|
|
@@ -42,8 +43,14 @@ module Kobako
|
|
|
42
43
|
# carriers — this collection-tier method reads their attributes
|
|
43
44
|
# externally via +entry_payload+ rather than asking each entry to
|
|
44
45
|
# self-encode.
|
|
46
|
+
#
|
|
47
|
+
# The bytes are memoized — the table is replayed verbatim on every
|
|
48
|
+
# invocation after B-33 seals it, so Frame 3 never changes between
|
|
49
|
+
# encodes; {#register} drops the memo while the table is still open.
|
|
45
50
|
def encode
|
|
46
|
-
|
|
51
|
+
return @encoded if @encoded
|
|
52
|
+
|
|
53
|
+
@encoded = Codec::Encoder.encode(@entries.map { |entry| entry_payload(entry) }).freeze
|
|
47
54
|
end
|
|
48
55
|
|
|
49
56
|
# Register one preloaded snippet in either of two forms
|
|
@@ -68,6 +75,7 @@ module Kobako
|
|
|
68
75
|
# missing keywords, wrong types, malformed +name+ (E-34), or
|
|
69
76
|
# duplicate +code:+ +name+ (E-33).
|
|
70
77
|
def register(code: nil, name: nil, binary: nil)
|
|
78
|
+
@encoded = nil
|
|
71
79
|
if binary
|
|
72
80
|
raise ArgumentError, "cannot combine binary: with code: / name:" if code || name
|
|
73
81
|
|
data/lib/kobako/codec/decoder.rb
CHANGED
|
@@ -64,7 +64,11 @@ module Kobako
|
|
|
64
64
|
case value
|
|
65
65
|
when String then Utils.assert_utf8!(value, "str payload") if value.encoding == Encoding::UTF_8
|
|
66
66
|
when Array then value.each { |v| validate_utf8!(v) }
|
|
67
|
-
when Hash
|
|
67
|
+
when Hash
|
|
68
|
+
value.each do |key, val|
|
|
69
|
+
validate_utf8!(key)
|
|
70
|
+
validate_utf8!(val)
|
|
71
|
+
end
|
|
68
72
|
end
|
|
69
73
|
end
|
|
70
74
|
end
|
data/lib/kobako/errors.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Kobako
|
|
|
21
21
|
# call that failed and was not rescued inside the
|
|
22
22
|
# script).
|
|
23
23
|
#
|
|
24
|
-
#
|
|
24
|
+
# Two further branches sit outside the invocation taxonomy:
|
|
25
25
|
#
|
|
26
26
|
# * {SetupError} — construction layer. Raised by `Kobako::Sandbox.new`
|
|
27
27
|
# when the wasm runtime cannot be built from the
|
|
@@ -29,6 +29,9 @@ module Kobako
|
|
|
29
29
|
# ({docs/behavior.md E-40 / E-41}[link:../../docs/behavior.md]).
|
|
30
30
|
# Not an invocation outcome, so it never passes
|
|
31
31
|
# through the two-step attribution decision.
|
|
32
|
+
# * {PoolTimeoutError} — pool checkout layer. Raised by `Kobako::Pool#with`
|
|
33
|
+
# when the checkout wait exceeds +checkout_timeout+
|
|
34
|
+
# ({docs/behavior.md E-46}[link:../../docs/behavior.md]).
|
|
32
35
|
#
|
|
33
36
|
# Subclasses pinned by docs/behavior.md Error Classes:
|
|
34
37
|
#
|
|
@@ -137,4 +140,11 @@ module Kobako
|
|
|
137
140
|
# snippet failures while callers wanting bytecode-specific handling
|
|
138
141
|
# can `rescue Kobako::BytecodeError` directly.
|
|
139
142
|
class BytecodeError < SandboxError; end
|
|
143
|
+
|
|
144
|
+
# Pool checkout layer. Raised by +Kobako::Pool#with+ when the checkout
|
|
145
|
+
# wait exceeded the configured +checkout_timeout+ while every slot was
|
|
146
|
+
# held ({docs/behavior.md E-46}[link:../../docs/behavior.md]). No
|
|
147
|
+
# Sandbox state is touched — retrying succeeds as soon as a holder
|
|
148
|
+
# returns its Sandbox.
|
|
149
|
+
class PoolTimeoutError < Error; end
|
|
140
150
|
end
|
data/lib/kobako/namespace.rb
CHANGED
|
@@ -18,15 +18,20 @@ module Kobako
|
|
|
18
18
|
def initialize(name)
|
|
19
19
|
@name = name
|
|
20
20
|
@members = {} # : Hash[String, untyped]
|
|
21
|
+
@sealed = false
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
# Bind +object+ under +member+ inside this Namespace. +member+ is a
|
|
24
25
|
# constant-form name as a +Symbol+ or +String+. +object+ is any Ruby
|
|
25
26
|
# object that responds to the methods guest code will invoke. Returns
|
|
26
27
|
# +self+ for chaining. Raises +ArgumentError+ when +member+ does not
|
|
27
|
-
# match the constant pattern,
|
|
28
|
-
# bound ({docs/behavior.md B-11}[link:../../docs/behavior.md])
|
|
28
|
+
# match the constant pattern, when a Member of the same name is
|
|
29
|
+
# already bound ({docs/behavior.md B-11}[link:../../docs/behavior.md]),
|
|
30
|
+
# or when the owning Sandbox's first invocation has sealed Service
|
|
31
|
+
# registration ({docs/behavior.md E-45}[link:../../docs/behavior.md]).
|
|
29
32
|
def bind(member, object)
|
|
33
|
+
raise ArgumentError, "cannot bind after first Sandbox invocation" if @sealed
|
|
34
|
+
|
|
30
35
|
member_str = validate_member_name!(member)
|
|
31
36
|
raise ArgumentError, "Member #{@name}::#{member_str} is already bound" if @members.key?(member_str)
|
|
32
37
|
|
|
@@ -34,6 +39,15 @@ module Kobako
|
|
|
34
39
|
self
|
|
35
40
|
end
|
|
36
41
|
|
|
42
|
+
# Mark this Namespace as sealed ({docs/behavior.md B-33}[link:../../docs/behavior.md]).
|
|
43
|
+
# Called by +Kobako::Catalog::Namespaces#seal!+ on the owning
|
|
44
|
+
# Sandbox's first invocation; afterwards {#bind} raises
|
|
45
|
+
# +ArgumentError+ (E-45). Idempotent; returns +self+.
|
|
46
|
+
def seal!
|
|
47
|
+
@sealed = true
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
37
51
|
# Member lookup; raises +KeyError+ when no Member is registered
|
|
38
52
|
# under +member+.
|
|
39
53
|
def fetch(member)
|
data/lib/kobako/pool.rb
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "errors"
|
|
4
|
+
require_relative "sandbox"
|
|
5
|
+
|
|
6
|
+
module Kobako
|
|
7
|
+
# Kobako::Pool — a bounded set of warm, identically set-up Sandboxes
|
|
8
|
+
# handed out one exclusive holder at a time
|
|
9
|
+
# ({docs/behavior.md B-46..B-48}[link:../../docs/behavior.md]).
|
|
10
|
+
#
|
|
11
|
+
# Construction forwards every +Kobako::Sandbox.new+ keyword verbatim
|
|
12
|
+
# and holds the optional block as the per-Sandbox setup hook; a
|
|
13
|
+
# checkout prefers an idle Sandbox and constructs a new one only when
|
|
14
|
+
# none is idle and fewer than +slots+ exist (B-46). +#with+ blocks up
|
|
15
|
+
# to +checkout_timeout+ seconds when every slot is held (E-46), applies
|
|
16
|
+
# the +TrapError+ discard-and-recreate contract at checkin (B-47), and
|
|
17
|
+
# the Pool releases everything with its own reachability — there is no
|
|
18
|
+
# teardown verb (B-48).
|
|
19
|
+
class Pool
|
|
20
|
+
# The +#with+ wait bound applied when +checkout_timeout+ is not given
|
|
21
|
+
# ({docs/behavior.md B-46}[link:../../docs/behavior.md]).
|
|
22
|
+
DEFAULT_CHECKOUT_TIMEOUT_SECONDS = 5.0
|
|
23
|
+
|
|
24
|
+
# Build a Pool of up to +slots+ Sandboxes
|
|
25
|
+
# ({docs/behavior.md B-46}[link:../../docs/behavior.md]). +slots+ is
|
|
26
|
+
# a positive Integer; +checkout_timeout+ bounds the +#with+ wait in
|
|
27
|
+
# seconds (+nil+ waits indefinitely); every other keyword is
|
|
28
|
+
# forwarded verbatim to +Kobako::Sandbox.new+. The optional block
|
|
29
|
+
# runs exactly once per constructed Sandbox — it is the setup window
|
|
30
|
+
# for +#define+ / +#preload+ before that Sandbox's first checkout.
|
|
31
|
+
# No Sandbox is constructed here. Raises +ArgumentError+ for an
|
|
32
|
+
# invalid +slots+ / +checkout_timeout+
|
|
33
|
+
# ({docs/behavior.md E-47}[link:../../docs/behavior.md]).
|
|
34
|
+
def initialize(slots:, checkout_timeout: DEFAULT_CHECKOUT_TIMEOUT_SECONDS, **sandbox_options, &setup)
|
|
35
|
+
validate_slots!(slots)
|
|
36
|
+
@slots = slots
|
|
37
|
+
@checkout_timeout = normalize_checkout_timeout(checkout_timeout)
|
|
38
|
+
@sandbox_options = sandbox_options
|
|
39
|
+
@setup = setup
|
|
40
|
+
@idle = [] # : Array[Kobako::Sandbox]
|
|
41
|
+
@constructed = 0
|
|
42
|
+
@mutex = Mutex.new
|
|
43
|
+
@slot_freed = ConditionVariable.new
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Yield one exclusively-held Sandbox to the block and return the
|
|
47
|
+
# block's value ({docs/behavior.md B-47}[link:../../docs/behavior.md]).
|
|
48
|
+
# Blocks while every slot is held; raises +Kobako::PoolTimeoutError+
|
|
49
|
+
# once the wait exceeds +checkout_timeout+
|
|
50
|
+
# ({docs/behavior.md E-46}[link:../../docs/behavior.md]). The Sandbox
|
|
51
|
+
# returns to the pool at block exit — unless the block raised
|
|
52
|
+
# +Kobako::TrapError+, in which case the unrecoverable Sandbox is
|
|
53
|
+
# discarded and its slot refills by a fresh construction on next
|
|
54
|
+
# demand.
|
|
55
|
+
def with
|
|
56
|
+
sandbox = checkout
|
|
57
|
+
begin
|
|
58
|
+
yield sandbox
|
|
59
|
+
rescue TrapError
|
|
60
|
+
release_capacity!
|
|
61
|
+
sandbox = nil
|
|
62
|
+
raise
|
|
63
|
+
ensure
|
|
64
|
+
checkin(sandbox) if sandbox
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# Acquire a Sandbox and hand it over in pre-invocation state — empty
|
|
71
|
+
# output buffers and truncation predicates false (B-47).
|
|
72
|
+
def checkout
|
|
73
|
+
acquire.tap(&:reset_invocation_state!)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The idle-first claim loop (B-46): an idle Sandbox wins, unclaimed
|
|
77
|
+
# capacity constructs, and a full pool waits for a checkin.
|
|
78
|
+
def acquire
|
|
79
|
+
timeout = @checkout_timeout
|
|
80
|
+
deadline = timeout && (monotonic_now + timeout)
|
|
81
|
+
loop do
|
|
82
|
+
action, sandbox = claim_or_wait(deadline)
|
|
83
|
+
return sandbox if action == :idle && sandbox
|
|
84
|
+
return construct_slot if action == :build
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Single locked decision point for one claim attempt. Waiting
|
|
89
|
+
# happens inside the lock (so a checkin can wake it); construction
|
|
90
|
+
# happens outside (so a slow setup block never holds the lock) —
|
|
91
|
+
# capacity is reserved here and released by +construct_slot+ on
|
|
92
|
+
# failure.
|
|
93
|
+
def claim_or_wait(deadline)
|
|
94
|
+
@mutex.synchronize do
|
|
95
|
+
return [:idle, @idle.pop] unless @idle.empty?
|
|
96
|
+
|
|
97
|
+
if @constructed < @slots
|
|
98
|
+
@constructed += 1
|
|
99
|
+
return [:build, nil]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
await_slot!(deadline)
|
|
103
|
+
[:retry, nil]
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Wait for a checkin or freed capacity; raises
|
|
108
|
+
# +Kobako::PoolTimeoutError+ once +deadline+ has passed (E-46). Must
|
|
109
|
+
# run while holding +@mutex+.
|
|
110
|
+
def await_slot!(deadline)
|
|
111
|
+
remaining = deadline && (deadline - monotonic_now)
|
|
112
|
+
if remaining && remaining <= 0
|
|
113
|
+
raise PoolTimeoutError,
|
|
114
|
+
"no Sandbox returned within #{@checkout_timeout}s: all #{@slots} slots are held"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
@slot_freed.wait(@mutex, remaining)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Construct and set up one pooled Sandbox against the capacity
|
|
121
|
+
# reserved by +claim_or_wait+. Construction and setup-block errors
|
|
122
|
+
# propagate to the checkout caller unchanged (B-46); the reserved
|
|
123
|
+
# capacity is released so a later checkout can retry.
|
|
124
|
+
def construct_slot
|
|
125
|
+
done = false
|
|
126
|
+
sandbox = Sandbox.new(**@sandbox_options)
|
|
127
|
+
@setup&.call(sandbox)
|
|
128
|
+
done = true
|
|
129
|
+
sandbox
|
|
130
|
+
ensure
|
|
131
|
+
release_capacity! unless done
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Return a Sandbox to the idle list and wake one waiting checkout.
|
|
135
|
+
def checkin(sandbox)
|
|
136
|
+
@mutex.synchronize do
|
|
137
|
+
@idle.push(sandbox)
|
|
138
|
+
@slot_freed.signal
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Give back reserved-but-unfilled capacity — a failed construction or
|
|
143
|
+
# a discarded Sandbox — and wake one waiting checkout to claim it.
|
|
144
|
+
def release_capacity!
|
|
145
|
+
@mutex.synchronize do
|
|
146
|
+
@constructed -= 1
|
|
147
|
+
@slot_freed.signal
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# The wait deadline runs on the monotonic clock so a wall-clock jump
|
|
152
|
+
# cannot stretch or cut the checkout wait.
|
|
153
|
+
def monotonic_now
|
|
154
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# E-47 pre-flight for +slots+ — no coercion, a positive Integer is
|
|
158
|
+
# the only accepted shape.
|
|
159
|
+
def validate_slots!(slots)
|
|
160
|
+
return if slots.is_a?(Integer) && slots.positive?
|
|
161
|
+
|
|
162
|
+
raise ArgumentError, "slots must be a positive Integer, got #{slots.inspect}"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Coerce +checkout_timeout+ into the Float seconds the wait loop
|
|
166
|
+
# consumes, or +nil+ to wait indefinitely — the E-39 normalisation
|
|
167
|
+
# idiom applied to E-47.
|
|
168
|
+
def normalize_checkout_timeout(checkout_timeout)
|
|
169
|
+
return nil if checkout_timeout.nil?
|
|
170
|
+
unless checkout_timeout.is_a?(Numeric)
|
|
171
|
+
raise ArgumentError, "checkout_timeout must be Numeric or nil, got #{checkout_timeout.inspect}"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
seconds = checkout_timeout.to_f
|
|
175
|
+
unless seconds.positive? && seconds.finite?
|
|
176
|
+
raise ArgumentError, "checkout_timeout must be > 0 and finite (got #{checkout_timeout})"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
seconds
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
data/lib/kobako/sandbox.rb
CHANGED
|
@@ -206,6 +206,22 @@ module Kobako
|
|
|
206
206
|
end
|
|
207
207
|
end
|
|
208
208
|
|
|
209
|
+
# Reset all per-invocation observable state to its pre-invocation
|
|
210
|
+
# sentinels — both per-channel captures
|
|
211
|
+
# ({docs/behavior.md B-05}[link:../../docs/behavior.md]) and the
|
|
212
|
+
# per-last-invocation usage record
|
|
213
|
+
# ({docs/behavior.md B-35}[link:../../docs/behavior.md]). Shared by
|
|
214
|
+
# +#initialize+ (first-time setup) and +#begin_invocation!+
|
|
215
|
+
# (between-invocation reset) so both paths agree on what
|
|
216
|
+
# "pre-invocation state" means; +Kobako::Pool+ calls it at checkout
|
|
217
|
+
# so a pooled Sandbox hands over empty output buffers
|
|
218
|
+
# ({docs/behavior.md B-47}[link:../../docs/behavior.md]).
|
|
219
|
+
def reset_invocation_state!
|
|
220
|
+
@stdout_capture = Capture::EMPTY
|
|
221
|
+
@stderr_capture = Capture::EMPTY
|
|
222
|
+
@usage = Usage::EMPTY
|
|
223
|
+
end
|
|
224
|
+
|
|
209
225
|
private
|
|
210
226
|
|
|
211
227
|
# Configure the +Runtime+'s host↔guest dispatch wiring
|
|
@@ -237,20 +253,6 @@ module Kobako
|
|
|
237
253
|
reset_invocation_state!
|
|
238
254
|
end
|
|
239
255
|
|
|
240
|
-
# Reset all per-invocation observable state to its pre-invocation
|
|
241
|
-
# sentinels — both per-channel captures
|
|
242
|
-
# ({docs/behavior.md B-05}[link:../../docs/behavior.md]) and the
|
|
243
|
-
# per-last-invocation usage record
|
|
244
|
-
# ({docs/behavior.md B-35}[link:../../docs/behavior.md]). Shared by
|
|
245
|
-
# +#initialize+ (first-time setup) and +#begin_invocation!+
|
|
246
|
-
# (between-invocation reset) so both paths agree on what
|
|
247
|
-
# "pre-invocation state" means.
|
|
248
|
-
def reset_invocation_state!
|
|
249
|
-
@stdout_capture = Capture::EMPTY
|
|
250
|
-
@stderr_capture = Capture::EMPTY
|
|
251
|
-
@usage = Usage::EMPTY
|
|
252
|
-
end
|
|
253
|
-
|
|
254
256
|
# Read the per-last-invocation +wall_time+ and +memory_peak+ from
|
|
255
257
|
# the ext and wrap them as a +Kobako::Usage+ value object
|
|
256
258
|
# ({docs/behavior.md B-35}[link:../../docs/behavior.md]). Runs in
|
data/lib/kobako/version.rb
CHANGED
data/lib/kobako.rb
CHANGED
data/release-please-config.json
CHANGED
|
@@ -73,13 +73,28 @@
|
|
|
73
73
|
"path": "/wasm/kobako-regexp/README.md"
|
|
74
74
|
}
|
|
75
75
|
]
|
|
76
|
+
},
|
|
77
|
+
"wasm/kobako-baker": {
|
|
78
|
+
"component": "kobako-baker",
|
|
79
|
+
"release-type": "rust",
|
|
80
|
+
"extra-files": [
|
|
81
|
+
{
|
|
82
|
+
"type": "toml",
|
|
83
|
+
"path": "/wasm/kobako-baker/Cargo.lock",
|
|
84
|
+
"jsonpath": "$.package[?(@.name=='kobako-baker')].version"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"type": "generic",
|
|
88
|
+
"path": "/wasm/kobako-baker/README.md"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
76
91
|
}
|
|
77
92
|
},
|
|
78
93
|
"plugins": [
|
|
79
94
|
{
|
|
80
95
|
"type": "linked-versions",
|
|
81
96
|
"groupName": "kobako guest crates",
|
|
82
|
-
"components": ["kobako-core", "kobako-rs", "kobako-io", "kobako-regexp"]
|
|
97
|
+
"components": ["kobako-core", "kobako-rs", "kobako-io", "kobako-regexp", "kobako-baker"]
|
|
83
98
|
}
|
|
84
99
|
],
|
|
85
100
|
"extra-files": [
|
data/sig/kobako/errors.rbs
CHANGED
data/sig/kobako/namespace.rbs
CHANGED
data/sig/kobako/pool.rbs
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Kobako
|
|
2
|
+
class Pool
|
|
3
|
+
DEFAULT_CHECKOUT_TIMEOUT_SECONDS: Float
|
|
4
|
+
|
|
5
|
+
@slots: Integer
|
|
6
|
+
@checkout_timeout: Float?
|
|
7
|
+
@sandbox_options: Hash[Symbol, untyped]
|
|
8
|
+
@setup: ^(Kobako::Sandbox) -> void | nil
|
|
9
|
+
@idle: Array[Kobako::Sandbox]
|
|
10
|
+
@constructed: Integer
|
|
11
|
+
@mutex: Thread::Mutex
|
|
12
|
+
@slot_freed: Thread::ConditionVariable
|
|
13
|
+
|
|
14
|
+
def initialize: (
|
|
15
|
+
slots: Integer,
|
|
16
|
+
?checkout_timeout: (Float | Integer)?,
|
|
17
|
+
**untyped sandbox_options
|
|
18
|
+
) ?{ (Kobako::Sandbox) -> void } -> void
|
|
19
|
+
|
|
20
|
+
def with: [T] () { (Kobako::Sandbox) -> T } -> T
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def checkout: () -> Kobako::Sandbox
|
|
25
|
+
|
|
26
|
+
def acquire: () -> Kobako::Sandbox
|
|
27
|
+
|
|
28
|
+
def claim_or_wait: (Float? deadline) -> [Symbol, Kobako::Sandbox?]
|
|
29
|
+
|
|
30
|
+
def await_slot!: (Float? deadline) -> void
|
|
31
|
+
|
|
32
|
+
def construct_slot: () -> Kobako::Sandbox
|
|
33
|
+
|
|
34
|
+
def checkin: (Kobako::Sandbox sandbox) -> void
|
|
35
|
+
|
|
36
|
+
def release_capacity!: () -> void
|
|
37
|
+
|
|
38
|
+
def monotonic_now: () -> Float
|
|
39
|
+
|
|
40
|
+
def validate_slots!: (untyped slots) -> void
|
|
41
|
+
|
|
42
|
+
def normalize_checkout_timeout: ((Float | Integer)? checkout_timeout) -> Float?
|
|
43
|
+
end
|
|
44
|
+
end
|
data/sig/kobako/sandbox.rbs
CHANGED
|
@@ -38,12 +38,12 @@ module Kobako
|
|
|
38
38
|
|
|
39
39
|
def eval: (String code) -> untyped
|
|
40
40
|
|
|
41
|
+
def reset_invocation_state!: () -> void
|
|
42
|
+
|
|
41
43
|
private
|
|
42
44
|
|
|
43
45
|
def install_dispatch_proc!: () -> void
|
|
44
46
|
|
|
45
|
-
def reset_invocation_state!: () -> void
|
|
46
|
-
|
|
47
47
|
def begin_invocation!: () -> void
|
|
48
48
|
|
|
49
49
|
def read_usage!: () -> void
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kobako
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aotokitsuruya
|
|
@@ -65,6 +65,7 @@ files:
|
|
|
65
65
|
- ext/kobako/src/runtime/dispatch.rs
|
|
66
66
|
- ext/kobako/src/runtime/exports.rs
|
|
67
67
|
- ext/kobako/src/runtime/guest_mem.rs
|
|
68
|
+
- ext/kobako/src/runtime/instance_pre.rs
|
|
68
69
|
- ext/kobako/src/runtime/invocation.rs
|
|
69
70
|
- ext/kobako/src/runtime/trap.rs
|
|
70
71
|
- ext/kobako/src/snapshot.rs
|
|
@@ -86,6 +87,7 @@ files:
|
|
|
86
87
|
- lib/kobako/namespace.rb
|
|
87
88
|
- lib/kobako/outcome.rb
|
|
88
89
|
- lib/kobako/outcome/panic.rb
|
|
90
|
+
- lib/kobako/pool.rb
|
|
89
91
|
- lib/kobako/runtime.rb
|
|
90
92
|
- lib/kobako/sandbox.rb
|
|
91
93
|
- lib/kobako/sandbox_options.rb
|
|
@@ -123,6 +125,7 @@ files:
|
|
|
123
125
|
- sig/kobako/namespace.rbs
|
|
124
126
|
- sig/kobako/outcome.rbs
|
|
125
127
|
- sig/kobako/outcome/panic.rbs
|
|
128
|
+
- sig/kobako/pool.rbs
|
|
126
129
|
- sig/kobako/runtime.rbs
|
|
127
130
|
- sig/kobako/sandbox.rbs
|
|
128
131
|
- sig/kobako/sandbox_options.rbs
|