Class: Sevgi::Executor
- Inherits:
-
Object
- Object
- Sevgi::Executor
- Includes:
- Singleton
- Defined in:
- lib/sevgi/executor.rb,
lib/sevgi/executor/error.rb,
lib/sevgi/executor/scope.rb,
lib/sevgi/executor/source.rb
Overview
Executes Sevgi script source inside an isolated module scope.
The executor is used by script mode and by the Load DSL word to preserve a
useful load stack while keeping DSL methods out of the caller's global object
whenever possible. Active scope stacks are isolated per Ruby fiber, so
concurrent executions can perform nested Load calls without sharing scope
state. The process SIGINT handler is shared by Ruby, so executor runs guard it
with a reference-counted critical section and restore the previous handler
after the last active execution finishes.
Defined Under Namespace
Class Method Summary collapse
-
.execute(string, file: nil, line: nil, require: nil, receiver: nil) { ... } ⇒ Sevgi::Executor::Scope?
Executes Ruby source inside a managed Sevgi script scope.
-
.execute_file(file, require: nil, receiver: nil) { ... } ⇒ Sevgi::Executor::Scope?
Executes a file inside a managed Sevgi script scope.
-
.load(file) ⇒ Sevgi::Executor::Scope
private
Loads a script file inside the current executor scope.
-
.shutdown ⇒ Sevgi::Executor::Scope?
private
Removes the current executor scope.
Instance Method Summary collapse
-
#create(scope = nil) ⇒ Sevgi::Executor::Scope
private
Creates and pushes a new executor scope.
-
#current ⇒ Sevgi::Executor::Scope?
private
Returns the active executor scope.
-
#initialize ⇒ Executor
constructor
A new instance of Executor.
-
#restore ⇒ void
private
Restores the process SIGINT handler when the last active execution ends.
-
#shutdown(scope = nil) ⇒ Sevgi::Executor::Scope?
private
Removes the active executor scope.
-
#trap ⇒ void
private
Installs the process SIGINT handler while one or more executions are active.
Constructor Details
#initialize ⇒ Executor
Returns a new instance of Executor.
83 84 85 86 87 |
# File 'lib/sevgi/executor.rb', line 83 def initialize @signal_count = 0 @signal_mutex = Mutex.new @signal_previous = nil end |
Class Method Details
.execute(string, file: nil, line: nil, require: nil, receiver: nil) { ... } ⇒ Sevgi::Executor::Scope?
Required-library load failures are captured as Error on the returned scope.
Reentrant and concurrent calls keep independent scope stacks per fiber. The temporary SIGINT handler remains process-global while any execution is active.
Executes Ruby source inside a managed Sevgi script scope.
51 52 53 |
# File 'lib/sevgi/executor.rb', line 51 def self.execute(string, file: nil, line: nil, require: nil, receiver: nil, &block) execute_source(Source.new(string:, file:, line:), require:, receiver:, &block) end |
.execute_file(file, require: nil, receiver: nil) { ... } ⇒ Sevgi::Executor::Scope?
File-read and required-library load failures are captured as Error on the returned scope.
Reentrant and concurrent calls keep independent scope stacks per fiber. The temporary SIGINT handler remains process-global while any execution is active.
Executes a file inside a managed Sevgi script scope.
65 66 67 68 69 70 71 72 73 74 |
# File 'lib/sevgi/executor.rb', line 65 def self.execute_file(file, require: nil, receiver: nil, &block) source = nil begin source = Source.load(file) rescue ::SystemCallError => e return capture_error(Source.new(string: "", file:, line: 1), e) end execute_source(source, require:, receiver:, &block) end |
.load(file) ⇒ Sevgi::Executor::Scope
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Uses the active executor scope from the current fiber.
Loads a script file inside the current executor scope.
33 34 35 36 37 |
# File 'lib/sevgi/executor.rb', line 33 def self.load(file, ...) PanicError.("box stack empty; create a box first") unless instance.current instance.current.load(file, ...) end |
.shutdown ⇒ Sevgi::Executor::Scope?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Removes the current executor scope.
79 80 81 |
# File 'lib/sevgi/executor.rb', line 79 def self.shutdown instance.shutdown end |
Instance Method Details
#create(scope = nil) ⇒ Sevgi::Executor::Scope
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Creates and pushes a new executor scope.
93 |
# File 'lib/sevgi/executor.rb', line 93 def create(scope = nil) = Scope.new(scope).tap { scopes << it } |
#current ⇒ Sevgi::Executor::Scope?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns the active executor scope.
98 |
# File 'lib/sevgi/executor.rb', line 98 def current = scopes.last |
#restore ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Restores the process SIGINT handler when the last active execution ends.
103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/sevgi/executor.rb', line 103 def restore @signal_mutex.synchronize do next if @signal_count.zero? @signal_count -= 1 next unless @signal_count.zero? Signal.trap("INT", @signal_previous) @signal_previous = nil end end |
#shutdown(scope = nil) ⇒ Sevgi::Executor::Scope?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Removes the active executor scope.
119 120 121 122 123 124 |
# File 'lib/sevgi/executor.rb', line 119 def shutdown(scope = nil) return scopes.pop unless scope return scopes.pop if scopes.last.equal?(scope) scopes.delete(scope) end |
#trap ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Installs the process SIGINT handler while one or more executions are active.
129 130 131 132 133 134 135 |
# File 'lib/sevgi/executor.rb', line 129 def trap @signal_mutex.synchronize do @signal_previous = Signal.trap("INT") { Kernel.abort("") } if @signal_count.zero? @signal_count += 1 end end |