Skip to content

Commit ae1dcc7

Browse files
committed
Merge master into tvars.
2 parents 810646b + 7fe65a9 commit ae1dcc7

24 files changed

+1231
-743
lines changed

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ rvm:
88
- jruby-19mode
99
- jruby-head
1010
- rbx-2
11+
branches:
12+
only:
13+
- master
14+
- tvars
1115
matrix:
1216
allow_failures:
1317
- rvm: ruby-head

Gemfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ gemspec
44

55
group :development do
66
gem 'rake'
7-
gem 'eventmachine'
87
gem 'countloc', :platforms => :mri
98
end
109

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ The design goals of this gem are:
3535
agent in [F#](http://msdn.microsoft.com/en-us/library/ee370357.aspx)
3636
* [Dataflow](https://github.com/jdantonio/concurrent-ruby/blob/master/md/dataflow.md) loosely based on the syntax of Akka and Habanero Java
3737
* [MVar](https://github.com/jdantonio/concurrent-ruby/blob/master/md/mvar.md) inspired by Haskell
38+
* [Thread local variables](https://github.com/jdantonio/concurrent-ruby/blob/master/md/threadlocalvar.md) with default value
3839

3940
### Semantic Versioning
4041

lib/concurrent.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
require 'concurrent/version'
22

3+
require 'concurrent/atomic_counter'
34
require 'concurrent/count_down_latch'
5+
require 'concurrent/condition'
46
require 'concurrent/copy_on_notify_observer_set'
57
require 'concurrent/copy_on_write_observer_set'
68
require 'concurrent/safe_task_executor'
@@ -21,6 +23,7 @@
2123
require 'concurrent/scheduled_task'
2224
require 'concurrent/stoppable'
2325
require 'concurrent/supervisor'
26+
require 'concurrent/threadlocalvar'
2427
require 'concurrent/timer_task'
2528
require 'concurrent/tvar'
2629
require 'concurrent/utilities'
@@ -31,8 +34,6 @@
3134
require 'concurrent/fixed_thread_pool'
3235
require 'concurrent/immediate_executor'
3336

34-
require 'concurrent/event_machine_defer_proxy' if defined?(EventMachine)
35-
3637
# Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell,
3738
# F#, C#, Java, and classic concurrency patterns.
3839
#

lib/concurrent/atomic_counter.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
module Concurrent
2+
3+
class AtomicCounter
4+
5+
def initialize(init = 0)
6+
raise ArgumentError.new('initial value must be an integer') unless init.is_a?(Integer)
7+
@counter = init
8+
@mutex = Mutex.new
9+
end
10+
11+
def value
12+
@mutex.synchronize do
13+
@counter
14+
end
15+
end
16+
17+
def increment
18+
@mutex.synchronize do
19+
@counter += 1
20+
end
21+
end
22+
alias_method :up, :increment
23+
24+
def decrement
25+
@mutex.synchronize do
26+
@counter -= 1
27+
end
28+
end
29+
alias_method :down, :decrement
30+
end
31+
end

lib/concurrent/condition.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
module Concurrent
2+
class Condition
3+
4+
class Result
5+
def initialize(remaining_time)
6+
@remaining_time = remaining_time
7+
end
8+
9+
attr_reader :remaining_time
10+
11+
def woken_up?
12+
@remaining_time.nil? || @remaining_time > 0
13+
end
14+
15+
def timed_out?
16+
@remaining_time != nil && @remaining_time <= 0
17+
end
18+
19+
alias_method :can_wait?, :woken_up?
20+
21+
end
22+
23+
def initialize
24+
@condition = ConditionVariable.new
25+
end
26+
27+
def wait(mutex, timeout = nil)
28+
start_time = Time.now.to_f
29+
@condition.wait(mutex, timeout)
30+
31+
if timeout.nil?
32+
Result.new(nil)
33+
else
34+
Result.new(start_time + timeout - Time.now.to_f)
35+
end
36+
end
37+
38+
def signal
39+
@condition.signal
40+
true
41+
end
42+
43+
def broadcast
44+
@condition.broadcast
45+
true
46+
end
47+
48+
end
49+
end

lib/concurrent/dataflow.rb

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,8 @@
1+
require 'concurrent/atomic_counter'
12
require 'concurrent/future'
23

34
module Concurrent
45

5-
class AtomicCounter
6-
7-
def initialize(init)
8-
@counter = init
9-
@mutex = Mutex.new
10-
end
11-
12-
def decrement
13-
@mutex.synchronize do
14-
@counter -= 1
15-
end
16-
end
17-
end
18-
196
class DependencyCounter
207

218
def initialize(count, &block)

lib/concurrent/event.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'thread'
22
require 'concurrent/utilities'
3+
require 'concurrent/condition'
34

45
module Concurrent
56

@@ -20,7 +21,7 @@ class Event
2021
def initialize
2122
@set = false
2223
@mutex = Mutex.new
23-
@condition = ConditionVariable.new
24+
@condition = Condition.new
2425
end
2526

2627
# Is the object in the set state?
@@ -66,7 +67,12 @@ def reset
6667
def wait(timeout = nil)
6768
@mutex.synchronize do
6869
return true if @set
69-
@condition.wait(@mutex, timeout)
70+
71+
remaining = Condition::Result.new(timeout)
72+
while !@set && remaining.can_wait?
73+
remaining = @condition.wait(@mutex, remaining.remaining_time)
74+
end
75+
7076
@set
7177
end
7278
end

lib/concurrent/event_machine_defer_proxy.rb

Lines changed: 0 additions & 22 deletions
This file was deleted.

lib/concurrent/mvar.rb

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,17 @@ class MVar
1212
def initialize(value = EMPTY, opts = {})
1313
@value = value
1414
@mutex = Mutex.new
15-
@empty_condition = ConditionVariable.new
16-
@full_condition = ConditionVariable.new
15+
@empty_condition = Condition.new
16+
@full_condition = Condition.new
1717
set_deref_options(opts)
1818
end
1919

2020
def take(timeout = nil)
2121
@mutex.synchronize do
22-
# If the value isn't empty, wait for full to be signalled
23-
@full_condition.wait(@mutex, timeout) if empty?
22+
wait_for_full(timeout)
2423

2524
# If we timed out we'll still be empty
26-
if full?
25+
if unlocked_full?
2726
value = @value
2827
@value = EMPTY
2928
@empty_condition.signal
@@ -36,11 +35,10 @@ def take(timeout = nil)
3635

3736
def put(value, timeout = nil)
3837
@mutex.synchronize do
39-
# Unless the value is empty, wait for empty to be signalled
40-
@empty_condition.wait(@mutex, timeout) if full?
38+
wait_for_empty(timeout)
4139

4240
# If we timed out we won't be empty
43-
if empty?
41+
if unlocked_empty?
4442
@value = value
4543
@full_condition.signal
4644
apply_deref_options(value)
@@ -54,11 +52,10 @@ def modify(timeout = nil)
5452
raise ArgumentError.new('no block given') unless block_given?
5553

5654
@mutex.synchronize do
57-
# If the value isn't empty, wait for full to be signalled
58-
@full_condition.wait(@mutex, timeout) if empty?
55+
wait_for_full(timeout)
5956

6057
# If we timed out we'll still be empty
61-
if full?
58+
if unlocked_full?
6259
value = @value
6360
@value = yield value
6461
@full_condition.signal
@@ -71,7 +68,7 @@ def modify(timeout = nil)
7168

7269
def try_take!
7370
@mutex.synchronize do
74-
if full?
71+
if unlocked_full?
7572
value = @value
7673
@value = EMPTY
7774
@empty_condition.signal
@@ -84,7 +81,7 @@ def try_take!
8481

8582
def try_put!(value)
8683
@mutex.synchronize do
87-
if empty?
84+
if unlocked_empty?
8885
@value = value
8986
@full_condition.signal
9087
true
@@ -103,13 +100,13 @@ def set!(value)
103100
end
104101
end
105102

106-
def modify!(timeout = nil)
103+
def modify!
107104
raise ArgumentError.new('no block given') unless block_given?
108105

109106
@mutex.synchronize do
110107
value = @value
111108
@value = yield value
112-
if @value == EMPTY
109+
if unlocked_empty?
113110
@empty_condition.signal
114111
else
115112
@full_condition.signal
@@ -119,13 +116,37 @@ def modify!(timeout = nil)
119116
end
120117

121118
def empty?
122-
@value == EMPTY
119+
@mutex.synchronize { @value == EMPTY }
123120
end
124121

125122
def full?
126123
not empty?
127124
end
128125

126+
private
127+
128+
def unlocked_empty?
129+
@value == EMPTY
130+
end
131+
132+
def unlocked_full?
133+
! unlocked_empty?
134+
end
135+
136+
def wait_for_full(timeout)
137+
remaining = Condition::Result.new(timeout)
138+
while unlocked_empty? && remaining.can_wait?
139+
remaining = @full_condition.wait(@mutex, remaining.remaining_time)
140+
end
141+
end
142+
143+
def wait_for_empty(timeout)
144+
remaining = Condition::Result.new(timeout)
145+
while unlocked_full? && remaining.can_wait?
146+
remaining = @empty_condition.wait(@mutex, remaining.remaining_time)
147+
end
148+
end
149+
129150
end
130151

131152
end

0 commit comments

Comments
 (0)