abid 0.2.3 → 0.2.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 22747c6ec011be62806ac2b1e0793b964e4c524b
4
- data.tar.gz: f0273c75da4be50a6147142c4cdcdf87b4b9ae04
3
+ metadata.gz: 7c41573b12be68e4e74a00c7d8014e7f2d3cc435
4
+ data.tar.gz: b9e43e1b4d742c798972984090eab802245db56f
5
5
  SHA512:
6
- metadata.gz: 289fba1f7416f23f89c3ab4e985516f9ffd605b8809695adbd2191a9d2141a7e6a0dd523a70dcf60b381b5cb28cc9b7450796cc18539ff125a39d40befaa9237
7
- data.tar.gz: a965a6f57ec1052befe48240a7fc108be79a0c3e3fa6d183a70cf652bd6c869da39118e3f3a620a9446ad1eaa9e47e258a23700d1b4bd9d86b98e2db87a7ad5e
6
+ metadata.gz: 8ea210978f2e500611a2ccee925413d8e1e85dcb54cc770ca626622ca588c52650324925c00aa5a47178e732cdf0adaa88fe0a7c79436fd399785d888426ff38
7
+ data.tar.gz: f5fd045370053a9fd27e88d007a071bb888f4b658082ed331d1b81aff6ee303fa040705908f64be6d3af21b3ea2f2dfd9ee80dff2597e4c13d3084cc97a20cca
data/.gitignore CHANGED
@@ -8,3 +8,4 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  *.db
11
+ /sample/out/
data/lib/abid.rb CHANGED
@@ -13,13 +13,13 @@ require 'sqlite3'
13
13
  require 'sequel'
14
14
 
15
15
  require 'abid/rake_extensions'
16
- require 'abid/concurrent_extention'
17
16
  require 'abid/version'
18
17
  require 'abid/abid_module'
19
18
  require 'abid/waiter'
20
19
  require 'abid/worker'
21
20
  require 'abid/params_parser'
22
21
  require 'abid/play'
22
+ require 'abid/session'
23
23
  require 'abid/state'
24
24
  require 'abid/task'
25
25
  require 'abid/task_manager'
data/lib/abid/play.rb CHANGED
@@ -25,12 +25,7 @@ module Abid
25
25
  end
26
26
 
27
27
  def hooks
28
- @hooks ||= {
29
- setup: [],
30
- before: [],
31
- after: [],
32
- around: []
33
- }
28
+ @hooks ||= Hash.new { |h, k| h[k] = [] }
34
29
  end
35
30
 
36
31
  def set(name, value = nil, &block)
@@ -65,10 +60,6 @@ module Abid
65
60
  hooks[:after] << block
66
61
  end
67
62
 
68
- def around(&block)
69
- hooks[:around] << block
70
- end
71
-
72
63
  def method_added(name)
73
64
  params_spec.delete(name) # undef param
74
65
  end
@@ -9,6 +9,16 @@ module Abid
9
9
  :default
10
10
  end
11
11
 
12
+ def session
13
+ @session ||= Session.new(self).tap do |session|
14
+ session.add_observer do |_, _, reason|
15
+ if session.successed? || session.failed?
16
+ call_hooks(:after_invoke, reason)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
12
22
  def state
13
23
  State.find(self)
14
24
  end
@@ -17,102 +27,98 @@ module Abid
17
27
  name
18
28
  end
19
29
 
30
+ def concerned?
31
+ true
32
+ end
33
+
34
+ def top_level?
35
+ application.top_level_tasks.any? { |t| application[t] == self }
36
+ end
37
+
38
+ def hooks
39
+ @hooks ||= Hash.new { |h, k| h[k] = [] }
40
+ end
41
+
42
+ def call_hooks(tag, *args)
43
+ hooks[tag].each { |h| h.call(*args) }
44
+ end
45
+
20
46
  def async_invoke(*args)
21
47
  task_args = Rake::TaskArguments.new(arg_names, args)
22
48
  async_invoke_with_call_chain(task_args, Rake::InvocationChain::EMPTY)
23
49
  end
24
50
 
25
51
  def async_invoke_with_call_chain(task_args, invocation_chain)
26
- state.reload
27
-
28
- new_chain = Rake::InvocationChain.append(self, invocation_chain)
52
+ session.enter do
53
+ session.capture_exception do
54
+ new_chain = Rake::InvocationChain.append(self, invocation_chain)
29
55
 
