concurrent-ruby-edge 0.1.0.pre3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Gem Version](https://badge.fury.io/rb/concurrent-ruby.svg)](http://badge.fury.io/rb/concurrent-ruby) [![Build Status](https://travis-ci.org/ruby-concurrency/concurrent-ruby.svg?branch=master)](https://travis-ci.org/ruby-concurrency/concurrent-ruby) [![Code Climate](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby.svg)](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby) [![Inline docs](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby.svg)](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby) [![Dependency Status](https://gemnasium.com/ruby-concurrency/concurrent-ruby.svg)](https://gemnasium.com/ruby-concurrency/concurrent-ruby) [![License](https://img.shields.io/badge/license-MIT-green.svg)](http://opensource.org/licenses/MIT) [![Gitter chat](http://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/ruby-concurrency/concurrent-ruby)
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/concurrent-ruby.svg)](http://badge.fury.io/rb/concurrent-ruby) [![Build Status](https://travis-ci.org/ruby-concurrency/concurrent-ruby.svg?branch=master)](https://travis-ci.org/ruby-concurrency/concurrent-ruby) [![Build status](https://ci.appveyor.com/api/projects/status/iq8aboyuu3etad4w?svg=true)](https://ci.appveyor.com/project/rubyconcurrency/concurrent-ruby) [![Code Climate](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby.svg)](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby) [![Inline docs](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby.svg)](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby) [![Dependency Status](https://gemnasium.com/ruby-concurrency/concurrent-ruby.svg)](https://gemnasium.com/ruby-concurrency/concurrent-ruby) [![License](https://img.shields.io/badge/license-MIT-green.svg)](http://opensource.org/licenses/MIT) [![Gitter chat](http://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](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
|