execjs-pcruntime 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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