30
- state.only_once do
31
- if !application.options.repair && state.successed?
32
- # skip if successed
33
- state.ivar.try_set(false)
34
- elsif !application.options.repair && state.failed? && !invocation_chain.empty?
35
- # fail if not top level
36
- fail "#{name} -- task has been failed" rescue state.ivar.try_fail($ERROR_INFO)
37
- else
38
- async_invoke_with_prerequisites(task_args, new_chain)
39
- end
40
- end
41
- state.ivar
42
- ensure
43
- state.ivar.try_fail($ERROR_INFO) if $ERROR_INFO
44
- end
56
+ unless concerned?
57
+ session.skip
58
+ break
59
+ end
45
60
 
46
- def async_invoke_with_prerequisites(task_args, invocation_chain)
47
- application.trace "** Invoke #{name_with_params}" if application.options.trace
61
+ call_hooks(:before_invoke)
48
62
 
49
- volatiles, non_volatiles = prerequisite_tasks.partition(&:volatile?)
63
+ async_invoke_prerequisites(task_args, new_chain)
50
64
 
51
- async_invoke_tasks(non_volatiles, task_args, invocation_chain) do |updated|
52
- if state.successed? && !updated
53
- application.trace "** Skip #{name_with_params}" if application.options.trace
54
- state.ivar.try_set(false)
55
- else
56
- async_invoke_tasks(volatiles, task_args, invocation_chain) do
57
- async_execute_with_session(task_args)
58
- end
65
+ async_execute_after_prerequisites(task_args)
59
66
  end
60
67
  end
61
68
  end
62
69
 
63
- def async_invoke_tasks(tasks, task_args, invocation_chain, &block)
64
- ivars = tasks.map do |t|
70
+ def async_invoke_prerequisites(task_args, invocation_chain)
71
+ prerequisite_tasks.each do |t|
65
72
  args = task_args.new_scope(t.arg_names)
66
73
  t.async_invoke_with_call_chain(args, invocation_chain)
67
74
  end
75
+ end
68
76
 
69
- if ivars.empty?
70
- block.call(false)
77
+ def async_execute_after_prerequisites(task_args)
78
+ if prerequisite_tasks.empty?
79
+ async_execute(task_args)
71
80
  else
72
- counter = Concurrent::DependencyCounter.new(ivars.size) do
73
- begin
74
- if ivars.any?(&:rejected?)
75
- state.ivar.try_fail(ivars.find(&:rejected?).reason)
76
- else
77
- updated = ivars.map(&:value).any?
78
- block.call(updated)
79
- end
80
- rescue Exception => err
81
- state.ivar.try_fail(err)
81
+ counter = Concurrent::DependencyCounter.new(prerequisite_tasks.size) do
82
+ session.capture_exception do
83
+ async_execute(task_args)
82
84
  end
83
85
  end
84
- ivars.each { |i| i.add_observer counter }
86
+ prerequisite_tasks.each { |t| t.session.add_observer counter }
85
87
  end
86
88
  end
87
89
 
88
- def async_execute_with_session(task_args)
89
- async_execute_in_worker do
90
- begin
91
- state.session do
92
- begin
93
- execute(task_args) if needed?
94
- finished = true
95
- ensure
96
- fail "#{name} -- thread killed" if $ERROR_INFO.nil? && !finished
97
- end
98
- end
90
+ def async_execute(task_args)
91
+ failed_task = prerequisite_tasks.find do |t|
92
+ t.session.failed? || t.session.canceled?
93
+ end
94
+ if failed_task
95
+ session.cancel(failed_task.session.error)
96
+ return
97
+ end
98
+
99
+ async_post(worker) do
100
+ if !needed?
101
+ session.skip
102
+ elsif session.lock
103
+ call_hooks(:before_execute)
104
+
105
+ execute(task_args)
99
106
 
100
- state.ivar.try_set(true)
101
- rescue AbidErrorTaskAlreadyRunning
102
- async_wait_complete
107
+ session.success
108
+ else
109
+ async_wait_external
103
110
  end
104
111
  end
105
112
  end
106
113
 
107
- def async_wait_complete
114
+ def async_wait_external
108
115
  unless application.options.wait_external_task
109
- err = RuntimeError.new("task #{name_with_params} already running")
110
- return state.ivar.try_fail(err)
116
+ fail "task #{name_with_params} already running"
111
117
  end
112
118
 
113
119
  application.trace "** Wait #{name_with_params}" if application.options.trace
114
120
 
115
- async_execute_in_worker(:waiter) do
121
+ async_post(:waiter) do
116
122
  interval = application.options.wait_external_task_interval || 10
