iruby 0.8.1 → 0.8.3

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: d874a72b0b879122f83a0a31cd86d387bf9ead180e4f1954a42d2ec82b5de6be
4
- data.tar.gz: 8cbe956be221da36f99cc8c550857fcc7dee567f4a4cbcf43a167ec8ce0b5a41
3
+ metadata.gz: 43bde0525564da42d868ec8eded9727ba0398ad0ae30e7fbcaea3eb795068a24
4
+ data.tar.gz: 32bc31a371dcccc92c04ee85b557754d3336f88a9aec72efd6dbe9cfe096552a
5
5
  SHA512:
6
- metadata.gz: 6cc1fdaf13bf195ad4d18206c8663f693a77a1c0bcf29446c3d1c91a8e6e88fc80d8612868caa47794752dae9bcf6edc07165374fbb3637e4af1e49d8e8efa96
7
- data.tar.gz: 9d092e97d5adc8b615f7d1f02a8fa5b52428e5c167e24e8997770a2d808e10d327a4df86fbaba8f986640110109785cd796cd5751a50ff45b298ca85037f8976
6
+ metadata.gz: a8c7e7275de0fb026784a66286a8e3474fe54a8d60b551e2103434d3d5a1ada05ad1d5dfd2e285f19aaa4ef4bff07d181b956cef23b2da788158e9c400cae871
7
+ data.tar.gz: 6f66cf0aab532b765d3760287fb0e1aab4578054484d7fa7b4cd5d724222154c943c2768597f36054bdddb6da6b9c8aa23040b1f278618d9917ead1f61ce3969
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
@@ -16,6 +16,7 @@ jobs:
16
16
  os:
17
17
  - ubuntu-latest
18
18
  ruby:
19
+ - "4.0"
19
20
  - "3.4"
20
21
  - "3.3"
21
22
  - "3.2"
@@ -27,7 +28,7 @@ jobs:
27
28
  - debug
28
29
 
29
30
  steps:
30
- - uses: actions/checkout@v4
31
+ - uses: actions/checkout@v7
31
32
  with:
32
33
  fetch-depth: 1
33
34
 
@@ -35,6 +36,15 @@ jobs:
35
36
  with:
36
37
  ruby-version: ${{ matrix.ruby }}
37
38
 
39
+ - uses: mamba-org/setup-micromamba@v3
40
+ with:
41
+ environment-name: iruby-ci
42
+ create-args: >-
43
+ python=3.12
44
+ jupyter_console
45
+ cache-environment: true
46
+ cache-downloads: true
47
+
38
48
  - run: rake build
39
49
 
40
50
  - name: Install ffi (if Ruby 2.7)
@@ -68,7 +78,10 @@ jobs:
68
78
  GEMFILE
69
79
  BUNDLE_GEMFILE=Gemfile.irb bundle install --jobs 4 --retry 3
70
80
 
