/
reactor.rb
112 lines (101 loc) · 3.55 KB
/
reactor.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
module BubbleWrap
module Reactor
module_function
# Always returns true - for compatibility with EM
def reactor_running?
true
end
alias reactor_thread? reactor_running?
# Call `callback` or the passed block in `interval` seconds.
# Returns a timer signature that can be passed into
# `cancel_timer`
def add_timer(interval, callback=nil, &blk)
@timers ||= {}
timer = Timer.new(interval,callback,&blk)
timer.on(:fired) do
@timers.delete(timer.object_id)
end
timer.on(:cancelled) do
@timers.delete(timer.object_id)
end
@timers[timer.object_id] = timer
timer.object_id
end
# Cancel a timer by passing in either a Timer object or
# a timer id (as returned by `add_timer` and
# `add_periodic_timer`).
def cancel_timer(timer)
return timer.cancel if timer.respond_to?(:cancel)
@timers ||= {}
return @timers[timer].cancel if @timers[timer]
false
end
# Call `callback` or the passed block every `interval` seconds.
# Returns a timer signature that can be passed into
# `cancel_timer`
# Optionally supply a callback as a second argument instead of a block
# (as per EventMachine API)
# Optionally supply :common_modes => true in args to schedule the timer
# for the runloop "common modes" (NSRunLoopCommonModes) instead of
# the default runloop mode.
def add_periodic_timer(interval, *args, &blk)
@timers ||= {}
timer = PeriodicTimer.new(interval,*args,&blk)
timer.on(:cancelled) do
@timers.delete(timer)
end
@timers[timer.object_id] = timer
timer.object_id
end
# Defer is for integrating blocking operations into the reactor's control
# flow.
# Call defer with one or two blocks, the second block is optional.
# operation = proc do
# # perform a long running operation here
# "result"
# end
# callback = proc do |result|
# # do something with the result here, such as trigger a UI change
# end
# BubbleWrap::Reactor.defer(operation,callback)
# The action of `defer` is to take the block specified in the first
# parameter (the "operation") and schedule it for asynchronous execution
# on a GCD concurrency queue. When the operation completes the result (if any)
# is passed into the callback (if present).
def defer(op=nil,cb=nil,&blk)
schedule do
result = (op||blk).call
schedule(result, &cb) if cb
end
end
# A version of `defer` which schedules both the operator
# and callback operations on the application's main thread.
def defer_on_main(op=nil,cb=nil,&blk)
schedule_on_main do
result = (op||blk).call
schedule_on_main(result, &cb) if cb
end
end
# Schedule a block for execution on the reactor queue.
def schedule(*args, &blk)
@queue ||= ::Dispatch::Queue.concurrent("#{NSBundle.mainBundle.bundleIdentifier}.reactor")
blk.weak! if blk && BubbleWrap.use_weak_callbacks?
cb = proc do
blk.call(*args)
end
@queue.async &cb
nil
end
# Schedule a block for execution on your application's main thread.
# This is useful as UI updates need to be executed from the main
# thread.
def schedule_on_main(*args, &blk)
blk.weak! if blk && BubbleWrap.use_weak_callbacks?
cb = proc do
blk.call(*args)
end
::Dispatch::Queue.main.async &cb
end
end
end
::EM = ::BubbleWrap::Reactor unless defined?(::EM) # Yes I dare!