xchan.rb 0.16.5 → 0.17.0

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: e5030f45f888a9c83fadde357be0c919cece744735af66016d80bcd5a9970dcc
4
- data.tar.gz: b9de8383ad5af12a57586087c0c95b37d08ff07e7838b12471583c06ebec214b
3
+ metadata.gz: bf2db46e92eb29ed0182377c209a8ff7511460d22a73225616c719cd05372884
4
+ data.tar.gz: 05b70d50483d354f79e29f9219628c44cb6f4b25e63cbd5aee7e0c0a14da8865
5
5
  SHA512:
6
- metadata.gz: 6716dcd591f6384a8665e2601fcc10d17f7ecb2842ed656ffc13de90d785bf5bd2e089fcc2ef5bba2a58ebde0b544014cc769cec6315413d0ac7c32d99724930
7
- data.tar.gz: 578c4aedba9104f7eeec52d1780df6ac7e7caed3040f19da4e4f20b436031e60f79ce2e7300e5ce6abbd6d638406143b5bcb0a4335b4400ae27cba05743f0db2
6
+ metadata.gz: f8fb7b0f73b09c4593dfa7892fd3e0061dce37242c0e9bebab0edd204cda399dd16c8fdbadce2ee6a5fac1fc6c1d583ad60eadd85c7ece4a145fdb18358aff0a
7
+ data.tar.gz: ef454d2422ae0a719a19b7fad3bf670a3cc4b46154ea6e87786a4c6404bbe9f7af5437e81b3ed8651fa9e1f438ee7f43c38d10497ca9b50facd3aea3ededc7f0
data/.projectile CHANGED
@@ -1 +1,4 @@
1
1
  +.
2
+ -/doc/
3
+ -/.yardoc/
4
+ -/.gems/
data/Gemfile CHANGED
@@ -1,6 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  source "https://rubygems.org"
4
- gem "lockf.rb", github: "0x1eef/lockf.rb", tag: "v0.10.6"
5
- gem "test-cmd.rb", github: "0x1eef/test-cmd.rb", tag: "v0.2.0"
6
4
  gemspec
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  ## About
2
2
 
3
- xchan.rb is an easy to use library that provides a channel for
4
- InterProcess Communication (IPC) between a parent Ruby process,
5
- and its child processes. The channel is implemented through both
6
- serialization, and an unbound unix socket.
3
+ xchan.rb is an easy to use library for InterProcess
4
+ Communication (IPC). The library provides a channel
5
+ that can transfer Ruby objects between Ruby processes
6
+ with a parent <-> child relationship.
7
7
 
8
8
  ## Examples
9
9
 
@@ -11,58 +11,53 @@ serialization, and an unbound unix socket.
11
11
 
12
12
  #### Options
13
13
 
