execjs-pcruntime 0.2.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aec9e69936452420277727dacd0c7d27331b7766c8008c433cd840a8c6f38f45
4
- data.tar.gz: 75271e7344781d9338c09263b1ac036355147cd228ae0b7581272cfb3dc53a1a
3
+ metadata.gz: a6f5cbb865a9d767748d30d1d4066d5739d93e218ba643a54872d49a139ed4dd
4
+ data.tar.gz: f8b27ca7a6faee4525e79f0252e2054afbe6d1698e85ddaeaf9602e62d4c392d
5
5
  SHA512:
6
- metadata.gz: b8a8075e90042f2e6183dae5faf84e3c8749b0b8138e9f30fc3499287eef1cf2a3120f5bc7d5c9e3427ce9fe088ad0b28f38cfa3a4471df6b9c25cb4b3c38006
7
- data.tar.gz: '088fe43e02e523346e26ad05cd8617aa3298e046b90760cdafcdb7d0f74fd9a4b3c0ab56e8489e65ac51493850611faf267bd1899a3486088153ddc9fd594ad0'
6
+ metadata.gz: 315555b3b6d1b661abd6c4c37b95899d94abfa84e0b525e2211d77d7ade941a55aa4ee8f17d0ef314f71ae79e7fc3eea6c329b63ffb01aca459128b464d4652a
7
+ data.tar.gz: 4bdc7ecfb0cbf781fee0c27fcefc6a218c1dbd3916ba71e3e3c97729a7c676e633e5d1451cf20e44a26f2e65694de94cfc7e24757a3d3ba4af4b47ed4f19e969
@@ -0,0 +1,23 @@
1
+ name: Check
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ rubocop:
11
+
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v3
16
+ - name: Set up Ruby
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: '3.3'
20
+ - name: Install dependencies
21
+ run: bundle install
22
+ - name: Run RuboCop
23
+ run: bundle exec rubocop
@@ -13,7 +13,8 @@ jobs:
13
13
 
14
14
  strategy:
15
15
  matrix:
16
- ruby-version: ['3.2', '3.1', '3.0']
16
+ ruby-version: ['3.3', '3.2', '3.1']
17
+ node-version: ['16', '18', '20', '22']
17
18
 
18
19
  steps:
19
20
  - uses: actions/checkout@v3
@@ -21,7 +22,11 @@ jobs:
21
22
  uses: ruby/setup-ruby@v1
22
23
  with:
23
24
  ruby-version: ${{ matrix.ruby-version }}
25
+ - name: Set up Node.js ${{ matrix.node-version }}
26
+ uses: actions/setup-node@v4
27
+ with:
28
+ node-version: ${{ matrix.node-version }}
24
29
  - name: Install dependencies
25
30
  run: bundle install
26
31
  - name: Run tests
27
- run: bundle exec rake
32
+ run: bundle exec rake
data/Gemfile CHANGED
@@ -16,5 +16,5 @@ end
16
16
 
17
17
  group :test do
18
18
  gem 'minitest', '~> 5.0'
19
- gem 'rake', '~> 12.0'
19
+ gem 'rake', '~> 13.0'
20
20
  end
data/README.md CHANGED
@@ -60,7 +60,7 @@ bundle exec rake
60
60
 
61
61
  ## Contributing
62
62
 
63
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/execjs-pcruntime. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
63
+ Bug reports and pull requests are welcome on GitHub at https://github.com/heyinc/execjs-pcruntime. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
64
64
 
65
65
  ## License
66
66
 
@@ -68,4 +68,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
68
68
 
69
69
  ## Code of Conduct
70
70
 
71
- Everyone interacting in the Execjs::Pcruntime project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/execjs-pcruntime/blob/master/CODE_OF_CONDUCT.md).
71
+ Everyone interacting in the Execjs::Pcruntime project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/heyinc/execjs-pcruntime/blob/master/CODE_OF_CONDUCT.md).
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.name = 'execjs-pcruntime'
9
9
  spec.version = Execjs::PCRuntime::VERSION
10
10
  spec.authors = ['White-Green']
