delayer-deferred 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,34 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "delayer/deferred/deferredable/chainable"
3
+ require "delayer/deferred/deferredable/node_sequence"
4
+ require "delayer/deferred/response"
5
+
6
+ module Delayer::Deferred::Deferredable
7
+ =begin rdoc
8
+ Promiseなど、親を持たず、自身がWorkerを作成できるもの。
9
+ =end
10
+ module Trigger
11
+ include NodeSequence
12
+ include Chainable
13
+
14
+ # Deferredを直ちに実行する。
15
+ # このメソッドはスレッドセーフです。
16
+ def call(value = nil)
17
+ execute(Delayer::Deferred::Response::Ok.new(value))
18
+ end
19
+
20
+ # Deferredを直ちに失敗させる。
21
+ # このメソッドはスレッドセーフです。
22
+ def fail(exception = nil)
23
+ execute(Delayer::Deferred::Response::Ng.new(exception))
24
+ end
25
+
26
+ private
27
+
28
+ def execute(value)
29
+ worker = Delayer::Deferred::Worker.new(delayer: self.class.delayer,
30
+ initial: value)
31
+ worker.push(self)
32
+ end
33
+ end
34
+ end
@@ -5,12 +5,10 @@ require "delayer/deferred/deferred"
5
5
  class Enumerator
6
6
  def deach(delayer=Delayer, &proc)
7
7
  delayer.Deferred.new.next do
8
- begin
9
- loop do
10
- proc.call(self.next())
11
- if delayer.expire?
12
- break deach(delayer, &proc) end end
13
- rescue StopIteration
14
- nil end end
8
+ self.each do |node|
9
+ delayer.Deferred.pass
10
+ proc.(node)
11
+ end
12
+ end
15
13
  end
16
14
  end
@@ -10,4 +10,13 @@ module Delayer::Deferred
10
10
  @process = process
11
11
  end
12
12
  end
13
+
14
+ SequenceError = Class.new(Error) do
15
+ attr_accessor :deferred
16
+ def initialize(message, deferred: nil)
17
+ super(message)
18
+ @deferred = deferred
19
+ end
20
+ end
21
+ MultipleAssignmentError = Class.new(SequenceError)
13
22
  end
