xchan.rb 0.16.5 → 0.17.1
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/.github/workflows/tests.yml +2 -2
- data/.gitlab-ci.yml +1 -1
- data/.projectile +3 -0
- data/Gemfile +0 -2
- data/README.md +62 -107
- data/lib/xchan/bytes.rb +17 -26
- data/lib/xchan/{stat.rb → counter.rb} +12 -13
- data/lib/xchan/tempfile.rb +57 -61
- data/lib/xchan/unix_socket.rb +43 -86
- 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/test/xchan_test.rb +7 -3
- data/xchan.rb.gemspec +3 -3
- metadata +10 -11
- 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: 5509107cf1cda5e847f3c15e19c81ba9323c2dbb524a7ef808eeb820d5bcdeb9
|
4
|
+
data.tar.gz: 43deff543ee38ace3ca8a083a043eb1c6bbedec9143bd2c8e17de50f13127031
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5455dd6c714bc4060ec1f563b46d8a9eaee32e5aa58c2a2508de99f53fc29e346d07ac04e719822bc394b66ac6ba6ddf5585bf7958c606a0f1f375c113b4310
|
7
|
+
data.tar.gz: '09bd28506d18a63e8e917ad1189c708cc6643b852707508ee0edf563064bd3618d3ae251d85e06f618672c414537b4da3db0538b61e4c11c2a6c64fff3394f38'
|
data/.github/workflows/tests.yml
CHANGED
@@ -12,7 +12,7 @@ jobs:
|
|
12
12
|
fail-fast: false
|
13
13
|
matrix:
|
14
14
|
os: [ubuntu-latest, macos-latest]
|
15
|
-
ruby: [3.1, 3.2]
|
15
|
+
ruby: [3.1, 3.2, 3.3]
|
16
16
|
runs-on: ${{ matrix.os }}
|
17
17
|
steps:
|
18
18
|
- uses: actions/checkout@v2
|
@@ -23,4 +23,4 @@ jobs:
|
|
23
23
|
- run: SERIALIZER=marshal bundle exec rake
|
24
24
|
- run: SERIALIZER=json bundle exec rake
|
25
25
|
- run: SERIALIZER=yaml bundle exec rake
|
26
|
-
- run: SERIALIZER=
|
26
|
+
- run: SERIALIZER=pure bundle exec rake
|
data/.gitlab-ci.yml
CHANGED
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/bytes.rb
CHANGED
@@ -1,39 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
##
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# the
|
9
|
-
# is read from a channel, the array decreases in
|
10
|
-
# size.
|
4
|
+
# {Chan::Bytes Chan::Bytes} represents a collection
|
5
|
+
# of byte counts for each object stored on a channel.
|
6
|
+
# When an object is written to a channel, the collection
|
7
|
+
# increases in size, and when an object is read from
|
8
|
+
# a channel, the collection decreases in size.
|
11
9
|
class Chan::Bytes
|
12
10
|
require "json"
|
13
|
-
require_relative "
|
14
|
-
|
15
|
-
##
|
16
|
-
# @return [Chan::Stat]
|
17
|
-
attr_reader :stat
|
11
|
+
require_relative "counter"
|
18
12
|
|
19
13
|
##
|
20
14
|
# @param [String] tmpdir
|
21
|
-
#
|
15
|
+
# Directory where temporary files are stored
|
22
16
|
#
|
23
17
|
# @return [Chan::Bytes]
|
24
18
|
def initialize(tmpdir)
|
25
|
-
@serializer = JSON
|
26
19
|
@io = Chan.temporary_file("xchan.bytes", tmpdir:)
|
27
20
|
@io.sync = true
|
28
|
-
@stat = Chan::Stat.new(tmpdir)
|
29
21
|
write(@io, [])
|
30
22
|
end
|
31
23
|
|
32
24
|
##
|
33
|
-
#
|
25
|
+
# Adds a count to the start of the collection
|
34
26
|
#
|
35
27
|
# @param [Integer] len
|
36
|
-
#
|
28
|
+
# The bytesize of an object
|
37
29
|
#
|
38
30
|
# @return [void]
|
39
31
|
def unshift(len)
|
@@ -41,15 +33,14 @@ class Chan::Bytes
|
|
41
33
|
bytes = read(@io)
|
42
34
|
bytes.unshift(len)
|
43
35
|
write(@io, bytes)
|
44
|
-
@stat.store(bytes_written: len)
|
45
36
|
len
|
46
37
|
end
|
47
38
|
|
48
39
|
##
|
49
|
-
#
|
40
|
+
# Adds a count to the end of the collection
|
50
41
|
#
|
51
42
|
# @param [Integer] len
|
52
|
-
#
|
43
|
+
# The bytesize of an object
|
53
44
|
#
|
54
45
|
# @return [void]
|
55
46
|
def push(len)
|
@@ -57,25 +48,25 @@ class Chan::Bytes
|
|
57
48
|
bytes = read(@io)
|
58
49
|
bytes.push(len)
|
59
50
|
write(@io, bytes)
|
60
|
-
@stat.store(bytes_written: len)
|
61
51
|
len
|
62
52
|
end
|
63
53
|
|
64
54
|
##
|
55
|
+
# Removes a count from the start of the collection
|
56
|
+
#
|
65
57
|
# @return [Integer]
|
66
|
-
# Returns
|
58
|
+
# Returns the removed byte count
|
67
59
|
def shift
|
68
60
|
bytes = read(@io)
|
69
61
|
return 0 if bytes.size.zero?
|
70
62
|
len = bytes.shift
|
71
63
|
write(@io, bytes)
|
72
|
-
@stat.store(bytes_read: len)
|
73
64
|
len
|
74
65
|
end
|
75
66
|
|
76
67
|
##
|
77
68
|
# @return [Integer]
|
78
|
-
# Returns the
|
69
|
+
# Returns the number of objects in the collection
|
79
70
|
def size
|
80
71
|
read(@io).size
|
81
72
|
end
|
@@ -100,10 +91,10 @@ class Chan::Bytes
|
|
100
91
|
end
|
101
92
|
|
102
93
|
def serialize(bytes)
|
103
|
-
|
94
|
+
JSON.dump(bytes)
|
104
95
|
end
|
105
96
|
|
106
97
|
def deserialize(bytes)
|
107
|
-
|
98
|
+
JSON.load(bytes)
|
108
99
|
end
|
109
100
|
end
|
@@ -1,33 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
##
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
class Chan::
|
4
|
+
# {Chan::Counter Chan::Counter} provides a counter
|
5
|
+
# for the number of written and received bytes on a
|
6
|
+
# given channel.
|
7
|
+
class Chan::Counter
|
8
8
|
require "json"
|
9
9
|
|
10
10
|
##
|
11
11
|
# @param [String] tmpdir
|
12
|
-
#
|
12
|
+
# Directory where temporary files are stored
|
13
13
|
#
|
14
|
-
# @return [Chan::
|
14
|
+
# @return [Chan::Counter]
|
15
15
|
def initialize(tmpdir)
|
16
|
-
@
|
17
|
-
@io = Chan.temporary_file("xchan.stat", tmpdir:)
|
16
|
+
@io = Chan.temporary_file("xchan.counter", tmpdir:)
|
18
17
|
write(@io, {"bytes_read" => 0, "bytes_written" => 0})
|
19
18
|
end
|
20
19
|
|
21
20
|
##
|
22
21
|
# @return [Integer]
|
23
|
-
# Returns the number of bytes written to a channel
|
22
|
+
# Returns the number of bytes written to a channel
|
24
23
|
def bytes_written
|
25
24
|
read(@io).fetch("bytes_written")
|
26
25
|
end
|
27
26
|
|
28
27
|
##
|
29
28
|
# @return [Integer]
|
30
|
-
# Returns the number of bytes read from a channel
|
29
|
+
# Returns the number of bytes read from a channel
|
31
30
|
def bytes_read
|
32
31
|
read(@io).fetch("bytes_read")
|
33
32
|
end
|
@@ -36,7 +35,7 @@ class Chan::Stat
|
|
36
35
|
# @param [Hash] new_stat
|
37
36
|
# @return [void]
|
38
37
|
# @private
|
39
|
-
def
|
38
|
+
def increment!(new_stat)
|
40
39
|
stat = read(@io)
|
41
40
|
new_stat.each { stat[_1.to_s] += _2 }
|
42
41
|
write(@io, stat)
|
@@ -54,10 +53,10 @@ class Chan::Stat
|
|
54
53
|
end
|
55
54
|
|
56
55
|
def serialize(bytes)
|
57
|
-
|
56
|
+
JSON.dump(bytes)
|
58
57
|
end
|
59
58
|
|
60
59
|
def deserialize(bytes)
|
61
|
-
|
60
|
+
JSON.load(bytes)
|
62
61
|
end
|
63
62
|
end
|
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,39 +25,40 @@ 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)
|
42
|
+
@counter = Chan::Counter.new(tmpdir)
|
33
43
|
@lock = LockFile.new Chan.temporary_file("xchan.lock", tmpdir:)
|
34
44
|
end
|
35
45
|
|
36
46
|
##
|
37
47
|
# @return [<#dump, #load>]
|
38
|
-
# Returns the serializer used by the channel
|
48
|
+
# Returns the serializer used by the channel
|
39
49
|
def serializer
|
40
50
|
@serializer
|
41
51
|
end
|
42
52
|
|
43
53
|
##
|
44
54
|
# @return [Boolean]
|
45
|
-
# Returns true when the channel is closed
|
55
|
+
# Returns true when the channel is closed
|
46
56
|
def closed?
|
47
57
|
@r.closed? and @w.closed?
|
48
58
|
end
|
49
59
|
|
50
60
|
##
|
51
|
-
# Closes the channel
|
61
|
+
# Closes the channel
|
52
62
|
#
|
53
63
|
# @raise [IOError]
|
54
64
|
# When the channel is closed.
|
@@ -70,13 +80,13 @@ class Chan::UNIXSocket
|
|
70
80
|
# Performs a blocking write
|
71
81
|
#
|
72
82
|
# @param [Object] object
|
73
|
-
# An object
|
83
|
+
# An object
|
74
84
|
#
|
75
85
|
# @raise [IOError]
|
76
|
-
# When the channel is closed
|
86
|
+
# When the channel is closed
|
77
87
|
#
|
78
88
|
# @return [Object]
|
79
|
-
# Returns the number of bytes written to the channel
|
89
|
+
# Returns the number of bytes written to the channel
|
80
90
|
def send(object)
|
81
91
|
send_nonblock(object)
|
82
92
|
rescue Chan::WaitWritable, Chan::WaitLockable
|
@@ -88,24 +98,25 @@ class Chan::UNIXSocket
|
|
88
98
|
# Performs a non-blocking write
|
89
99
|
#
|
90
100
|
# @param [Object] object
|
91
|
-
# An object
|
101
|
+
# An object
|
92
102
|
#
|
93
103
|
# @raise [IOError]
|
94
|
-
# When the channel is closed
|
104
|
+
# When the channel is closed
|
95
105
|
#
|
96
106
|
# @raise [Chan::WaitWritable]
|
97
|
-
# When a write to
|
107
|
+
# When a write to {#w} blocks
|
98
108
|
#
|
99
109
|
# @raise [Chan::WaitLockable]
|
100
|
-
# When a write blocks because of a lock held by another process
|
110
|
+
# When a write blocks because of a lock held by another process
|
101
111
|
#
|
102
112
|
# @return [Integer, nil]
|
103
|
-
# Returns the number of bytes written to the channel
|
113
|
+
# Returns the number of bytes written to the channel
|
104
114
|
def send_nonblock(object)
|
105
115
|
@lock.lock_nonblock
|
106
116
|
raise IOError, "channel closed" if closed?
|
107
117
|
len = @w.write_nonblock(serialize(object))
|
108
118
|
@bytes.push(len)
|
119
|
+
@counter.increment!(bytes_written: len)
|
109
120
|
len.tap { @lock.release }
|
110
121
|
rescue IOError, IO::WaitWritable, Errno::ENOBUFS => ex
|
111
122
|
@lock.release
|
@@ -125,10 +136,10 @@ class Chan::UNIXSocket
|
|
125
136
|
# Performs a blocking read
|
126
137
|
#
|
127
138
|
# @raise [IOError]
|
128
|
-
# When the channel is closed
|
139
|
+
# When the channel is closed
|
129
140
|
#
|
130
141
|
# @return [Object]
|
131
|
-
# Returns an object from the channel
|
142
|
+
# Returns an object from the channel
|
132
143
|
def recv
|
133
144
|
recv_nonblock
|
134
145
|
rescue Chan::WaitReadable
|
@@ -143,21 +154,22 @@ class Chan::UNIXSocket
|
|
143
154
|
# Performs a non-blocking read
|
144
155
|
#
|
145
156
|
# @raise [IOError]
|
146
|
-
# When the channel is closed
|
157
|
+
# When the channel is closed
|
147
158
|
#
|
148
159
|
# @raise [Chan::WaitReadable]
|
149
|
-
# When a read from
|
160
|
+
# When a read from {#r} blocks
|
150
161
|
#
|
151
162
|
# @raise [Chan::WaitLockable]
|
152
|
-
# When a read blocks because of a lock held by another process
|
163
|
+
# When a read blocks because of a lock held by another process
|
153
164
|
#
|
154
165
|
# @return [Object]
|
155
|
-
# Returns an object from the channel
|
166
|
+
# Returns an object from the channel
|
156
167
|
def recv_nonblock
|
157
168
|
@lock.lock_nonblock
|
158
169
|
raise IOError, "closed channel" if closed?
|
159
170
|
len = @bytes.shift
|
160
171
|
obj = deserialize(@r.read_nonblock(len.zero? ? 1 : len))
|
172
|
+
@counter.increment!(bytes_read: len)
|
161
173
|
obj.tap { @lock.release }
|
162
174
|
rescue IOError => ex
|
163
175
|
@lock.release
|
@@ -181,7 +193,7 @@ class Chan::UNIXSocket
|
|
181
193
|
# ch.to_a.last # => 4
|
182
194
|
#
|
183
195
|
# @return [Array<Object>]
|
184
|
-
# Returns the
|
196
|
+
# Returns the contents of the channel
|
185
197
|
def to_a
|
186
198
|
lock do
|
187
199
|
[].tap { _1.push(recv) until empty? }
|
@@ -190,28 +202,28 @@ class Chan::UNIXSocket
|
|
190
202
|
|
191
203
|
##
|
192
204
|
# @return [Boolean]
|
193
|
-
# Returns true when the channel is empty
|
205
|
+
# Returns true when the channel is empty
|
194
206
|
def empty?
|
195
207
|
return true if closed?
|
196
208
|
lock { size.zero? }
|
197
209
|
end
|
198
210
|
|
199
211
|
##
|
200
|
-
# @group
|
212
|
+
# @group Stat methods
|
201
213
|
|
202
214
|
##
|
203
215
|
# @return [Integer]
|
204
|
-
# Returns the total number of bytes written to the channel
|
216
|
+
# Returns the total number of bytes written to the channel
|
205
217
|
def bytes_sent
|
206
|
-
lock { @
|
218
|
+
lock { @counter.bytes_written }
|
207
219
|
end
|
208
220
|
alias_method :bytes_written, :bytes_sent
|
209
221
|
|
210
222
|
##
|
211
223
|
# @return [Integer]
|
212
|
-
# Returns the total number of bytes read from the channel
|
224
|
+
# Returns the total number of bytes read from the channel
|
213
225
|
def bytes_received
|
214
|
-
lock { @
|
226
|
+
lock { @counter.bytes_read }
|
215
227
|
end
|
216
228
|
alias_method :bytes_read, :bytes_received
|
217
229
|
|
@@ -255,61 +267,6 @@ class Chan::UNIXSocket
|
|
255
267
|
##
|
256
268
|
# @endgroup
|
257
269
|
|
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
270
|
private
|
314
271
|
|
315
272
|
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/test/xchan_test.rb
CHANGED
@@ -4,7 +4,7 @@ require_relative "setup"
|
|
4
4
|
|
5
5
|
class Chan::Test < Test::Unit::TestCase
|
6
6
|
def setup
|
7
|
-
@ch = xchan
|
7
|
+
@ch = xchan(serializer)
|
8
8
|
end
|
9
9
|
|
10
10
|
def teardown
|
@@ -17,9 +17,13 @@ class Chan::Test < Test::Unit::TestCase
|
|
17
17
|
@ch
|
18
18
|
end
|
19
19
|
|
20
|
+
def serializer
|
21
|
+
ENV.fetch("SERIALIZER", "pure").to_sym
|
22
|
+
end
|
23
|
+
|
20
24
|
def object
|
21
|
-
case
|
22
|
-
when
|
25
|
+
case serializer
|
26
|
+
when :pure then "xchan"
|
23
27
|
else %w[xchan]
|
24
28
|
end
|
25
29
|
end
|
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.1
|
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,8 +127,7 @@ files:
|
|
127
127
|
- Rakefile.rb
|
128
128
|
- lib/xchan.rb
|
129
129
|
- lib/xchan/bytes.rb
|
130
|
-
- lib/xchan/
|
131
|
-
- lib/xchan/stat.rb
|
130
|
+
- lib/xchan/counter.rb
|
132
131
|
- lib/xchan/tempfile.rb
|
133
132
|
- lib/xchan/unix_socket.rb
|
134
133
|
- lib/xchan/version.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
|