71
- - run: gem install pkg/*.gem
81
+ - name: Install IRuby gem
82
+ run: |
83
+ gem install rubygems-requirements-system
84
+ gem install pkg/*.gem
72
85
 
73
86
  - run: ruby -r iruby -e "p IRuby::SessionAdapter.select_adapter_class"
74
87
  env:
@@ -78,23 +91,17 @@ jobs:
78
91
  run: |
79
92
  sudo apt update
80
93
  sudo apt install -y --no-install-recommends \
81
- libczmq-dev \
82
- python3 \
83
- python3-pip \
84
- python3-setuptools
85
- sudo pip3 install wheel
86
- sudo pip3 install -r ci/requirements.txt
94
+ libczmq-dev
87
95
 
88
96
  - run: bundle install --jobs 4 --retry 3
89
97
 
90
98
  - name: Run tests
91
99
  env:
92
- PYTHON: python3
93
100
  ADAPTERS: cztop ffi-rzmq
94
101
  run: |
95
102
  for adapter in $ADAPTERS; do
96
103
  export IRUBY_TEST_SESSION_ADAPTER_NAME=$adapter
97
- bundle exec rake test TESTOPTS="-v"
104
+ micromamba run -n iruby-ci bundle exec rake test TESTOPTS="-v"
98
105
  done
99
106
 
100
107
  windows:
@@ -102,7 +109,7 @@ jobs:
102
109
  runs-on: windows-latest
103
110
 
104
111
  steps:
105
- - uses: actions/checkout@v4
112
+ - uses: actions/checkout@v7
106
113
  with:
107
114
  fetch-depth: 1
108
115
 
@@ -123,7 +130,7 @@ jobs:
123
130
  runs-on: macos-latest
124
131
 
125
132
  steps:
126
- - uses: actions/checkout@v4
133
+ - uses: actions/checkout@v7
127
134
  with:
128
135
  fetch-depth: 1
129
136
 
@@ -133,7 +140,7 @@ jobs:
133
140
 
134
141
  - run: rake build
135
142
 
136
- - run: gem install pkg/*.gem
143
+ - run: gem install rubygems-requirements-system pkg/*.gem
137
144
 
138
145
  - run: ruby -r iruby -e "p IRuby::SessionAdapter.select_adapter_class"
139
146
  env:
data/CHANGES.md CHANGED
@@ -1,3 +1,22 @@
1
+ # 0.8.3 (2026-06-30)
2
+
3
+ * Remove MultiJson dependency and replace with JSON by @kojix2 in https://github.com/SciRuby/iruby/pull/373
4
+ * Fix undefined var in FfirzmqAdapter by @kojix2 in https://github.com/SciRuby/iruby/pull/377
5
+ * Avoid conflict with Ruby's numbered parameters by @zalt50 in https://github.com/SciRuby/iruby/pull/380
6
+ * Remove legacy IRUBY_OLD_SESSION session implementations by @kojix2 in https://github.com/SciRuby/iruby/pull/384
7
+ * Add explicit session cleanup by @kojix2 in https://github.com/SciRuby/iruby/pull/388
8
+ * Use raw zmq_proxy for ffi-rzmq heartbeat by @kojix2 in https://github.com/SciRuby/iruby/pull/391
9
+ * Remove obsolete PyCall dependency by @kojix2 in https://github.com/SciRuby/iruby/pull/393
10
+ * Add Ruby 4.0 to CI workflow matrix by @kojix2 in https://github.com/SciRuby/iruby/pull/394
11
+
12
+ # 0.8.2 (2025-04-10)
13
+
14
+ * Update CI to refresh apt packages before installing IRuby gem by @kojix2 in https://github.com/SciRuby/iruby/pull/367
15
+ * Fix missing `OStream#closed?` method by @RobinDaugherty in https://github.com/SciRuby/iruby/pull/368
16
+ * Use rubygems-requirements-system to install system dependencies automatically by @kou in https://github.com/SciRuby/iruby/pull/369
17
+ * Various typo corrections by @kojix2 in https://github.com/SciRuby/iruby/pull/370
18
+ * Minor changes to README by @kojix2 in https://github.com/SciRuby/iruby/pull/371
19
+
1
20
  # 0.8.1 (2025-02-16)
2
21
 
3
22
  * Add support for jupyter widgets by @matt-do-it in https://github.com/SciRuby/iruby/pull/350
@@ -133,7 +152,7 @@
133
152
 
134
153
  * Improvements to IRuby dependency detection using `Bundler::Dependencies#specs` (@kou).
135
154
  * Use less memory forcing pry to store only the last 3 commands in memory (@kylekyle).
136
- * Use bigger z-index that is used accross all browsers (@kylekyle).
155
+ * Use bigger z-index that is used across all browsers (@kylekyle).
137
156
  * Ability to input date values as DateTime objects in IRuby/Input (@kylekyle).
138
157
  * Add option to have check boxes checked by default (@kylekyle).
139
158
  * Option for multi-select in drop down menus in the prompter (@kylekyle).
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
1
  source 'https://rubygems.org'
2
+
3
+ plugin 'rubygems-requirements-system'
4
+
2
5
  gemspec
3
6
 
4
7
  group :pry do
data/README.md CHANGED
@@ -4,70 +4,51 @@
4
4
  [![Build Status](https://github.com/SciRuby/iruby/workflows/CI/badge.svg)](https://github.com/SciRuby/iruby/actions)
5
5
  [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/RubyData/binder/master?filepath=ruby-data.ipynb)
6
6
 
7
- IRuby is a Ruby kernel for [Jupyter project](http://try.jupyter.org/).
7
+ IRuby is a Ruby kernel for [Jupyter project](https://jupyter.org/).
8
8
 
9
- ## Try IRuby
10
-
11
- You can try IRuby with a sample notebook on Binder (the same link as the banner placed above):
12
-
13
- <https://mybinder.org/v2/gh/RubyData/binder/master?filepath=ruby-data.ipynb>
14
-
15
- The following URL launches JupyterLab directly on Binder.
16
-
17
- <https://mybinder.org/v2/gh/RubyData/binder/master?filepath=../lab>
18
9
 
19
10
  ## Installation
20
11
 
21
12
  ### Requirements
22
13
 
23
- * [Jupyter](https://jupyter.org)
14
+ - [Jupyter](https://jupyter.org)
24
15
 
25
16
  The following requirements are automatically installed.
26
17
 
27
- * [ffi-rzmq](https://github.com/chuckremes/ffi-rzmq)
28
- * [libzmq](https://github.com/zeromq/libzmq)
18
+ - [ffi-rzmq](https://github.com/chuckremes/ffi-rzmq)
19
+ - [libzmq](https://github.com/zeromq/libzmq)
29
20
 
30
21
  The following dependencies are optional.
31
22
 
32
- * [Pry][Pry], if you want to use [Pry][Pry] instead of IRB for the code execution backend
23
+ - [Pry][Pry], if you want to use [Pry][Pry] instead of IRB for the code execution backend
33
24
 
34
25
  ### Installing Jupyter Notebook and/or JupyterLab
35
26
 
36
27
  See the official document to know how to install Jupyter Notebook and/or JupyterLab.
37
28
 
38
- * <https://jupyter.readthedocs.io/en/latest/install/notebook-classic.html>
39
- * <https://jupyter.readthedocs.io/en/latest/install.html>
29
+ - <https://docs.jupyter.org/en/latest/install/notebook-classic.html>
30
+ - <https://docs.jupyter.org/en/latest/install.html>
40
31
 
41
32
  ### Ubuntu
42
33
 
43
- #### Ubuntu 17+
34
+ #### Ubuntu 22.04+
44
35
 
45
36
  ```shell
46
37
  sudo apt install libtool libffi-dev ruby ruby-dev make
47
38
 
48
- gem install --user-install iruby
49
- iruby register --force
50
- ```
51
-
52
- #### Ubuntu 16
53
-
54
- The latest IRuby requires Ruby >= 2.4 while Ubuntu's official Ruby package is version 2.3.
55
- So you need to install Ruby >= 2.4 by yourself before preparing IRuby.
56
- We recommend to use rbenv.
57
-
58
- ```shell
59
- sudo apt install libtool libffi-dev ruby ruby-dev make
39
+ gem install --user-install rubygems-requirements-system
60
40
  gem install --user-install iruby
61
41
  iruby register --force
62
42
  ```
63
43
 
64
44
  ### Fedora
65
45
 
66
- #### Fedora 36
46
+ #### Fedora 40+
67
47
 
68
48
  ```shell
69
49
  sudo dnf install ruby ruby-dev make zeromq-devel
70
50
 
51
+ gem install --user-install rubygems-requirements-system
71
52
  gem install --user-install iruby
72
53
  iruby register --force
73
54
  ```
@@ -89,6 +70,7 @@ Install Jupyter.
89
70
  #### Homebrew
90
71
 
91
72
  ```shell
73
+ gem install rubygems-requirements-system
92
74
  gem install iruby
93
75
  iruby register --force
94
76
  ```
@@ -99,6 +81,7 @@ If you are using macports, run the following commands.
99
81
 
100
82
  ```shell
101
83
  port install libtool autoconf automake autogen
84
+ gem install rubygems-requirements-system
102
85
  gem install iruby
103
86
  iruby register --force
104
87
  ```
@@ -116,8 +99,8 @@ docker run --rm -it -p 8888:8888 rubydata/datascience-notebook
116
99
 
117
100
  You can use Java classes in your IRuby notebook.
118
101
 
119
- * JRuby version >= 9.0.4.0
120
- * iruby gem
102
+ - JRuby version >= 9.0.4.0
103
+ - iruby gem
121
104
 
122
105
  After installation, make sure that your `env` is set up to use jruby.
123
106
 
@@ -160,8 +143,8 @@ export IRUBY_SESSION_ADAPTER="cztop"
160
143
 
161
144
  There are two backends: PlainBackend and PryBackend.
162
145
 
163
- * PlainBackend is the default backend. It uses [IRB](https://github.com/ruby/irb).
164
- * PryBackend uses [Pry][Pry].
146
+ - PlainBackend is the default backend. It uses [IRB](https://github.com/ruby/irb).
147
+ - PryBackend uses [Pry][Pry].
165
148
 
166
149
  You can switch the backend to PryBackend by running the code below.
167
150
 
@@ -171,9 +154,15 @@ IRuby::Kernel.instance.switch_backend!(:pry)
171
154
 
172
155
  ## Notebooks
173
156
 
174
- Take a look at the [example notebook](http://nbviewer.ipython.org/urls/raw.github.com/SciRuby/sciruby-notebooks/master/getting_started.ipynb)
157
+ Take a look at the [example notebook](https://nbviewer.jupyter.org/urls/raw.github.com/SciRuby/sciruby-notebooks/master/getting_started.ipynb)
175
158
  and the [collection of notebooks](https://github.com/SciRuby/sciruby-notebooks/) which includes a Dockerfile to create a containerized installation of iruby
176
- and other scientific gems. You can find the prebuild image at [dockerhub](https://registry.hub.docker.com/u/minad/sciruby-notebooks/).
159
+ and other scientific gems.
160
+
161
+ ## Try IRuby
162
+
163
+ - [Try a sample notebook on Binder](https://mybinder.org/v2/gh/RubyData/binder/master?filepath=ruby-data.ipynb)
164
+
165
+ - [Launch JupyterLab on Binder](https://mybinder.org/v2/gh/RubyData/binder/master?filepath=../lab)
177
166
 
178
167
  ## Contributing
179
168
 
data/Rakefile CHANGED
@@ -16,27 +16,3 @@ task :test do
16
16
  end
17
17
 
18
18
  task default: 'test'
19
-
20
- namespace :docker do
21
- def root_dir
22
- @root_dir ||= File.expand_path("..", __FILE__)
23
- end
24
-
25
- task :build do
26
- container_name = "iruby_build"
27
- image_name = "mrkn/iruby"
28
- sh "docker", "run",
29
- "--name", container_name,
30
- "-v", "#{root_dir}:/tmp/iruby",
31
- "rubylang/ruby", "/bin/bash", "/tmp/iruby/docker/setup.sh"
32
- sh "docker", "commit", container_name, image_name
33
- sh "docker", "rm", container_name
34
- end
35
-
36
- task :test do
37
- root_dir = File.expand_path("..", __FILE__)
38
- sh "docker", "run", "-it", "--rm",
39
- "-v", "#{root_dir}:/tmp/iruby",
40
- "mrkn/iruby", "/bin/bash", "/tmp/iruby/docker/test.sh"
41
- end
42
- end
data/iruby.gemspec CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
6
6
  s.authors = ['Daniel Mendler', 'The SciRuby developers']
7
7
  s.email = ['mail@daniel-mendler.de']
8
8
  s.summary = 'Ruby Kernel for Jupyter'
9
- s.description = 'A Ruby kernel for Jupyter environment. Try it at try.jupyter.org.'
9
+ s.description = 'A Ruby kernel for Jupyter environment.'
10
10
  s.homepage = 'https://github.com/SciRuby/iruby'
11
11
  s.license = 'MIT'
12
12
 
@@ -15,7 +15,6 @@ Gem::Specification.new do |s|
15
15
  s.executables = %w[iruby]
16
16
  s.test_files = s.files.grep(%r{^test/})
17
17
  s.require_paths = %w[lib]
18
- s.extensions = %w[ext/Rakefile]
19
18
 
20
19
  s.required_ruby_version = '>= 2.3.0'
21
20
 
@@ -24,13 +23,20 @@ Gem::Specification.new do |s|
24
23
  s.add_dependency 'irb'
25
24
  s.add_dependency 'logger'
26
25
  s.add_dependency 'mime-types', '>= 3.3.1'
27
- s.add_dependency 'multi_json', '~> 1.11'
28
- s.add_dependency 'native-package-installer'
29
26
 
30
- s.add_development_dependency 'pycall', '>= 1.2.1'
31
27
  s.add_development_dependency 'rake'
32
28
  s.add_development_dependency 'test-unit'
33
29
  s.add_development_dependency 'test-unit-rr'
34
30
 
31
+ [
32
+ ['arch_linux', 'zeromq'],
33
+ ['debian', 'libzmq3-dev'],
34
+ ['freebsd', 'libzmq4'],
35
+ ['homebrew', 'zmq'],
36
+ ['macports', 'zmq'],
37
+ ].each do |platform, package|
38
+ s.requirements << "system: libzmq: #{platform}: #{package}"
39
+ end
40
+
35
41
  s.metadata['msys2_mingw_dependencies'] = 'zeromq'
36
42
  end
@@ -225,7 +225,7 @@ module IRuby
225
225
  end
226
226
 
227
227
  if ipython_dir
228
- File.join(ipython_dir, 'kerenels')
228
+ File.join(ipython_dir, 'kernels')
229
229
  else
230
230
  Jupyter.kernelspec_dir
231
231
  end
data/lib/iruby/backend.rb CHANGED
@@ -18,7 +18,7 @@ module IRuby
18
18
  # TODO Add IRuby.cache_size which controls the size of the Out array
19
19
  # and sets the oldest entries and _<n> variables to nil.
20
20
  if store_history
21
- b.local_variable_set("_#{Out.size}", out)
21
+ b.local_variable_set("_o#{Out.size}", out)
22
22
  b.local_variable_set("_i#{In.size}", code)
23
23
 
24
24
  Out << out
data/lib/iruby/display.rb CHANGED
@@ -7,7 +7,7 @@ module IRuby
7
7
  "text/markdown" => :to_markdown,
8
8
  "image/svg+xml" => :to_svg,
9
9
  "image/png" => :to_png,
10
- "appliation/pdf" => :to_pdf,
10
+ "application/pdf" => :to_pdf,
11
11
  "image/jpeg" => :to_jpeg,
12
12
  "text/latex" => [:to_latex, :to_tex],
13
13
  # NOTE: Do not include the entry of "application/json" because
@@ -113,7 +113,7 @@
113
113
  "\n",
114
114
  "The enter key will submit an input or form and the escape key will cancel it. Canceled inputs are returned as `nil`. Inputs are automatically canceled if destroyed. An input can be destroyed by clearing its cell's output. The `cancel` button will cancel a form and all other buttons will submit it. \n",
115
115
  "\n",
116
- "After a form destroyed, the cell's output is cleared. Be careful not to prompt for input in a block that has previous output you would like to keep. Output is cleared to prevent forms from interferring with one another and to ensure that inputs are not inadvertently saved to the notebook. "
116
+ "After a form destroyed, the cell's output is cleared. Be careful not to prompt for input in a block that has previous output you would like to keep. Output is cleared to prevent forms from interfering with one another and to ensure that inputs are not inadvertently saved to the notebook. "
117
117
  ]
118
118
  },
119
119
  {
@@ -184,7 +184,7 @@
184
184
  "source": [
185
185
  "## Defaults\n",
186
186
  "\n",
187
- "Most inputs will accept a `default` parameter. If no default is given, the deault is `nil`. Since checkboxes can have multiple values selected, you can pass an array of values. To check everything, pass `true` as the default. "
187
+ "Most inputs will accept a `default` parameter. If no default is given, the default is `nil`. Since checkboxes can have multiple values selected, you can pass an array of values. To check everything, pass `true` as the default. "
188
188
  ]
189
189
  },
190
190
  {
@@ -67,7 +67,7 @@ end
67
67
 
68
68
  The enter key will submit an input or form and the escape key will cancel it. Canceled inputs are returned as `nil`. Inputs are automatically canceled if destroyed. An input can be destroyed by clearing its cell's output. The `cancel` button will cancel a form and all other buttons will submit it.
69
69
 
70
- After a form destroyed, the cell's output is cleared. Be careful not to prompt for input in a block that has previous output you would like to keep. Output is cleared to prevent forms from interferring with one another and to ensure that inputs are not inadvertently saved to the notebook.
70
+ After a form destroyed, the cell's output is cleared. Be careful not to prompt for input in a block that has previous output you would like to keep. Output is cleared to prevent forms from interfering with one another and to ensure that inputs are not inadvertently saved to the notebook.
71
71
 
72
72
 
73
73
  ```ruby
@@ -105,7 +105,7 @@ end
105
105
 
106
106
  ## Defaults
107
107
 
108
- Most inputs will accept a `default` parameter. If no default is given, the deault is `nil`. Since checkboxes can have multiple values selected, you can pass an array of values. To check everything, pass `true` as the default.
108
+ Most inputs will accept a `default` parameter. If no default is given, the default is `nil`. Since checkboxes can have multiple values selected, you can pass an array of values. To check everything, pass `true` as the default.
109
109
 
110
110
 
111
111
  ```ruby
@@ -63,7 +63,7 @@ module IRuby
63
63
  end
64
64
 
65
65
  def submit
66
- result = MultiJson.load(Kernel.instance.session.recv_input)
66
+ result = JSON.parse(Kernel.instance.session.recv_input)
67
67
 
68
68
  unless result.has_key? @id
69
69
  submit
@@ -74,4 +74,4 @@ module IRuby
74
74
  end
75
75
  end
76
76
  end
77
- end
77
+ end
data/lib/iruby/kernel.rb CHANGED
@@ -38,7 +38,7 @@ module IRuby
38
38
  ].freeze
39
39
 
40
40
  def initialize(config_file, session_adapter_name=nil)
41
- @config = MultiJson.load(File.read(config_file))
41
+ @config = JSON.parse(File.read(config_file))
42
42
  IRuby.logger.debug("IRuby kernel start with config #{@config}")
43
43
  Kernel.instance = self
44
44
 
@@ -126,6 +126,13 @@ module IRuby
126
126
  while @running
127
127
  dispatch
128
128
  end
129
+ ensure
130
+ close
131
+ end
132
+
133
+ def close
134
+ @session&.close
135
+ stop_parent_process_poller
129
136
  end
130
137
 
131
138
  # @private
@@ -314,6 +321,13 @@ module IRuby
314
321
  end
315
322
  end
316
323
 
324
+ def stop_parent_process_poller
325
+ return unless @parent_poller&.alive?
326
+
327
+ @parent_poller.kill
328
+ @parent_poller.join
329
+ end
330
+
317
331
  def start_parent_process_pollar_unix
318
332
  Thread.start do
319
333
  IRuby.logger.warn("parent process poller thread started.")
data/lib/iruby/ostream.rb CHANGED
@@ -11,6 +11,10 @@ module IRuby
11
11
  @session = nil
12
12
  end
13
13
 
14
+ def closed?
15
+ @session.nil?
16
+ end
17
+
14
18
  def flush
15
19
  end
16
20
 
data/lib/iruby/session.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'iruby/session_adapter'
2
- require 'iruby/session/mixin'
2
+ require 'iruby/session_serializer'
3
3
 
4
4
  require 'securerandom'
5
5
  require 'time'
@@ -25,6 +25,26 @@ module IRuby
25
25
  "#{@adapter.name} session adapter"
26
26
  end
27
27
 
28
+ def close
29
+ return if @closed
30
+
31
+ @closed = true
32
+ begin
33
+ @adapter.shutdown_heartbeat(@hb_socket) if @hb_socket
34
+ ensure
35
+ begin
36
+ stop_heartbeat
37
+ ensure
38
+ begin
39
+ close_sockets
40
+ ensure
41
+ @adapter.close
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ # Optional setup hook.
28
48
  def setup
29
49
  end
30
50
 
@@ -51,10 +71,10 @@ module IRuby
51
71
  @hb_socket, @hb_port = @adapter.make_rep_socket(protocol, host, hb_port)
52
72
  @heartbeat_thread = Thread.start do
53
73
  begin
54
- # NOTE: this loop is copied from CZTop's old session code
74
+ # Adapters should return when cleanup closes the heartbeat socket/context.
55
75
  @adapter.heartbeat_loop(@hb_socket)
56
76
  rescue Exception => e
57
- IRuby.logger.fatal "Kernel heartbeat died: #{e.message}\n#{e.backtrace.join("\n")}"
77
+ IRuby.logger.fatal "Kernel heartbeat died: #{e.message}\n#{e.backtrace.join("\n")}" unless @closed
58
78
  end
59
79
  end
60
80
  end
@@ -102,6 +122,22 @@ module IRuby
102
122
 
103
123
  private
104
124
 
125
+ def close_sockets
126
+ @sockets&.values&.each do |socket|
127
+ @adapter.close_socket(socket)
128
+ end
129
+ end
130
+
131
+ def stop_heartbeat
132
+ return unless @heartbeat_thread&.alive?
133
+
134
+ @heartbeat_thread.join(1)
135
+ return unless @heartbeat_thread.alive?
136
+
137
+ @heartbeat_thread.kill
138
+ @heartbeat_thread.join
139
+ end
140
+
105
141
  def check_socket_type(socket_type)
106
142
  case socket_type
107
143
  when :publish, :reply, :stdin
@@ -26,23 +26,41 @@ module IRuby
26
26
  end
27
27
 
28
28
  def heartbeat_loop(sock)
29
- @heartbeat_device = ZMQ::Device.new(sock, sock)
29
+ # Avoid ZMQ::Device in #357; call libzmq's proxy directly.
30
+ rc = LibZMQ.zmq_proxy(sock.socket, sock.socket, nil)
31
+ errno = ZMQ::Util.errno
32
+ return if rc == -1 && (zmq_errno?(:ETERM, errno) || zmq_errno?(:EINTR, errno))
33
+
34
+ ZMQ::Util.error_check('zmq_proxy', rc)
35
+ end
36
+
37
+ def shutdown_heartbeat(sock)
38
+ if @zmq_context&.context && LibZMQ.respond_to?(:zmq_ctx_shutdown)
39
+ LibZMQ.zmq_ctx_shutdown(@zmq_context.context)
40
+ end
41
+ close_socket(sock)
42
+ end
43
+
44
+ def close
45
+ @zmq_context&.terminate
46
+ @zmq_context = nil
30
47
  end
31
48
 
32
49
  private
33
50
 
34
- def make_socket(type, protocol, host, port)
35
- case type
51
+ def make_socket(type_symbol, protocol, host, port)
52
+ case type_symbol
36
53
  when :ROUTER, :PUB, :REP
37
- type = ZMQ.const_get(type)
54
+ type = ZMQ.const_get(type_symbol)
38
55
  else
39
- if ZMQ.const_defined?(type)
56
+ if ZMQ.const_defined?(type_symbol)
40
57
  raise ArgumentError, "Unsupported ZMQ socket type: #{type_symbol}"
41
58
  else
42
59
  raise ArgumentError, "Invalid ZMQ socket type: #{type_symbol}"
43
60
  end
44
61
  end
45
62
  zmq_context.socket(type).tap do |sock|
63
+ sock.setsockopt(ZMQ::LINGER, 0) if type_symbol == :REP
46
64
  sock.bind("#{protocol}://#{host}:#{port}")
47
65
  end
48
66
  end
@@ -50,6 +68,10 @@ module IRuby
50
68
  def zmq_context
51
69
  @zmq_context ||= ZMQ::Context.new
52
70
  end
71
+
72
+ def zmq_errno?(name, errno)
73
+ ZMQ.const_defined?(name) && ZMQ.const_get(name) == errno
74
+ end
53
75
  end
54
76
  end
55
77
  end
@@ -1,4 +1,4 @@
1
- require 'iruby/session/mixin'
1
+ require 'iruby/session_serializer'
2
2
 
3
3
  module IRuby
4
4
  module SessionAdapter
@@ -20,9 +20,14 @@ module IRuby
20
20
 
21
21
  @send_callback = nil
22
22
  @recv_callback = nil
23
+ @closed_sockets = []
24
+ @closed = false
25
+ @heartbeat_started = false
26
+ @heartbeat_finished = false
23
27
  end
24
28
 
25
29
  attr_accessor :send_callback, :recv_callback
30
+ attr_reader :closed_sockets, :closed, :heartbeat_started, :heartbeat_finished
26
31
 
27
32
  def send(sock, data)
28
33
  unless @send_callback.nil?
@@ -37,6 +42,22 @@ module IRuby
37
42
  end
38
43
 
39
44
  def heartbeat_loop(sock)
45
+ @heartbeat_started = true
46
+ sleep 0.01 until @closed
47
+ @heartbeat_finished = true
48
+ end
49
+
50
+ def close_socket(sock)
51
+ @closed_sockets << sock
52
+ end
53
+
54
+ def shutdown_heartbeat(sock)
55
+ @closed = true
56
+ close_socket(sock)
57
+ end
58
+
59
+ def close
60
+ @closed = true
40
61
  end
41
62
 
42
63
  private