1
+ require 'set'
2
+
1
3
require 'concurrent/threadlocalvar'
2
4
3
5
module Concurrent
@@ -6,12 +8,15 @@ module Concurrent
6
8
7
9
CURRENT_TRANSACTION = ThreadLocalVar . new ( nil )
8
10
11
+ ReadLogEntry = Struct . new ( :tvar , :version )
9
12
UndoLogEntry = Struct . new ( :tvar , :value )
10
13
11
14
class TVar
12
15
13
16
def initialize ( value )
14
17
@value = value
18
+ @version = 0
19
+ @lock = Mutex . new
15
20
end
16
21
17
22
def value
@@ -34,25 +39,64 @@ def unsafe_value=(value)
34
39
@value = value
35
40
end
36
41
42
+ def unsafe_version
43
+ @version
44
+ end
45
+
46
+ def unsafe_increment_version
47
+ @version += 1
48
+ end
49
+
50
+ def unsafe_lock
51
+ @lock
52
+ end
53
+
37
54
end
38
55
39
56
class Transaction
40
57
41
- LOCK = Mutex . new
42
-
43
58
def initialize
59
+ @write_set = Set . new
60
+ @read_log = [ ]
44
61
@undo_log = [ ]
45
-
46
- LOCK . lock
47
62
end
48
63
49
64
def read ( tvar )
50
- validate
65
+ Concurrent ::abort_transaction unless valid?
66
+ @read_log . push ( ReadLogEntry . new ( tvar , tvar . unsafe_version ) )
51
67
tvar . unsafe_value
52
68
end
53
69
54
70
def write ( tvar , value )
71
+ # Have we already written to this TVar?
72
+
73
+ unless @write_set . include? tvar
74
+ # Try to lock the TVar
75
+
76
+ unless tvar . unsafe_lock . try_lock
77
+ # Someone else is writing to this TVar - abort
78
+ Concurrent ::abort_transaction
79
+ end
80
+
81
+ # We've locked it - add it to the write set
82
+
83
+ @write_set . add ( tvar )
84
+
85
+ # If we previously wrote to it, check the version hasn't changed
86
+
87
+ @read_log . each do |log_entry |
88
+ if log_entry . tvar == tvar and tvar . unsafe_version > log_entry . version
89
+ Concurrent ::abort_transaction
90
+ end
91
+ end
92
+ end
93
+
94
+ # Record the current value of the TVar so we can undo it later
95
+
55
96
@undo_log . push ( UndoLogEntry . new ( tvar , tvar . unsafe_value ) )
97
+
98
+ # Write the new value to the TVar
99
+
56
100
tvar . unsafe_value = value
57
101
end
58
102
@@ -65,16 +109,33 @@ def abort
65
109
end
66
110
67
111
def commit
68
- validate
112
+ return false unless valid?
113
+
114
+ @write_set . each do |tvar |
115
+ tvar . unsafe_increment_version
116
+ end
117
+
69
118
unlock
119
+
70
120
true
71
121
end
72
122
73
- def validate
123
+ def valid?
124
+ @read_log . each do |log_entry |
125
+ unless @write_set . include? log_entry . tvar
126
+ if log_entry . tvar . unsafe_version > log_entry . version
127
+ return false
128
+ end
129
+ end
130
+ end
131
+
132
+ true
74
133
end
75
134
76
135
def unlock
77
- LOCK . unlock
136
+ @write_set . each do |tvar |
137
+ tvar . unsafe_lock . unlock
138
+ end
78
139
end
79
140
80
141
def self . current
0 commit comments