1module Rake 2 3 # A Promise object represents a promise to do work (a chore) in the 4 # future. The promise is created with a block and a list of 5 # arguments for the block. Calling value will return the value of 6 # the promised chore. 7 # 8 # Used by ThreadPool. 9 # 10 class Promise # :nodoc: all 11 NOT_SET = Object.new.freeze # :nodoc: 12 13 attr_accessor :recorder 14 15 # Create a promise to do the chore specified by the block. 16 def initialize(args, &block) 17 @mutex = Mutex.new 18 @result = NOT_SET 19 @error = NOT_SET 20 @args = args.collect { |a| begin; a.dup; rescue; a; end } 21 @block = block 22 end 23 24 # Return the value of this promise. 25 # 26 # If the promised chore is not yet complete, then do the work 27 # synchronously. We will wait. 28 def value 29 unless complete? 30 stat :sleeping_on, :item_id => object_id 31 @mutex.synchronize do 32 stat :has_lock_on, :item_id => object_id 33 chore 34 stat :releasing_lock_on, :item_id => object_id 35 end 36 end 37 error? ? raise(@error) : @result 38 end 39 40 # If no one else is working this promise, go ahead and do the chore. 41 def work 42 stat :attempting_lock_on, :item_id => object_id 43 if @mutex.try_lock 44 stat :has_lock_on, :item_id => object_id 45 chore 46 stat :releasing_lock_on, :item_id => object_id 47 @mutex.unlock 48 else 49 stat :bailed_on, :item_id => object_id 50 end 51 end 52 53 private 54 55 # Perform the chore promised 56 def chore 57 if complete? 58 stat :found_completed, :item_id => object_id 59 return 60 end 61 stat :will_execute, :item_id => object_id 62 begin 63 @result = @block.call(*@args) 64 rescue Exception => e 65 @error = e 66 end 67 stat :did_execute, :item_id => object_id 68 discard 69 end 70 71 # Do we have a result for the promise 72 def result? 73 ! @result.equal?(NOT_SET) 74 end 75 76 # Did the promise throw an error 77 def error? 78 ! @error.equal?(NOT_SET) 79 end 80 81 # Are we done with the promise 82 def complete? 83 result? || error? 84 end 85 86 # free up these items for the GC 87 def discard 88 @args = nil 89 @block = nil 90 end 91 92 # Record execution statistics if there is a recorder 93 def stat(*args) 94 @recorder.call(*args) if @recorder 95 end 96 97 end 98 99end 100