117
123
  timeout = application.options.wait_external_task_timeout || 3600
118
124
  timeout_tm = Time.now.to_f + timeout
@@ -120,11 +126,10 @@ module Abid
120
126
  loop do
121
127
  state.reload
122
128
  if !state.running?
123
- state.ivar.try_set(true)
129
+ session.success
124
130
  break
125
131
  elsif Time.now.to_f >= timeout_tm
126
- fail "#{name} -- timeout exceeded" rescue state.ivar.try_fail($ERROR_INFO)
127
- break
132
+ fail "#{name} -- timeout exceeded"
128
133
  else
129
134
  sleep interval
130
135
  end
@@ -132,12 +137,15 @@ module Abid
132
137
  end
133
138
  end
134
139
 
135
- def async_execute_in_worker(worker = nil, &block)
136
- application.worker[worker || self.worker].post do
137
- begin
138
- block.call
139
- rescue Exception => err
140
- state.ivar.try_fail(err)
140
+ def async_post(worker_name, &block)
141
+ application.worker[worker_name].post do
142
+ session.capture_exception do
143
+ begin
144
+ block.call
145
+ finished = true
146
+ ensure
147
+ fail 'thread killed' if $ERROR_INFO.nil? && !finished
148
+ end
141
149
  end
142
150
  end
143
151
  end
@@ -0,0 +1,92 @@
1
+ module Abid
2
+ class Session
3
+ extend MonitorMixin
4
+
5
+ %w(successed skipped failed canceled).each do |result|
6
+ define_method(:"#{result}?") { @result == result.to_sym }
7
+ end
8
+ attr_reader :error
9
+
10
+ def initialize(task)
11
+ @task = task
12
+ @state = task.state
13
+
14
+ @entered = false
15
+ @locked = false
16
+ @result = nil
17
+ @error = nil
18
+ @ivar = Concurrent::IVar.new
19
+
20
+ @on_success = []
21
+ @on_fail = []
22
+ end
23
+
24
+ def synchronize(&block)
25
+ self.class.synchronize(&block)
26
+ end
27
+
28
+ def enter(&block)
29
+ synchronize do
30
+ return @ivar if @entered
31
+ @entered = true
32
+ end
33
+ block.call
34
+ @ivar
35
+ end
36
+
37
+ def capture_exception(&block)
38
+ block.call
39
+ rescue Exception => e
40
+ self.fail(e)
41
+ end
42
+
43
+ def add_observer(*args, &block)
44
+ @ivar.add_observer(*args, &block)
45
+ end
46
+
47
+ def lock
48
+ synchronize do
49
+ @state.start unless @locked
50
+ @locked = true
51
+ true
52
+ end
53
+ rescue AbidErrorTaskAlreadyRunning
54
+ false
55
+ end
56
+
57
+ def unlock(error = nil)
58
+ synchronize do
59
+ @state.finish(error) if @locked
60
+ @locked = false
61
+ end
62
+ end
63
+
64
+ def success
65
+ unlock
66
+ @result = :successed
67
+ @ivar.try_set(true)
68
+ end
69
+
70
+ def skip
71
+ unlock
72
+ @result = :skipped
73
+ @ivar.try_set(false)
74
+ end
75
+
76
+ def fail(error)
77
+ @result = :failed
78
+ @error = error
79
+ unlock(error)
80
+ @ivar.fail(error) rescue Concurrent::MultipleAssignmentError
81
+ rescue Exception => e
82
+ @ivar.fail(e) rescue Concurrent::MultipleAssignmentError
83
+ end
84
+
85
+ def cancel(error)
86
+ unlock(error)
87
+ @result = :canceled
88
+ @error = error
89
+ @ivar.fail(error) rescue Concurrent::MultipleAssignmentError
90
+ end
91
+ end
92
+ end
data/lib/abid/state.rb CHANGED
@@ -56,24 +56,14 @@ module Abid
56
56
  end
57
57
 
58
58
  def_delegators 'self.class', :serialize, :deserialize
59
- attr_reader :ivar
60
59
 
61
60
  def initialize(task)
62
61
  @task = task
63
62
  @record = nil
64
- @ivar = Concurrent::IVar.new
65
- @already_invoked = false
63
+ @started = false
66
64
  reload
67
65
  end
68
66
 
69
- def only_once(&block)
70
- self.class.synchronize do
71
- return if @already_invoked
72
- @already_invoked = true
73
- end
74
- block.call
75
- end
76
-
77
67
  def database
78
68
  Rake.application.database
79
69
  end