@@ -0,0 +1,78 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "delayer/deferred/tools"
3
+ require "delayer/deferred/deferredable/trigger"
4
+
5
+ module Delayer::Deferred
6
+ class Promise
7
+ extend Delayer::Deferred::Tools
8
+ include Deferredable::Trigger
9
+
10
+ class << self
11
+ def new(stop=false, name: caller_locations(1,1).first.to_s, &block)
12
+ result = promise = super(name: name)
13
+ result = promise.next(&block) if block_given?
14
+ promise.call(true) unless stop
15
+ result
16
+ end
17
+
18
+ def Thread
19
+ @thread_class ||= gen_thread_class end
20
+
21
+ def Promise
22
+ self
23
+ end
24
+
25
+ def delayer
26
+ ::Delayer
27
+ end
28
+
29
+ def to_s
30
+ "#{self.delayer}.Promise"
31
+ end
32
+
33
+ private
34
+
35
+ def gen_thread_class
36
+ the_delayer = delayer
37
+ Class.new(Thread) do
38
+ define_singleton_method(:delayer) do
39
+ the_delayer
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def initialize(name:)
46
+ super()
47
+ @name = name
48
+ end
49
+
50
+ def activate(response)
51
+ change_sequence(:activate)
52
+ change_sequence(:complete)
53
+ response
54
+ end
55
+
56
+ def inspect
57
+ "#<#{self.class} seq:#{sequence.name}>"
58
+ end
59
+
60
+ def ancestor
61
+ self
62
+ end
63
+
64
+ def parent=(chainable)
65
+ fail Error, "#{self.class} can't has parent."
66
+ end
67
+
68
+ private
69
+
70
+ def graph_shape
71
+ 'egg'.freeze
72
+ end
73
+
74
+ def node_name
75
+ @name.to_s
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,61 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # -*- coding: utf-8 -*-
4
+
5
+ module Delayer::Deferred::Request
6
+ class Base
7
+ attr_reader :value
8
+ def initialize(value)
9
+ @value = value
10
+ end
11
+ end
12
+
13
+ =begin rdoc
14
+ Fiberが次のWorkerを要求している時に返す値。
15
+ 新たなインスタンスは作らず、 _NEXT_WORKER_ にあるインスタンスを使うこと。
16
+ =end
17
+ class NextWorker < Base
18
+ # _deferred_ に渡された次のChainableに、 _deferred_ の戻り値を渡す要求を出す。
19
+ # ==== Args
20
+ # [deferred] 実行が完了したDeferred 。次のDeferredとして _deferred.child_ を呼び出す
21
+ # [worker] このDeferredチェインを実行しているWorker
22
+ def accept_request(worker:, deferred:)
23
+ if deferred.has_child?
24
+ worker.push(deferred.child)
25
+ else
26
+ deferred.add_child_observer(worker)
27
+ end
28
+ end
29
+ end
30
+
31
+ =begin rdoc
32
+ Chainable#+@ が呼ばれた時に、一旦そこで処理を止めるためのリクエスト。
33
+ _value_ には、実行完了を待つDeferredが入っている。
34
+ ==== わかりやすい!
35
+ accept_requestメソッドの引数のdeferred {
36
+ +value
37
+ }
38
+ =end
39
+ class Await < Base
40
+ alias_method :foreign_deferred, :value
41
+ def accept_request(worker:, deferred:)
42
+ deferred.enter_await
43
+ foreign_deferred.add_child(Delayer::Deferred::Chain::Await.new(worker: worker, deferred: deferred))
44
+ end
45
+ end
46
+
47
+ =begin rdoc
48
+ 一旦処理を中断して、Delayerキューに並び直すためのリクエスト。
49
+ Tools#pass から利用される。
50
+ 新たなインスタンスは作らず、 _PASS_ にあるインスタンスを使うこと。
51
+ =end
52
+ class Pass < Base
53
+ def accept_request(worker:, deferred:)
54
+ deferred.enter_pass
55
+ worker.resume_pass(deferred)
56
+ end
57
+ end
58
+
59
+ NEXT_WORKER = NextWorker.new(nil).freeze
60
+ PASS = Pass.new(nil).freeze
61
+ end
@@ -0,0 +1,26 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Delayer::Deferred::Response
4
+ class Base
5
+ attr_reader :value
6
+ def initialize(value)
7
+ @value = value
8
+ end
9
+
10
+ def ng?
11
+ !ok?
12
+ end
13
+ end
14
+
15
+ class Ok < Base
16
+ def ok?
17
+ true
18
+ end
19
+ end
20
+
21
+ class Ng < Base
22
+ def ok?
23
+ false
24
+ end
25
+ end
26
+ end
@@ -1,26 +1,40 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require "delayer"
3
- require "delayer/deferred/deferredable"
3
+ require "delayer/deferred/deferredable/awaitable"
4
4
 
5
5
  class Thread
6
- include ::Delayer::Deferred::Deferredable
6
+ include ::Delayer::Deferred::Deferredable::Awaitable
7
7
 
8
8
  def self.delayer
9
9
  Delayer
10
10
  end
11
11
 
12
- def next(*rest, &block)
13
- __gen_promise.next(*rest, &block)
12
+ # このDeferredが成功した場合の処理を追加する。
13
+ # 新しいDeferredのインスタンスを返す。
14
+ # このメソッドはスレッドセーフです。
15
+ # TODO: procが空のとき例外を発生させる
16
+ def next(name: caller_locations(1,1).first.to_s, &proc)
17
+ add_child(Delayer::Deferred::Chain::Next.new(&proc), name: name)
14
18
  end
