execjs-pcruntime 0.2.1 → 0.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aec9e69936452420277727dacd0c7d27331b7766c8008c433cd840a8c6f38f45
4
- data.tar.gz: 75271e7344781d9338c09263b1ac036355147cd228ae0b7581272cfb3dc53a1a
3
+ metadata.gz: 0ed5dacd1dab7ff7ef42dc43084d6c00da36cd3f228e02517cd0e33492bce55b
4
+ data.tar.gz: 486214245b193a1000bd930758767776398c3f4f47f68a49086a09cf500300eb
5
5
  SHA512:
6
- metadata.gz: b8a8075e90042f2e6183dae5faf84e3c8749b0b8138e9f30fc3499287eef1cf2a3120f5bc7d5c9e3427ce9fe088ad0b28f38cfa3a4471df6b9c25cb4b3c38006
7
- data.tar.gz: '088fe43e02e523346e26ad05cd8617aa3298e046b90760cdafcdb7d0f74fd9a4b3c0ab56e8489e65ac51493850611faf267bd1899a3486088153ddc9fd594ad0'
6
+ metadata.gz: b625a73b8da1b28469a4df46500cba801b16279a81adde6479d0671d7f2a942d4d45f73552bd967422237bcc2dcc398083310dea69c24b81c7dc00f031f43d6d
7
+ data.tar.gz: '088e05880ba92eaf812c25aac7c6e56935294c1af8cc36693fb1d7a3ae26da0f783c8307a7a3f283f976a43cff595bc1b0ec7101e921dc7c72ec8b991be421ac'
@@ -20,10 +20,7 @@ module ExecJS
20
20
  super
21
21
 
22
22
  # @type [JSRuntimeHandle]
23
- @runtime = runtime.create_runtime_handle
24
-
25
- # load initial source to Context
26
- @runtime.evaluate(source.encode('UTF-8'))
23
+ @runtime = runtime.create_runtime_handle source.encode('UTF-8')
27
24
  end
28
25
 
29
26
  # implementation of ExecJS::Runtime::Context#eval
@@ -52,17 +49,19 @@ module ExecJS
52
49
 
53
50
  # Handle of JavaScript Runtime
54
51
  # launch Runtime by .new and finished on finalizer
52
+ # rubocop:disable Metrics/ClassLength
55
53
  class JSRuntimeHandle
56
54
  # @param [Array<String>] binary Launch command for the node(or similar JavaScript Runtime) binary,
57
55
  # such as ['node'], ['deno', 'run'].
58
56
  # @param [String] initial_source_path Path of .js Runtime loads at startup.
59
- def initialize(binary, initial_source_path)
60
- Dir::Tmpname.create 'execjs_pcruntime' do |path|
61
- # Dir::Tmpname.create rescues Errno::EEXIST and retry block
62
- # So, raise it if failed to create Process.
63
- @runtime_pid = create_process(path, *binary, initial_source_path) || raise(Errno::EEXIST)
64
- @socket_path = path
65
- end
57
+ def initialize(binary, initial_source_path, compile_source, semaphore)
58
+ @semaphore = semaphore
59
+ @binary = binary
60
+ @initial_source_path = initial_source_path
61
+ @compile_source = compile_source
62
+ @recreate_process_lock = Mutex.new
63
+ @runtime_pid, @socket_path = initialize_process
64
+ evaluate(@compile_source)
66
65
  ObjectSpace.define_finalizer(self, self.class.finalizer(@runtime_pid))
67
66
  end
68
67
 
@@ -70,7 +69,35 @@ module ExecJS
70
69
  # @param [String] source JavaScript source code
71
70
  # @return [object]
72
71
  def evaluate(source)
73
- post_request(@socket_path, '/eval', 'text/javascript', source)
72
+ socket_path = @socket_path
73
+ post_request(socket_path, '/eval', 'text/javascript', source)
74
+ rescue RuntimeError, ProgramError => e
75
+ raise e
76
+ rescue StandardError => e
77
+ warn e.full_message
78
+ retry if socket_path != @socket_path
79
+ @recreate_process_lock.synchronize do
80
+ @runtime_pid, @socket_path = recreate_process if socket_path == @socket_path
81
+ end
82
+ retry
83
+ end
84
+
85
+ # kill JavaScript runtime process and re-create
86
+ # @return [[Integer, String]] [runtime_pid, socket_path]
87
+ def recreate_process
88
+ runtime_pid = @runtime_pid
89
+ begin
90
+ err = self.class.kill_process(runtime_pid)
91
+ warn err.full_message unless err.nil?
92
+ runtime_pid, socket_path = initialize_process
93
+ post_request(socket_path, '/eval', 'text/javascript', @compile_source)
94
+ rescue RuntimeError, ProgramError => e
95
+ raise e
96
+ rescue StandardError => e
97
+ warn e.full_message
98
+ retry
99
+ end
100
+ [runtime_pid, socket_path]
74
101
  end
