ractor-tvar 0.1.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 +7 -0
- data/.github/workflows/main.yml +20 -0
- data/.gitignore +8 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +64 -0
- data/Rakefile +18 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/ext/ractor_tvar/Makefile +266 -0
- data/ext/ractor_tvar/extconf.rb +2 -0
- data/ext/ractor_tvar/ractor_tvar.c +782 -0
- data/lib/ractor/tvar.rb +14 -0
- data/lib/ractor/tvar/version.rb +7 -0
- data/ractor-tvar.gemspec +32 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9d5e6608afb50a599080693860be49ec99d69d07fe2edeae5ad6d2fa89a5b32d
|
4
|
+
data.tar.gz: 586d6d05b0cb7ebb088bc268efaa04ec5e941fa46ebee1a3484a1be6b0109e40
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a5cdb09fcc88fa3151a6e736b97bbf564cb62f6bc1f5f961a020d3911fb12c9918a93bd3efc15936a5842c4e338c8010a116c056fdabe6ded89b5b2e918e3881
|
7
|
+
data.tar.gz: e64e8c33dadc4cffa6900ecccc79847b7a275a65fcc7347dc342752dbb1c396b8a8c8005b1a186ecd404d429ba8844082e8ae945cf7a52e86a7fca7527f7be66
|
@@ -0,0 +1,20 @@
|
|
1
|
+
name: Development
|
2
|
+
on: [push,pull_request]
|
3
|
+
jobs:
|
4
|
+
test:
|
5
|
+
strategy:
|
6
|
+
fail-fast: false
|
7
|
+
matrix:
|
8
|
+
os: [ubuntu]
|
9
|
+
ruby: [head, debug]
|
10
|
+
|
11
|
+
runs-on: ${{ matrix.os }}-latest
|
12
|
+
continue-on-error: ${{ matrix.ruby == 'head' || matrix.ruby == 'debug' }}
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v2
|
15
|
+
- uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: ${{ matrix.ruby }}
|
18
|
+
|
19
|
+
- run: bundle install
|
20
|
+
- run: bundle exec rake
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Koichi Sasada
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Ractor::TVar
|
2
|
+
|
3
|
+
[Software transactional memory](https://en.wikipedia.org/wiki/Software_transactional_memory) implementation for Ractor and Thread on Ruby 3.0.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
require 'ractor/tvar'
|
7
|
+
|
8
|
+
tv = Ractor::TVar.new(0)
|
9
|
+
|
10
|
+
N = 10_000
|
11
|
+
|
12
|
+
r = Ractor.new tv do |tv|
|
13
|
+
N.times do
|
14
|
+
Ractor.atomically do
|
15
|
+
tv.value += 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
N.times do
|
21
|
+
Ractor.atomically do
|
22
|
+
tv.value += 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
r.take # wait for the ractor
|
27
|
+
|
28
|
+
p tv.value #=> 20000 (= N * 2)
|
29
|
+
```
|
30
|
+
|
31
|
+
This script shows that there is no race between ractors.
|
32
|
+
|
33
|
+
## Installation
|
34
|
+
|
35
|
+
You need recent Ruby 3.0 (development).
|
36
|
+
|
37
|
+
Add this line to your application's Gemfile:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
gem 'ractor-tvar'
|
41
|
+
```
|
42
|
+
|
43
|
+
And then execute:
|
44
|
+
|
45
|
+
$ bundle install
|
46
|
+
|
47
|
+
Or install it yourself as:
|
48
|
+
|
49
|
+
$ gem install ractor-tvar
|
50
|
+
|
51
|
+
## Development
|
52
|
+
|
53
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test-unit` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
54
|
+
|
55
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
56
|
+
|
57
|
+
## Contributing
|
58
|
+
|
59
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ko1/ractor-tvar.
|
60
|
+
|
61
|
+
|
62
|
+
## License
|
63
|
+
|
64
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
|
+
require "rake/extensiontask"
|
6
|
+
|
7
|
+
Rake::TestTask.new(:test) do |t|
|
8
|
+
t.libs << "test"
|
9
|
+
t.libs << "lib"
|
10
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
11
|
+
end
|
12
|
+
|
13
|
+
Rake::ExtensionTask.new "ractor_tvar" do |ext|
|
14
|
+
ext.lib_dir = "lib/ractor/tvar"
|
15
|
+
end
|
16
|
+
|
17
|
+
task :test => :compile
|
18
|
+
task default: :test
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "ractor/tvar"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require "irb"
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,266 @@
|
|
1
|
+
|
2
|
+
SHELL = /bin/sh
|
3
|
+
|
4
|
+
# V=0 quiet, V=1 verbose. other values don't work.
|
5
|
+
V = 0
|
6
|
+
Q1 = $(V:1=)
|
7
|
+
Q = $(Q1:0=@)
|
8
|
+
ECHO1 = $(V:1=@ :)
|
9
|
+
ECHO = $(ECHO1:0=@ echo)
|
10
|
+
NULLCMD = :
|
11
|
+
|
12
|
+
#### Start of system configuration section. ####
|
13
|
+
|
14
|
+
srcdir = .
|
15
|
+
topdir = /home/ko1/ruby/install/trunk/include/ruby-3.0.0
|
16
|
+
hdrdir = $(topdir)
|
17
|
+
arch_hdrdir = /home/ko1/ruby/install/trunk/include/ruby-3.0.0/x86_64-linux
|
18
|
+
PATH_SEPARATOR = :
|
19
|
+
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
|
20
|
+
prefix = $(DESTDIR)/home/ko1/ruby/install/trunk
|
21
|
+
rubysitearchprefix = $(rubylibprefix)/$(sitearch)
|
22
|
+
rubyarchprefix = $(rubylibprefix)/$(arch)
|
23
|
+
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
|
24
|
+
exec_prefix = $(prefix)
|
25
|
+
vendorarchhdrdir = $(vendorhdrdir)/$(sitearch)
|
26
|
+
sitearchhdrdir = $(sitehdrdir)/$(sitearch)
|
27
|
+
rubyarchhdrdir = $(rubyhdrdir)/$(arch)
|
28
|
+
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
|
29
|
+
sitehdrdir = $(rubyhdrdir)/site_ruby
|
30
|
+
rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
|
31
|
+
vendorarchdir = $(vendorlibdir)/$(sitearch)
|
32
|
+
vendorlibdir = $(vendordir)/$(ruby_version)
|
33
|
+
vendordir = $(rubylibprefix)/vendor_ruby
|
34
|
+
sitearchdir = $(sitelibdir)/$(sitearch)
|
35
|
+
sitelibdir = $(sitedir)/$(ruby_version)
|
36
|
+
sitedir = $(rubylibprefix)/site_ruby
|
37
|
+
rubyarchdir = $(rubylibdir)/$(arch)
|
38
|
+
rubylibdir = $(rubylibprefix)/$(ruby_version)
|
39
|
+
sitearchincludedir = $(includedir)/$(sitearch)
|
40
|
+
archincludedir = $(includedir)/$(arch)
|
41
|
+
sitearchlibdir = $(libdir)/$(sitearch)
|
42
|
+
archlibdir = $(libdir)/$(arch)
|
43
|
+
ridir = $(datarootdir)/$(RI_BASE_NAME)
|
44
|
+
mandir = $(datarootdir)/man
|
45
|
+
localedir = $(datarootdir)/locale
|
46
|
+
libdir = $(exec_prefix)/lib
|
47
|
+
psdir = $(docdir)
|
48
|
+
pdfdir = $(docdir)
|
49
|
+
dvidir = $(docdir)
|
50
|
+
htmldir = $(docdir)
|
51
|
+
infodir = $(datarootdir)/info
|
52
|
+
docdir = $(datarootdir)/doc/$(PACKAGE)
|
53
|
+
oldincludedir = $(DESTDIR)/usr/include
|
54
|
+
includedir = $(prefix)/include
|
55
|
+
runstatedir = $(localstatedir)/run
|
56
|
+
localstatedir = $(prefix)/var
|
57
|
+
sharedstatedir = $(prefix)/com
|
58
|
+
sysconfdir = $(prefix)/etc
|
59
|
+
datadir = $(datarootdir)
|
60
|
+
datarootdir = $(prefix)/share
|
61
|
+
libexecdir = $(exec_prefix)/libexec
|
62
|
+
sbindir = $(exec_prefix)/sbin
|
63
|
+
bindir = $(exec_prefix)/bin
|
64
|
+
archdir = $(rubyarchdir)
|
65
|
+
|
66
|
+
|
67
|
+
CC_WRAPPER =
|
68
|
+
CC = gcc
|
69
|
+
CXX = g++
|
70
|
+
LIBRUBY = $(LIBRUBY_SO)
|
71
|
+
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
72
|
+
LIBRUBYARG_SHARED = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)
|
73
|
+
LIBRUBYARG_STATIC = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)-static $(MAINLIBS)
|
74
|
+
empty =
|
75
|
+
OUTFLAG = -o $(empty)
|
76
|
+
COUTFLAG = -o $(empty)
|
77
|
+
CSRCFLAG = $(empty)
|
78
|
+
|
79
|
+
RUBY_EXTCONF_H =
|
80
|
+
cflags = $(optflags) $(debugflags) $(warnflags)
|
81
|
+
cxxflags =
|
82
|
+
optflags = -O3
|
83
|
+
debugflags = -ggdb3
|
84
|
+
warnflags = -Wall -Wextra -Wdeprecated-declarations -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wwrite-strings -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable
|
85
|
+
cppflags =
|
86
|
+
CCDLFLAGS = -fPIC
|
87
|
+
CFLAGS = $(CCDLFLAGS) $(cflags) -fPIC $(ARCH_FLAG)
|
88
|
+
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
|
89
|
+
DEFS =
|
90
|
+
CPPFLAGS = $(DEFS) $(cppflags)
|
91
|
+
CXXFLAGS = $(CCDLFLAGS) $(ARCH_FLAG)
|
92
|
+
ldflags = -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic
|
93
|
+
dldflags = -Wl,--compress-debug-sections=zlib
|
94
|
+
ARCH_FLAG =
|
95
|
+
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
|
96
|
+
LDSHARED = $(CC) -shared
|
97
|
+
LDSHAREDXX = $(CXX) -shared
|
98
|
+
AR = gcc-ar
|
99
|
+
EXEEXT =
|
100
|
+
|
101
|
+
RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)
|
102
|
+
RUBY_SO_NAME = ruby
|
103
|
+
RUBYW_INSTALL_NAME =
|
104
|
+
RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
|
105
|
+
RUBYW_BASE_NAME = rubyw
|
106
|
+
RUBY_BASE_NAME = ruby
|
107
|
+
|
108
|
+
arch = x86_64-linux
|
109
|
+
sitearch = $(arch)
|
110
|
+
ruby_version = 3.0.0
|
111
|
+
ruby = $(bindir)/$(RUBY_BASE_NAME)
|
112
|
+
RUBY = $(ruby)
|
113
|
+
ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
|
114
|
+
|
115
|
+
RM = rm -f
|
116
|
+
RM_RF = $(RUBY) -run -e rm -- -rf
|
117
|
+
RMDIRS = rmdir --ignore-fail-on-non-empty -p
|
118
|
+
MAKEDIRS = /usr/bin/mkdir -p
|
119
|
+
INSTALL = /usr/bin/install -c
|
120
|
+
INSTALL_PROG = $(INSTALL) -m 0755
|
121
|
+
INSTALL_DATA = $(INSTALL) -m 644
|
122
|
+
COPY = cp
|
123
|
+
TOUCH = exit >
|
124
|
+
|
125
|
+
#### End of system configuration section. ####
|
126
|
+
|
127
|
+
preload =
|
128
|
+
libpath = . $(libdir)
|
129
|
+
LIBPATH = -L. -L$(libdir) -Wl,-rpath,$(libdir)
|
130
|
+
DEFFILE =
|
131
|
+
|
132
|
+
CLEANFILES = mkmf.log
|
133
|
+
DISTCLEANFILES =
|
134
|
+
DISTCLEANDIRS =
|
135
|
+
|
136
|
+
extout =
|
137
|
+
extout_prefix =
|
138
|
+
target_prefix =
|
139
|
+
LOCAL_LIBS =
|
140
|
+
LIBS = $(LIBRUBYARG_SHARED) -lm -lc
|
141
|
+
ORIG_SRCS = ractor_tvar.c
|
142
|
+
SRCS = $(ORIG_SRCS)
|
143
|
+
OBJS = ractor_tvar.o
|
144
|
+
HDRS =
|
145
|
+
LOCAL_HDRS =
|
146
|
+
TARGET = ractor_tvar
|
147
|
+
TARGET_NAME = ractor_tvar
|
148
|
+
TARGET_ENTRY = Init_$(TARGET_NAME)
|
149
|
+
DLLIB = $(TARGET).so
|
150
|
+
EXTSTATIC =
|
151
|
+
STATIC_LIB =
|
152
|
+
|
153
|
+
TIMESTAMP_DIR = .
|
154
|
+
BINDIR = $(bindir)
|
155
|
+
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
|
156
|
+
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
|
157
|
+
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
|
158
|
+
HDRDIR = $(rubyhdrdir)/ruby$(target_prefix)
|
159
|
+
ARCHHDRDIR = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)
|
160
|
+
TARGET_SO_DIR =
|
161
|
+
TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
|
162
|
+
CLEANLIBS = $(TARGET_SO)
|
163
|
+
CLEANOBJS = *.o *.bak
|
164
|
+
|
165
|
+
all: $(DLLIB)
|
166
|
+
static: $(STATIC_LIB)
|
167
|
+
.PHONY: all install static install-so install-rb
|
168
|
+
.PHONY: clean clean-so clean-static clean-rb
|
169
|
+
|
170
|
+
clean-static::
|
171
|
+
clean-rb-default::
|
172
|
+
clean-rb::
|
173
|
+
clean-so::
|
174
|
+
clean: clean-so clean-static clean-rb-default clean-rb
|
175
|
+
-$(Q)$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
|
176
|
+
|
177
|
+
distclean-rb-default::
|
178
|
+
distclean-rb::
|
179
|
+
distclean-so::
|
180
|
+
distclean-static::
|
181
|
+
distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
|
182
|
+
-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
|
183
|
+
-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
|
184
|
+
-$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
|
185
|
+
|
186
|
+
realclean: distclean
|
187
|
+
install: install-so install-rb
|
188
|
+
|
189
|
+
install-so: $(DLLIB) $(TIMESTAMP_DIR)/.sitearchdir.time
|
190
|
+
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
|
191
|
+
clean-static::
|
192
|
+
-$(Q)$(RM) $(STATIC_LIB)
|
193
|
+
install-rb: pre-install-rb do-install-rb install-rb-default
|
194
|
+
install-rb-default: pre-install-rb-default do-install-rb-default
|
195
|
+
pre-install-rb: Makefile
|
196
|
+
pre-install-rb-default: Makefile
|
197
|
+
do-install-rb:
|
198
|
+
do-install-rb-default:
|
199
|
+
pre-install-rb-default:
|
200
|
+
@$(NULLCMD)
|
201
|
+
$(TIMESTAMP_DIR)/.sitearchdir.time:
|
202
|
+
$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
|
203
|
+
$(Q) $(TOUCH) $@
|
204
|
+
|
205
|
+
site-install: site-install-so site-install-rb
|
206
|
+
site-install-so: install-so
|
207
|
+
site-install-rb: install-rb
|
208
|
+
|
209
|
+
.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
|
210
|
+
|
211
|
+
.cc.o:
|
212
|
+
$(ECHO) compiling $(<)
|
213
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
214
|
+
|
215
|
+
.cc.S:
|
216
|
+
$(ECHO) translating $(<)
|
217
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
218
|
+
|
219
|
+
.mm.o:
|
220
|
+
$(ECHO) compiling $(<)
|
221
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
222
|
+
|
223
|
+
.mm.S:
|
224
|
+
$(ECHO) translating $(<)
|
225
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
226
|
+
|
227
|
+
.cxx.o:
|
228
|
+
$(ECHO) compiling $(<)
|
229
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
230
|
+
|
231
|
+
.cxx.S:
|
232
|
+
$(ECHO) translating $(<)
|
233
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
234
|
+
|
235
|
+
.cpp.o:
|
236
|
+
$(ECHO) compiling $(<)
|
237
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
238
|
+
|
239
|
+
.cpp.S:
|
240
|
+
$(ECHO) translating $(<)
|
241
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
242
|
+
|
243
|
+
.c.o:
|
244
|
+
$(ECHO) compiling $(<)
|
245
|
+
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
246
|
+
|
247
|
+
.c.S:
|
248
|
+
$(ECHO) translating $(<)
|
249
|
+
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
250
|
+
|
251
|
+
.m.o:
|
252
|
+
$(ECHO) compiling $(<)
|
253
|
+
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
254
|
+
|
255
|
+
.m.S:
|
256
|
+
$(ECHO) translating $(<)
|
257
|
+
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
258
|
+
|
259
|
+
$(TARGET_SO): $(OBJS) Makefile
|
260
|
+
$(ECHO) linking shared-object $(DLLIB)
|
261
|
+
-$(Q)$(RM) $(@)
|
262
|
+
$(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
|
263
|
+
|
264
|
+
|
265
|
+
|
266
|
+
$(OBJS): $(HDRS) $(ruby_headers)
|
@@ -0,0 +1,782 @@
|
|
1
|
+
#include "ruby/ruby.h"
|
2
|
+
#include "ruby/util.h"
|
3
|
+
#include "ruby/thread_native.h"
|
4
|
+
#include "ruby/ractor.h"
|
5
|
+
|
6
|
+
// quoted (and modified) from "internal/fixnum.h"
|
7
|
+
|
8
|
+
static inline long
|
9
|
+
rb_overflowed_fix_to_int(long x)
|
10
|
+
{
|
11
|
+
return (long)((unsigned long)(x >> 1) ^ (1LU << (SIZEOF_LONG * CHAR_BIT - 1)));
|
12
|
+
}
|
13
|
+
|
14
|
+
static inline VALUE
|
15
|
+
rb_fix_plus_fix(VALUE x, VALUE y)
|
16
|
+
{
|
17
|
+
#if !HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW
|
18
|
+
long lz = FIX2LONG(x) + FIX2LONG(y);
|
19
|
+
return LONG2NUM(lz);
|
20
|
+
#else
|
21
|
+
long lz;
|
22
|
+
/* NOTE
|
23
|
+
* (1) `LONG2FIX(FIX2LONG(x)+FIX2LONG(y))`
|
24
|
+
+ = `((lx*2+1)/2 + (ly*2+1)/2)*2+1`
|
25
|
+
+ = `lx*2 + ly*2 + 1`
|
26
|
+
+ = `(lx*2+1) + (ly*2+1) - 1`
|
27
|
+
+ = `x + y - 1`
|
28
|
+
* (2) Fixnum's LSB is always 1.
|
29
|
+
* It means you can always run `x - 1` without overflow.
|
30
|
+
* (3) Of course `z = x + (y-1)` may overflow.
|
31
|
+
* At that time true value is
|
32
|
+
* * positive: 0b0 1xxx...1, and z = 0b1xxx...1
|
33
|
+
* * nevative: 0b1 0xxx...1, and z = 0b0xxx...1
|
34
|
+
* To convert this true value to long,
|
35
|
+
* (a) Use arithmetic shift
|
36
|
+
* * positive: 0b11xxx...
|
37
|
+
* * negative: 0b00xxx...
|
38
|
+
* (b) invert MSB
|
39
|
+
* * positive: 0b01xxx...
|
40
|
+
* * negative: 0b10xxx...
|
41
|
+
*/
|
42
|
+
if (__builtin_add_overflow((long)x, (long)y-1, &lz)) {
|
43
|
+
return rb_int2big(rb_overflowed_fix_to_int(lz));
|
44
|
+
}
|
45
|
+
else {
|
46
|
+
return (VALUE)lz;
|
47
|
+
}
|
48
|
+
#endif
|
49
|
+
}
|
50
|
+
|
51
|
+
static inline unsigned int
|
52
|
+
rb_popcount32(uint32_t x)
|
53
|
+
{
|
54
|
+
#if defined(_MSC_VER) && defined(__AVX__)
|
55
|
+
/* Note: CPUs since Nehalem and Barcelona have had this instruction so SSE
|
56
|
+
* 4.2 should suffice, but it seems there is no such thing like __SSE_4_2__
|
57
|
+
* predefined macro in MSVC. They do have __AVX__ so use it instead. */
|
58
|
+
return (unsigned int)__popcnt(x);
|
59
|
+
|
60
|
+
#elif HAVE_BUILTIN___BUILTIN_POPCOUNT
|
61
|
+
return (unsigned int)__builtin_popcount(x);
|
62
|
+
#else
|
63
|
+
x = (x & 0x55555555) + (x >> 1 & 0x55555555);
|
64
|
+
x = (x & 0x33333333) + (x >> 2 & 0x33333333);
|
65
|
+
x = (x & 0x0f0f0f0f) + (x >> 4 & 0x0f0f0f0f);
|
66
|
+
x = (x & 0x001f001f) + (x >> 8 & 0x001f001f);
|
67
|
+
x = (x & 0x0000003f) + (x >>16 & 0x0000003f);
|
68
|
+
return (unsigned int)x;
|
69
|
+
#endif
|
70
|
+
}
|
71
|
+
|
72
|
+
// Thread/Ractor support transactional variable Thread::TVar
|
73
|
+
|
74
|
+
// 0: null (BUG/only for evaluation)
|
75
|
+
// 1: mutex
|
76
|
+
// TODO: 1: atomic
|
77
|
+
#define SLOT_LOCK_TYPE 1
|
78
|
+
|
79
|
+
struct slot_lock {
|
80
|
+
#if SLOT_LOCK_TYPE == 0
|
81
|
+
#elif SLOT_LOCK_TYPE == 1
|
82
|
+
rb_nativethread_lock_t lock;
|
83
|
+
#else
|
84
|
+
#error unknown
|
85
|
+
#endif
|
86
|
+
};
|
87
|
+
|
88
|
+
struct tvar_slot {
|
89
|
+
uint64_t version;
|
90
|
+
VALUE value;
|
91
|
+
VALUE index;
|
92
|
+
struct slot_lock lock;
|
93
|
+
};
|
94
|
+
|
95
|
+
struct tx_global {
|
96
|
+
uint64_t version;
|
97
|
+
rb_nativethread_lock_t version_lock;
|
98
|
+
|
99
|
+
uint64_t slot_index;
|
100
|
+
rb_nativethread_lock_t slot_index_lock;
|
101
|
+
};
|
102
|
+
|
103
|
+
struct tx_log {
|
104
|
+
VALUE value;
|
105
|
+
struct tvar_slot *slot;
|
106
|
+
VALUE tvar; // mark slot
|
107
|
+
};
|
108
|
+
|
109
|
+
struct tx_logs {
|
110
|
+
uint64_t version;
|
111
|
+
uint32_t logs_cnt;
|
112
|
+
uint32_t logs_capa;
|
113
|
+
|
114
|
+
struct tx_log *logs;
|
115
|
+
|
116
|
+
bool enabled;
|
117
|
+
bool stop_adding;
|
118
|
+
|
119
|
+
uint32_t retry_history;
|
120
|
+
size_t retry_on_commit;
|
121
|
+
size_t retry_on_read_lock;
|
122
|
+
size_t retry_on_read_version;
|
123
|
+
};
|
124
|
+
|
125
|
+
static struct tx_global tx_global;
|
126
|
+
|
127
|
+
static VALUE rb_eTxRetry;
|
128
|
+
static VALUE rb_eTxError;
|
129
|
+
static VALUE rb_exc_tx_retry;
|
130
|
+
static VALUE rb_cRactorTVar;
|
131
|
+
static VALUE rb_cRactorTxLogs;
|
132
|
+
|
133
|
+
static ID id_tx_logs;
|
134
|
+
|
135
|
+
static struct tx_global *
|
136
|
+
tx_global_ptr(void)
|
137
|
+
{
|
138
|
+
return &tx_global;
|
139
|
+
}
|
140
|
+
|
141
|
+
#define TVAR_DEBUG_LOG(...)
|
142
|
+
|
143
|
+
static VALUE
|
144
|
+
txg_next_index(struct tx_global *txg)
|
145
|
+
{
|
146
|
+
VALUE index;
|
147
|
+
rb_native_mutex_lock(&txg->slot_index_lock);
|
148
|
+
{
|
149
|
+
txg->slot_index++;
|
150
|
+
index = INT2FIX(txg->slot_index);
|
151
|
+
}
|
152
|
+
rb_native_mutex_unlock(&txg->slot_index_lock);
|
153
|
+
|
154
|
+
return index;
|
155
|
+
}
|
156
|
+
|
157
|
+
static uint64_t
|
158
|
+
txg_version(const struct tx_global *txg)
|
159
|
+
{
|
160
|
+
uint64_t version;
|
161
|
+
version = txg->version;
|
162
|
+
return version;
|
163
|
+
}
|
164
|
+
|
165
|
+
static uint64_t
|
166
|
+
txg_next_version(struct tx_global *txg)
|
167
|
+
{
|
168
|
+
uint64_t version;
|
169
|
+
|
170
|
+
rb_native_mutex_lock(&txg->version_lock);
|
171
|
+
{
|
172
|
+
txg->version++;
|
173
|
+
version = txg->version;
|
174
|
+
TVAR_DEBUG_LOG("new_version:%lu", version);
|
175
|
+
}
|
176
|
+
rb_native_mutex_unlock(&txg->version_lock);
|
177
|
+
|
178
|
+
return version;
|
179
|
+
}
|
180
|
+
|
181
|
+
// tx: transaction
|
182
|
+
|
183
|
+
static void
|
184
|
+
tx_slot_lock_init(struct slot_lock *lock)
|
185
|
+
{
|
186
|
+
#if SLOT_LOCK_TYPE == 0
|
187
|
+
#elif SLOT_LOCK_TYPE == 1
|
188
|
+
rb_native_mutex_initialize(&lock->lock);
|
189
|
+
#else
|
190
|
+
#error unknown
|
191
|
+
#endif
|
192
|
+
}
|
193
|
+
|
194
|
+
static void
|
195
|
+
tx_slot_lock_free(struct slot_lock *lock)
|
196
|
+
{
|
197
|
+
#if SLOT_LOCK_TYPE == 0
|
198
|
+
#elif SLOT_LOCK_TYPE == 1
|
199
|
+
rb_native_mutex_destroy(&lock->lock);
|
200
|
+
#else
|
201
|
+
#error unknown
|
202
|
+
#endif
|
203
|
+
}
|
204
|
+
|
205
|
+
static bool
|
206
|
+
tx_slot_lock_trylock(struct slot_lock *lock)
|
207
|
+
{
|
208
|
+
#if SLOT_LOCK_TYPE == 0
|
209
|
+
return true;
|
210
|
+
#elif SLOT_LOCK_TYPE == 1
|
211
|
+
return rb_native_mutex_trylock(&lock->lock) == 0;
|
212
|
+
#else
|
213
|
+
#error unknown
|
214
|
+
#endif
|
215
|
+
}
|
216
|
+
|
217
|
+
static void
|
218
|
+
tx_slot_lock_lock(struct slot_lock *lock)
|
219
|
+
{
|
220
|
+
#if SLOT_LOCK_TYPE == 0
|
221
|
+
#elif SLOT_LOCK_TYPE == 1
|
222
|
+
rb_native_mutex_lock(&lock->lock);
|
223
|
+
#else
|
224
|
+
#error unknown
|
225
|
+
#endif
|
226
|
+
}
|
227
|
+
|
228
|
+
static void
|
229
|
+
tx_slot_lock_unlock(struct slot_lock *lock)
|
230
|
+
{
|
231
|
+
#if SLOT_LOCK_TYPE == 0
|
232
|
+
#elif SLOT_LOCK_TYPE == 1
|
233
|
+
rb_native_mutex_unlock(&lock->lock);
|
234
|
+
#else
|
235
|
+
#error unknown
|
236
|
+
#endif
|
237
|
+
}
|
238
|
+
|
239
|
+
static bool
|
240
|
+
tx_slot_trylock(struct tvar_slot *slot)
|
241
|
+
{
|
242
|
+
return tx_slot_lock_trylock(&slot->lock);
|
243
|
+
}
|
244
|
+
|
245
|
+
static void
|
246
|
+
tx_slot_lock(struct tvar_slot *slot)
|
247
|
+
{
|
248
|
+
tx_slot_lock_lock(&slot->lock);
|
249
|
+
}
|
250
|
+
|
251
|
+
static void
|
252
|
+
tx_slot_unlock(struct tvar_slot *slot)
|
253
|
+
{
|
254
|
+
tx_slot_lock_unlock(&slot->lock);
|
255
|
+
}
|
256
|
+
|
257
|
+
|
258
|
+
static void
|
259
|
+
tx_mark(void *ptr)
|
260
|
+
{
|
261
|
+
struct tx_logs *tx = (struct tx_logs *)ptr;
|
262
|
+
|
263
|
+
for (uint32_t i=0; i<tx->logs_cnt; i++) {
|
264
|
+
rb_gc_mark(tx->logs[i].value);
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
static void
|
269
|
+
tx_free(void *ptr)
|
270
|
+
{
|
271
|
+
struct tx_logs *tx = (struct tx_logs *)ptr;
|
272
|
+
|
273
|
+
TVAR_DEBUG_LOG("retry %5lu commit:%lu read_lock:%lu read_version:%lu",
|
274
|
+
tx->retry_on_commit + tx->retry_on_read_lock + tx->retry_on_read_version,
|
275
|
+
tx->retry_on_commit,
|
276
|
+
tx->retry_on_read_lock,
|
277
|
+
tx->retry_on_read_version);
|
278
|
+
|
279
|
+
ruby_xfree(tx->logs);
|
280
|
+
ruby_xfree(tx);
|
281
|
+
}
|
282
|
+
|
283
|
+
static const rb_data_type_t txlogs_type = {
|
284
|
+
"txlogs",
|
285
|
+
{tx_mark, tx_free, NULL,},
|
286
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
287
|
+
};
|
288
|
+
|
289
|
+
static struct tx_logs *
|
290
|
+
tx_logs(void)
|
291
|
+
{
|
292
|
+
VALUE cth = rb_thread_current();
|
293
|
+
VALUE txobj = rb_thread_local_aref(cth, id_tx_logs);
|
294
|
+
|
295
|
+
if (txobj == Qnil) {
|
296
|
+
struct tx_logs *tx;
|
297
|
+
txobj = TypedData_Make_Struct(rb_cRactorTxLogs, struct tx_logs, &txlogs_type, tx);
|
298
|
+
tx->logs_capa = 0x10; // default
|
299
|
+
tx->logs = ALLOC_N(struct tx_log, tx->logs_capa);
|
300
|
+
rb_thread_local_aset(cth, id_tx_logs, txobj);
|
301
|
+
return tx;
|
302
|
+
}
|
303
|
+
else {
|
304
|
+
// TODO: check
|
305
|
+
return DATA_PTR(txobj);
|
306
|
+
}
|
307
|
+
}
|
308
|
+
|
309
|
+
static struct tx_log *
|
310
|
+
tx_lookup(struct tx_logs *tx, VALUE tvar)
|
311
|
+
{
|
312
|
+
struct tx_log *copies = tx->logs;
|
313
|
+
uint32_t cnt = tx->logs_cnt;
|
314
|
+
|
315
|
+
for (uint32_t i = 0; i< cnt; i++) {
|
316
|
+
if (copies[i].tvar == tvar) {
|
317
|
+
return &copies[i];
|
318
|
+
}
|
319
|
+
}
|
320
|
+
|
321
|
+
return NULL;
|
322
|
+
}
|
323
|
+
|
324
|
+
static void
|
325
|
+
tx_add(struct tx_logs *tx, VALUE val, struct tvar_slot *slot, VALUE tvar)
|
326
|
+
{
|
327
|
+
if (RB_UNLIKELY(tx->logs_capa == tx->logs_cnt)) {
|
328
|
+
uint32_t new_capa = tx->logs_capa * 2;
|
329
|
+
RB_REALLOC_N(tx->logs, struct tx_log, new_capa);
|
330
|
+
tx->logs_capa = new_capa;
|
331
|
+
}
|
332
|
+
if (RB_UNLIKELY(tx->stop_adding)) {
|
333
|
+
rb_raise(rb_eTxError, "can not handle more transactional variable: %"PRIxVALUE, rb_inspect(tvar));
|
334
|
+
}
|
335
|
+
struct tx_log *log = &tx->logs[tx->logs_cnt++];
|
336
|
+
|
337
|
+
log->value = val;
|
338
|
+
log->slot = slot;
|
339
|
+
log->tvar = tvar;
|
340
|
+
}
|
341
|
+
|
342
|
+
static VALUE
|
343
|
+
tx_get(struct tx_logs *tx, struct tvar_slot *slot, VALUE tvar)
|
344
|
+
{
|
345
|
+
struct tx_log *ent = tx_lookup(tx, tvar);
|
346
|
+
|
347
|
+
if (ent == NULL) {
|
348
|
+
VALUE val;
|
349
|
+
|
350
|
+
if (tx_slot_trylock(slot)) {
|
351
|
+
if (slot->version > tx->version) {
|
352
|
+
TVAR_DEBUG_LOG("RV < slot->V slot:%u slot->version:%lu, tx->version:%lu", FIX2INT(slot->index), slot->version, tx->version);
|
353
|
+
tx_slot_unlock(slot);
|
354
|
+
tx->retry_on_read_version++;
|
355
|
+
goto abort_and_retry;
|
356
|
+
}
|
357
|
+
val = slot->value;
|
358
|
+
tx_slot_unlock(slot);
|
359
|
+
}
|
360
|
+
else {
|
361
|
+
TVAR_DEBUG_LOG("RV < slot->V slot:%u slot->version:%lu, tx->version:%lu", FIX2INT(slot->index), slot->version, tx->version);
|
362
|
+
tx->retry_on_read_lock++;
|
363
|
+
goto abort_and_retry;
|
364
|
+
}
|
365
|
+
tx_add(tx, val, slot, tvar);
|
366
|
+
return val;
|
367
|
+
|
368
|
+
abort_and_retry:
|
369
|
+
rb_raise(rb_eTxRetry, "retry");
|
370
|
+
}
|
371
|
+
else {
|
372
|
+
return ent->value;
|
373
|
+
}
|
374
|
+
}
|
375
|
+
|
376
|
+
static void
|
377
|
+
tx_set(struct tx_logs *tx, VALUE val, struct tvar_slot *slot, VALUE tvar)
|
378
|
+
{
|
379
|
+
struct tx_log *ent = tx_lookup(tx, tvar);
|
380
|
+
|
381
|
+
if (ent == NULL) {
|
382
|
+
tx_add(tx, val, slot, tvar);
|
383
|
+
}
|
384
|
+
else {
|
385
|
+
ent->value = val;
|
386
|
+
}
|
387
|
+
}
|
388
|
+
|
389
|
+
static void
|
390
|
+
tx_check(struct tx_logs *tx)
|
391
|
+
{
|
392
|
+
if (RB_UNLIKELY(!tx->enabled)) {
|
393
|
+
rb_raise(rb_eTxError, "can not set without transaction");
|
394
|
+
}
|
395
|
+
}
|
396
|
+
|
397
|
+
static void
|
398
|
+
tx_setup(struct tx_global *txg, struct tx_logs *tx)
|
399
|
+
{
|
400
|
+
RUBY_ASSERT(tx->enabled);
|
401
|
+
RUBY_ASSERT(tx->logs_cnt == 0);
|
402
|
+
|
403
|
+
tx->version = txg_version(txg);
|
404
|
+
|
405
|
+
TVAR_DEBUG_LOG("tx:%lu", tx->version);
|
406
|
+
}
|
407
|
+
|
408
|
+
static struct tx_logs *
|
409
|
+
tx_begin(void)
|
410
|
+
{
|
411
|
+
struct tx_global *txg = tx_global_ptr();
|
412
|
+
struct tx_logs *tx = tx_logs();
|
413
|
+
|
414
|
+
RUBY_ASSERT(tx->stop_adding == false);
|
415
|
+
RUBY_ASSERT(tx->logs_cnt == 0);
|
416
|
+
|
417
|
+
if (tx->enabled == false) {
|
418
|
+
tx->enabled = true;
|
419
|
+
tx_setup(txg, tx);
|
420
|
+
return tx;
|
421
|
+
}
|
422
|
+
else {
|
423
|
+
return NULL;
|
424
|
+
}
|
425
|
+
}
|
426
|
+
|
427
|
+
static VALUE
|
428
|
+
tx_reset(struct tx_logs *tx)
|
429
|
+
{
|
430
|
+
struct tx_global *txg = tx_global_ptr();
|
431
|
+
tx->logs_cnt = 0;
|
432
|
+
|
433
|
+
// contention management (CM)
|
434
|
+
if (tx->retry_history != 0) {
|
435
|
+
int recent_retries = 0; rb_popcount32(tx->retry_history);
|
436
|
+
TVAR_DEBUG_LOG("retry recent_retries:%d", recent_retries);
|
437
|
+
|
438
|
+
struct timeval tv = {
|
439
|
+
.tv_sec = 0,
|
440
|
+
.tv_usec = 1 * recent_retries,
|
441
|
+
};
|
442
|
+
|
443
|
+
TVAR_DEBUG_LOG("CM tv_usec:%lu", (unsigned long)tv.tv_usec);
|
444
|
+
rb_thread_wait_for(tv);
|
445
|
+
}
|
446
|
+
|
447
|
+
tx_setup(txg, tx);
|
448
|
+
TVAR_DEBUG_LOG("tx:%lu", tx->version);
|
449
|
+
|
450
|
+
return Qnil;
|
451
|
+
}
|
452
|
+
|
453
|
+
static VALUE
|
454
|
+
tx_end(struct tx_logs *tx)
|
455
|
+
{
|
456
|
+
TVAR_DEBUG_LOG("tx:%lu", tx->version);
|
457
|
+
|
458
|
+
RUBY_ASSERT(tx->enabled);
|
459
|
+
RUBY_ASSERT(tx->stop_adding == false);
|
460
|
+
tx->enabled = false;
|
461
|
+
tx->logs_cnt = 0;
|
462
|
+
return Qnil;
|
463
|
+
}
|
464
|
+
|
465
|
+
static void
|
466
|
+
tx_commit_release(struct tx_logs *tx, uint32_t n)
|
467
|
+
{
|
468
|
+
struct tx_log *copies = tx->logs;
|
469
|
+
|
470
|
+
for (uint32_t i = 0; i<n; i++) {
|
471
|
+
struct tx_log *copy = &copies[i];
|
472
|
+
struct tvar_slot *slot = copy->slot;
|
473
|
+
tx_slot_unlock(slot);
|
474
|
+
}
|
475
|
+
}
|
476
|
+
|
477
|
+
static VALUE
|
478
|
+
tx_commit(struct tx_logs *tx)
|
479
|
+
{
|
480
|
+
struct tx_global *txg = tx_global_ptr();
|
481
|
+
uint32_t i;
|
482
|
+
struct tx_log *copies = tx->logs;
|
483
|
+
uint32_t logs_cnt = tx->logs_cnt;
|
484
|
+
|
485
|
+
for (i=0; i<logs_cnt; i++) {
|
486
|
+
struct tx_log *copy = &copies[i];
|
487
|
+
struct tvar_slot *slot = copy->slot;
|
488
|
+
|
489
|
+
if (RB_LIKELY(tx_slot_trylock(slot))) {
|
490
|
+
if (RB_UNLIKELY(slot->version > tx->version)) {
|
491
|
+
TVAR_DEBUG_LOG("RV < slot->V slot:%lu tx:%lu rs:%lu", slot->version, tx->version, txg->version);
|
492
|
+
tx_commit_release(tx, i+1);
|
493
|
+
goto abort_and_retry;
|
494
|
+
}
|
495
|
+
else {
|
496
|
+
// lock success
|
497
|
+
TVAR_DEBUG_LOG("lock slot:%lu tx:%lu rs:%lu", slot->version, tx->version, txg->version);
|
498
|
+
}
|
499
|
+
}
|
500
|
+
else {
|
501
|
+
TVAR_DEBUG_LOG("trylock fail slot:%lu tx:%lu rs:%lu", slot->version, tx->version, txg->version);
|
502
|
+
tx_commit_release(tx, i);
|
503
|
+
goto abort_and_retry;
|
504
|
+
}
|
505
|
+
}
|
506
|
+
|
507
|
+
// ok
|
508
|
+
tx->retry_history <<= 1;
|
509
|
+
|
510
|
+
uint64_t new_version = txg_next_version(txg);
|
511
|
+
|
512
|
+
for (i=0; i<logs_cnt; i++) {
|
513
|
+
struct tx_log *copy = &copies[i];
|
514
|
+
struct tvar_slot *slot = copy->slot;
|
515
|
+
|
516
|
+
if (slot->value != copy->value) {
|
517
|
+
TVAR_DEBUG_LOG("write slot:%d %d->%d slot->version:%lu->%lu tx:%lu rs:%lu",
|
518
|
+
FIX2INT(slot->index), FIX2INT(slot->value), FIX2INT(copy->value),
|
519
|
+
slot->version, new_version, tx->version, txg->version);
|
520
|
+
|
521
|
+
slot->version = new_version;
|
522
|
+
slot->value = copy->value;
|
523
|
+
}
|
524
|
+
}
|
525
|
+
|
526
|
+
tx_commit_release(tx, logs_cnt);
|
527
|
+
|
528
|
+
return Qtrue;
|
529
|
+
|
530
|
+
abort_and_retry:
|
531
|
+
tx->retry_on_commit++;
|
532
|
+
|
533
|
+
return Qfalse;
|
534
|
+
}
|
535
|
+
|
536
|
+
// tvar
|
537
|
+
|
538
|
+
static void
|
539
|
+
tvar_mark(void *ptr)
|
540
|
+
{
|
541
|
+
struct tvar_slot *slot = (struct tvar_slot *)ptr;
|
542
|
+
rb_gc_mark(slot->value);
|
543
|
+
}
|
544
|
+
|
545
|
+
static void
|
546
|
+
tvar_free(void *ptr)
|
547
|
+
{
|
548
|
+
struct tvar_slot *slot = (struct tvar_slot *)ptr;
|
549
|
+
tx_slot_lock_free(&slot->lock);
|
550
|
+
ruby_xfree(slot);
|
551
|
+
}
|
552
|
+
|
553
|
+
static const rb_data_type_t tvar_data_type = {
|
554
|
+
"Thread::TVar",
|
555
|
+
{tvar_mark, tvar_free, NULL,},
|
556
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
557
|
+
};
|
558
|
+
|
559
|
+
static VALUE
|
560
|
+
tvar_new_(VALUE self, VALUE init)
|
561
|
+
{
|
562
|
+
// init should be shareable
|
563
|
+
if (RB_UNLIKELY(!rb_ractor_shareable_p(init))) {
|
564
|
+
rb_raise(rb_eArgError, "only shareable object are allowed");
|
565
|
+
}
|
566
|
+
|
567
|
+
struct tx_global *txg = tx_global_ptr();
|
568
|
+
struct tvar_slot *slot;
|
569
|
+
VALUE obj = TypedData_Make_Struct(rb_cRactorTVar, struct tvar_slot, &tvar_data_type, slot);
|
570
|
+
slot->version = 0;
|
571
|
+
slot->value = init;
|
572
|
+
slot->index = txg_next_index(txg);
|
573
|
+
tx_slot_lock_init(&slot->lock);
|
574
|
+
|
575
|
+
FL_SET_RAW(obj, RUBY_FL_SHAREABLE);
|
576
|
+
|
577
|
+
return obj;
|
578
|
+
}
|
579
|
+
|
580
|
+
static VALUE
|
581
|
+
tvar_new(int argc, VALUE *argv, VALUE self)
|
582
|
+
{
|
583
|
+
VALUE init = Qnil;
|
584
|
+
rb_scan_args(argc, argv, "01", &init);
|
585
|
+
return tvar_new_(self, init);
|
586
|
+
}
|
587
|
+
|
588
|
+
static VALUE
|
589
|
+
tvar_value(VALUE self)
|
590
|
+
{
|
591
|
+
struct tx_logs *tx = tx_logs();
|
592
|
+
struct tvar_slot *slot = DATA_PTR(self);
|
593
|
+
|
594
|
+
if (tx->enabled) {
|
595
|
+
return tx_get(tx, slot, self);
|
596
|
+
}
|
597
|
+
else {
|
598
|
+
// TODO: warn on multi-ractors?
|
599
|
+
return slot->value;
|
600
|
+
}
|
601
|
+
}
|
602
|
+
|
603
|
+
static VALUE
|
604
|
+
tvar_value_set(VALUE self, VALUE val)
|
605
|
+
{
|
606
|
+
if (RB_UNLIKELY(!rb_ractor_shareable_p(val))) {
|
607
|
+
rb_raise(rb_eArgError, "only shareable object are allowed");
|
608
|
+
}
|
609
|
+
|
610
|
+
struct tx_logs *tx = tx_logs();
|
611
|
+
tx_check(tx);
|
612
|
+
struct tvar_slot *slot = DATA_PTR(self);
|
613
|
+
tx_set(tx, val, slot, self);
|
614
|
+
return val;
|
615
|
+
}
|
616
|
+
|
617
|
+
static VALUE
|
618
|
+
tvar_calc_inc(VALUE v, VALUE inc)
|
619
|
+
{
|
620
|
+
if (RB_LIKELY(FIXNUM_P(v) && FIXNUM_P(inc))) {
|
621
|
+
return rb_fix_plus_fix(v, inc);
|
622
|
+
}
|
623
|
+
else {
|
624
|
+
return Qundef;
|
625
|
+
}
|
626
|
+
}
|
627
|
+
|
628
|
+
static VALUE
|
629
|
+
tvar_value_increment_(VALUE self, VALUE inc)
|
630
|
+
{
|
631
|
+
struct tx_global *txg = tx_global_ptr();
|
632
|
+
struct tx_logs *tx = tx_logs();
|
633
|
+
VALUE recv, ret;
|
634
|
+
struct tvar_slot *slot = DATA_PTR(self);
|
635
|
+
|
636
|
+
if (!tx->enabled) {
|
637
|
+
tx_slot_lock(slot);
|
638
|
+
{
|
639
|
+
uint64_t new_version = txg_next_version(txg);
|
640
|
+
recv = slot->value;
|
641
|
+
ret = tvar_calc_inc(recv, inc);
|
642
|
+
|
643
|
+
if (RB_LIKELY(ret != Qundef)) {
|
644
|
+
slot->value = ret;
|
645
|
+
slot->version = new_version;
|
646
|
+
txg->version = new_version;
|
647
|
+
}
|
648
|
+
}
|
649
|
+
tx_slot_unlock(slot);
|
650
|
+
|
651
|
+
if (RB_UNLIKELY(ret == Qundef)) {
|
652
|
+
// atomically{ self.value += inc }
|
653
|
+
ret = rb_funcall(self, rb_intern("__increment__"), 1, inc);
|
654
|
+
}
|
655
|
+
}
|
656
|
+
else {
|
657
|
+
recv = tx_get(tx, slot, self);
|
658
|
+
if (RB_UNLIKELY((ret = tvar_calc_inc(recv, inc)) == Qundef)) {
|
659
|
+
ret = rb_funcall(recv, rb_intern("+"), 1, inc);
|
660
|
+
}
|
661
|
+
tx_set(tx, ret, slot, self);
|
662
|
+
}
|
663
|
+
|
664
|
+
return ret;
|
665
|
+
}
|
666
|
+
|
667
|
+
static VALUE
|
668
|
+
tvar_value_increment(int argc, VALUE *argv, VALUE self)
|
669
|
+
{
|
670
|
+
switch (argc) {
|
671
|
+
case 0: return tvar_value_increment_(self, INT2FIX(1));
|
672
|
+
case 1: return tvar_value_increment_(self, argv[0]);
|
673
|
+
// todo: scan args
|
674
|
+
default: rb_raise(rb_eArgError, "2 or more arguments");
|
675
|
+
}
|
676
|
+
}
|
677
|
+
|
678
|
+
#if 0 // unused
|
679
|
+
static struct tvar_slot *
|
680
|
+
tvar_slot_ptr(VALUE v)
|
681
|
+
{
|
682
|
+
if (rb_typeddata_is_kind_of(v, &tvar_data_type)) {
|
683
|
+
return DATA_PTR(v);
|
684
|
+
}
|
685
|
+
else {
|
686
|
+
rb_raise(rb_eArgError, "TVar is needed");
|
687
|
+
}
|
688
|
+
}
|
689
|
+
#endif
|
690
|
+
|
691
|
+
static VALUE
|
692
|
+
tx_atomically_body2(VALUE txptr)
|
693
|
+
{
|
694
|
+
struct tx_logs *tx = (struct tx_logs *)txptr;
|
695
|
+
|
696
|
+
while (1) {
|
697
|
+
VALUE ret = rb_yield(Qnil);
|
698
|
+
|
699
|
+
if (tx_commit(tx)) {
|
700
|
+
return ret;
|
701
|
+
}
|
702
|
+
else {
|
703
|
+
tx_reset(tx);
|
704
|
+
}
|
705
|
+
}
|
706
|
+
}
|
707
|
+
|
708
|
+
static VALUE
|
709
|
+
tx_atomically_rescue(VALUE txptr, VALUE err)
|
710
|
+
{
|
711
|
+
struct tx_logs *tx = (struct tx_logs *)txptr;
|
712
|
+
tx_reset(tx);
|
713
|
+
return Qundef;
|
714
|
+
}
|
715
|
+
|
716
|
+
static VALUE
|
717
|
+
tx_atomically_body(VALUE txptr)
|
718
|
+
{
|
719
|
+
VALUE ret;
|
720
|
+
|
721
|
+
do {
|
722
|
+
ret = rb_rescue2(tx_atomically_body2, (VALUE)txptr,
|
723
|
+
tx_atomically_rescue, (VALUE)txptr,
|
724
|
+
rb_eTxRetry, 0);
|
725
|
+
} while (ret == Qundef);
|
726
|
+
|
727
|
+
return ret;
|
728
|
+
}
|
729
|
+
|
730
|
+
static VALUE
|
731
|
+
tx_atomically_ensure(VALUE txptr)
|
732
|
+
{
|
733
|
+
struct tx_logs *tx = (struct tx_logs *)txptr;
|
734
|
+
tx_end(tx);
|
735
|
+
return 0;
|
736
|
+
}
|
737
|
+
|
738
|
+
static VALUE
|
739
|
+
tx_atomically(VALUE self)
|
740
|
+
{
|
741
|
+
struct tx_logs *tx = tx_begin();
|
742
|
+
if (tx != NULL) {
|
743
|
+
return rb_ensure(tx_atomically_body, (VALUE)tx,
|
744
|
+
tx_atomically_ensure, (VALUE)tx);
|
745
|
+
}
|
746
|
+
else {
|
747
|
+
return rb_yield(Qnil);
|
748
|
+
}
|
749
|
+
}
|
750
|
+
|
751
|
+
void
|
752
|
+
Init_ractor_tvar(void)
|
753
|
+
{
|
754
|
+
// initialixe tx_global
|
755
|
+
struct tx_global *txg = tx_global_ptr();
|
756
|
+
txg->slot_index = 0;
|
757
|
+
txg->version = 0;
|
758
|
+
rb_native_mutex_initialize(&txg->slot_index_lock);
|
759
|
+
rb_native_mutex_initialize(&txg->version_lock);
|
760
|
+
|
761
|
+
id_tx_logs = rb_intern("__ractor_tvar_tls__");
|
762
|
+
|
763
|
+
// errors
|
764
|
+
rb_eTxError = rb_define_class_under(rb_cRactor, "TransactionError", rb_eRuntimeError);
|
765
|
+
rb_eTxRetry = rb_define_class_under(rb_cRactor, "RetryTransaction", rb_eException);
|
766
|
+
rb_exc_tx_retry = rb_exc_new_cstr(rb_eTxRetry, "Thread::RetryTransaction");
|
767
|
+
rb_obj_freeze(rb_exc_tx_retry);
|
768
|
+
rb_gc_register_mark_object(rb_exc_tx_retry);
|
769
|
+
|
770
|
+
// TxLogs
|
771
|
+
rb_cRactorTxLogs = rb_define_class_under(rb_cRactor, "TxLogs", rb_cObject); // hidden object
|
772
|
+
|
773
|
+
// TVar APIs
|
774
|
+
rb_define_singleton_method(rb_cRactor, "atomically", tx_atomically, 0);
|
775
|
+
|
776
|
+
rb_cRactorTVar = rb_define_class_under(rb_cRactor, "TVar", rb_cObject);
|
777
|
+
rb_define_singleton_method(rb_cRactorTVar, "new", tvar_new, -1);
|
778
|
+
rb_define_method(rb_cRactorTVar, "value", tvar_value, 0);
|
779
|
+
rb_define_method(rb_cRactorTVar, "value=", tvar_value_set, 1);
|
780
|
+
rb_define_method(rb_cRactorTVar, "increment", tvar_value_increment, -1);
|
781
|
+
// rb_define_method(rb_cRactorTVar, "inspect", tvar_inspect, 0);
|
782
|
+
}
|
data/lib/ractor/tvar.rb
ADDED
data/ractor-tvar.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/ractor/tvar/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "ractor-tvar"
|
7
|
+
spec.version = Ractor::TVar::VERSION
|
8
|
+
spec.authors = ["Koichi Sasada"]
|
9
|
+
spec.email = ["ko1@atdot.net"]
|
10
|
+
|
11
|
+
spec.summary = "Ractor::TVar"
|
12
|
+
spec.description = "Ractor::TVar"
|
13
|
+
spec.homepage = "https://github.com/ko1/ractor-tvar"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
16
|
+
spec.extensions = %w(ext/ractor_tvar/extconf.rb)
|
17
|
+
|
18
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
19
|
+
|
20
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
21
|
+
spec.metadata["source_code_uri"] = "https://github.com/ko1/ractor-tvar"
|
22
|
+
spec.metadata["changelog_uri"] = "https://github.com/ko1/ractor-tvar"
|
23
|
+
|
24
|
+
# Specify which files should be added to the gem when it is released.
|
25
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
26
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
27
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
28
|
+
end
|
29
|
+
spec.require_paths = ["lib"]
|
30
|
+
spec.add_development_dependency "rake"
|
31
|
+
spec.add_development_dependency "rake-compiler"
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ractor-tvar
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Koichi Sasada
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-11-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake-compiler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Ractor::TVar
|
42
|
+
email:
|
43
|
+
- ko1@atdot.net
|
44
|
+
executables: []
|
45
|
+
extensions:
|
46
|
+
- ext/ractor_tvar/extconf.rb
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".github/workflows/main.yml"
|
50
|
+
- ".gitignore"
|
51
|
+
- Gemfile
|
52
|
+
- LICENSE.txt
|
53
|
+
- README.md
|
54
|
+
- Rakefile
|
55
|
+
- bin/console
|
56
|
+
- bin/setup
|
57
|
+
- ext/ractor_tvar/Makefile
|
58
|
+
- ext/ractor_tvar/extconf.rb
|
59
|
+
- ext/ractor_tvar/ractor_tvar.c
|
60
|
+
- lib/ractor/tvar.rb
|
61
|
+
- lib/ractor/tvar/version.rb
|
62
|
+
- ractor-tvar.gemspec
|
63
|
+
homepage: https://github.com/ko1/ractor-tvar
|
64
|
+
licenses:
|
65
|
+
- MIT
|
66
|
+
metadata:
|
67
|
+
homepage_uri: https://github.com/ko1/ractor-tvar
|
68
|
+
source_code_uri: https://github.com/ko1/ractor-tvar
|
69
|
+
changelog_uri: https://github.com/ko1/ractor-tvar
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 2.3.0
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubygems_version: 3.2.0.rc.2
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Ractor::TVar
|
89
|
+
test_files: []
|