14
- When a channel is written to or read from, a Ruby object is serialized
15
- (on write) or deserialized (on read). The default serializers are available as
16
- `xchan(:marshal)`, `xchan(:json)`, and `xchan(:yaml)`. For scenarios where it
17
- is preferred to send and receive plain strings, the "plain" serializer is
18
- available as `xchan(:plain)`. This example uses
14
+ The first argument given to xchan is the serializer
15
+ that it should use. A channel that will communicate
16
+ in pure strings (ie with no serialization) is
17
+ available as `xchan(:pure)`.
18
+
19
+ Otherwise, when a channel is written to or read from,
20
+ a Ruby object is serialized (on write) or deserialized
21
+ (on read). The serializers available to choose from
22
+ are `xchan(:marshal)`, `xchan(:json)`, and `xchan(:yaml)`.
23
+ The example uses
19
24
  [`Marshal`](https://www.rubydoc.info/stdlib/core/Marshal):
20
25
 
21
26
  ```ruby
22
27
  require "xchan"
23
28
 
24
29
  ##
25
- # This channel uses Marshal to serialize objects.
26
- ch = xchan
27
- pid = fork { print "Received message: ", ch.recv[:msg], "\n" }
28
- ch.send(msg: "serialized by Marshal")
29
- ch.close
30
- Process.wait(pid)
31
-
32
- ##
33
- # This channel also uses Marshal to serialize objects.
30
+ # This channel uses Marshal to serialize objects
34
31
  ch = xchan(:marshal)
35
- pid = fork { print "Received message: ", ch.recv[:msg], "\n"
36
- ch.send(msg: "serialized by Marshal")
32
+ Process.wait fork { ch.send(5) }
33
+ print "There are ", ch.recv + 7, " disciples and the same number of tribes", "\n"
37
34
  ch.close
38
- Process.wait(pid)
39
35
 
40
36
  ##
41
- # Received message: serialized by Marshal
42
- # Received message: serialized by Marshal
37
+ # There are 12 disciples and the same number of tribes
43
38
  ```
44
39
 
45
40
  ### Read operations
46
41
 
47
42
  #### `#recv`
48
43
 
49
- The `ch.recv` method performs a blocking read. A read can block when
50
- a lock is held by another process, or when a read from the underlying
51
- socket blocks. This example performs a read that blocks in a child
52
- process until the parent process writes to the channel:
44
+ The `ch.recv` method performs a blocking read. A read
45
+ can block when a lock is held by another process, or
46
+ when a read from
47
+ [Chan::UNIXSocket#r](https://0x1eef.github,io/x/xchan.rb/Chan/UNIXSocket.html#r-instance_method)
48
+ blocks. The example performs a read that blocks until
49
+ the parent process writes to the channel:
53
50
 
54
51
  ```ruby
55
52
  require "xchan"
56
53
 
57
- ch = xchan
58
- pid = fork do
54
+ ch = xchan(:marshal)
55
+ Process.detach fork {
59
56
  print "Received a random number (child process): ", ch.recv, "\n"
60
- end
61
- # Delay for a second to let a process fork, and call "ch.recv"
57
+ }
62
58
  sleep(1)
63
59
  print "Send a random number (from parent process)", "\n"
64
60
  ch.send(rand(21))
65
- Process.wait(pid)
66
61
  ch.close
67
62
 
68
63
  ##
@@ -72,11 +67,12 @@ ch.close
72
67
 
73
68
  #### `#recv_nonblock`
74
69
 
75
- The non-blocking counterpart to `#recv` is `#recv_nonblock`. The `#recv_nonblock`
76
- method raises `Chan::WaitLockable` when a read blocks because of a lock held by
77
- another process, and the method raises `Chan::WaitReadable` when a read on the
78
- underlying socket blocks. This example performs a read that will
79
- raise `Chan::WaitReadable`:
70
+ The non-blocking counterpart to `#recv` is `#recv_nonblock`.
71
+ The `#recv_nonblock` method raises `Chan::WaitLockable` when
72
+ a read blocks because of a lock held by another process, and
73
+ the method raises `Chan::WaitReadable` when a read from
74
+ [Chan::UNIXSocket#r](https://0x1eef.github,io/x/xchan.rb/Chan/UNIXSocket.html#r-instance_method)
75
+ blocks:
80
76
 
81
77
  ```ruby
82
78
  require "xchan"
@@ -92,7 +88,7 @@ rescue Chan::WaitLockable
92
88
  retry
93
89
  end
94
90
  trap("SIGINT") { exit(1) }
95
- read(xchan)
91
+ read(xchan(:marshal))
96
92
 
97
93
  ##
98
94
  # Wait 1 second for channel to be readable
@@ -104,15 +100,17 @@ read(xchan)
104
100
 
105
101
  #### `#send`
106
102
 
107
- The `ch.send` method performs a blocking write. A write can block when a lock
108
- is held by another process, or when a write to the underlying socket blocks.
109
- This example performs a write that will block when the send buffer becomes full:
103
+ The `ch.send` method performs a blocking write.
104
+ A write can block when a lock is held by another
105
+ process, or when a write to
106
+ [Chan::UNIXSocket#w](https://0x1eef.github,io/x/xchan.rb/Chan/UNIXSocket.html#w-instance_method)
107
+ blocks. The example fills the send buffer:
110
108
 
111
109
  ```ruby
112
110
  require "xchan"
113
111
 
114
112
  ch = xchan(:marshal, sock_type: Socket::SOCK_STREAM)
115
- sndbuf = ch.getsockopt(:reader, Socket::SOL_SOCKET, Socket::SO_SNDBUF)
113
+ sndbuf = ch.w.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF)
116
114
  while ch.bytes_sent <= sndbuf.int
117
115
  ch.send(1)
118
116
  end
@@ -120,11 +118,13 @@ end
120
118
 
121
119
  #### `#send_nonblock`
122
120
 
123
- The non-blocking counterpart to `#send` is `#send_nonblock`. The `#send_nonblock`
124
- method raises `Chan::WaitLockable` when a write blocks because of a lock held
125
- by another process, and the method raises `Chan::WaitReadable` when a write to
126
- the underlying socket blocks. This example builds on the previous example by
127
- freeing space on the send buffer when a write is found to block:
121
+ The non-blocking counterpart to `#send` is
122
+ `#send_nonblock`. The `#send_nonblock` method raises
123
+ `Chan::WaitLockable` when a write blocks because of
124
+ a lock held by another process, and the method raises
125
+ `Chan::WaitWritable` when a write to
126
+ [Chan::UNIXSocket#w](https://0x1eef.github,io/x/xchan.rb/Chan/UNIXSocket.html#w-instance_method)
127
+ blocks. The example frees space on the send buffer:
128
128
 
129
129
  ```ruby
130
130
  require "xchan"
@@ -141,7 +141,7 @@ rescue Chan::WaitLockable
141
141
  end
142
142
 
143
143
  ch = xchan(:marshal, sock_type: Socket::SOCK_STREAM)
144
- sndbuf = ch.getsockopt(:writer, Socket::SOL_SOCKET, Socket::SO_SNDBUF)
144
+ sndbuf = ch.w.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF)
145
145
  while ch.bytes_sent <= sndbuf.int
146
146
  send_nonblock(ch, 1)
147
147
  end
@@ -152,31 +152,14 @@ end
152
152
 
153
153
  ### Socket
154
154
 
155
- #### Types
156
-
157
- A channel can be created with one of three sockets types:
158
-
159
- * `Socket::SOCK_DGRAM`
160
- * `Socket::SOCK_STREAM`
161
- * `Socket::SOCK_SEQPACKET`
162
-
163
- The default is `Socket::SOCK_DGRAM` because its default settings
164
- provide the most buffer space. The socket type can be specified as
165
- a keyword argument:
166
-
167
- ```ruby
168
- require "xchan"
169
- ch = xchan(:marshal, sock_type: Socket::SOCK_STREAM)
170
- ```
171
-
172
155
  #### Options
173
156
 
174
- A channel is composed of two sockets, one for reading and the other for writing.
175
- Socket options can be read and set on either of the two sockets with the
176
- `Chan::UNIXSocket#getsockopt`, and `Chan::UNIXSocket#setsockopt` methods.
177
- Besides the first argument (`:reader`, or `:writer`), the rest of the arguments
178
- are identical to `Socket#{getsockopt,setsockopt}`. This example's results can
179
- vary depending on the operating system it is run on:
157
+ A channel has one socket for read operations and another
158
+ socket for write operations.
159
+ [Chan::UNIXSocket#r](https://0x1eef.github,io/x/xchan.rb/Chan/UNIXSocket.html#r-instance_method)
160
+ returns the socket used for read operations, and
161
+ [Chan::UNIXSocket#w](https://0x1eef.github,io/x/xchan.rb/Chan/UNIXSocket.html#w-instance_method)
162
+ returns the socket used for write operations:
180
163
 
181
164
  ```ruby
182
165
  require "xchan"
@@ -184,12 +167,12 @@ ch = xchan(:marshal)
184
167
 
185
168
  ##
186
169
  # Print the value of SO_RCVBUF
187
- rcvbuf = ch.getsockopt(:reader, Socket::SOL_SOCKET, Socket::SO_RCVBUF)
170
+ rcvbuf = ch.r.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVBUF)
188
171
  print "The read buffer can contain a maximum of: ", rcvbuf.int, " bytes.\n"
189
172
 
190
173
  ##
191
174
  # Print the value of SO_SNDBUF
192
- sndbuf = ch.getsockopt(:writer, Socket::SOL_SOCKET, Socket::SO_SNDBUF)
175
+ sndbuf = ch.w.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF)
193
176
  print "The maximum size of a single message is: ", sndbuf.int, " bytes.\n"
194
177
 
195
178
  ##
@@ -197,50 +180,22 @@ print "The maximum size of a single message is: ", sndbuf.int, " bytes.\n"
197
180
  # The maximum size of a single message is: 2048 bytes.
198
181
  ```
199
182
 
200
- ### Temporary files
183
+ ## Documentation
201
184
 
202
- #### tmpdir
185
+ A complete API reference is available at
186
+ [0x1eef.github.io/x/xchan.rb](https://0x1eef.github.io/x/xchan.rb/).
203
187
 
204
- A single channel creates three temporary files that are removed
205
- from the filesystem as soon as they are created. By default the
206
- files are stored - for a short time - in `Dir.tmpdir`. Read and
207
- write permissions are reserved for the process that created
208
- them, inclusive of its child processes.
188
+ ## Install
209
189
 
210
- The parent directory of the temporary files can be changed with the
211
- `tmpdir` option:
190
+ xchan.rb can be installed via rubygems.org:
212
191
 
213
- ```ruby
214
- require "xchan"
215
- ch = xchan(:marshal, tmpdir: Dir.home)
216
- ```
192
+ gem install xchan.rb
217
193
 
218
194
  ## Sources
219
195
 
220
196
  * [Source code (GitHub)](https://github.com/0x1eef/xchan.rb#readme)
221
197
  * [Source code (GitLab)](https://gitlab.com/0x1eef/xchan.rb#about)
222
198
 
223
- ## Install
224
-
225
- **Git**
226
-
227
- xchan.rb is distributed as a RubyGem through its git repositories. <br>
228
- [GitHub](https://github.com/0x1eef/xchan.rb),
229
- and
230
- [GitLab](https://gitlab.com/0x1eef/xchan.rb)
231
- are available as sources.
232
-
233
- ``` ruby
234
- # Gemfile
235
- gem "xchan.rb", github: "0x1eef/xchan.rb", tag: "v0.16.5"
236
- ```
237
-
238
- **Rubygems.org**
239
-
240
- xchan.rb can also be installed via rubygems.org.
241
-
242
- gem install xchan.rb
243
-
244
199
  ## <a id="license"> License </a>
245
200
 
246
201
  [BSD Zero Clause](https://choosealicense.com/licenses/0bsd/).
@@ -2,11 +2,7 @@ require 'delegate'
2
2
  require 'tmpdir'
3
3
 
4
4
  ##
5
- # {Chan::Tempfile Chan::Tempfile} is a fork of Tempfile from
6
- # Ruby's standard library. The primary difference between
7
- # {Chan::Tempfile Chan::Tempfile}, and the standard library is
8
- # that [ruby/tempfile#22](https://github.com/ruby/tempfile/pull/22)
9
- # is applied in this fork.
5
+ # @private
10
6
  class Chan::Tempfile < DelegateClass(File)
11
7
  VERSION = "0.1.3"
12
8
 
@@ -260,63 +256,63 @@ class Chan::Tempfile < DelegateClass(File)
260
256
  end
261
257
  end
262
258
 
263
- # Creates a file in the underlying file system;
264
- # returns a new \File object based on that file.
265
- #
266
- # With no block given and no arguments, creates and returns file whose:
267
- #
268
- # - Class is {File}[https://docs.ruby-lang.org/en/master/File.html] (not \Tempfile).
269
- # - Directory is the system temporary directory (system-dependent).
270
- # - Generated filename is unique in that directory.
271
- # - Permissions are <tt>0600</tt>;
272
- # see {File Permissions}[https://docs.ruby-lang.org/en/master/File.html#label-File+Permissions].
273
- # - Mode is <tt>'w+'</tt> (read/write mode, positioned at the end).
274
- #
275
- # With no block, the file is not removed automatically,
276
- # and so should be explicitly removed.
277
- #
278
- # Example:
279
- #
280
- # f = Tempfile.create # => #<File:/tmp/20220505-9795-17ky6f6>
281
- # f.class # => File
282
- # f.path # => "/tmp/20220505-9795-17ky6f6"
283
- # f.stat.mode.to_s(8) # => "100600"
284
- # File.exist?(f.path) # => true
285
- # File.unlink(f.path)
286
- # File.exist?(f.path) # => false
287
- #
288
- # Argument +basename+, if given, may be one of:
289
- #
290
- # - A string: the generated filename begins with +basename+:
291
- #
292
- # Tempfile.create('foo') # => #<File:/tmp/foo20220505-9795-1gok8l9>
293
- #
294
- # - An array of two strings <tt>[prefix, suffix]</tt>:
295
- # the generated filename begins with +prefix+ and ends with +suffix+:
296
- #
297
- # Tempfile.create(%w/foo .jpg/) # => #<File:/tmp/foo20220505-17839-tnjchh.jpg>
298
- #
299
- # With arguments +basename+ and +tmpdir+, the file is created in directory +tmpdir+:
300
- #
301
- # Tempfile.create('foo', '.') # => #<File:./foo20220505-9795-1emu6g8>
302
- #
303
- # Keyword arguments +mode+ and +options+ are passed directly to method
304
- # {File.open}[https://docs.ruby-lang.org/en/master/File.html#method-c-open]:
305
- #
306
- # - The value given with +mode+ must be an integer,
307
- # and may be expressed as the logical OR of constants defined in
308
- # {File::Constants}[https://docs.ruby-lang.org/en/master/File/Constants.html].
309
- # - For +options+, see {Open Options}[https://docs.ruby-lang.org/en/master/IO.html#class-IO-label-Open+Options].
310
- #
311
- # With a block given, creates the file as above, passes it to the block,
312
- # and returns the block's value;
313
- # before the return, the file object is closed and the underlying file is removed:
314
- #
315
- # Tempfile.create {|file| file.path } # => "/tmp/20220505-9795-rkists"
316
- #
317
- # Related: Tempfile.new.
318
- #
319
259
  module Chan
260
+ # Creates a file in the underlying file system;
261
+ # returns a new \File object based on that file.
262
+ #
263
+ # With no block given and no arguments, creates and returns file whose:
264
+ #
265
+ # - Class is {File}[https://docs.ruby-lang.org/en/master/File.html] (not \Tempfile).
266
+ # - Directory is the system temporary directory (system-dependent).
267
+ # - Generated filename is unique in that directory.
268
+ # - Permissions are <tt>0600</tt>;
269
+ # see {File Permissions}[https://docs.ruby-lang.org/en/master/File.html#label-File+Permissions].
270
+ # - Mode is <tt>'w+'</tt> (read/write mode, positioned at the end).
271
+ #
272
+ # With no block, the file is not removed automatically,
273
+ # and so should be explicitly removed.
274
+ #
275
+ # Example:
276
+ #
277
+ # f = Tempfile.create # => #<File:/tmp/20220505-9795-17ky6f6>
278
+ # f.class # => File
279
+ # f.path # => "/tmp/20220505-9795-17ky6f6"
280
+ # f.stat.mode.to_s(8) # => "100600"
281
+ # File.exist?(f.path) # => true
282
+ # File.unlink(f.path)
283
+ # File.exist?(f.path) # => false
284
+ #
285
+ # Argument +basename+, if given, may be one of:
286
+ #
287
+ # - A string: the generated filename begins with +basename+:
288
+ #
289
+ # Tempfile.create('foo') # => #<File:/tmp/foo20220505-9795-1gok8l9>
290
+ #
291
+ # - An array of two strings <tt>[prefix, suffix]</tt>:
292
+ # the generated filename begins with +prefix+ and ends with +suffix+:
293
+ #
294
+ # Tempfile.create(%w/foo .jpg/) # => #<File:/tmp/foo20220505-17839-tnjchh.jpg>
295
+ #
296
+ # With arguments +basename+ and +tmpdir+, the file is created in directory +tmpdir+:
297
+ #
298
+ # Tempfile.create('foo', '.') # => #<File:./foo20220505-9795-1emu6g8>
299
+ #
300
+ # Keyword arguments +mode+ and +options+ are passed directly to method
301
+ # {File.open}[https://docs.ruby-lang.org/en/master/File.html#method-c-open]:
302
+ #
303
+ # - The value given with +mode+ must be an integer,
304
+ # and may be expressed as the logical OR of constants defined in
305
+ # {File::Constants}[https://docs.ruby-lang.org/en/master/File/Constants.html].
306
+ # - For +options+, see {Open Options}[https://docs.ruby-lang.org/en/master/IO.html#class-IO-label-Open+Options].
307
+ #
308
+ # With a block given, creates the file as above, passes it to the block,
309
+ # and returns the block's value;
310
+ # before the return, the file object is closed and the underlying file is removed:
311
+ #
312
+ # Tempfile.create {|file| file.path } # => "/tmp/20220505-9795-rkists"
313
+ #
314
+ # Related: Tempfile.new.
315
+ #
320
316
  def Tempfile.create(basename="", tmpdir=nil, mode: 0, perm: 0600, **options)
321
317
  tmpfile = nil
322
318
  Dir::Tmpname.create(basename, tmpdir, **options) do |tmpname, n, opts|
@@ -1,13 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  ##
4
- # The {Chan::UNIXSocket Chan::UNIXSocket} class implements a channel
5
- # for interprocess communication (IPC) using an unnamed UNIXSocket.
4
+ # An easy-to-use InterProcess Communication (IPC) library.
6
5
  class Chan::UNIXSocket
7
6
  require "socket"
8
7
  require "lockf"
9
8
  require_relative "bytes"
10
9
 
10
+ ##
11
+ # @return [UNIXSocket]
12
+ # Returns a socket used for read operations
13
+ attr_reader :r
14
+
15
+ ##
16
+ # @return [UNIXSocket]
17
+ # Returns a socket used for write operations
18
+ attr_reader :w
19
+
11
20
  ##
12
21
  # @example
13
22
  # ch = Chan::UNIXSocket.new(:marshal)
@@ -16,17 +25,17 @@ class Chan::UNIXSocket
16
25
  # ch.close
17
26
  #
18
27
  # @param [Symbol, <#dump, #load>] serializer
19
- # A serializer.
28
+ # The name of a serializer
20
29
  #
21
30
  # @param [Integer] sock_type
22
- # A socket type (eg Socket::SOCK_STREAM).
31
+ # Type of socket (eg `Socket::SOCK_STREAM`)
23
32
  #
24
33
  # @param [String] tmpdir
25
- # Path to a directory where temporary files will be stored.
34
+ # Directory where temporary files can be stored
26
35
  #
27
36
  # @return [Chan::UNIXSocket]
28
- # Returns an instance of {Chan::UNIXSocket Chan::UNIXSocket}.
29
- def initialize(serializer, tmpdir: Dir.tmpdir, sock_type: Socket::SOCK_DGRAM)
37
+ # Returns an instance of {Chan::UNIXSocket Chan::UNIXSocket}
38
+ def initialize(serializer, sock_type: Socket::SOCK_DGRAM, tmpdir: Dir.tmpdir)
30
39
  @serializer = Chan.serializers[serializer]&.call || serializer
31
40
  @r, @w = ::UNIXSocket.pair(sock_type)
32
41
  @bytes = Chan::Bytes.new(tmpdir)
@@ -197,7 +206,7 @@ class Chan::UNIXSocket
197
206
  end
198
207
 
199
208
  ##
200
- # @group Size methods
209
+ # @group Stat methods
201
210
 
202
211
  ##
203
212
  # @return [Integer]
@@ -255,61 +264,6 @@ class Chan::UNIXSocket
255
264
  ##
256
265
  # @endgroup
257
266
 
258
- ##
259
- # @group Socket options
260
-
261
- ##
262
- # @param [String, Symbol] target
263
- # `:reader`, or `:writer`.
264
- #
265
- # @param [Integer] level
266
- # The level (eg `Socket::SOL_SOCKET` for the socket level).
267
- #
268
- # @param [Integer] option_name
269
- # The name of an option (eg `Socket::SO_RCVBUF`).
270
- #
271
- # @param [Boolean, Integer] option_value
272
- # The option value (eg 12345)
273
- #
274
- # @return [Integer]
275
- # Returns 0 on success.
276
- def setsockopt(target, level, option_name, option_value)
277
- @lock.lock
278
- if !%w[reader writer].include?(target.to_s)
279
- raise ArgumentError, "target can be ':reader', or ':writer'"
280
- end
281
- target = (target == :reader) ? @r : @w
282
- target.setsockopt(level, option_name, option_value)
283
- ensure
284
- @lock.release
285
- end
286
-
287
- ##
288
- # @param [String, Symbol] target
289
- # `:reader`, or `:writer`.
290
- #
291
- # @param [Integer] level
292
- # The level (eg `Socket::SOL_SOCKET` for the socket level).
293
- #
294
- # @param [Integer] option_name
295
- # The name of an option (eg `Socket::SO_RCVBUF`).
296
- #
297
- # @return [Socket::Option]
298
- # Returns an instance of `Socket::Option`.
299
- def getsockopt(target, level, option_name)
300
- @lock.lock
301
- if !%w[reader writer].include?(target.to_s)
302
- raise ArgumentError, "target can be ':reader', or ':writer'"
303
- end
304
- target = (target == :reader) ? @r : @w
305
- target.getsockopt(level, option_name)
306
- ensure
307
- @lock.release
308
- end
309
-
310
- ##
311
- # @endgroup
312
-
313
267
  private
314
268
 
315
269
  def lock
data/lib/xchan/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Chan
4
- VERSION = "0.16.5"
4
+ VERSION = "0.17.0"
5
5
  end
data/lib/xchan.rb CHANGED
@@ -4,12 +4,25 @@ module Chan
4
4
  require_relative "xchan/version"
5
5
  require_relative "xchan/unix_socket"
6
6
  require_relative "xchan/tempfile"
7
- require_relative "xchan/mixin"
8
7
 
9
8
  WaitReadable = Class.new(IO::EAGAINWaitReadable)
10
9
  WaitWritable = Class.new(IO::EAGAINWaitWritable)
11
10
  WaitLockable = Class.new(Errno::EWOULDBLOCK)
12
- Plain = Class.new do
11
+
12
+ ##
13
+ # The Pure serializer won't perform
14
+ # serialization that goes beyond calling
15
+ # `.to_s` on the object it is given. It
16
+ # can be useful when you want to communicate
17
+ # purely in strings.
18
+ #
19
+ # @example
20
+ # ch = xchan(:pure)
21
+ # Process.wait fork {
22
+ # ch.send "Hello world"
23
+ # }
24
+ # puts ch.recv
25
+ Pure = Class.new do
13
26
  def self.dump(str) = str.to_s
14
27
  def self.load(str) = str.to_s
15
28
  end
@@ -21,20 +34,23 @@ module Chan
21
34
  # processes other than that.
22
35
  #
23
36
  # @param [String] basename
24
- # Basename of the temporary file.
37
+ # Basename of the temporary file
25
38
  #
26
39
  # @param [String] tmpdir
27
- # Parent directory of the temporary file.
40
+ # Parent directory of the temporary file
28
41
  #
29
42
  # @return [Chan::Tempfile]
30
- # Returns an instance of {Chan::Tempfile Chan::Tempfile}.
43
+ # Returns an instance of {Chan::Tempfile Chan::Tempfile}
31
44
  def self.temporary_file(basename, tmpdir: Dir.tmpdir)
32
45
  Chan::Tempfile.new(basename, tmpdir, perm: 0).tap(&:unlink)
33
46
  end
34
47
 
48
+ ##
49
+ # @return [Hash<Symbol, Proc>]
50
+ # A mapping of serializers
35
51
  def self.serializers
36
52
  {
37
- plain: lambda { Plain },
53
+ pure: lambda { Pure },
38
54
  marshal: lambda { Marshal },
39
55
  json: lambda {
40
56
  require "json" unless defined?(JSON)
@@ -48,6 +64,19 @@ module Chan
48
64
  end
49
65
  end
50
66
 
51
- class Object
52
- include Chan::Mixin
67
+ module Kernel
68
+ ##
69
+ # @example
70
+ # ch = xchan
71
+ # ch.send([1,2,3])
72
+ # ch.recv.pop # => 3
73
+ # ch.close
74
+ #
75
+ # @param serializer (see Chan::UNIXSocket#initialize)
76
+ # @param sock_type (see Chan::UNIXSocket#initialize)
77
+ # @param tmpdir (see Chan::UNIXSocket#initialize)
78
+ # @return (see Chan::UNIXSocket#initialize)
79
+ def xchan(serializer, **kw_args)
80
+ Chan::UNIXSocket.new(serializer, **kw_args)
81
+ end
53
82
  end
@@ -4,15 +4,13 @@ require_relative "../setup"
4
4
  require "xchan"
5
5
 
6
6
  $stdout.sync = true
7
- ch = xchan
8
- pid = fork do
7
+ ch = xchan(:marshal)
8
+ Process.detach fork {
9
9
  print "Received random number (child process): ", ch.recv, "\n"
10
- end
11
- # Delay for a second to let a process fork, and call "ch.recv"
10
+ }
12
11
  sleep(1)
13
12
  print "Send a random number (from parent process)", "\n"
14
13
  ch.send(rand(21))
15
- Process.wait(pid)
16
14
  ch.close
17
15
 
18
16
  ##
@@ -14,4 +14,4 @@ rescue Chan::WaitLockable
14
14
  retry
15
15
  end
16
16
  trap("SIGINT") { exit(1) }
17
- read(xchan)
17
+ read(xchan(:marshal))
@@ -4,17 +4,11 @@ require_relative "../setup"
4
4
  require "xchan"
5
5
 
6
6
  ##
7
- # This channel uses Marshal to serialize objects.
8
- ch = xchan
9
- pid = fork { print "Received message: ", ch.recv[:msg], "\n" }
10
- ch.send(msg: "serialized by Marshal")
7
+ # This channel uses Marshal to serialize objects
8
+ ch = xchan(:marshal)
9
+ Process.wait fork { ch.send(5) }
10
+ print "There are ", ch.recv + 7, " disciples and the same number of tribes", "\n"
11
11
  ch.close
12
- Process.wait(pid)
13
12
 
14
13
  ##
15
- # This channel also uses Marshal to serialize objects.
16
- ch = xchan(:marshal)
17
- pid = fork { print "Received message: ", ch.recv[:msg], "\n" }
18
- ch.send(msg: "serialized by Marshal")
19
- ch.close
20
- Process.wait(pid)
14
+ # There are 12 disciples and the same number of tribes
@@ -3,10 +3,10 @@ ch = xchan(:marshal)
3
3
 
4
4
  ##
5
5
  # Print the value of SO_RCVBUF
6
- rcvbuf = ch.getsockopt(:reader, Socket::SOL_SOCKET, Socket::SO_RCVBUF)
6
+ rcvbuf = ch.r.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVBUF)
7
7
  print "The read buffer can contain a maximum of: ", rcvbuf.int, " bytes.\n"
8
8
 
9
9
  ##
10
10
  # Print the value of SO_SNDBUF
11
- sndbuf = ch.getsockopt(:writer, Socket::SOL_SOCKET, Socket::SO_SNDBUF)
11
+ sndbuf = ch.w.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF)
12
12
  print "The maximum size of a single message is: ", sndbuf.int, " bytes.\n"
@@ -4,7 +4,7 @@ require_relative "../setup"
4
4
  require "xchan"
5
5
 
6
6
  ch = xchan(:marshal, sock_type: Socket::SOCK_STREAM)
7
- sndbuf = ch.getsockopt(:reader, Socket::SOL_SOCKET, Socket::SO_SNDBUF)
7
+ sndbuf = ch.w.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF)
8
8
  while ch.bytes_sent <= sndbuf.int
9
9
  ch.send(1)
10
10
  end
@@ -15,7 +15,7 @@ rescue Chan::WaitLockable
15
15
  end
16
16
 
17
17
  ch = xchan(:marshal, sock_type: Socket::SOCK_STREAM)
18
- sndbuf = ch.getsockopt(:writer, Socket::SOL_SOCKET, Socket::SO_SNDBUF)
18
+ sndbuf = ch.w.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF)
19
19
  while ch.bytes_sent <= sndbuf.int
20
20
  send_nonblock(ch, 1)
21
21
  end
data/test/readme_test.rb CHANGED
@@ -4,10 +4,8 @@ require_relative "setup"
4
4
  require "test/cmd"
5
5
 
6
6
  class Chan::ReadmeTest < Test::Unit::TestCase
7
- include Test::Cmd
8
-
9
7
  def test_serialization_1_serializers
10
- assert_equal "Received message: serialized by Marshal\n" * 2,
8
+ assert_equal "There are 12 disciples and the same number of tribes\n",
11
9
  readme_example("serialization/1_serializers.rb").stdout
12
10
  end
13
11
 
data/xchan.rb.gemspec CHANGED
@@ -10,13 +10,13 @@ Gem::Specification.new do |gem|
10
10
  gem.licenses = ["0BSD"]
11
11
  gem.files = `git ls-files`.split($/)
12
12
  gem.require_paths = ["lib"]
13
- gem.summary = "An easy to use InterProcess Communication (IPC) library."
13
+ gem.summary = "An easy to use InterProcess Communication (IPC) library"
14
14
  gem.description = gem.summary
15
- gem.add_runtime_dependency "lockf.rb", "~> 0.10.6"
15
+ gem.add_runtime_dependency "lockf.rb", "~> 1.0"
16
16
  gem.add_development_dependency "test-unit", "~> 3.5.7"
17
17
  gem.add_development_dependency "yard", "~> 0.9"
18
18
  gem.add_development_dependency "redcarpet", "~> 3.5"
19
19
  gem.add_development_dependency "standard", "~> 1.13"
20
- gem.add_development_dependency "test-cmd.rb", "~> 0.2"
20
+ gem.add_development_dependency "test-cmd.rb", "~> 0.8"
21
21
  gem.add_development_dependency "rake", "~> 13.1"
22
22
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xchan.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.5
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - '0x1eef'
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-06 00:00:00.000000000 Z
11
+ date: 2024-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lockf.rb
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.10.6
19
+ version: '1.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.10.6
26
+ version: '1.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: test-unit
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.2'
89
+ version: '0.8'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.2'
96
+ version: '0.8'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rake
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -108,7 +108,7 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '13.1'
111
- description: An easy to use InterProcess Communication (IPC) library.
111
+ description: An easy to use InterProcess Communication (IPC) library
112
112
  email:
113
113
  - 0x1eef@protonmail.com
114
114
  executables: []
@@ -127,7 +127,6 @@ files:
127
127
  - Rakefile.rb
128
128
  - lib/xchan.rb
129
129
  - lib/xchan/bytes.rb
130
- - lib/xchan/mixin.rb
131
130
  - lib/xchan/stat.rb
132
131
  - lib/xchan/tempfile.rb
133
132
  - lib/xchan/unix_socket.rb
@@ -163,8 +162,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
162
  - !ruby/object:Gem::Version
164
163
  version: '0'
165
164
  requirements: []
166
- rubygems_version: 3.5.3
165
+ rubygems_version: 3.5.9
167
166
  signing_key:
168
167
  specification_version: 4
169
- summary: An easy to use InterProcess Communication (IPC) library.
168
+ summary: An easy to use InterProcess Communication (IPC) library
170
169
  test_files: []
data/lib/xchan/mixin.rb DELETED
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- ##
4
- # A module that is included into Ruby's {Object} class.
5
- module Chan::Mixin
6
- ##
7
- # @example
8
- # ch = xchan
9
- # ch.send([1,2,3])
10
- # ch.recv.pop # => 3
11
- # ch.close
12
- #
13
- # @param serializer (see Chan::UNIXSocket#initialize)
14
- # @param sock_type (see Chan::UNIXSocket#initialize)
15
- # @param tmpdir (see Chan::UNIXSocket#initialize)
16
- # @return (see Chan::UNIXSocket#initialize)
17
- def xchan(serializer = :marshal, **kw_args)
18
- Chan::UNIXSocket.new(serializer, **kw_args)
19
- end
20
- end