11
- spec.required_ruby_version = '>=3.0.0'
11
+ spec.required_ruby_version = '>=3.1.0'
12
12
  spec.summary = 'Fast ExecJS Runtime using Process as a Context.'
13
13
  spec.homepage = 'https://rubygems.org/gems/execjs-pcruntime'
14
14
  spec.license = 'MIT'
@@ -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
@@ -155,6 +197,7 @@ module ExecJS
155
197
  socket.write_timeout *= 100
156
198
 
157
199
  request = Net::HTTP::Post.new(path)
200
+ request['Host'] = 'localhost'
158
201
  request['Connection'] = 'close'
159
202
  unless content_type.nil?
160
203
  request['Content-Type'] = content_type
@@ -187,10 +230,15 @@ module ExecJS
187
230
  error.set_backtrace(stack)
188
231
  raise error
189
232
  end
233
+ ensure
234
+ @semaphore.release
190
235
  end
236
+
191
237
  # rubocop:enable Metrics/MethodLength,Metrics/AbcSize
192
238
  end
193
239
 
240
+ # rubocop:enable Metrics/ClassLength
241
+
194
242
  attr_reader :name
195
243
 
196
244
  # @param [String] name name of Runtime
@@ -203,6 +251,8 @@ module ExecJS
203
251
  @runner_path = runner_path
204
252
  @binary = nil
205
253
  @deprecated = deprecated
254
+ # limit number of threads 128 to avoid Errno::ECONNREFUSED
255
+ @semaphore = Semaphore.new 128
206
256
  end
207
257
 
208
258
  # implementation of ExecJS::Runtime#available?
@@ -217,8 +267,8 @@ module ExecJS
217
267
 
218
268
  # Launch JavaScript Runtime and return its handle.
219
269
  # @return [JSRuntimeHandle]
220
- def create_runtime_handle
221
- JSRuntimeHandle.new(binary, @runner_path)
270
+ def create_runtime_handle(compile_source)
271
+ JSRuntimeHandle.new(binary, @runner_path, compile_source, @semaphore)
222
272
  end
223
273
 
224
274
  private
@@ -256,5 +306,26 @@ module ExecJS
256
306
  nil
257
307
  end
258
308
  end
309
+
310
+ # Semaphore
311
+ # implemented with Thread::Queue
312
+ # since faster than Concurrent::Semaphore and Mutex+ConditionVariable
313
+ class Semaphore
314
+ # @param [Integer] limit
315
+ def initialize(limit)
316
+ @queue = Thread::Queue.new
317
+ limit.times { @queue.push nil }
318
+ end
319
+
320
+ # acquires 1 of permits from this semaphore, blocking until be available.
321
+ def acquire
322
+ @queue.pop
323
+ end
324
+
325
+ # releases 1 of permits
326
+ def release
327
+ @queue.push nil
328
+ end
329
+ end
259
330
  end
260
331
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Execjs
4
4
  module PCRuntime
5
- VERSION = '0.2.1'
5
+ VERSION = '0.3.0'
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.3.0
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: 2024-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: execjs
@@ -30,6 +30,7 @@ executables: []
30
30
  extensions: []
31
31
  extra_rdoc_files: []
32
32
  files:
33
+ - ".github/workflows/check.yml"
33
34
  - ".github/workflows/test.yml"
34
35
  - ".gitignore"
35
36
  - ".rubocop.yml"
@@ -69,14 +70,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
69
70
  requirements:
70
71
  - - ">="
71
72
  - !ruby/object:Gem::Version
72
- version: 3.0.0
73
+ version: 3.1.0
73
74
  required_rubygems_version: !ruby/object:Gem::Requirement
74
75
  requirements:
75
76
  - - ">="
76
77
  - !ruby/object:Gem::Version
77
78
  version: '0'
78
79
  requirements: []
79
- rubygems_version: 3.3.26
80
+ rubygems_version: 3.5.16
80
81
  signing_key:
81
82
  specification_version: 4
82
83
  summary: Fast ExecJS Runtime using Process as a Context.