75
102
 
76
103
  # Create a procedure to kill the Process that has specified pid.
@@ -96,6 +123,20 @@ module ExecJS
96
123
 
97
124
  private
98
125
 
126
+ # create temporary filename for UNIX Domain Socket and spawn JavaScript runtime process
127
+ # @return [[Integer, String]] [runtime_pid, socket_path]
128
+ def initialize_process
129
+ runtime_pid = 0
130
+ socket_path = ''
131
+ Dir::Tmpname.create 'execjs_pcruntime' do |path|
132
+ # Dir::Tmpname.create rescues Errno::EEXIST and retry block
133
+ # So, raise it if failed to create Process.
134
+ runtime_pid = create_process(path, *@binary, @initial_source_path) || raise(Errno::EEXIST)
135
+ socket_path = path
136
+ end
137
+ [runtime_pid, socket_path]
138
+ end
139
+
99
140
  # Attempt to execute the block several times, spacing out the attempts over a certain period.
100
141
  # @param [Integer] times maximum number of attempts
101
142
  # @yieldreturn [Boolean] true iff succeed execute
@@ -148,6 +189,7 @@ module ExecJS
148
189
  # so suppressing lint errors.
149
190
  # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
150
191
  def post_request(socket_path, path, content_type = nil, body = nil)
192
+ @semaphore.acquire
151
193
  socket = create_socket socket_path
152
194
 
153
195
  # timeout occurred during the test
@@ -187,10 +229,15 @@ module ExecJS
187
229
  error.set_backtrace(stack)
188
230
  raise error
189
231
  end
232
+ ensure
233
+ @semaphore.release
190
234
  end
235
+
191
236
  # rubocop:enable Metrics/MethodLength,Metrics/AbcSize
192
237
  end
193
238
 
239
+ # rubocop:enable Metrics/ClassLength
240
+
194
241
  attr_reader :name
195
242
 
196
243
  # @param [String] name name of Runtime
@@ -203,6 +250,8 @@ module ExecJS
203
250
  @runner_path = runner_path
204
251
  @binary = nil
205
252
  @deprecated = deprecated
253
+ # limit number of threads 128 to avoid Errno::ECONNREFUSED
254
+ @semaphore = Semaphore.new 128
206
255
  end
207
256
 
208
257
  # implementation of ExecJS::Runtime#available?
@@ -217,8 +266,8 @@ module ExecJS
217
266
 
218
267
  # Launch JavaScript Runtime and return its handle.
219
268
  # @return [JSRuntimeHandle]
220
- def create_runtime_handle
221
- JSRuntimeHandle.new(binary, @runner_path)
269
+ def create_runtime_handle(compile_source)
270
+ JSRuntimeHandle.new(binary, @runner_path, compile_source, @semaphore)
222
271
  end
223
272
 
224
273
  private
@@ -256,5 +305,26 @@ module ExecJS
256
305
  nil
257
306
  end
258
307
  end
308
+
309
+ # Semaphore
310
+ # implemented with Thread::Queue
311
+ # since faster than Concurrent::Semaphore and Mutex+ConditionVariable
312
+ class Semaphore
313
+ # @param [Integer] limit
314
+ def initialize(limit)
315
+ @queue = Thread::Queue.new
316
+ limit.times { @queue.push nil }
317
+ end
318
+
319
+ # acquires 1 of permits from this semaphore, blocking until be available.
320
+ def acquire
321
+ @queue.pop
322
+ end
323
+
324
+ # releases 1 of permits
325
+ def release
326
+ @queue.push nil
327
+ end
328
+ end
259
329
  end
260
330
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Execjs
4
4
  module PCRuntime
5
- VERSION = '0.2.1'
5
+ VERSION = '0.2.2'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: execjs-pcruntime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - White-Green
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-28 00:00:00.000000000 Z
11
+ date: 2023-08-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: execjs