xchan.rb 0.16.5 → 0.17.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 +4 -4
- data/.projectile +3 -0
- data/Gemfile +0 -2
- data/README.md +62 -107
- data/lib/xchan/tempfile.rb +57 -61
- data/lib/xchan/unix_socket.rb +17 -63
- data/lib/xchan/version.rb +1 -1
- data/lib/xchan.rb +37 -8
- data/share/xchan.rb/examples/read_operations/1_blocking_read.rb +3 -5
- data/share/xchan.rb/examples/read_operations/2_nonblocking_read.rb +1 -1
- data/share/xchan.rb/examples/serialization/1_serializers.rb +5 -11
- data/share/xchan.rb/examples/socket/2_options.rb +2 -2
- data/share/xchan.rb/examples/write_operations/1_blocking_write.rb +1 -1
- data/share/xchan.rb/examples/write_operations/2_nonblocking_write.rb +1 -1
- data/test/readme_test.rb +1 -3
- data/xchan.rb.gemspec +3 -3
- metadata +9 -10
- data/lib/xchan/mixin.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf2db46e92eb29ed0182377c209a8ff7511460d22a73225616c719cd05372884
|
4
|
+
data.tar.gz: 05b70d50483d354f79e29f9219628c44cb6f4b25e63cbd5aee7e0c0a14da8865
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8fb7b0f73b09c4593dfa7892fd3e0061dce37242c0e9bebab0edd204cda399dd16c8fdbadce2ee6a5fac1fc6c1d583ad60eadd85c7ece4a145fdb18358aff0a
|
7
|
+
data.tar.gz: ef454d2422ae0a719a19b7fad3bf670a3cc4b46154ea6e87786a4c6404bbe9f7af5437e81b3ed8651fa9e1f438ee7f43c38d10497ca9b50facd3aea3ededc7f0
|
data/.projectile
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
## About
|
2
2
|
|
3
|
-
xchan.rb is an easy to use library
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
36
|
-
ch.
|
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
|
-
#
|
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
|
50
|
-
a lock is held by another process, or
|
51
|
-
|
52
|
-
|
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
|
-
|
54
|
+
ch = xchan(:marshal)
|
55
|
+
Process.detach fork {
|
59
56
|
print "Received a random number (child process): ", ch.recv, "\n"
|
60
|
-
|
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`.
|
76
|
-
method raises `Chan::WaitLockable` when
|
77
|
-
|
78
|
-
|
79
|
-
|
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.
|
108
|
-
|
109
|
-
|
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(
|
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
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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(
|
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
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
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(
|
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(
|
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
|
-
|
183
|
+
## Documentation
|
201
184
|
|
202
|
-
|
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
|
-
|
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
|
-
|
211
|
-
`tmpdir` option:
|
190
|
+
xchan.rb can be installed via rubygems.org:
|
212
191
|
|
213
|
-
|
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/).
|
data/lib/xchan/tempfile.rb
CHANGED
@@ -2,11 +2,7 @@ require 'delegate'
|
|
2
2
|
require 'tmpdir'
|
3
3
|
|
4
4
|
##
|
5
|
-
#
|
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|
|
data/lib/xchan/unix_socket.rb
CHANGED
@@ -1,13 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
##
|
4
|
-
#
|
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
|
-
#
|
28
|
+
# The name of a serializer
|
20
29
|
#
|
21
30
|
# @param [Integer] sock_type
|
22
|
-
#
|
31
|
+
# Type of socket (eg `Socket::SOCK_STREAM`)
|
23
32
|
#
|
24
33
|
# @param [String] tmpdir
|
25
|
-
#
|
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
|
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
|
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
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
|
-
|
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
|
-
|
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
|
-
|
52
|
-
|
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
|
-
|
7
|
+
ch = xchan(:marshal)
|
8
|
+
Process.detach fork {
|
9
9
|
print "Received random number (child process): ", ch.recv, "\n"
|
10
|
-
|
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
|
##
|
@@ -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
|
-
|
10
|
-
ch.
|
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
|
-
#
|
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(
|
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(
|
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(
|
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(
|
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 "
|
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
|
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.
|
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.
|
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-
|
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
|
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
|
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.
|
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.
|
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.
|
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
|