rx 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitattributes +22 -0
- data/.gitignore +173 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/Rakefile +11 -0
- data/examples/aggregate.rb +39 -0
- data/examples/amb.rb +25 -0
- data/examples/ambproto.rb +24 -0
- data/examples/and.rb +26 -0
- data/examples/as_observable.rb +25 -0
- data/examples/average.rb +43 -0
- data/examples/buffer_with_count.rb +44 -0
- data/examples/buffer_with_time.rb +51 -0
- data/examples/case.rb +29 -0
- data/examples/catch.rb +20 -0
- data/examples/catchproto.rb +39 -0
- data/examples/combine_latest.rb +35 -0
- data/examples/combine_latestproto.rb +33 -0
- data/examples/concat.rb +22 -0
- data/examples/concat_all.rb +27 -0
- data/examples/concat_map.rb +61 -0
- data/examples/concat_map_observer.rb +29 -0
- data/examples/concatproto.rb +25 -0
- data/examples/connect.rb +41 -0
- data/examples/contains.rb +37 -0
- data/examples/count.rb +36 -0
- data/examples/create.rb +55 -0
- data/examples/debounce.rb +35 -0
- data/examples/default_if_empty.rb +35 -0
- data/examples/defer.rb +20 -0
- data/examples/delay.rb +49 -0
- data/examples/delay_with_selector.rb +63 -0
- data/examples/dematerialize.rb +22 -0
- data/examples/disposable.rb +12 -0
- data/examples/distinct.rb +43 -0
- data/examples/distinct_until_changed.rb +43 -0
- data/examples/do.rb +59 -0
- data/examples/empty.rb +16 -0
- data/examples/for.rb +26 -0
- data/examples/fork_join.rb +23 -0
- data/examples/from.rb +106 -0
- data/examples/from_array.rb +21 -0
- data/examples/from_callback.rb +21 -0
- data/examples/generate.rb +24 -0
- data/examples/group_join.rb +39 -0
- data/examples/if.rb +46 -0
- data/examples/intervals.rb +26 -0
- data/examples/merge.rb +36 -0
- data/examples/merge_all.rb +27 -0
- data/examples/multicast.rb +32 -0
- data/examples/never.rb +15 -0
- data/examples/of.rb +19 -0
- data/examples/on_error_resume_next.rb +21 -0
- data/examples/pairs.rb +26 -0
- data/examples/publish.rb +79 -0
- data/examples/range.rb +19 -0
- data/examples/reduce.rb +18 -0
- data/examples/repeat.rb +19 -0
- data/examples/return.rb +17 -0
- data/examples/scan.rb +41 -0
- data/examples/start.rb +29 -0
- data/examples/throw.rb +17 -0
- data/examples/time_intervals.rb +28 -0
- data/examples/timer.rb +26 -0
- data/examples/timestamp.rb +28 -0
- data/examples/to_a.rb +23 -0
- data/examples/to_async.rb +26 -0
- data/examples/using.rb +52 -0
- data/examples/when.rb +26 -0
- data/examples/while.rb +25 -0
- data/examples/window_with_time.rb +78 -0
- data/examples/zip.rb +27 -0
- data/examples/zip_array.rb +25 -0
- data/lib/core_ext/enumerable.rb +22 -0
- data/lib/rx_ruby.rb +27 -0
- data/lib/rx_ruby/concurrency/async_lock.rb +57 -0
- data/lib/rx_ruby/concurrency/current_thread_scheduler.rb +75 -0
- data/lib/rx_ruby/concurrency/default_scheduler.rb +51 -0
- data/lib/rx_ruby/concurrency/historical_scheduler.rb +16 -0
- data/lib/rx_ruby/concurrency/immediate_scheduler.rb +68 -0
- data/lib/rx_ruby/concurrency/local_scheduler.rb +39 -0
- data/lib/rx_ruby/concurrency/periodic_scheduler.rb +74 -0
- data/lib/rx_ruby/concurrency/scheduled_item.rb +42 -0
- data/lib/rx_ruby/concurrency/scheduler.rb +150 -0
- data/lib/rx_ruby/concurrency/virtual_time_scheduler.rb +170 -0
- data/lib/rx_ruby/core/async_lock_observer.rb +46 -0
- data/lib/rx_ruby/core/auto_detach_observer.rb +59 -0
- data/lib/rx_ruby/core/checked_observer.rb +66 -0
- data/lib/rx_ruby/core/notification.rb +161 -0
- data/lib/rx_ruby/core/observable.rb +104 -0
- data/lib/rx_ruby/core/observe_on_observer.rb +50 -0
- data/lib/rx_ruby/core/observer.rb +119 -0
- data/lib/rx_ruby/core/scheduled_observer.rb +83 -0
- data/lib/rx_ruby/core/synchronized_observer.rb +47 -0
- data/lib/rx_ruby/core/time_interval.rb +17 -0
- data/lib/rx_ruby/internal/priority_queue.rb +122 -0
- data/lib/rx_ruby/internal/util.rb +9 -0
- data/lib/rx_ruby/joins/active_plan.rb +45 -0
- data/lib/rx_ruby/joins/join_observer.rb +51 -0
- data/lib/rx_ruby/joins/pattern.rb +14 -0
- data/lib/rx_ruby/joins/plan.rb +44 -0
- data/lib/rx_ruby/linq/connectable_observable.rb +34 -0
- data/lib/rx_ruby/linq/observable/_observable_timer_date_and_period.rb +22 -0
- data/lib/rx_ruby/linq/observable/_observable_timer_time_span.rb +14 -0
- data/lib/rx_ruby/linq/observable/_observable_timer_time_span_and_period.rb +20 -0
- data/lib/rx_ruby/linq/observable/aggregate.rb +7 -0
- data/lib/rx_ruby/linq/observable/and.rb +7 -0
- data/lib/rx_ruby/linq/observable/case.rb +15 -0
- data/lib/rx_ruby/linq/observable/concat_all.rb +7 -0
- data/lib/rx_ruby/linq/observable/concat_map.rb +35 -0
- data/lib/rx_ruby/linq/observable/concat_map_observer.rb +43 -0
- data/lib/rx_ruby/linq/observable/contains.rb +28 -0
- data/lib/rx_ruby/linq/observable/debounce.rb +41 -0
- data/lib/rx_ruby/linq/observable/delay.rb +81 -0
- data/lib/rx_ruby/linq/observable/delay_with_selector.rb +64 -0
- data/lib/rx_ruby/linq/observable/do.rb +42 -0
- data/lib/rx_ruby/linq/observable/for.rb +13 -0
- data/lib/rx_ruby/linq/observable/fork_join.rb +55 -0
- data/lib/rx_ruby/linq/observable/from.rb +34 -0
- data/lib/rx_ruby/linq/observable/group_join.rb +108 -0
- data/lib/rx_ruby/linq/observable/if.rb +17 -0
- data/lib/rx_ruby/linq/observable/interval.rb +5 -0
- data/lib/rx_ruby/linq/observable/multicast.rb +14 -0
- data/lib/rx_ruby/linq/observable/of.rb +11 -0
- data/lib/rx_ruby/linq/observable/pairs.rb +7 -0
- data/lib/rx_ruby/linq/observable/pluck.rb +7 -0
- data/lib/rx_ruby/linq/observable/publish.rb +11 -0
- data/lib/rx_ruby/linq/observable/start.rb +7 -0
- data/lib/rx_ruby/linq/observable/time_interval.rb +15 -0
- data/lib/rx_ruby/linq/observable/timer.rb +26 -0
- data/lib/rx_ruby/linq/observable/timestamp.rb +9 -0
- data/lib/rx_ruby/linq/observable/to_async.rb +40 -0
- data/lib/rx_ruby/linq/observable/when.rb +36 -0
- data/lib/rx_ruby/linq/observable/while.rb +41 -0
- data/lib/rx_ruby/operators/aggregates.rb +611 -0
- data/lib/rx_ruby/operators/creation.rb +220 -0
- data/lib/rx_ruby/operators/multiple.rb +735 -0
- data/lib/rx_ruby/operators/single.rb +399 -0
- data/lib/rx_ruby/operators/standard_query_operators.rb +279 -0
- data/lib/rx_ruby/operators/synchronization.rb +47 -0
- data/lib/rx_ruby/operators/time.rb +120 -0
- data/lib/rx_ruby/subjects/async_subject.rb +161 -0
- data/lib/rx_ruby/subjects/behavior_subject.rb +149 -0
- data/lib/rx_ruby/subjects/replay_subject.rb +39 -0
- data/lib/rx_ruby/subjects/subject.rb +131 -0
- data/lib/rx_ruby/subjects/subject_extensions.rb +45 -0
- data/lib/rx_ruby/subscriptions/composite_subscription.rb +91 -0
- data/lib/rx_ruby/subscriptions/ref_count_subscription.rb +88 -0
- data/lib/rx_ruby/subscriptions/scheduled_subscription.rb +32 -0
- data/lib/rx_ruby/subscriptions/serial_subscription.rb +60 -0
- data/lib/rx_ruby/subscriptions/single_assignment_subscription.rb +64 -0
- data/lib/rx_ruby/subscriptions/subscription.rb +56 -0
- data/lib/rx_ruby/testing/cold_observable.rb +45 -0
- data/lib/rx_ruby/testing/hot_observable.rb +47 -0
- data/lib/rx_ruby/testing/mock_observer.rb +33 -0
- data/lib/rx_ruby/testing/reactive_test.rb +94 -0
- data/lib/rx_ruby/testing/recorded.rb +17 -0
- data/lib/rx_ruby/testing/test_scheduler.rb +96 -0
- data/lib/rx_ruby/testing/test_subscription.rb +22 -0
- data/lib/rx_ruby/version.rb +3 -0
- data/license.txt +13 -0
- data/readme.md +152 -0
- data/rx_ruby.gemspec +22 -0
- data/test/rx_ruby/concurrency/helpers/historical_virtual_scheduler_helper.rb +135 -0
- data/test/rx_ruby/concurrency/helpers/immediate_local_scheduler_helper.rb +51 -0
- data/test/rx_ruby/concurrency/test_async_lock.rb +56 -0
- data/test/rx_ruby/concurrency/test_current_thread_scheduler.rb +44 -0
- data/test/rx_ruby/concurrency/test_default_scheduler.rb +44 -0
- data/test/rx_ruby/concurrency/test_historical_scheduler.rb +18 -0
- data/test/rx_ruby/concurrency/test_immediate_scheduler.rb +53 -0
- data/test/rx_ruby/concurrency/test_local_scheduler.rb +12 -0
- data/test/rx_ruby/concurrency/test_periodic_scheduler.rb +53 -0
- data/test/rx_ruby/concurrency/test_scheduled_item.rb +50 -0
- data/test/rx_ruby/concurrency/test_scheduler.rb +128 -0
- data/test/rx_ruby/concurrency/test_virtual_time_scheduler.rb +14 -0
- data/test/rx_ruby/core/test_notification.rb +129 -0
- data/test/rx_ruby/core/test_observable_creation.rb +483 -0
- data/test/rx_ruby/core/test_observer.rb +634 -0
- data/test/rx_ruby/internal/test_priority_queue.rb +71 -0
- data/test/rx_ruby/subscriptions/test_composite_subscription.rb +116 -0
- data/test/rx_ruby/subscriptions/test_serial_subscription.rb +62 -0
- data/test/rx_ruby/subscriptions/test_singleassignment_subscription.rb +61 -0
- data/test/rx_ruby/subscriptions/test_subscription.rb +27 -0
- data/test/test_helper.rb +11 -0
- metadata +291 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
require 'rx_ruby/concurrency/current_thread_scheduler'
|
4
|
+
require 'rx_ruby/concurrency/immediate_scheduler'
|
5
|
+
require 'rx_ruby/core/observer'
|
6
|
+
require 'rx_ruby/core/auto_detach_observer'
|
7
|
+
require 'rx_ruby/subscriptions/subscription'
|
8
|
+
|
9
|
+
module RxRuby
|
10
|
+
|
11
|
+
module Observable
|
12
|
+
|
13
|
+
def subscribe(*args)
|
14
|
+
case args.size
|
15
|
+
when 0
|
16
|
+
if block_given?
|
17
|
+
_subscribe Observer.configure {|o| o.on_next(&Proc.new) }
|
18
|
+
else
|
19
|
+
_subscribe Observer.configure
|
20
|
+
end
|
21
|
+
when 1
|
22
|
+
_subscribe args[0]
|
23
|
+
when 3
|
24
|
+
_subscribe Observer.configure {|o|
|
25
|
+
o.on_next(&args[0])
|
26
|
+
o.on_error(&args[1])
|
27
|
+
o.on_completed(&args[2])
|
28
|
+
}
|
29
|
+
else
|
30
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 0..1 or 3)"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Subscribes the given observer to the observable sequence.
|
35
|
+
# @param [Observer] observer
|
36
|
+
# @return [Subscription]
|
37
|
+
def _subscribe(observer)
|
38
|
+
|
39
|
+
auto_detach_observer = AutoDetachObserver.new observer
|
40
|
+
|
41
|
+
if CurrentThreadScheduler.schedule_required?
|
42
|
+
CurrentThreadScheduler.instance.schedule_with_state auto_detach_observer, method(:schedule_subscribe)
|
43
|
+
else
|
44
|
+
begin
|
45
|
+
auto_detach_observer.subscription = subscribe_core auto_detach_observer
|
46
|
+
rescue => e
|
47
|
+
raise e unless auto_detach_observer.fail e
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
auto_detach_observer
|
52
|
+
end
|
53
|
+
|
54
|
+
# Subscribes the given block to the on_next action of the observable sequence.
|
55
|
+
# @param [Object] block
|
56
|
+
# @return [Subscription]
|
57
|
+
def subscribe_on_next(&block)
|
58
|
+
raise ArgumentError.new 'Block is required' unless block_given?
|
59
|
+
subscribe(Observer.configure {|o| o.on_next(&block) })
|
60
|
+
end
|
61
|
+
|
62
|
+
# Subscribes the given block to the on_error action of the observable sequence.
|
63
|
+
def subscribe_on_error(&block)
|
64
|
+
raise ArgumentError.new 'Block is required' unless block_given?
|
65
|
+
subscribe(Observer.configure {|o| o.on_error(&block) })
|
66
|
+
end
|
67
|
+
|
68
|
+
# Subscribes the given block to the on_completed action of the observable sequence.
|
69
|
+
def subscribe_on_completed(&block)
|
70
|
+
raise ArgumentError.new 'Block is required' unless block_given?
|
71
|
+
subscribe(Observer.configure {|o| o.on_completed(&block) })
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def schedule_subscribe(_, auto_detach_observer)
|
77
|
+
begin
|
78
|
+
auto_detach_observer.subscription = subscribe_core auto_detach_observer
|
79
|
+
rescue => e
|
80
|
+
raise e unless auto_detach_observer.fail e
|
81
|
+
end
|
82
|
+
|
83
|
+
Subscription.empty
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
class AnonymousObservable
|
89
|
+
|
90
|
+
include Observable
|
91
|
+
|
92
|
+
def initialize(&subscribe)
|
93
|
+
@subscribe = subscribe
|
94
|
+
end
|
95
|
+
|
96
|
+
protected
|
97
|
+
|
98
|
+
def subscribe_core(obs)
|
99
|
+
@subscribe.call(obs) || Subscription.empty
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
require 'rx_ruby/core/scheduled_observer'
|
5
|
+
|
6
|
+
module RxRuby
|
7
|
+
|
8
|
+
module Observer
|
9
|
+
# Schedules the invocation of observer methods on the given scheduler.
|
10
|
+
def notify_on(scheduler)
|
11
|
+
ObserveOnObserver.new(scheduler, self, nil)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class ObserveOnObserver < ScheduledObserver
|
16
|
+
|
17
|
+
def initialize(scheduler, observer, cancel = nil)
|
18
|
+
@cancel = cancel
|
19
|
+
|
20
|
+
super(scheduler, observer)
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_next_core(value)
|
24
|
+
ensure_active
|
25
|
+
super(value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_error_core(error)
|
29
|
+
ensure_active
|
30
|
+
super(error)
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_completed_core
|
34
|
+
ensure_active
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def unsubscribe
|
39
|
+
super
|
40
|
+
|
41
|
+
cancel = nil
|
42
|
+
Mutex.new.synchronize do
|
43
|
+
cancel = @cancel
|
44
|
+
@cancel = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
canel.unsubscribe if cancel
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
module RxRuby
|
4
|
+
|
5
|
+
# Configuration class for storing Observer actions
|
6
|
+
class ObserverConfiguration
|
7
|
+
|
8
|
+
DEFAULT_ON_NEXT = lambda {|x| }
|
9
|
+
DEFAULT_ON_ERROR = lambda {|error| raise error }
|
10
|
+
DEFAULT_ON_COMPLETED = lambda { }
|
11
|
+
|
12
|
+
attr_reader :on_next_action, :on_error_action, :on_completed_action
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@on_next_action = DEFAULT_ON_NEXT
|
16
|
+
@on_error_action = DEFAULT_ON_ERROR
|
17
|
+
@on_completed_action = DEFAULT_ON_COMPLETED
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_next(&on_next_action)
|
21
|
+
@on_next_action = on_next_action
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_error(&on_error_action)
|
25
|
+
@on_error_action = on_error_action
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_completed(&on_completed_action)
|
29
|
+
@on_completed_action = on_completed_action
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Module for all Observers
|
34
|
+
module Observer
|
35
|
+
|
36
|
+
# Hides the identity of an observer.
|
37
|
+
def as_observer
|
38
|
+
Observer.configure do |o|
|
39
|
+
o.on_next(&method(:on_next))
|
40
|
+
o.on_error(&method(:on_error))
|
41
|
+
o.on_completed(&method(:on_completed))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Creates a notification callback from an observer.
|
46
|
+
def to_notifier
|
47
|
+
lambda {|n| n.accept self}
|
48
|
+
end
|
49
|
+
|
50
|
+
class << self
|
51
|
+
|
52
|
+
# Configures a new instance of an Observer
|
53
|
+
def configure
|
54
|
+
config = ObserverConfiguration.new
|
55
|
+
yield config if block_given?
|
56
|
+
ObserverBase.new config
|
57
|
+
end
|
58
|
+
|
59
|
+
def create(on_next = nil, on_error = nil, on_completed = nil)
|
60
|
+
configure do |o|
|
61
|
+
o.on_next(&on_next) if on_next
|
62
|
+
o.on_error(&on_error) if on_error
|
63
|
+
o.on_completed(&on_completed) if on_completed
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
# Base class for all Observer implementations
|
71
|
+
class ObserverBase
|
72
|
+
include Observer
|
73
|
+
|
74
|
+
def initialize(config)
|
75
|
+
@config = config
|
76
|
+
@stopped = false
|
77
|
+
end
|
78
|
+
|
79
|
+
# Unsubscribes from the current observer causing it to transition to the stopped state.
|
80
|
+
def unsubscribe
|
81
|
+
@stopped = true
|
82
|
+
end
|
83
|
+
|
84
|
+
def dispose
|
85
|
+
unsubscribe
|
86
|
+
end
|
87
|
+
|
88
|
+
# Notifies the observer of a new element in the sequence.
|
89
|
+
def on_next(value)
|
90
|
+
@config.on_next_action.call value unless @stopped
|
91
|
+
end
|
92
|
+
|
93
|
+
# Notifies the observer that an exception has occurred.
|
94
|
+
def on_error(error)
|
95
|
+
raise 'Error cannot be nil' unless error
|
96
|
+
unless @stopped
|
97
|
+
@stopped = true
|
98
|
+
@config.on_error_action.call error
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Notifies the observer of the end of the sequence.
|
103
|
+
def on_completed
|
104
|
+
unless @stopped
|
105
|
+
@stopped = true
|
106
|
+
@config.on_completed_action.call
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def fail(error)
|
111
|
+
unless @stopped
|
112
|
+
@stopped = true
|
113
|
+
@config.on_error_action.call error
|
114
|
+
return true
|
115
|
+
end
|
116
|
+
return false
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
require 'monitor'
|
4
|
+
require 'rx_ruby/subscriptions/serial_subscription'
|
5
|
+
require 'rx_ruby/core/observer'
|
6
|
+
|
7
|
+
module RxRuby
|
8
|
+
|
9
|
+
class ScheduledObserver < ObserverBase
|
10
|
+
|
11
|
+
def initialize(scheduler, observer)
|
12
|
+
@scheduler = scheduler
|
13
|
+
@observer = observer
|
14
|
+
@gate = Monitor.new
|
15
|
+
@queue = []
|
16
|
+
@subscriber = SerialSubscription.new
|
17
|
+
@acquired = false
|
18
|
+
@faulted = false
|
19
|
+
|
20
|
+
config = ObserverConfiguration.new
|
21
|
+
config.on_next(&method(:on_next_core))
|
22
|
+
config.on_error(&method(:on_error_core))
|
23
|
+
config.on_completed(&method(:on_completed_core))
|
24
|
+
|
25
|
+
super(config)
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_next_core(value)
|
29
|
+
@gate.synchronize { @queue.push(lambda { @observer.on_next value }) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_error_core(error)
|
33
|
+
@gate.synchronize { @queue.push(lambda { @observer.on_error error }) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_completed_core
|
37
|
+
@gate.synchronize { @queue.push(lambda { @observer.on_completed }) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def ensure_active(n=0)
|
41
|
+
owner = false
|
42
|
+
|
43
|
+
@gate.synchronize do
|
44
|
+
if !@faulted && @queue.length > 0
|
45
|
+
owner = !@acquired
|
46
|
+
@acquired = true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
@subscriber.subscription = @scheduler.schedule_recursive_with_state(nil, method(:run)) if owner
|
51
|
+
end
|
52
|
+
|
53
|
+
def run(state, recurse)
|
54
|
+
work = nil
|
55
|
+
@gate.synchronize do
|
56
|
+
if @queue.length > 0
|
57
|
+
work = @queue.shift
|
58
|
+
else
|
59
|
+
@acquired = false
|
60
|
+
return
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
begin
|
65
|
+
work.call
|
66
|
+
rescue => e
|
67
|
+
@queue = []
|
68
|
+
@faulted = true
|
69
|
+
|
70
|
+
raise e
|
71
|
+
end
|
72
|
+
|
73
|
+
recurse.call state
|
74
|
+
end
|
75
|
+
|
76
|
+
def unsubscribe
|
77
|
+
super
|
78
|
+
@subscriber.unsubscribe
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
require 'monitor'
|
4
|
+
require 'rx_ruby/core/observer'
|
5
|
+
|
6
|
+
module RxRuby
|
7
|
+
|
8
|
+
module Observer
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# Synchronizes access to the observer such that its callback methods cannot be called concurrently by multiple threads, using the specified gate object for use by a Monitor based lock.
|
12
|
+
# This overload is useful when coordinating multiple observers that access shared state by synchronizing on a common gate object if given.
|
13
|
+
# Notice reentrant observer callbacks on the same thread are still possible.
|
14
|
+
def allow_reentrancy(observer, gate = Monitor.new)
|
15
|
+
SynchronizedObserver.new(observer, gate)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class SynchronizedObserver < RxRuby::ObserverBase
|
21
|
+
|
22
|
+
def on_next_core(value)
|
23
|
+
@gate.synchronize { @observer.on_next value }
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_error_core(error)
|
27
|
+
@gate.synchronize { @observer.on_error error }
|
28
|
+
end
|
29
|
+
|
30
|
+
def on_completed_core
|
31
|
+
@gate.synchronize { @observer.on_completed }
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(observer, gate)
|
35
|
+
@observer = observer
|
36
|
+
@gate = gate
|
37
|
+
|
38
|
+
config = ObserverConfiguration.new
|
39
|
+
config.on_next(&method(:on_next_core))
|
40
|
+
config.on_error(&method(:on_error_core))
|
41
|
+
config.on_completed(&method(:on_completed_core))
|
42
|
+
|
43
|
+
super(config)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
module RxRuby
|
4
|
+
|
5
|
+
# Record of a value including the virtual time it was produced on.
|
6
|
+
class TimeInterval < Struct.new(:interval, :value)
|
7
|
+
|
8
|
+
def initialize(interval, value)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"(#{value})@(#{interval})"
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
module RxRuby
|
4
|
+
|
5
|
+
# Priority Queue implemented as a binary heap.
|
6
|
+
class PriorityQueue
|
7
|
+
def initialize
|
8
|
+
@items = []
|
9
|
+
@mutex = Mutex.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def peek
|
13
|
+
@mutex.synchronize do
|
14
|
+
unsafe_peek
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def shift
|
19
|
+
@mutex.synchronize do
|
20
|
+
result = unsafe_peek
|
21
|
+
delete_at 0
|
22
|
+
result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def push(item)
|
27
|
+
@mutex.synchronize do
|
28
|
+
@items.push IndexedItem.new(item)
|
29
|
+
percolate length - 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete(item)
|
34
|
+
@mutex.synchronize do
|
35
|
+
index = @items.index {|it| it.value == item }
|
36
|
+
if index
|
37
|
+
delete_at index
|
38
|
+
true
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def length
|
46
|
+
@items.length
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def unsafe_peek
|
52
|
+
@items.first.value unless @items.empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete_at(index)
|
56
|
+
substitute = @items.pop
|
57
|
+
if substitute and index < @items.length
|
58
|
+
@items[index] = substitute
|
59
|
+
heapify index
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# bubble up an item while it's smaller than parents
|
64
|
+
def percolate(index)
|
65
|
+
parent = (index - 1) / 2
|
66
|
+
return if parent < 0
|
67
|
+
|
68
|
+
current_value = @items[index]
|
69
|
+
parent_value = @items[parent]
|
70
|
+
|
71
|
+
if current_value < parent_value
|
72
|
+
@items[index] = parent_value
|
73
|
+
@items[parent] = current_value
|
74
|
+
percolate parent
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# bubble down an item while it's bigger than children
|
79
|
+
def heapify(index)
|
80
|
+
current_index = index
|
81
|
+
left_index = 2 * index + 1
|
82
|
+
right_index = 2 * index + 2
|
83
|
+
|
84
|
+
current_value = @items[index]
|
85
|
+
left_value = @items[left_index]
|
86
|
+
right_value = @items[right_index]
|
87
|
+
|
88
|
+
if right_value && right_value < current_value && right_value < left_value
|
89
|
+
current_index = right_index
|
90
|
+
elsif left_value && left_value < current_value
|
91
|
+
current_index = left_index
|
92
|
+
end
|
93
|
+
|
94
|
+
if current_index != index
|
95
|
+
@items[index] = @items[current_index]
|
96
|
+
@items[current_index] = current_value
|
97
|
+
heapify current_index
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class IndexedItem
|
102
|
+
include Comparable
|
103
|
+
attr_reader :id , :value
|
104
|
+
|
105
|
+
@@length = 0
|
106
|
+
|
107
|
+
def initialize(value)
|
108
|
+
@id = @@length += 1
|
109
|
+
@value = value
|
110
|
+
end
|
111
|
+
|
112
|
+
def <=>(other)
|
113
|
+
if @value == other.value
|
114
|
+
@id <=> other.id
|
115
|
+
else
|
116
|
+
@value <=> other.value
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|