ractor-tmvar 0.2.0 → 0.4.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/.github/workflows/build-gem.yml +54 -0
- data/.github/workflows/main.yml +1 -1
- data/.github/workflows/release.yml +17 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +7 -1
- data/Gemfile +3 -1
- data/Gemfile.lock +64 -25
- data/README.md +76 -15
- data/lib/ractor/tmvar/version.rb +1 -1
- data/lib/ractor/tmvar.rb +160 -57
- data/ractor-tmvar.gemspec +3 -2
- metadata +8 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6f3245eebf48796294d345ac5d97677c3f536347f87a204555223781cec1a87d
|
|
4
|
+
data.tar.gz: 3bf7ca844e8556db49d981b2d50c6e00867927f70348d59242220b66d0e20a59
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b30e17fdb2e91c2aa60e83edf47098926a017a63a192a409545116ef7ec605ab2203361f00862de5e8f2e443058dc3c7111b69a77bf4373d9f718e74faff5c1c
|
|
7
|
+
data.tar.gz: 567905c4e3ae0b351a0ecaaa69b97a5de09d77e7047da0e07cee7e45818251097e2e12bf32e3ebf173bfff48ccf3dada4ba40c29ad153a87da2176028f098058
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Build and attest gem
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
id-token: write
|
|
15
|
+
attestations: write
|
|
16
|
+
steps:
|
|
17
|
+
- name: Check out tagged source
|
|
18
|
+
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
19
|
+
with:
|
|
20
|
+
ref: ${{ github.sha }}
|
|
21
|
+
persist-credentials: false
|
|
22
|
+
|
|
23
|
+
- name: Set up Ruby
|
|
24
|
+
uses: ruby/setup-ruby@e81a8fa391b11a595c1b7ed84177cc0fe02faf83 # v1
|
|
25
|
+
with:
|
|
26
|
+
ruby-version: '4.0'
|
|
27
|
+
bundler-cache: true
|
|
28
|
+
|
|
29
|
+
- name: Check release tag matches gem version
|
|
30
|
+
run: |
|
|
31
|
+
ruby -r ./lib/ractor/tmvar/version -e '
|
|
32
|
+
expected = "refs/tags/v#{Ractor::TMVar::VERSION}"
|
|
33
|
+
abort "Expected #{expected}, got #{ENV.fetch("GITHUB_REF")}" unless ENV.fetch("GITHUB_REF") == expected
|
|
34
|
+
'
|
|
35
|
+
|
|
36
|
+
- name: Run tests and lint
|
|
37
|
+
run: bundle exec rake
|
|
38
|
+
|
|
39
|
+
- name: Build gem
|
|
40
|
+
run: |
|
|
41
|
+
mkdir -p pkg
|
|
42
|
+
gem build ractor-tmvar.gemspec --output "pkg/ractor-tmvar-${GITHUB_REF_NAME#v}.gem"
|
|
43
|
+
|
|
44
|
+
- name: Attest gem provenance
|
|
45
|
+
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4
|
|
46
|
+
with:
|
|
47
|
+
subject-path: pkg/*.gem
|
|
48
|
+
|
|
49
|
+
- name: Save gem
|
|
50
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
51
|
+
with:
|
|
52
|
+
name: ractor-tmvar-${{ github.ref_name }}
|
|
53
|
+
path: pkg/*.gem
|
|
54
|
+
if-no-files-found: error
|
data/.github/workflows/main.yml
CHANGED
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
plugins:
|
|
2
|
+
- rubocop-rake
|
|
3
|
+
|
|
1
4
|
AllCops:
|
|
2
|
-
TargetRubyVersion: 2.7
|
|
3
5
|
NewCops: enable
|
|
4
6
|
|
|
5
7
|
Style/StringLiterals:
|
|
@@ -17,3 +19,7 @@ Metrics/ClassLength:
|
|
|
17
19
|
Enabled: true
|
|
18
20
|
Exclude:
|
|
19
21
|
- "test/**/*"
|
|
22
|
+
|
|
23
|
+
Naming/PredicateMethod:
|
|
24
|
+
AllowedMethods:
|
|
25
|
+
- try_put
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,46 +1,85 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
ractor-tmvar (0.
|
|
5
|
-
ractor-
|
|
4
|
+
ractor-tmvar (0.4.0)
|
|
5
|
+
ractor-sharing (~> 0.3.0)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
ast (2.4.
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
ast (2.4.3)
|
|
11
|
+
json (3.0.2)
|
|
12
|
+
language_server-protocol (3.17.0.6)
|
|
13
|
+
lint_roller (1.1.0)
|
|
14
|
+
parallel (1.28.0)
|
|
15
|
+
parser (3.3.12.0)
|
|
13
16
|
ast (~> 2.4.1)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
racc
|
|
18
|
+
power_assert (3.1.0)
|
|
19
|
+
prism (1.9.0)
|
|
20
|
+
racc (1.8.1)
|
|
21
|
+
ractor-sharing (0.3.0)
|
|
22
|
+
rainbow (3.1.1)
|
|
23
|
+
rake (13.4.2)
|
|
24
|
+
regexp_parser (2.13.1)
|
|
25
|
+
rubocop (1.91.0)
|
|
26
|
+
json (>= 2.3)
|
|
27
|
+
language_server-protocol (~> 3.17.0.2)
|
|
28
|
+
lint_roller (~> 1.1.0)
|
|
29
|
+
parallel (>= 1.10)
|
|
30
|
+
parser (>= 3.3.0.2)
|
|
23
31
|
rainbow (>= 2.2.2, < 4.0)
|
|
24
|
-
regexp_parser (>=
|
|
25
|
-
|
|
26
|
-
rubocop-ast (>= 0.6.0)
|
|
32
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
33
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
27
34
|
ruby-progressbar (~> 1.7)
|
|
28
|
-
unicode-display_width (>=
|
|
29
|
-
rubocop-ast (1.
|
|
30
|
-
parser (>=
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
36
|
+
rubocop-ast (1.50.0)
|
|
37
|
+
parser (>= 3.3.7.2)
|
|
38
|
+
prism (~> 1.7)
|
|
39
|
+
rubocop-rake (0.7.1)
|
|
40
|
+
lint_roller (~> 1.1)
|
|
41
|
+
rubocop (>= 1.72.1)
|
|
42
|
+
ruby-progressbar (1.13.0)
|
|
43
|
+
test-unit (3.7.8)
|
|
33
44
|
power_assert
|
|
34
|
-
unicode-display_width (
|
|
45
|
+
unicode-display_width (3.3.0)
|
|
46
|
+
unicode-emoji (~> 4.3)
|
|
47
|
+
unicode-emoji (4.3.0)
|
|
35
48
|
|
|
36
49
|
PLATFORMS
|
|
37
50
|
ruby
|
|
51
|
+
x86_64-linux
|
|
38
52
|
|
|
39
53
|
DEPENDENCIES
|
|
40
54
|
ractor-tmvar!
|
|
41
55
|
rake (~> 13.0)
|
|
42
|
-
rubocop (~>
|
|
56
|
+
rubocop (~> 1.91)
|
|
57
|
+
rubocop-rake (~> 0.7)
|
|
43
58
|
test-unit (~> 3.0)
|
|
44
59
|
|
|
60
|
+
CHECKSUMS
|
|
61
|
+
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
62
|
+
bundler (4.0.16) sha256=d6ca5dd440c24f9abce9844cf44cc8e18c6a553de65a47efb4544137af92c47d
|
|
63
|
+
json (3.0.2) sha256=8e6d7e7b11384c21230430cef90b71f14849a34a1f4452796670f7c981bd19df
|
|
64
|
+
language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
|
|
65
|
+
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
|
66
|
+
parallel (1.28.0) sha256=33e6de1484baf2524792d178b0913fc8eb94c628d6cfe45599ad4458c638c970
|
|
67
|
+
parser (3.3.12.0) sha256=21a6d7f755d5a24dfbdc6e6b772e4e879a52e7631a88bc5a3a134606052c9828
|
|
68
|
+
power_assert (3.1.0) sha256=fefe98ed5d3140628138f3f9f9194d6ece01f1320e69700d3c928adca7518251
|
|
69
|
+
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
|
|
70
|
+
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
|
|
71
|
+
ractor-sharing (0.3.0) sha256=9adb2f899e4e38f477a2d3c02368e8ff40be96b119172312253338ee1e22f63f
|
|
72
|
+
ractor-tmvar (0.4.0)
|
|
73
|
+
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
|
74
|
+
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
|
|
75
|
+
regexp_parser (2.13.1) sha256=5aedb6b7c35688f51e86eef17a15374dfd287c83f4ca644b2c5f3ec50a33b44b
|
|
76
|
+
rubocop (1.91.0) sha256=9c82b7bf391c5d7e3798c5b9996e22a1fe3bd7468e351dfdeb96140c058296d0
|
|
77
|
+
rubocop-ast (1.50.0) sha256=b9ca88300da0803ee222ad20cdb30494c0a784eed06fdc35d254b06d662788db
|
|
78
|
+
rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d
|
|
79
|
+
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
|
80
|
+
test-unit (3.7.8) sha256=689d1ca53f4d46f678b4e5d68d8e4294f87fc5e8b9238cc4de7c5727e8d65943
|
|
81
|
+
unicode-display_width (3.3.0) sha256=4b7aa66a4b11db50f6f7e98411215cd0dd10eaecb665d36eec5bcacb3fa0b613
|
|
82
|
+
unicode-emoji (4.3.0) sha256=11c02fa73290378c066bb0562cd4c87d8e1b706fbbe0059ca12746a4244de8ce
|
|
83
|
+
|
|
45
84
|
BUNDLED WITH
|
|
46
|
-
|
|
85
|
+
4.0.16
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Ractor::TMVar
|
|
2
2
|
|
|
3
|
-
TMVar for Ractor inspired by [Haskell's TMVar](https://hackage.haskell.org/package/stm-2.5.0.0/docs/Control-Concurrent-STM-TMVar.html) based on [Ractor::TVar](https://github.com/ko1/ractor-tvar).
|
|
3
|
+
TMVar for Ractor inspired by [Haskell's TMVar](https://hackage.haskell.org/package/stm-2.5.0.0/docs/Control-Concurrent-STM-TMVar.html) based on [Ractor::TVar](https://github.com/ko1/ractor-sharing/blob/main/docs/tvar.md).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -20,11 +20,8 @@ Or install it yourself as:
|
|
|
20
20
|
|
|
21
21
|
## Usage
|
|
22
22
|
|
|
23
|
-
Please see [Ractor::TVar](https://github.com/ko1/ractor-tvar) before using this.
|
|
24
|
-
Now we have only `value` and `value=` operations.
|
|
25
|
-
|
|
26
23
|
```ruby
|
|
27
|
-
require 'ractor
|
|
24
|
+
require 'ractor/tmvar'
|
|
28
25
|
|
|
29
26
|
tv1 = Ractor::TMVar.new(0)
|
|
30
27
|
tv2 = Ractor::TMVar.new(0)
|
|
@@ -33,27 +30,91 @@ rs = 100.times.map do
|
|
|
33
30
|
value1 = nil
|
|
34
31
|
value2 = nil
|
|
35
32
|
Ractor.atomically do
|
|
36
|
-
# take value
|
|
37
|
-
value1 = v1.
|
|
38
|
-
value2 = v2.
|
|
33
|
+
# take value like takeTMVar in Haskell
|
|
34
|
+
value1 = v1.take
|
|
35
|
+
value2 = v2.take
|
|
39
36
|
end
|
|
40
37
|
Ractor.atomically do
|
|
41
|
-
# set value
|
|
42
|
-
v2.
|
|
43
|
-
v1.
|
|
38
|
+
# set value like putTMVar in Haskell
|
|
39
|
+
v2.put(value2 + 2)
|
|
40
|
+
v1.put(value1 + 1)
|
|
44
41
|
end
|
|
45
42
|
end
|
|
46
43
|
end
|
|
47
|
-
rs.each(&:
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
rs.each(&:value)
|
|
45
|
+
|
|
46
|
+
tv1.read #=> 100
|
|
47
|
+
tv2.read #=> 200
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`Ractor::TMVar.new` creates an empty variable, while `Ractor::TMVar.new(value)` or
|
|
51
|
+
`Ractor::TMVar.new_filled(value)` creates a variable already containing `value`.
|
|
52
|
+
`nil` can be stored as a normal value:
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
tv = Ractor::TMVar.new_filled(nil)
|
|
56
|
+
tv.empty? #=> false
|
|
57
|
+
tv.read #=> nil
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Transactional operations
|
|
61
|
+
|
|
62
|
+
`take`, `put`, `swap`, `try_take`, and `try_put` must be called inside `Ractor.atomically`. `read` and
|
|
63
|
+
`empty?` can be called outside of a transaction.
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
tv = Ractor::TMVar.new
|
|
67
|
+
Ractor.atomically { tv.put(42) }
|
|
68
|
+
Ractor.atomically { tv.take } #=> 42
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Non-blocking try operations
|
|
72
|
+
|
|
73
|
+
`try_take` and `try_read` never retry. Without a block, they return the stored
|
|
74
|
+
value, or `nil` when empty. With a block, when the variable is
|
|
75
|
+
empty they return `nil` without calling the block. When it is full they yield the
|
|
76
|
+
stored value and return the block's result, so a stored `nil` can be told apart
|
|
77
|
+
from an empty variable. `try_take` empties the variable before yielding;
|
|
78
|
+
`try_read` leaves it full.
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
tv = Ractor::TMVar.new(5)
|
|
82
|
+
|
|
83
|
+
tv.try_read { |v| v * 2 } #=> 10
|
|
84
|
+
tv.read #=> 5
|
|
85
|
+
|
|
86
|
+
Ractor.atomically { tv.try_take { |v| v * 2 } } #=> 10
|
|
87
|
+
tv.empty? #=> true
|
|
88
|
+
|
|
89
|
+
empty = Ractor::TMVar.new
|
|
90
|
+
Ractor.atomically { empty.try_take { |v| [v] } } #=> nil (block not called)
|
|
91
|
+
|
|
92
|
+
filled_with_nil = Ractor::TMVar.new_filled(nil)
|
|
93
|
+
filled_with_nil.try_read { |v| [v] } #=> [nil]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`try_put` returns `true` when it stored the value and `false` when the variable
|
|
97
|
+
was already full.
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
tv = Ractor::TMVar.new
|
|
101
|
+
Ractor.atomically { tv.try_put(1) } #=> true
|
|
102
|
+
Ractor.atomically { tv.try_put(2) } #=> false
|
|
50
103
|
```
|
|
51
104
|
|
|
52
105
|
## Development
|
|
53
106
|
|
|
54
107
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
55
108
|
|
|
56
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
109
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
110
|
+
|
|
111
|
+
## Verifying gem provenance
|
|
112
|
+
|
|
113
|
+
Verify a downloaded gem's build provenance attestation with the GitHub CLI:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
gh attestation verify ractor-tmvar-0.3.0.gem -R yoshitsugu/ractor-tmvar
|
|
117
|
+
```
|
|
57
118
|
|
|
58
119
|
## Contributing
|
|
59
120
|
|
data/lib/ractor/tmvar/version.rb
CHANGED
data/lib/ractor/tmvar.rb
CHANGED
|
@@ -1,126 +1,229 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "ractor/tvar"
|
|
4
|
+
require_relative "tmvar/version"
|
|
4
5
|
|
|
5
6
|
class Ractor
|
|
6
|
-
#
|
|
7
|
+
# Transactional MVar-like variable for Ractor, inspired by Haskell's
|
|
8
|
+
# +Control.Concurrent.STM.TMVar+ and built on Ractor::TVar.
|
|
9
|
+
#
|
|
10
|
+
# A TMVar is either empty or full. When full it holds a single value.
|
|
11
|
+
# Reading and writing operations are transactional through {Ractor.atomically}.
|
|
12
|
+
#
|
|
13
|
+
# The implementation stores +nil+ when empty and a single-element array +[value]+
|
|
14
|
+
# when full, so any Ruby object (including +nil+) can be stored without colliding
|
|
15
|
+
# with the empty state.
|
|
7
16
|
class TMVar
|
|
8
17
|
#
|
|
9
|
-
#
|
|
18
|
+
# Create a new TMVar.
|
|
10
19
|
#
|
|
11
|
-
|
|
20
|
+
# @overload initialize
|
|
21
|
+
# Creates an empty TMVar (Haskell's +newEmptyTMVar+).
|
|
22
|
+
# @overload initialize(value)
|
|
23
|
+
# Creates a TMVar filled with +value+ (Haskell's +newTMVar+).
|
|
24
|
+
# @param [Object] value value to store. With ractor-sharing 0.3, the value is
|
|
25
|
+
# automatically made Ractor-shareable and the passed object is frozen.
|
|
26
|
+
# This behavior is provided by the upstream Ractor::TVar, not a TMVar guarantee.
|
|
27
|
+
#
|
|
28
|
+
# @raise [ArgumentError] if more than one argument is given.
|
|
29
|
+
#
|
|
30
|
+
def initialize(*args)
|
|
31
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..1)" if args.size > 1
|
|
32
|
+
|
|
33
|
+
@tvar = Ractor::TVar.new(args.empty? ? nil : [args[0]])
|
|
34
|
+
end
|
|
12
35
|
|
|
13
36
|
#
|
|
14
|
-
#
|
|
37
|
+
# Create a new TMVar explicitly filled with +value+.
|
|
15
38
|
#
|
|
16
|
-
#
|
|
39
|
+
# This is equivalent to +TMVar.new(value)+ but makes the intent clear when the
|
|
40
|
+
# stored value might be +nil+.
|
|
41
|
+
#
|
|
42
|
+
# @param [Object] value value to store. With ractor-sharing 0.3, the value is
|
|
43
|
+
# automatically made Ractor-shareable and the passed object is frozen.
|
|
44
|
+
# This behavior is provided by the upstream Ractor::TVar, not a TMVar guarantee.
|
|
17
45
|
#
|
|
18
46
|
# @return [TMVar]
|
|
19
47
|
#
|
|
20
|
-
def
|
|
21
|
-
|
|
48
|
+
def self.new_filled(value)
|
|
49
|
+
new(value)
|
|
22
50
|
end
|
|
23
51
|
|
|
24
52
|
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
53
|
+
# Take the value, leaving the TMVar empty.
|
|
54
|
+
#
|
|
55
|
+
# If the TMVar is empty, the transaction is retried.
|
|
56
|
+
#
|
|
57
|
+
# @note Must be called inside {Ractor.atomically}.
|
|
29
58
|
#
|
|
30
|
-
# @return [Object]
|
|
59
|
+
# @return [Object] the stored value.
|
|
31
60
|
#
|
|
32
61
|
def take
|
|
33
|
-
|
|
34
|
-
raise Ractor::RetryTransaction if
|
|
62
|
+
cell = @tvar.value
|
|
63
|
+
raise Ractor::RetryTransaction if cell.nil?
|
|
35
64
|
|
|
36
|
-
@tvar.value =
|
|
37
|
-
|
|
65
|
+
@tvar.value = nil
|
|
66
|
+
cell[0]
|
|
38
67
|
end
|
|
39
68
|
|
|
40
69
|
#
|
|
41
|
-
#
|
|
42
|
-
# If the value is "empty", it returns nil.
|
|
70
|
+
# Try to take the value without retrying.
|
|
43
71
|
#
|
|
44
|
-
#
|
|
72
|
+
# If the TMVar is empty, returns +nil+ without calling the block.
|
|
73
|
+
# If the TMVar is full, empties it, yields the value and returns the block's result.
|
|
74
|
+
# Without a block, returns the stored value unchanged.
|
|
75
|
+
# Because the block is called only when full, a stored +nil+ can be told apart
|
|
76
|
+
# from an empty TMVar.
|
|
45
77
|
#
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
78
|
+
# @note Must be called inside {Ractor.atomically}.
|
|
79
|
+
#
|
|
80
|
+
# @overload try_take
|
|
81
|
+
# @return [Object, nil] the stored value, or +nil+ if empty.
|
|
82
|
+
# @overload try_take { |value| ... }
|
|
83
|
+
# @yieldparam value [Object] the stored value, including +nil+.
|
|
84
|
+
# @yieldreturn [Object] the value to return from +try_take+.
|
|
85
|
+
# @return [Object, nil] the block's result, or +nil+ if empty.
|
|
86
|
+
#
|
|
87
|
+
# @example Take a value without a block
|
|
88
|
+
# tv = Ractor::TMVar.new(5)
|
|
89
|
+
# Ractor.atomically { tv.try_take } #=> 5
|
|
90
|
+
# tv.empty? #=> true
|
|
91
|
+
#
|
|
92
|
+
# @example Distinguish a stored nil from an empty TMVar
|
|
93
|
+
# tv = Ractor::TMVar.new(nil)
|
|
94
|
+
# Ractor.atomically { tv.try_take { |value| [value] } } #=> [nil]
|
|
95
|
+
# Ractor.atomically { tv.try_take { |value| [value] } } #=> nil
|
|
96
|
+
#
|
|
97
|
+
def try_take(&block)
|
|
98
|
+
cell = @tvar.value
|
|
99
|
+
return nil if cell.nil?
|
|
49
100
|
|
|
50
|
-
@tvar.value =
|
|
51
|
-
|
|
101
|
+
@tvar.value = nil
|
|
102
|
+
(block || method(:identity)).call(cell[0])
|
|
52
103
|
end
|
|
53
104
|
|
|
54
105
|
#
|
|
55
|
-
#
|
|
56
|
-
# The difference between +take+ and +read+ is +take+ leaves the value blank,
|
|
57
|
-
# but +read+ not change the value to blank.
|
|
106
|
+
# Read the value, leaving the TMVar full.
|
|
58
107
|
#
|
|
59
|
-
#
|
|
108
|
+
# If the TMVar is empty, the transaction is retried.
|
|
109
|
+
# It can also be called outside {Ractor.atomically}.
|
|
110
|
+
#
|
|
111
|
+
# @return [Object] the stored value.
|
|
60
112
|
#
|
|
61
113
|
def read
|
|
62
|
-
|
|
63
|
-
raise Ractor::RetryTransaction if
|
|
114
|
+
cell = @tvar.value
|
|
115
|
+
raise Ractor::RetryTransaction if cell.nil?
|
|
64
116
|
|
|
65
|
-
|
|
117
|
+
cell[0]
|
|
66
118
|
end
|
|
67
119
|
|
|
68
120
|
#
|
|
69
|
-
# read the value
|
|
121
|
+
# Try to read the value without retrying.
|
|
122
|
+
#
|
|
123
|
+
# If the TMVar is empty, returns +nil+ without calling the block.
|
|
124
|
+
# If the TMVar is full, yields the value and returns the block's result.
|
|
125
|
+
# Without a block, returns the stored value unchanged.
|
|
126
|
+
# The TMVar remains full.
|
|
127
|
+
#
|
|
128
|
+
# @note Can be called outside {Ractor.atomically}.
|
|
70
129
|
#
|
|
71
|
-
# @
|
|
130
|
+
# @overload try_read
|
|
131
|
+
# @return [Object, nil] the stored value, or +nil+ if empty.
|
|
132
|
+
# @overload try_read { |value| ... }
|
|
133
|
+
# @yieldparam value [Object] the stored value, including +nil+.
|
|
134
|
+
# @yieldreturn [Object] the value to return from +try_read+.
|
|
135
|
+
# @return [Object, nil] the block's result, or +nil+ if empty.
|
|
72
136
|
#
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
137
|
+
# @example Read or transform a value without removing it
|
|
138
|
+
# tv = Ractor::TMVar.new(5)
|
|
139
|
+
# tv.try_read #=> 5
|
|
140
|
+
# tv.try_read { |value| value * 2 } #=> 10
|
|
141
|
+
# tv.read #=> 5
|
|
142
|
+
#
|
|
143
|
+
# @example Distinguish a stored nil from an empty TMVar
|
|
144
|
+
# Ractor::TMVar.new(nil).try_read { |value| [value] } #=> [nil]
|
|
145
|
+
# Ractor::TMVar.new.try_read { |value| [value] } #=> nil
|
|
146
|
+
#
|
|
147
|
+
def try_read(&block)
|
|
148
|
+
cell = @tvar.value
|
|
149
|
+
return nil if cell.nil?
|
|
150
|
+
|
|
151
|
+
(block || method(:identity)).call(cell[0])
|
|
76
152
|
end
|
|
77
153
|
|
|
78
154
|
#
|
|
79
|
-
#
|
|
80
|
-
#
|
|
155
|
+
# Put a value into the TMVar.
|
|
156
|
+
#
|
|
157
|
+
# If the TMVar is already full, the transaction is retried.
|
|
81
158
|
#
|
|
82
|
-
# @
|
|
159
|
+
# @note Must be called inside {Ractor.atomically}.
|
|
160
|
+
#
|
|
161
|
+
# @param [Object] new_value value to store. With ractor-sharing 0.3, the value is
|
|
162
|
+
# automatically made Ractor-shareable and the passed object is frozen.
|
|
163
|
+
# This behavior is provided by the upstream Ractor::TVar, not a TMVar guarantee.
|
|
164
|
+
#
|
|
165
|
+
# @return [Object] +new_value+.
|
|
83
166
|
#
|
|
84
167
|
def put(new_value)
|
|
85
|
-
raise Ractor::RetryTransaction
|
|
168
|
+
raise Ractor::RetryTransaction unless @tvar.value.nil?
|
|
86
169
|
|
|
87
|
-
@tvar.value = new_value
|
|
170
|
+
@tvar.value = [new_value]
|
|
171
|
+
new_value
|
|
88
172
|
end
|
|
89
173
|
|
|
90
174
|
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
175
|
+
# Try to put a value into the TMVar without retrying.
|
|
176
|
+
#
|
|
177
|
+
# @note Must be called inside {Ractor.atomically}.
|
|
94
178
|
#
|
|
95
|
-
# @param [Object] new_value
|
|
179
|
+
# @param [Object] new_value value to store. With ractor-sharing 0.3, the value is
|
|
180
|
+
# automatically made Ractor-shareable and the passed object is frozen.
|
|
181
|
+
# This behavior is provided by the upstream Ractor::TVar, not a TMVar guarantee.
|
|
182
|
+
#
|
|
183
|
+
# @return [Boolean] +true+ if the value was stored, +false+ if the TMVar was full.
|
|
96
184
|
#
|
|
97
185
|
def try_put(new_value)
|
|
98
|
-
return false
|
|
186
|
+
return false unless @tvar.value.nil?
|
|
99
187
|
|
|
100
|
-
@tvar.value = new_value
|
|
188
|
+
@tvar.value = [new_value]
|
|
101
189
|
true
|
|
102
190
|
end
|
|
103
191
|
|
|
104
192
|
#
|
|
105
|
-
#
|
|
193
|
+
# Return whether the TMVar is currently empty.
|
|
194
|
+
# It can also be called outside {Ractor.atomically}.
|
|
106
195
|
#
|
|
107
|
-
# @
|
|
196
|
+
# @return [Boolean]
|
|
108
197
|
#
|
|
109
198
|
def empty?
|
|
110
|
-
@tvar.value
|
|
199
|
+
@tvar.value.nil?
|
|
111
200
|
end
|
|
112
201
|
|
|
113
202
|
#
|
|
114
|
-
#
|
|
203
|
+
# Replace the stored value and return the previous value.
|
|
204
|
+
#
|
|
205
|
+
# If the TMVar is empty, the transaction is retried.
|
|
206
|
+
#
|
|
207
|
+
# @note Must be called inside {Ractor.atomically}.
|
|
208
|
+
#
|
|
209
|
+
# @param [Object] new_value value to store. With ractor-sharing 0.3, the value is
|
|
210
|
+
# automatically made Ractor-shareable and the passed object is frozen.
|
|
211
|
+
# This behavior is provided by the upstream Ractor::TVar, not a TMVar guarantee.
|
|
115
212
|
#
|
|
116
|
-
# @
|
|
213
|
+
# @return [Object] the previous value.
|
|
117
214
|
#
|
|
118
|
-
def swap(
|
|
119
|
-
|
|
120
|
-
raise Ractor::RetryTransaction if
|
|
215
|
+
def swap(new_value)
|
|
216
|
+
cell = @tvar.value
|
|
217
|
+
raise Ractor::RetryTransaction if cell.nil?
|
|
218
|
+
|
|
219
|
+
@tvar.value = [new_value]
|
|
220
|
+
cell[0]
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
private
|
|
121
224
|
|
|
122
|
-
|
|
123
|
-
|
|
225
|
+
def identity(value)
|
|
226
|
+
value
|
|
124
227
|
end
|
|
125
228
|
end
|
|
126
229
|
end
|
data/ractor-tmvar.gemspec
CHANGED
|
@@ -12,13 +12,14 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.description = "Introduce Ractor::TMVar based on Ractor::TVar"
|
|
13
13
|
spec.homepage = "https://github.com/yoshitsugu/ractor-tmvar"
|
|
14
14
|
spec.license = "MIT"
|
|
15
|
-
spec.required_ruby_version = Gem::Requirement.new(">=
|
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 4.0")
|
|
16
16
|
|
|
17
17
|
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
|
18
18
|
|
|
19
19
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
20
20
|
spec.metadata["source_code_uri"] = spec.homepage
|
|
21
21
|
spec.metadata["changelog_uri"] = spec.homepage
|
|
22
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
22
23
|
|
|
23
24
|
# Specify which files should be added to the gem when it is released.
|
|
24
25
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
@@ -29,5 +30,5 @@ Gem::Specification.new do |spec|
|
|
|
29
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
30
31
|
spec.require_paths = ["lib"]
|
|
31
32
|
|
|
32
|
-
spec.
|
|
33
|
+
spec.add_dependency "ractor-sharing", "~> 0.3.0"
|
|
33
34
|
end
|
metadata
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ractor-tmvar
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- yoshitsugu
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: ractor-
|
|
13
|
+
name: ractor-sharing
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
16
|
- - "~>"
|
|
@@ -31,7 +30,9 @@ executables: []
|
|
|
31
30
|
extensions: []
|
|
32
31
|
extra_rdoc_files: []
|
|
33
32
|
files:
|
|
33
|
+
- ".github/workflows/build-gem.yml"
|
|
34
34
|
- ".github/workflows/main.yml"
|
|
35
|
+
- ".github/workflows/release.yml"
|
|
35
36
|
- ".gitignore"
|
|
36
37
|
- ".rubocop.yml"
|
|
37
38
|
- Gemfile
|
|
@@ -51,7 +52,7 @@ metadata:
|
|
|
51
52
|
homepage_uri: https://github.com/yoshitsugu/ractor-tmvar
|
|
52
53
|
source_code_uri: https://github.com/yoshitsugu/ractor-tmvar
|
|
53
54
|
changelog_uri: https://github.com/yoshitsugu/ractor-tmvar
|
|
54
|
-
|
|
55
|
+
rubygems_mfa_required: 'true'
|
|
55
56
|
rdoc_options: []
|
|
56
57
|
require_paths:
|
|
57
58
|
- lib
|
|
@@ -59,15 +60,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
59
60
|
requirements:
|
|
60
61
|
- - ">="
|
|
61
62
|
- !ruby/object:Gem::Version
|
|
62
|
-
version:
|
|
63
|
+
version: '4.0'
|
|
63
64
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
65
|
requirements:
|
|
65
66
|
- - ">="
|
|
66
67
|
- !ruby/object:Gem::Version
|
|
67
68
|
version: '0'
|
|
68
69
|
requirements: []
|
|
69
|
-
rubygems_version:
|
|
70
|
-
signing_key:
|
|
70
|
+
rubygems_version: 4.0.20
|
|
71
71
|
specification_version: 4
|
|
72
72
|
summary: Ractor::TMVar
|
|
73
73
|
test_files: []
|