@@ -155,14 +145,7 @@ module Abid
155
145
  end
156
146
  end
157
147
 
158
- def session(&block)
159
- started = start_session
160
- block.call
161
- ensure
162
- close_session($ERROR_INFO) if started
163
- end
164
-
165
- def start_session
148
+ def start
166
149
  return true if disabled? || preview?
167
150
 
168
151
  database.transaction do
@@ -189,13 +172,15 @@ module Abid
189
172
  @record = { id: id, **new_state }
190
173
  end
191
174
 
175
+ @started = true
192
176
  true
193
177
  end
194
178
  end
195
179
 
196
- def close_session(error = nil)
180
+ def finish(error = nil)
197
181
  return if disabled? || preview?
198
182
  return unless @record
183
+ return unless @started
199
184
  state = error ? FAILED : SUCCESSED
200
185
  dataset.where(id: @record[:id]).update(state: state, end_time: Time.now)
201
186
  reload
data/lib/abid/task.rb CHANGED
@@ -39,8 +39,10 @@ module Abid
39
39
  @prerequisites = []
40
40
  @params = sorted_params
41
41
  @play = play_class.new(t)
42
+ call_play_hooks(:setup)
43
+ bind_play_hooks(:before, :before_execute)
44
+ bind_play_hooks(:after, :after_invoke)
42
45
  end
43
- play_class.hooks[:setup].each { |blk| t.play.instance_eval(&blk) }
44
46
  end
45
47
  end
46
48
 
@@ -97,22 +99,32 @@ module Abid
97
99
  application.trace "** Execute #{name_with_params}"
98
100
  end
99
101
 
100
- play_class.hooks[:before].each { |blk| play.instance_eval(&blk) }
102
+ play.run
103
+ end
104
+
105
+ def concerned?
106
+ state.reload
101
107
 
102
- call_around_hooks(play_class.hooks[:around]) { play.run }
108
+ if !application.options.repair && state.failed? && !top_level?
109
+ fail "#{name} -- task has been failed"
110
+ end
103
111
 
104
- play_class.hooks[:after].each { |blk| play.instance_eval(&blk) }
112
+ application.options.repair || !state.successed?
105
113
  end
106
114
 
107
- def call_around_hooks(hooks, &body)
108
- if hooks.empty?
109
- body.call
110
- else
111
- h, *rest = hooks
112
- play.instance_exec(-> { call_around_hooks(rest, &body) }, &h)
113
- end
115
+ def needed?
116
+ !state.successed? || prerequisite_tasks.any? { |t| t.session.successed? }
117
+ end
118
+
119
+ def bind_play_hooks(tag, to = nil)
120
+ to ||= tag
121
+ hooks[to] = [proc { |*args| call_play_hooks(tag, *args) }]
122
+ end
123
+
124
+ def call_play_hooks(tag, *args)
125
+ return unless bound?
126
+ play_class.hooks[tag].each { |blk| play.instance_exec(*args, &blk) }
114
127
  end
115
- private :call_around_hooks
116
128
 
117
129
  class <<self
118
130
  def define_play(*args, &block) # :nodoc:
data/lib/abid/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Abid
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: abid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hikaru Ojima
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-03-23 00:00:00.000000000 Z
11
+ date: 2016-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -159,13 +159,12 @@ files:
159
159
  - lib/abid.rb
160
160
  - lib/abid/abid_module.rb
161
161
  - lib/abid/application.rb
162
- - lib/abid/concurrent_extention.rb
163
- - lib/abid/concurrent_extention/ivar.rb
164
162
  - lib/abid/dsl_definition.rb
165
163
  - lib/abid/params_parser.rb
166
164
  - lib/abid/play.rb
167
165
  - lib/abid/rake_extensions.rb
168
166
  - lib/abid/rake_extensions/task.rb
167
+ - lib/abid/session.rb
169
168
  - lib/abid/state.rb
170
169
  - lib/abid/task.rb
171
170
  - lib/abid/task_manager.rb
@@ -1,6 +0,0 @@
1
- module Abid
2
- module ConcurrentExtention
3
- require 'abid/concurrent_extention/ivar'
4
- Concurrent::IVar.include IVar
5
- end
6
- end
@@ -1,12 +0,0 @@
1
- module Abid
2
- module ConcurrentExtention
3
- module IVar
4
- def try_fail(reason = StandardError.new)
5
- self.fail(reason)
6
- true
7
- rescue Concurrent::MultipleAssignmentError
8
- false
9
- end
10
- end
11
- end
12
- end