aboutsummaryrefslogtreecommitdiffstats
path: root/core/lib/deferred/deferred.rb
blob: 453c9bbef778d5189b27f8403c1be76f6d1f4763 (plain)
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
# -*- coding: utf-8 -*-

class Deferred
  include Deferredable

  def initialize(follow = nil)
    @follow = follow
    @backtrace = caller if Mopt.debug
  end

  alias :deferredable_cancel :cancel
  def cancel
    deferredable_cancel
    @follow.cancel if @follow.is_a? Deferredable end

  class << self
    # 実行中のDeferredを失敗させる。raiseと違って、Exception以外のオブジェクトをtrap()に渡すことができる。
    # Deferredのnextとtrapの中でだけ呼び出すことができる。
    # ==== Args
    # [value] trap()に渡す値
    # ==== Throw
    # :__deferredable_fail をthrowする
    def fail(value)
      throw(:__deferredable_fail, value) end

    # 複数のdeferredを引数に取って、それら全ての実行が終了したら、
    # その結果を引数の順番通りに格納したArrayを引数に呼ばれるDeferredを返す。
    # 引数のDeferredが一つでも失敗するとこのメソッドの返すDeferredも失敗する。
    # ==== Args
    # [defer] 終了を待つDeferredオブジェクト
    # [*follow] 他のDeferredオブジェクト
    # ==== Return
    # Deferred
    def when(defer = nil, *follow)
      return deferred{ [] } if defer.nil?
      defer.next{ |res|
        Deferred.when(*follow).next{ |follow_res|
          [res] + follow_res
        }
      }
    end

    # Kernel#systemを呼び出して、コマンドが成功たら成功するDeferredを返す。
    # 失敗した場合、trap{}ブロックには $? の値(Process::Status)か、例外が発生した場合それが渡される
    # ==== Args
    # [*args] Kernel#system の引数
    # ==== Return
    # Deferred
    def system(*args)
      promise = Deferred.new
      Thread.new{
        if Kernel.system(*args)
          promise.call(true)
        else
          promise.fail($?) end
      }.trap{ |e|
        promise.fail(e) }
      promise
    end
  end

end

class Thread
  include Deferredable

  alias _deferredable_trap initialize
  def initialize(*args, &proc)
    _deferredable_trap(*args){ |*args|
      begin
        result = proc.call(*args)
        self.call(result)
        result
      rescue Exception => e
        self.fail(e)
      end
    }
  end

  alias :deferredable_cancel :cancel
  def cancel
    deferredable_cancel
    kill end

end

module Enumerable
  # 遅延each。あとで実行されるし、あんまりループに時間がかかるようなら一旦ループを終了する
  def deach(&proc)
    iteratee = to_a
    iteratee = dup if equal?(iteratee)
    deferred{
      result = nil
      while not iteratee.empty?
        item = iteratee.shift
        proc.call(item)
        if Delayer.expire?
          break result = iteratee.deach(&proc) end end
      result }
  end
end

def deferred(&proc)
  Deferred.new.next(&proc) end
# ~> -:4: uninitialized constant Deferred::Deferredable (NameError)