19
+ alias deferred next
20
+
21
+ # このDeferredが失敗した場合の処理を追加する。
22
+ # 新しいDeferredのインスタンスを返す。
23
+ # このメソッドはスレッドセーフです。
24
+ # TODO: procが空のとき例外を発生させる
25
+ def trap(name: caller_locations(1,1).first.to_s, &proc)
26
+ add_child(Delayer::Deferred::Chain::Trap.new(&proc), name: name)
27
+ end
28
+ alias error trap
15
29
 
16
- def trap(*rest, &block)
17
- __gen_promise.trap(*rest, &block)
30
+ def add_child(chainable, name: caller_locations(1,1).first.to_s)
31
+ __gen_promise(name).add_child(chainable)
18
32
  end
19
33
 
20
34
  private
21
35
 
22
- def __gen_promise
23
- promise = delayer.Deferred.new(true)
36
+ def __gen_promise(name)
37
+ promise = self.class.delayer.Promise.new(true, name: name)
24
38
  Thread.new(self) do |tt|
25
39
  __promise_callback(tt, promise)
26
40
  end
@@ -28,15 +42,16 @@ class Thread
28
42
  end
29
43
 
30
44
  def __promise_callback(tt, promise)
31
- failed = catch(:__deferredable_fail) do
32
- begin
33
- promise.call(tt.value)
34
- rescue Exception => err
45
+ begin
46
+ result = tt.value
47
+ self.class.delayer.new do
48
+ promise.call(result)
49
+ end
50
+ rescue Exception => err
51
+ self.class.delayer.new do
35
52
  promise.fail(err)
36
53
  end
37
- return
38
54
  end
39
- promise.fail(failed)
40
55
  end
41
56
 
42
57
  end
@@ -18,6 +18,12 @@ module Delayer::Deferred
18
18
  def fail(value)
19
19
  throw(:__deferredable_fail, value) end
20
20
 
21
+ # 実行中のDeferredを、Delayerのタイムリミットが来ている場合に限り一旦中断する。
22
+ # 長期に渡る可能性のある処理で、必要に応じて他のタスクを先に実行してもよい場合に呼び出す。
23
+ def pass
24
+ Fiber.yield(Request::PASS) if delayer.expire?
25
+ end
26
+
21
27
  # 複数のdeferredを引数に取って、それら全ての実行が終了したら、
22
28
  # その結果を引数の順番通りに格納したArrayを引数に呼ばれるDeferredを返す。
23
29
  # 引数のDeferredが一つでも失敗するとこのメソッドの返すDeferredも失敗する。
@@ -27,16 +33,20 @@ module Delayer::Deferred
27
33
  # Deferred
28
34
  def when(*args)
29
35
  return self.next{[]} if args.empty?
30
- defer, *follow = args
31
- raise TypeError, "Argument of Deferred.when must be Delayer::Deferred::Deferredable" unless defer.is_a? Delayer::Deferred::Deferredable
32
- if follow.empty?
33
- defer.next{|res| [res] }
34
- else
35
- remain = self.when(*follow)
36
- defer.next do |res|
37
- remain.next do |follow_res|
38
- follow_res.unshift(res) end end end end
39
-
36
+ args = args.flatten
37
+ args.each_with_index{|d, index|
38
+ unless d.is_a?(Deferredable::Chainable) || d.is_a?(Deferredable::Awaitable)
39
+ raise TypeError, "Argument #{index} of Deferred.when must be #{Deferredable::Chainable}, but given #{d.class}"
40
+ end
41
+ if d.respond_to?(:has_child?) && d.has_child?
42
+ raise "Already assigned child for argument #{index}"
43
+ end
44
+ }
45
+ defer, *follow = *args
46
+ defer.next{|res|
47
+ [res, *follow.map{|d| +d }]
48
+ }
49
+ end
40
50
  # Kernel#systemを呼び出して、コマンドが成功たら成功するDeferredを返す。
41
51
  # 失敗した場合、trap{}ブロックには $? の値(Process::Status)か、例外が発生した場合それが渡される
42
52
  # ==== Args
@@ -1,5 +1,5 @@
1
1
  module Delayer
2
2
  module Deferred
