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.
@@ -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
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in ractor-tvar.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "test-unit", "~> 3.0"
@@ -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.
@@ -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).
@@ -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
@@ -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__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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,2 @@
1
+ require 'mkmf'
2
+ create_makefile('ractor_tvar')
@@ -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
+ }
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ractor/tvar/version"
4
+ require 'ractor/tvar/ractor_tvar.so'
5
+
6
+ class Ractor
7
+ class TVar
8
+ def __increment__ inc
9
+ Ractor::atomically do
10
+ self.value += inc
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Ractor
4
+ class TVar
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -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: []