Skip to content
This repository has been archived by the owner on Aug 29, 2024. It is now read-only.

Commit

Permalink
Timeout support
Browse files Browse the repository at this point in the history
  • Loading branch information
mattgreen committed Oct 4, 2013
1 parent a90b485 commit 291fc95
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 99 deletions.
10 changes: 10 additions & 0 deletions lib/elevate/operation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,15 @@ def main
#
# @api public
attr_reader :result

# Cancels any waiting operation with a TimeoutError, interrupting
# execution. This is not the same as #cancel.
#
# @return [void]
#
# @api public
def timeout
@coordinator.cancel(TimeoutError)
end
end
end
57 changes: 35 additions & 22 deletions lib/elevate/task.rb
Original file line number Diff line number Diff line change
@@ -1,45 +1,47 @@
module Elevate
class Task
def initialize(definition, controller, active_tasks)
@name = definition.name
@definition = definition
@controller = WeakRef.new(controller)
@active_tasks = active_tasks
@operation = nil
@channel = Channel.new(method(:on_update))
@handlers = definition.handlers
@timer = nil
end

def cancel
if @operation
@operation.cancel
end
end

attr_reader :name

def on_finish=(block)
@handlers[:on_finish] = block
end

def on_start=(block)
@handlers[:on_start] = block
if @timer
@timer.invalidate
end
end
end

def on_update=(block)
@handlers[:on_update] = block
def name
@definition.name
end

def start(args)
raise "invalid argument count" if args.length != @handlers[:body].arity
raise "invalid argument count" if args.length != handlers[:body].arity

@operation = ElevateOperation.alloc.initWithTarget(@handlers[:body],
@operation = ElevateOperation.alloc.initWithTarget(handlers[:body],
args: args,
channel: WeakRef.new(@channel))

@operation.addObserver(self, forKeyPath: "isFinished", options: NSKeyValueObservingOptionNew, context: nil)
queue.addOperation(@operation)
@active_tasks << self

if interval = @definition.options[:timeout_interval]
@timer = NSTimer.scheduledTimerWithTimeInterval(interval,
target: self,
selector: :"on_timeout:",
userInfo: nil,
repeats: false)
end

performSelectorOnMainThread(:on_start, withObject: nil, waitUntilDone: false)
end

Expand All @@ -55,8 +57,12 @@ def error_handler_for(exception)
handler_name.to_sym
end

def handlers
@definition.handlers
end

def invoke(block, *args)
return if @operation.isCancelled
return if @operation.cancelled?
return unless block

@controller.instance_exec(*args, &block)
Expand All @@ -79,19 +85,26 @@ def observeValueForKeyPath(path, ofObject: operation, change: change, context: c
end

def on_start
invoke(@handlers[:on_start])
invoke(handlers[:on_start])
end

def on_finish
@operation.removeObserver(self, forKeyPath: "isFinished")

@active_tasks.delete(self)

if @timer
@timer.invalidate
end

if exception = @operation.exception
invoke(@handlers.fetch(error_handler_for(exception), @handlers[:on_error]), exception)
invoke(handlers.fetch(error_handler_for(exception), handlers[:on_error]), exception)
end

invoke(@handlers[:on_finish], @operation.result, @operation.exception)
invoke(handlers[:on_finish], @operation.result, @operation.exception)
end

def on_timeout(timer)
@operation.timeout
end

def on_update(args)
Expand All @@ -100,7 +113,7 @@ def on_update(args)
return
end

invoke(@handlers[:on_update], *args)
invoke(handlers[:on_update], *args)
end
end
end
132 changes: 55 additions & 77 deletions spec/elevate_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,24 @@ def initialize
#Dispatch::Queue.main.async { resume }
end
end

task :timeout_test do
timeout 0.3

task do
Elevate::HTTP.get("http://example.com/")
end

on_timeout do |e|
self.invocations[:timeout] = counter
self.counter += 1
end

on_finish do |result, ex|
self.invocations[:finish] = counter
self.counter += 1
end
end
end

describe Elevate do
Expand Down Expand Up @@ -213,8 +231,8 @@ def initialize
end
end

describe 'error handling' do
it "invokes specific error handlers" do
describe "error handling" do
it "invokes an error handling correspding to the raised exception" do
@controller.launch(:custom_error_handlers)

wait 0.5 do
Expand All @@ -224,92 +242,52 @@ def initialize
invocations[:error].should.be.nil
end
end
end

#describe "#async" do
#describe "timeouts" do
#before do
#stub_request(:get, "http://example.com/").
#to_return(body: "Hello!", content_type: "text/plain", delay: 1.0)
#end

#it "does not cancel the operation if it completes in time" do
#@timed_out = false

#async do
#timeout 3.0

#task do
#Elevate::HTTP.get("http://example.com/")

#"finished"
#end

#on_finish do |result, exception|
#@result = result
#resume
#end
#end

#wait_max 5.0 do
#@result.should == "finished"
#@timed_out.should.be.false
#end
#end

#it "stops the operation when timeout interval has elapsed" do
#@result = nil

#@task = async do
#timeout 0.5
it "invokes on_error if there is not a specific handler" do
@controller.launch(:test_task, true)

#task do
#Elevate::HTTP.get("http://example.com/")
wait 0.5 do
invocations = @controller.invocations

#"finished"
#end
invocations[:error].should.not.be.nil
end
end
end

#on_finish do |result, exception|
#@result = result
#resume
#end
#end
describe "timeouts" do
it "does not cancel the operation if it completes in time" do
stub_request(:get, "http://example.com/").
to_return(body: "Hello!", content_type: "text/plain")

#wait_max 5.0 do
#@result.should.not == "finished"
@controller.launch(:timeout_test)

#@task.timed_out?.should.be.true
#end
#end
wait 0.5 do
@controller.invocations[:timeout].should.be.nil
@controller.invocations[:finish].should.not.be.nil
end
end

#it "invokes on_timeout when a timeout occurs" do
#@result = ""
#@timed_out = false
it "stops the operation when it exceeds the timeout" do
stub_request(:get, "http://example.com/").
to_return(body: "Hello!", content_type: "text/plain", delay: 1.0)

#async do
#timeout 0.5
@controller.launch(:timeout_test)

#task do
#Elevate::HTTP.get("http://example.com/")
wait 0.5 do
@controller.invocations[:finish].should.not.be.nil
end
end

#"finished"
#end
it "invokes on_timeout when the operation times out" do
stub_request(:get, "http://example.com/").
to_return(body: "Hello!", content_type: "text/plain", delay: 1.0)

#on_timeout do
#@timed_out = true
#end
@controller.launch(:timeout_test)

#on_finish do |result, exception|
#@result = result
#resume
#end
#end
wait 0.5 do
@controller.invocations[:timeout].should.not.be.nil
end

#wait_max 5.0 do
#@result.should.not == "finished"
#@timed_out.should.be.true
#end
#end
#end
#end
end
end
end

0 comments on commit 291fc95

Please sign in to comment.