3
- VERSION = "1.1.1"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
  end
@@ -0,0 +1,112 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "delayer/deferred/request"
3
+ require "delayer/deferred/response"
4
+
5
+ module Delayer::Deferred
6
+ =begin rdoc
7
+ Deferredを実行するためのWorker。Deferredチェインを実行するFiberを
8
+ 管理する。
9
+
10
+ == pushに渡すオブジェクトについて
11
+ Worker#push に渡す引数は、activateメソッドを実装している必要がある。
12
+
13
+ === activate(response)
14
+ ==== Args
15
+ response :: Delayer::Deferred::Response::Base Deferredに渡す値
16
+ ==== Returns
17
+ [Delayer::Deferred::Response::Base]
18
+ これを返すと、値の自動変換が行われないため、意図的に失敗させたり、Deferredを次のブロックに伝搬させることができる。
19
+ [Delayer::Deferred::Chainable]
20
+ 戻り値のDeferredが終わるまでWorkerの処理を停止する。
21
+ 再開された時、結果は戻り値のDeferredの結果に置き換えられる。
22
+ [else]
23
+ _Delayer::Deferred::Response::Ok.new_ の引数に渡され、その結果が利用される
24
+ =end
25
+ class Worker
26
+ def initialize(delayer:, initial:)
27
+ @delayer, @initial = delayer, initial
28
+ end
29
+
30
+ def push(deferred)
31
+ deferred.reserve_activate
32
+ @delayer.new do
33
+ next if deferred.spoiled?
34
+ begin
35
+ fiber.resume(deferred).accept_request(worker: self,
36
+ deferred: deferred)
37
+ rescue Delayer::Deferred::SequenceError => err
38
+ err.deferred = deferred
39
+ raise
40
+ end
41
+ end
42
+ nil
43
+ end
44
+
45
+ # Awaitから復帰した時に呼ばれる。
46
+ # ==== Args
47
+ # [response] Awaitの結果(Delayer::Deferred::Response::Base)
48
+ # [deferred] 現在実行中のDeferred
49
+ def give_response(response, deferred)
50
+ @delayer.new do
51
+ next if deferred.spoiled?
52
+ deferred.exit_await
53
+ fiber.resume(response).accept_request(worker: self,
54
+ deferred: deferred)
55
+ end
56
+ nil
57
+ end
58
+
59
+ # Tools#pass から復帰した時に呼ばれる。
60
+ # ==== Args
61
+ # [deferred] 現在実行中のDeferred
62
+ def resume_pass(deferred)
63
+ deferred.exit_pass
64
+ @delayer.new do
65
+ next if deferred.spoiled?
66
+ fiber.resume(nil).accept_request(worker: self,
67
+ deferred: deferred)
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def fiber
74
+ @fiber ||= Fiber.new{|response|
75
+ loop do
76
+ response = wait_and_activate(response)
77
+ case response.value
78
+ when Delayer::Deferred::SequenceError
79
+ raise response.value
80
+ end
81
+ end
82
+ }.tap{|f| f.resume(@initial); @initial = nil }
83
+ end
84
+
85
+ def wait_and_activate(argument)
86
+ response = catch(:success) do
87
+ failed = catch(:__deferredable_fail) do
88
+ begin
89
+ if argument.value.is_a? Deferredable::Awaitable
90
+ throw :success, +argument.value
91
+ else
92
+ defer = Fiber.yield(Request::NEXT_WORKER)
93
+ res = defer.activate(argument)
94
+ if res.is_a? Delayer::Deferred::Deferredable::Awaitable
95
+ defer.add_awaited(res)
96
+ end
97
+ end
98
+ throw :success, res
99
+ rescue Exception => err
100
+ throw :__deferredable_fail, err
101
+ end
102
+ end
103
+ Response::Ng.new(failed)
104
+ end
105
+ if response.is_a?(Response::Base)
106
+ response
107
+ else
108
+ Response::Ok.new(response)
109
+ end
110
+ end
111
+ end
112
+ end