concurrent-ruby-edge 0.1.0.pre3 → 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 +4 -4
- data/README.md +17 -10
- data/lib/concurrent-edge.rb +1 -0
- data/lib/concurrent/actor/behaviour.rb +1 -9
- data/lib/concurrent/actor/behaviour/termination.rb +23 -14
- data/lib/concurrent/actor/internal_delegations.rb +4 -4
- data/lib/concurrent/actor/utils/ad_hoc.rb +0 -1
- data/lib/concurrent/edge/future.rb +20 -9
- data/lib/concurrent/edge/lock_free_linked_set.rb +146 -0
- data/lib/concurrent/edge/lock_free_linked_set/node.rb +72 -0
- data/lib/concurrent/edge/lock_free_linked_set/window.rb +49 -0
- metadata +9 -7
- data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d078f232b2a8d30df70a0d008daeaee93884f58
|
4
|
+
data.tar.gz: 767e96651e435eb373a2751fb11bd45fc336ab51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 231af4d7c79ca4bba8ec44d262b893a62a1a935bf6345a55dae736e55dc524e1939c8ee869b6c6c1f727e8f42a8134f71392f6dcac2056e717975db991b9a58a
|
7
|
+
data.tar.gz: 5f1df67b0ba95a112067cbf69dfd3301c26e44430d97c6f7120ee30efad6a2261d7c52decdfa70c572ac567e7710f355c194b148b6f7944ea3d07a313ecbd5f1
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Concurrent Ruby
|
2
|
-
[](http://badge.fury.io/rb/concurrent-ruby) [](https://travis-ci.org/ruby-concurrency/concurrent-ruby) [](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby) [](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby) [](https://gemnasium.com/ruby-concurrency/concurrent-ruby) [](http://opensource.org/licenses/MIT) [](https://gitter.im/ruby-concurrency/concurrent-ruby)
|
2
|
+
[](http://badge.fury.io/rb/concurrent-ruby) [](https://travis-ci.org/ruby-concurrency/concurrent-ruby) [](https://ci.appveyor.com/project/rubyconcurrency/concurrent-ruby) [](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby) [](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby) [](https://gemnasium.com/ruby-concurrency/concurrent-ruby) [](http://opensource.org/licenses/MIT) [](https://gitter.im/ruby-concurrency/concurrent-ruby)
|
3
3
|
|
4
4
|
<table>
|
5
5
|
<tr>
|
@@ -39,6 +39,7 @@
|
|
39
39
|
|
40
40
|
MRI 1.9.3, 2.0, 2.1, 2.2, JRuby (1.9 mode), and Rubinius 2.x are supported.
|
41
41
|
This gem should be fully compatible with any interpreter that is compliant with Ruby 1.9.3 or newer.
|
42
|
+
Java 8 is required for JRuby (Java 7 support is deprecated in version 0.9 and will be removed in 1.0).
|
42
43
|
|
43
44
|
## Features & Documentation
|
44
45
|
|
@@ -127,14 +128,14 @@ be obeyed though. Features developed in `concurrent-ruby-edge` are expected to m
|
|
127
128
|
|
128
129
|
#### Statuses:
|
129
130
|
|
130
|
-
*Why
|
131
|
+
*Why are these not in core?*
|
131
132
|
|
132
|
-
- **Actor** -
|
133
|
-
- **Future/Promise Framework** - partial documentation and tests
|
134
|
-
- **Agent** -
|
135
|
-
- **Channel** -
|
136
|
-
- **Exchanger** -
|
137
|
-
- **LazyRegister** -
|
133
|
+
- **Actor** - Partial documentation and tests; stability is good.
|
134
|
+
- **Future/Promise Framework** - API changes; partial documentation and tests; stability good.
|
135
|
+
- **Agent** - Incomplete behaviour compared to Clojure's models; stability good.
|
136
|
+
- **Channel** - Missing documentation; limted features; stability good.
|
137
|
+
- **Exchanger** - Known race condition requiring a new implementation.
|
138
|
+
- **LazyRegister** - Missing documentation and tests.
|
138
139
|
|
139
140
|
## Usage
|
140
141
|
|
@@ -248,10 +249,16 @@ bundle exec rake build # Build JRuby-specific core gem (alias for `
|
|
248
249
|
bundle exec rake build:core # Build concurrent-ruby-<version>-java.gem into the pkg directory
|
249
250
|
|
250
251
|
*All except JRuby*
|
251
|
-
bundle exec rake build # Build core and extension gems
|
252
252
|
bundle exec rake build:core # Build concurrent-ruby-<version>.gem into the pkg directory
|
253
253
|
bundle exec rake build:ext # Build concurrent-ruby-ext-<version>.gem into the pkg directory
|
254
254
|
|
255
|
+
*When Docker IS installed*
|
256
|
+
bundle exec rake build:windows # Build the windows binary <version> gems per rake-compiler-dock
|
257
|
+
bundle exec rake build # Build core, extension, and edge gems, including Windows binaries
|
258
|
+
|
259
|
+
*When Docker is NOT installed*
|
260
|
+
bundle exec rake build # Build core, extension, and edge gems (excluding Windows binaries)
|
261
|
+
|
255
262
|
*All*
|
256
263
|
bundle exec rake clean # Remove any temporary products
|
257
264
|
bundle exec rake clobber # Remove any generated file
|
@@ -260,7 +267,7 @@ bundle exec rake compile # Compile all the extensions
|
|
260
267
|
|
261
268
|
## Maintainers
|
262
269
|
|
263
|
-
* [Jerry D'Antonio](https://github.com/jdantonio)
|
270
|
+
* [Jerry D'Antonio](https://github.com/jdantonio) (creator)
|
264
271
|
* [Michele Della Torre](https://github.com/mighe)
|
265
272
|
* [Chris Seaton](https://github.com/chrisseaton)
|
266
273
|
* [Lucas Allan](https://github.com/lucasallan)
|
data/lib/concurrent-edge.rb
CHANGED
@@ -37,10 +37,6 @@ module Concurrent
|
|
37
37
|
#
|
38
38
|
# > {include:Actor::Behaviour::Termination}
|
39
39
|
#
|
40
|
-
# - {Behaviour::TerminatesChildren}:
|
41
|
-
#
|
42
|
-
# > {include:Actor::Behaviour::TerminatesChildren}
|
43
|
-
#
|
44
40
|
# - {Behaviour::RemovesChild}:
|
45
41
|
#
|
46
42
|
# > {include:Actor::Behaviour::RemovesChild}
|
@@ -66,14 +62,12 @@ module Concurrent
|
|
66
62
|
require 'concurrent/actor/behaviour/sets_results'
|
67
63
|
require 'concurrent/actor/behaviour/supervising'
|
68
64
|
require 'concurrent/actor/behaviour/termination'
|
69
|
-
require 'concurrent/actor/behaviour/terminates_children'
|
70
65
|
|
71
66
|
# Array of behaviours and their construction parameters.
|
72
67
|
#
|
73
68
|
# [[Behaviour::SetResults, :terminate!],
|
74
69
|
# [Behaviour::RemovesChild],
|
75
70
|
# [Behaviour::Termination],
|
76
|
-
# [Behaviour::TerminatesChildren],
|
77
71
|
# [Behaviour::Linking],
|
78
72
|
# [Behaviour::Awaits],
|
79
73
|
# [Behaviour::ExecutesContext],
|
@@ -91,7 +85,6 @@ module Concurrent
|
|
91
85
|
# [[Behaviour::SetResults, :pause!],
|
92
86
|
# [Behaviour::RemovesChild],
|
93
87
|
# [Behaviour::Termination],
|
94
|
-
# [Behaviour::TerminatesChildren],
|
95
88
|
# [Behaviour::Linking],
|
96
89
|
# [Behaviour::Pausing],
|
97
90
|
# [Behaviour::Supervising, :reset!, :one_for_one],
|
@@ -113,8 +106,7 @@ module Concurrent
|
|
113
106
|
[[SetResults, on_error],
|
114
107
|
# has to be before Termination to be able to remove children from terminated actor
|
115
108
|
RemovesChild,
|
116
|
-
Termination
|
117
|
-
TerminatesChildren]
|
109
|
+
Termination]
|
118
110
|
end
|
119
111
|
|
120
112
|
# @see '' its source code
|
@@ -2,22 +2,22 @@ module Concurrent
|
|
2
2
|
module Actor
|
3
3
|
module Behaviour
|
4
4
|
|
5
|
-
# Handles actor termination.
|
5
|
+
# Handles actor termination. Waits until all its children are terminated,
|
6
|
+
# can be configured on behaviour initialization.
|
6
7
|
# @note Actor rejects envelopes when terminated.
|
7
8
|
# @note TODO missing example
|
8
9
|
class Termination < Abstract
|
9
10
|
|
10
11
|
# @!attribute [r] terminated
|
11
12
|
# @return [Edge::Event] event which will become set when actor is terminated.
|
12
|
-
|
13
|
-
attr_reader :terminated, :reason
|
13
|
+
attr_reader :terminated
|
14
14
|
|
15
|
-
def initialize(core, subsequent, core_options, trapping = false)
|
15
|
+
def initialize(core, subsequent, core_options, trapping = false, terminate_children = true)
|
16
16
|
super core, subsequent, core_options
|
17
|
-
@terminated
|
18
|
-
@public_terminated
|
19
|
-
@
|
20
|
-
@
|
17
|
+
@terminated = Concurrent.future
|
18
|
+
@public_terminated = @terminated.hide_completable
|
19
|
+
@trapping = trapping
|
20
|
+
@terminate_children = terminate_children
|
21
21
|
end
|
22
22
|
|
23
23
|
# @note Actor rejects envelopes when terminated.
|
@@ -43,7 +43,7 @@ module Concurrent
|
|
43
43
|
if trapping? && reason != :kill
|
44
44
|
pass envelope
|
45
45
|
else
|
46
|
-
terminate! reason
|
46
|
+
terminate! reason, envelope
|
47
47
|
end
|
48
48
|
when :termination_event
|
49
49
|
@public_terminated
|
@@ -59,14 +59,23 @@ module Concurrent
|
|
59
59
|
|
60
60
|
# Terminates the actor. Any Envelope received after termination is rejected.
|
61
61
|
# Terminates all its children, does not wait until they are terminated.
|
62
|
-
def terminate!(reason =
|
63
|
-
# TODO return after all children are terminated
|
62
|
+
def terminate!(reason = nil, envelope = nil)
|
64
63
|
return true if terminated?
|
65
|
-
|
66
|
-
|
64
|
+
|
65
|
+
self_termination = Concurrent.completed_future(reason.nil?, reason.nil? || nil, reason)
|
66
|
+
all_terminations = if @terminate_children
|
67
|
+
Concurrent.zip(*children.map { |ch| ch.ask(:terminate!) }, self_termination)
|
68
|
+
else
|
69
|
+
self_termination
|
70
|
+
end
|
71
|
+
|
72
|
+
all_terminations.chain_completable(@terminated)
|
73
|
+
all_terminations.chain_completable(envelope.future) if envelope && envelope.future
|
74
|
+
|
67
75
|
broadcast(true, [:terminated, reason]) # TODO do not end up in Dead Letter Router
|
68
76
|
parent << :remove_child if parent
|
69
|
-
|
77
|
+
|
78
|
+
MESSAGE_PROCESSED
|
70
79
|
end
|
71
80
|
end
|
72
81
|
end
|
@@ -18,10 +18,10 @@ module Concurrent
|
|
18
18
|
behaviour!(Behaviour::Termination).terminated?
|
19
19
|
end
|
20
20
|
|
21
|
-
# @see Termination#reason
|
22
|
-
def reason
|
23
|
-
|
24
|
-
end
|
21
|
+
# # @see Termination#reason
|
22
|
+
# def reason
|
23
|
+
# behaviour!(Behaviour::Termination).reason
|
24
|
+
# end
|
25
25
|
|
26
26
|
# delegates to core.log
|
27
27
|
# @see Logging#log
|
@@ -39,9 +39,19 @@ module Concurrent
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
# @return [Future] which is already completed
|
43
|
-
def completed_future(value, default_executor = :io)
|
44
|
-
ImmediateFuturePromise.new(default_executor, value).future
|
42
|
+
# @return [Future] which is already completed
|
43
|
+
def completed_future(success, value, reason, default_executor = :io)
|
44
|
+
ImmediateFuturePromise.new(default_executor, success, value, reason).future
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Future] which is already completed in success state with value
|
48
|
+
def succeeded_future(value, default_executor = :io)
|
49
|
+
completed_future true, value, nil, default_executor
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Future] which is already completed in failed state with reason
|
53
|
+
def failed_future(reason, default_executor = :io)
|
54
|
+
completed_future false, nil, reason, default_executor
|
45
55
|
end
|
46
56
|
|
47
57
|
# @return [Event] which is already completed
|
@@ -678,10 +688,10 @@ module Concurrent
|
|
678
688
|
|
679
689
|
alias_method :|, :any
|
680
690
|
|
681
|
-
# only proof of concept
|
682
691
|
# @note may block
|
692
|
+
# @note only proof of concept
|
683
693
|
def then_push(channel)
|
684
|
-
on_success { |value| channel.push value }
|
694
|
+
on_success(:io) { |value| channel.push value }
|
685
695
|
end
|
686
696
|
|
687
697
|
# @yield [value] executed async on `executor` when success
|
@@ -1057,7 +1067,7 @@ module Concurrent
|
|
1057
1067
|
evaluate_to lambda { done_future.apply task }
|
1058
1068
|
end
|
1059
1069
|
else
|
1060
|
-
complete_with
|
1070
|
+
complete_with done_future.internal_state
|
1061
1071
|
end
|
1062
1072
|
end
|
1063
1073
|
end
|
@@ -1104,8 +1114,9 @@ module Concurrent
|
|
1104
1114
|
|
1105
1115
|
# @!visibility private
|
1106
1116
|
class ImmediateFuturePromise < InnerPromise
|
1107
|
-
def initialize(default_executor, value)
|
1108
|
-
super Future.new(self, default_executor).
|
1117
|
+
def initialize(default_executor, success, value, reason)
|
1118
|
+
super Future.new(self, default_executor).
|
1119
|
+
complete_with(success ? Future::Success.new(value) : Future::Failed.new(reason))
|
1109
1120
|
end
|
1110
1121
|
end
|
1111
1122
|
|
@@ -1360,7 +1371,7 @@ module Concurrent
|
|
1360
1371
|
end
|
1361
1372
|
end
|
1362
1373
|
|
1363
|
-
# proof of concept
|
1374
|
+
# @note proof of concept
|
1364
1375
|
class Channel < Synchronization::Object
|
1365
1376
|
# TODO make lock free
|
1366
1377
|
def initialize
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'concurrent/edge/lock_free_linked_set/node'
|
2
|
+
require 'concurrent/edge/lock_free_linked_set/window'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
module Edge
|
6
|
+
# This class implements a lock-free linked set. The general idea of this
|
7
|
+
# implementation is this: each node has a successor which is an Atomic
|
8
|
+
# Markable Reference. This is used to ensure that all modifications to the
|
9
|
+
# list are atomic, preserving the structure of the linked list under _any_
|
10
|
+
# circumstance in a multithreaded application.
|
11
|
+
#
|
12
|
+
# One interesting aspect of this algorithm occurs with removing a node.
|
13
|
+
# Instead of physically removing a node when remove is called, a node is
|
14
|
+
# logically removed, by 'marking it.' By doing this, we prevent calls to
|
15
|
+
# `remove` from traversing the list twice to perform a physical removal.
|
16
|
+
# Instead, we have have calls to `add` and `remove` clean up all marked
|
17
|
+
# nodes they encounter while traversing the list.
|
18
|
+
#
|
19
|
+
# This algorithm is a variation of the Nonblocking Linked Set found in
|
20
|
+
# 'The Art of Multiprocessor Programming' by Herlihy and Shavit.
|
21
|
+
class LockFreeLinkedSet
|
22
|
+
include Enumerable
|
23
|
+
|
24
|
+
# @!macro [attach] lock_free_linked_list_method_initialize
|
25
|
+
#
|
26
|
+
# @param [Fixnum] initial_size the size of the linked_list to initialize
|
27
|
+
def initialize(initial_size = 0, val = nil)
|
28
|
+
@head = Head.new
|
29
|
+
|
30
|
+
initial_size.times do
|
31
|
+
val = block_given? ? yield : val
|
32
|
+
add val
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @!macro [attach] lock_free_linked_list_method_add
|
37
|
+
#
|
38
|
+
# Atomically adds the item to the set if it does not yet exist. Note:
|
39
|
+
# internally the set uses `Object#hash` to compare equality of items,
|
40
|
+
# meaning that Strings and other objects will be considered equal
|
41
|
+
# despite being different objects.
|
42
|
+
#
|
43
|
+
# @param [Object] item the item you wish to insert
|
44
|
+
#
|
45
|
+
# @return [Boolean] `true` if successful. A `false` return indicates
|
46
|
+
# that the item was already in the set.
|
47
|
+
def add(item)
|
48
|
+
loop do
|
49
|
+
window = Window.find @head, item
|
50
|
+
|
51
|
+
pred, curr = window.pred, window.curr
|
52
|
+
|
53
|
+
# Item already in set
|
54
|
+
return false if curr == item
|
55
|
+
|
56
|
+
node = Node.new item, curr
|
57
|
+
|
58
|
+
if pred.Successor_reference.compare_and_set curr, node, false, false
|
59
|
+
return true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @!macro [attach] lock_free_linked_list_method_<<
|
65
|
+
#
|
66
|
+
# Atomically adds the item to the set if it does not yet exist.
|
67
|
+
#
|
68
|
+
# @param [Object] item the item you wish to insert
|
69
|
+
#
|
70
|
+
# @return [Oject] the set on which the :<< method was invoked
|
71
|
+
def <<(item)
|
72
|
+
add item
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
# @!macro [attach] lock_free_linked_list_method_contains
|
77
|
+
#
|
78
|
+
# Atomically checks to see if the set contains an item. This method
|
79
|
+
# compares equality based on the `Object#hash` method, meaning that the
|
80
|
+
# hashed contents of an object is what determines equality instead of
|
81
|
+
# `Object#object_id`
|
82
|
+
#
|
83
|
+
# @param [Object] item the item you to check for presence in the set
|
84
|
+
#
|
85
|
+
# @return [Boolean] whether or not the item is in the set
|
86
|
+
def contains?(item)
|
87
|
+
curr = @head
|
88
|
+
|
89
|
+
while curr < item
|
90
|
+
curr = curr.next_node
|
91
|
+
marked = curr.Successor_reference.marked?
|
92
|
+
end
|
93
|
+
|
94
|
+
curr == item && !marked
|
95
|
+
end
|
96
|
+
|
97
|
+
# @!macro [attach] lock_free_linked_list_method_remove
|
98
|
+
#
|
99
|
+
# Atomically attempts to remove an item, comparing using `Object#hash`.
|
100
|
+
#
|
101
|
+
# @param [Object] item the item you to remove from the set
|
102
|
+
#
|
103
|
+
# @return [Boolean] whether or not the item was removed from the set
|
104
|
+
def remove(item)
|
105
|
+
loop do
|
106
|
+
window = Window.find @head, item
|
107
|
+
pred, curr = window.pred, window.curr
|
108
|
+
|
109
|
+
return false if curr != item
|
110
|
+
|
111
|
+
succ = curr.next_node
|
112
|
+
removed = curr.Successor_reference.compare_and_set succ, succ, false, true
|
113
|
+
|
114
|
+
next_node unless removed
|
115
|
+
|
116
|
+
pred.Successor_reference.compare_and_set curr, succ, false, false
|
117
|
+
|
118
|
+
return true
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# @!macro [attach] lock_free_linked_list_method_each
|
123
|
+
#
|
124
|
+
# An iterator to loop through the set.
|
125
|
+
#
|
126
|
+
# @param [Object] item the item you to remove from the set
|
127
|
+
# @yeild [Object] each item in the set
|
128
|
+
#
|
129
|
+
# @return [Object] self: the linked set on which each was called
|
130
|
+
def each
|
131
|
+
return to_enum unless block_given?
|
132
|
+
|
133
|
+
curr = @head
|
134
|
+
|
135
|
+
until curr.last?
|
136
|
+
curr = curr.next_node
|
137
|
+
marked = curr.Successor_reference.marked?
|
138
|
+
|
139
|
+
yield curr.Data unless marked
|
140
|
+
end
|
141
|
+
|
142
|
+
self
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'concurrent/edge/atomic_markable_reference'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
module Edge
|
5
|
+
class LockFreeLinkedSet
|
6
|
+
class Node < Synchronization::Object
|
7
|
+
include Comparable
|
8
|
+
|
9
|
+
attr_reader :Data, :Successor_reference, :Key
|
10
|
+
|
11
|
+
def initialize(data = nil, successor = nil)
|
12
|
+
super()
|
13
|
+
|
14
|
+
@Successor_reference = AtomicMarkableReference.new(successor || Tail.new)
|
15
|
+
@Data = data
|
16
|
+
@Key = key_for data
|
17
|
+
|
18
|
+
ensure_ivar_visibility!
|
19
|
+
end
|
20
|
+
|
21
|
+
# Check to see if the node is the last in the list.
|
22
|
+
def last?
|
23
|
+
@Successor_reference.value.is_a? Tail
|
24
|
+
end
|
25
|
+
|
26
|
+
# Next node in the list. Note: this is not the AtomicMarkableReference
|
27
|
+
# of the next node, this is the actual Node itself.
|
28
|
+
def next_node
|
29
|
+
@Successor_reference.value
|
30
|
+
end
|
31
|
+
|
32
|
+
# This method provides a unqiue key for the data which will be used for
|
33
|
+
# ordering. This is configurable, and changes depending on how you wish
|
34
|
+
# the nodes to be ordered.
|
35
|
+
def key_for(data)
|
36
|
+
data.hash
|
37
|
+
end
|
38
|
+
|
39
|
+
# We use `Object#hash` as a way to enforce ordering on the nodes. This
|
40
|
+
# can be configurable in the future; for example, you could enforce a
|
41
|
+
# split-ordering on the nodes in the set.
|
42
|
+
def <=>(other)
|
43
|
+
@Key <=> other.hash
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Internal sentinel node for the Tail. It is always greater than all
|
48
|
+
# other nodes, and it is self-referential; meaning its successor is
|
49
|
+
# a self-loop.
|
50
|
+
class Tail < Node
|
51
|
+
def initialize(_data = nil, _succ = nil)
|
52
|
+
@Successor_reference = AtomicMarkableReference.new self
|
53
|
+
end
|
54
|
+
|
55
|
+
# Always greater than other nodes. This means that traversal will end
|
56
|
+
# at the tail node since we are comparing node size in the traversal.
|
57
|
+
def <=>(_other)
|
58
|
+
1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# Internal sentinel node for the Head of the set. Head is always smaller
|
64
|
+
# than any other node.
|
65
|
+
class Head < Node
|
66
|
+
def <=>(_other)
|
67
|
+
-1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Edge
|
3
|
+
class LockFreeLinkedSet
|
4
|
+
class Window
|
5
|
+
attr_accessor :pred, :curr
|
6
|
+
|
7
|
+
def initialize(pred, curr)
|
8
|
+
@pred, @curr = pred, curr
|
9
|
+
end
|
10
|
+
|
11
|
+
# This method is used to find a 'window' for which `add` and `remove`
|
12
|
+
# methods can use to know where to add and remove from the list. However,
|
13
|
+
# it has another responsibilility, which is to physically unlink any
|
14
|
+
# nodes marked for removal in the set. This prevents adds/removes from
|
15
|
+
# having to retraverse the list to physically unlink nodes.
|
16
|
+
def self.find(head, item)
|
17
|
+
loop do
|
18
|
+
break_inner_loops = false
|
19
|
+
pred = head
|
20
|
+
curr = pred.next_node
|
21
|
+
|
22
|
+
loop do
|
23
|
+
succ, marked = curr.Successor_reference.get
|
24
|
+
|
25
|
+
# Remove sequence of marked nodes
|
26
|
+
while marked
|
27
|
+
removed = pred.Successor_reference.compare_and_set curr, succ, false, false
|
28
|
+
|
29
|
+
# If could not remove node, try again
|
30
|
+
break_inner_loops = true && break unless removed
|
31
|
+
|
32
|
+
curr = succ
|
33
|
+
succ, marked = curr.Successor_reference.get
|
34
|
+
end
|
35
|
+
|
36
|
+
break if break_inner_loops
|
37
|
+
|
38
|
+
# We have found a window
|
39
|
+
return new pred, curr if curr >= item
|
40
|
+
|
41
|
+
pred = curr
|
42
|
+
curr = succ
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concurrent-ruby-edge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jerry D'Antonio
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2015-
|
13
|
+
date: 2015-07-10 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: concurrent-ruby
|
@@ -18,14 +18,14 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - "~>"
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.9.0
|
21
|
+
version: 0.9.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
26
|
- - "~>"
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: 0.9.0
|
28
|
+
version: 0.9.0
|
29
29
|
description: |
|
30
30
|
These features are under active development and may change frequently. They are expected not to
|
31
31
|
keep backward compatibility (there may also lack tests and documentation). Semantic versions will
|
@@ -55,7 +55,6 @@ files:
|
|
55
55
|
- lib/concurrent/actor/behaviour/removes_child.rb
|
56
56
|
- lib/concurrent/actor/behaviour/sets_results.rb
|
57
57
|
- lib/concurrent/actor/behaviour/supervising.rb
|
58
|
-
- lib/concurrent/actor/behaviour/terminates_children.rb
|
59
58
|
- lib/concurrent/actor/behaviour/termination.rb
|
60
59
|
- lib/concurrent/actor/context.rb
|
61
60
|
- lib/concurrent/actor/core.rb
|
@@ -82,6 +81,9 @@ files:
|
|
82
81
|
- lib/concurrent/channel/waitable_list.rb
|
83
82
|
- lib/concurrent/edge/atomic_markable_reference.rb
|
84
83
|
- lib/concurrent/edge/future.rb
|
84
|
+
- lib/concurrent/edge/lock_free_linked_set.rb
|
85
|
+
- lib/concurrent/edge/lock_free_linked_set/node.rb
|
86
|
+
- lib/concurrent/edge/lock_free_linked_set/window.rb
|
85
87
|
- lib/concurrent/edge/lock_free_stack.rb
|
86
88
|
homepage: http://www.concurrent-ruby.com
|
87
89
|
licenses:
|
@@ -98,9 +100,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
98
100
|
version: 1.9.3
|
99
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
102
|
requirements:
|
101
|
-
- - "
|
103
|
+
- - ">="
|
102
104
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
105
|
+
version: '0'
|
104
106
|
requirements: []
|
105
107
|
rubyforge_project:
|
106
108
|
rubygems_version: 2.4.8
|
@@ -1,14 +0,0 @@
|
|
1
|
-
module Concurrent
|
2
|
-
module Actor
|
3
|
-
module Behaviour
|
4
|
-
# Terminates all children when the actor terminates.
|
5
|
-
class TerminatesChildren < Abstract
|
6
|
-
def on_event(public, event)
|
7
|
-
event_name, _ = event
|
8
|
-
children.map { |ch| ch << :terminate! } if event_name == :terminated
|
9
|
-
super public, event
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|