procrastinate 0.3.1 → 0.4.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.
- data/HISTORY.txt +37 -0
- data/README +4 -0
- data/Rakefile +28 -59
- data/examples/locking.rb +26 -0
- data/examples/pascal.rb +30 -0
- data/examples/simple.rb +20 -0
- data/examples/throttled.rb +22 -0
- data/lib/procrastinate/implicit.rb +11 -0
- data/lib/procrastinate/process_manager.rb +35 -20
- data/lib/procrastinate/process_manager/child_process.rb +24 -7
- data/lib/procrastinate/proxy.rb +2 -1
- data/lib/procrastinate/scheduler.rb +13 -3
- data/lib/procrastinate/task.rb +1 -1
- data/lib/procrastinate/task/callable.rb +27 -0
- data/lib/procrastinate/task/result.rb +2 -1
- data/lib/procrastinate/utils/one_time_flag.rb +17 -1
- metadata +17 -9
- data/lib/procrastinate/task/method_call.rb +0 -35
data/HISTORY.txt
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
= 0.4.0 / ???
|
2
|
+
|
3
|
+
+ Schedules blocks as well (Scheduler#schedule).
|
4
|
+
|
5
|
+
! Fixes a small timing bug that would provoke a ChildDeath where the child
|
6
|
+
exited correctly in reality.
|
7
|
+
|
8
|
+
! Fixes a data race on the result variable. This would have your process
|
9
|
+
hang forever waiting for a result that was already available.
|
10
|
+
|
11
|
+
= 0.3.1 / 13Sep2011
|
12
|
+
|
13
|
+
! fix processor detection for Mac OS X Lion
|
14
|
+
|
15
|
+
= 0.3.0 / 27Dez2010
|
16
|
+
* create_proxy is now just 'proxy' to friends. This looks cleaner.
|
17
|
+
|
18
|
+
* require 'procrastinate/implicit' allows ignoring the scheduler in daily
|
19
|
+
usage. This is probably what will be more common.
|
20
|
+
|
21
|
+
* Auto-detection of the number of cores procrastinate runs on.
|
22
|
+
(SpawnStrategy::Default)
|
23
|
+
|
24
|
+
* Ruby 1.8 now uses absolutely poor busy-loop synchs. This is still better
|
25
|
+
than inserting puts in the code to unblock some magical mystical internal
|
26
|
+
state. I'll try to find a better solution.
|
27
|
+
|
28
|
+
= 0.2.0 / 22Dez2010
|
29
|
+
* Big rewrite, trying to make things more clear. Also: There might be more
|
30
|
+
features lurking in there.
|
31
|
+
|
32
|
+
* All proxy method calls now have a return value that works like a future:
|
33
|
+
Access to the real return value through future.value, which might block.
|
34
|
+
|
35
|
+
= 0.1.0 / 10Dez2010
|
36
|
+
|
37
|
+
* Initial version
|
data/README
CHANGED
@@ -60,6 +60,10 @@ This library runs with at least Ruby 1.8.7 and Ruby 1.9.
|
|
60
60
|
Ruby 1.9-p136 users must use this patch:
|
61
61
|
https://gist.github.com/762807
|
62
62
|
|
63
|
+
As a general remark: Interaction with Ruby versions is significant. Please
|
64
|
+
use the latest version available to you, since fork & threading bugs are
|
65
|
+
likely to be fixed there.
|
66
|
+
|
63
67
|
KNOWN BUGS
|
64
68
|
|
65
69
|
Due to the way we handle signal traps, you cannot start more than one
|
data/Rakefile
CHANGED
@@ -1,70 +1,39 @@
|
|
1
|
-
require 'rspec'
|
2
|
-
require 'rspec/core/rake_task'
|
3
|
-
Rspec::Core::RakeTask.new
|
4
|
-
task :default => :spec
|
5
|
-
|
6
1
|
require "rubygems"
|
7
|
-
require "
|
8
|
-
require
|
9
|
-
|
10
|
-
# This builds the actual gem. For details of what all these options
|
11
|
-
# mean, and other ones you can add, check the documentation here:
|
12
|
-
#
|
13
|
-
# http://rubygems.org/read/chapter/20
|
14
|
-
#
|
15
|
-
spec = Gem::Specification.new do |s|
|
16
|
-
|
17
|
-
# Change these as appropriate
|
18
|
-
s.name = "procrastinate"
|
19
|
-
s.version = "0.3.1"
|
20
|
-
s.summary = "Framework to run tasks in separate processes."
|
21
|
-
s.authors = ['Kaspar Schiess', 'Patrick Marchi']
|
22
|
-
s.email = ['kaspar.schiess@absurd.li', 'mail@patrickmarchi.ch']
|
23
|
-
s.homepage = "http://github.com/kschiess/procrastinate"
|
24
|
-
|
25
|
-
s.has_rdoc = true
|
26
|
-
s.extra_rdoc_files = %w(README)
|
27
|
-
s.rdoc_options = %w(--main README)
|
2
|
+
require "rdoc/task"
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
require 'rubygems/package_task'
|
28
5
|
|
29
|
-
|
30
|
-
|
31
|
-
s.require_paths = ["lib"]
|
6
|
+
desc "Run all tests: Exhaustive."
|
7
|
+
RSpec::Core::RakeTask.new
|
32
8
|
|
33
|
-
|
34
|
-
# relevant versions
|
35
|
-
s.add_dependency("state_machine", "~> 0.9.4")
|
9
|
+
task :default => :spec
|
36
10
|
|
37
|
-
|
38
|
-
|
39
|
-
|
11
|
+
task :stats do
|
12
|
+
%w(lib spec).each do |path|
|
13
|
+
printf "%10s:", path
|
14
|
+
system %Q(find #{path} -name "*.rb" | xargs wc -l | grep total)
|
15
|
+
end
|
40
16
|
end
|
41
17
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
18
|
+
# Generate documentation
|
19
|
+
RDoc::Task.new do |rdoc|
|
20
|
+
rdoc.title = "procrastinate - a framework to run tasks in separate processes."
|
21
|
+
rdoc.options << '--line-numbers'
|
22
|
+
rdoc.options << '--fmt' << 'shtml' # explictly set shtml generator
|
23
|
+
rdoc.template = 'direct' # lighter template used on railsapi.com
|
24
|
+
rdoc.main = "README"
|
25
|
+
rdoc.rdoc_files.include("README", "lib/**/*.rb")
|
26
|
+
rdoc.rdoc_dir = "rdoc"
|
47
27
|
end
|
48
28
|
|
49
|
-
|
50
|
-
|
51
|
-
# be automatically building a gem for this project. If you're not
|
52
|
-
# using GitHub, edit as appropriate.
|
53
|
-
#
|
54
|
-
# To publish your gem online, install the 'gemcutter' gem; Read more
|
55
|
-
# about that here: http://gemcutter.org/pages/gem_docs
|
56
|
-
Rake::GemPackageTask.new(spec) do |pkg|
|
57
|
-
pkg.gem_spec = spec
|
58
|
-
end
|
29
|
+
desc 'Clear out RDoc'
|
30
|
+
task :clean => [:clobber_rdoc, :clobber_package]
|
59
31
|
|
60
|
-
#
|
61
|
-
|
62
|
-
|
63
|
-
rd.rdoc_files.include("README", "lib/**/*.rb")
|
64
|
-
rd.rdoc_dir = "rdoc"
|
65
|
-
end
|
32
|
+
# This task actually builds the gem.
|
33
|
+
task :gem => :spec
|
34
|
+
spec = eval(File.read('procrastinate.gemspec'))
|
66
35
|
|
67
|
-
desc
|
68
|
-
|
69
|
-
|
36
|
+
desc "Generate the gem package."
|
37
|
+
Gem::PackageTask.new(spec) do |pkg|
|
38
|
+
# pkg.need_tar = true
|
70
39
|
end
|
data/examples/locking.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
|
4
|
+
require 'procrastinate'
|
5
|
+
|
6
|
+
class Worker < Struct.new(:runtime)
|
7
|
+
def do_work
|
8
|
+
puts "#{Process.pid}: starting..."
|
9
|
+
runtime.lock('lock') do
|
10
|
+
puts "#{Process.pid}: holds lock"
|
11
|
+
sleep 0.1
|
12
|
+
puts "#{Process.pid}: releases"
|
13
|
+
end
|
14
|
+
puts "#{Process.pid}: done."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Procrastinate::Lock.base = '/tmp' # This will have to be moved into scheduler
|
19
|
+
scheduler = Procrastinate::Scheduler.start
|
20
|
+
worker = scheduler.proxy(Worker.new(scheduler.runtime))
|
21
|
+
|
22
|
+
10.times do
|
23
|
+
worker.do_work
|
24
|
+
end
|
25
|
+
|
26
|
+
scheduler.shutdown
|
data/examples/pascal.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
# Computes Pascals triangle using procrastinate. This is mainly a stress test
|
3
|
+
# for result handling code and is NOT how I would parallelize this task!
|
4
|
+
|
5
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
6
|
+
|
7
|
+
require 'procrastinate'
|
8
|
+
require 'procrastinate/implicit'
|
9
|
+
include Procrastinate
|
10
|
+
|
11
|
+
V = Struct.new(:value) do
|
12
|
+
def ready?; true; end
|
13
|
+
end
|
14
|
+
|
15
|
+
current = [
|
16
|
+
V.new(1),
|
17
|
+
V.new(1)
|
18
|
+
]
|
19
|
+
|
20
|
+
loop do
|
21
|
+
last = current
|
22
|
+
|
23
|
+
puts last.map { |e| sprintf("%3d", e.value) }.join(' ')
|
24
|
+
|
25
|
+
current = [V.new(1)] +
|
26
|
+
last.each_cons(2).map { |l,r|
|
27
|
+
Procrastinate.schedule { l.value + r.value }
|
28
|
+
} +
|
29
|
+
[V.new(1)]
|
30
|
+
end
|
data/examples/simple.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
|
4
|
+
require 'procrastinate/implicit'
|
5
|
+
|
6
|
+
class Worker
|
7
|
+
def do_work
|
8
|
+
puts "> Starting work in process #{Process.pid}"
|
9
|
+
sleep 2
|
10
|
+
puts "< Work completed in process #{Process.pid}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
worker = Procrastinate.proxy(Worker.new)
|
15
|
+
|
16
|
+
10.times do
|
17
|
+
worker.do_work
|
18
|
+
end
|
19
|
+
|
20
|
+
Procrastinate.join
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
|
4
|
+
require 'procrastinate'
|
5
|
+
include Procrastinate
|
6
|
+
|
7
|
+
class Worker
|
8
|
+
def do_work
|
9
|
+
puts "> Starting work in process #{Process.pid}"
|
10
|
+
sleep 2
|
11
|
+
puts "< Work completed in process #{Process.pid}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
scheduler = Scheduler.start(SpawnStrategy::Throttled.new(5))
|
16
|
+
worker = scheduler.proxy(Worker.new)
|
17
|
+
|
18
|
+
10.times do
|
19
|
+
worker.do_work
|
20
|
+
end
|
21
|
+
|
22
|
+
scheduler.shutdown
|
@@ -29,6 +29,17 @@ module Procrastinate
|
|
29
29
|
end
|
30
30
|
module_function :proxy
|
31
31
|
|
32
|
+
# call-seq:
|
33
|
+
# Procrastinate.schedule { some_work }
|
34
|
+
#
|
35
|
+
# Schedules a block to be executed in its own thread. Returns the future that
|
36
|
+
# will return the blocks return value.
|
37
|
+
#
|
38
|
+
def schedule(&block)
|
39
|
+
scheduler.schedule(&block)
|
40
|
+
end
|
41
|
+
module_function :schedule
|
42
|
+
|
32
43
|
# call-seq:
|
33
44
|
# Procrastinate.join
|
34
45
|
#
|
@@ -89,9 +89,12 @@ class Procrastinate::ProcessManager
|
|
89
89
|
trap('CHLD', 'DEFAULT')
|
90
90
|
end
|
91
91
|
|
92
|
-
# Called from the child management thread, will put that thread to sleep
|
92
|
+
# Called from the child management thread, will put that thread to sleep
|
93
93
|
# until someone requests it to become active again. See #wakeup.
|
94
94
|
#
|
95
|
+
# This method also depletes the child queue, reading end of processing
|
96
|
+
# messages from all childs and dispatching them to the children.
|
97
|
+
#
|
95
98
|
def wait_for_event
|
96
99
|
cp_read_end = control_pipe.first
|
97
100
|
|
@@ -100,11 +103,10 @@ class Procrastinate::ProcessManager
|
|
100
103
|
|
101
104
|
read_child_messages if ready.include? @cmc_server
|
102
105
|
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
|
107
|
-
kill_children
|
106
|
+
# Send the tracking code for the child processes the final notifications
|
107
|
+
# and remove them from the children hash. At this point we know that
|
108
|
+
# no messages are waiting in the child queue.
|
109
|
+
finalize_children
|
108
110
|
|
109
111
|
if ready.include? cp_read_end
|
110
112
|
# Consume the data (not important)
|
@@ -112,18 +114,19 @@ class Procrastinate::ProcessManager
|
|
112
114
|
return
|
113
115
|
end
|
114
116
|
end
|
115
|
-
|
116
|
-
# rescue Errno::EAGAIN, Errno::EINTR
|
117
|
-
# TODO Is this needed?
|
118
|
-
# A signal has been received. Mostly, this is as if we had received
|
119
|
-
# something in the control pipe.
|
120
117
|
end
|
121
118
|
|
122
|
-
def
|
123
|
-
children.
|
119
|
+
def finalize_children
|
120
|
+
children.
|
121
|
+
select { |pid, child| child.stopped? }.
|
122
|
+
each { |pid, child| child.finalize }
|
123
|
+
|
124
|
+
children.delete_if { |pid, child|
|
125
|
+
child.removable? }
|
124
126
|
end
|
125
127
|
|
126
|
-
# Once the @cmc_server endpoint is ready, loops and reads all child
|
128
|
+
# Once the @cmc_server endpoint is ready, loops and reads all child
|
129
|
+
# communication.
|
127
130
|
#
|
128
131
|
def read_child_messages
|
129
132
|
loop do
|
@@ -138,15 +141,18 @@ class Procrastinate::ProcessManager
|
|
138
141
|
# that still needs decoding.
|
139
142
|
#
|
140
143
|
def decode_and_handle_message(msg)
|
141
|
-
pid, obj =
|
144
|
+
pid, obj = begin
|
145
|
+
Marshal.load(msg)
|
146
|
+
rescue => b
|
147
|
+
# Messages that cannot be unmarshalled will be ignored.
|
148
|
+
warn "Can't unmarshal child communication: #{b}"
|
149
|
+
end
|
150
|
+
|
142
151
|
if child=children[pid]
|
143
152
|
child.incoming_message(obj)
|
144
153
|
else
|
145
154
|
warn "Communication from child #{pid} received, but child is gone."
|
146
155
|
end
|
147
|
-
rescue => b
|
148
|
-
# Messages that cannot be unmarshalled will be ignored.
|
149
|
-
warn "Can't unmarshal child communication."
|
150
156
|
end
|
151
157
|
|
152
158
|
# Calls completion handlers for all the childs that have now exited.
|
@@ -158,7 +164,11 @@ class Procrastinate::ProcessManager
|
|
158
164
|
|
159
165
|
# Trigger the completion callback
|
160
166
|
if child=children[child_pid]
|
161
|
-
child.
|
167
|
+
child.sigchld_received
|
168
|
+
# Maybe there are messages queued for this child. If nothing is queued
|
169
|
+
# up, the thread will hang in the select in #wait_for_event unless
|
170
|
+
# we wake it up.
|
171
|
+
wakeup
|
162
172
|
end
|
163
173
|
end
|
164
174
|
rescue Errno::ECHILD
|
@@ -190,11 +200,16 @@ class Procrastinate::ProcessManager
|
|
190
200
|
|
191
201
|
exit! # this seems to be needed to avoid rspecs cleanup tasks
|
192
202
|
end
|
203
|
+
|
204
|
+
# This should never fire: New children are spawned only after we loose
|
205
|
+
# track of the old ones because they have been successfully processed.
|
206
|
+
fail "PID REUSE!" if children.has_key?(pid)
|
193
207
|
|
194
208
|
# The spawning is done in the same thread as the reaping is done. This is
|
195
209
|
# why no race condition to the following line exists. (or in other code,
|
196
210
|
# for that matter.)
|
197
|
-
children[pid] = ChildProcess.new(completion_handler, result).
|
211
|
+
children[pid] = ChildProcess.new(completion_handler, result).
|
212
|
+
tap { |s| s.start }
|
198
213
|
end
|
199
214
|
|
200
215
|
# Gets executed in child process to clean up file handles and pipes that the
|
@@ -2,21 +2,38 @@
|
|
2
2
|
# a child exits and the object that will handle child-master communication
|
3
3
|
# if desired.
|
4
4
|
#
|
5
|
-
|
5
|
+
Procrastinate::ProcessManager::ChildProcess =
|
6
|
+
Struct.new(:handler, :result, :state) do
|
7
|
+
|
8
|
+
def initialize(handler, result)
|
9
|
+
super(handler, result, "new")
|
10
|
+
end
|
11
|
+
|
6
12
|
state_machine :state, :initial => :new do
|
7
|
-
event(:start)
|
8
|
-
event(:
|
13
|
+
event(:start) { transition :new => :running }
|
14
|
+
event(:sigchld_received) { transition :running => :stopped }
|
15
|
+
event(:finalize) { transition :stopped => :removable }
|
9
16
|
|
10
|
-
after_transition :on => :
|
17
|
+
after_transition :on => :sigchld_received,
|
18
|
+
:do => :call_completion_handlers
|
19
|
+
after_transition :on => :finalize,
|
20
|
+
:do => :notify_result
|
11
21
|
end
|
12
22
|
|
13
|
-
# Calls the completion handler for the child.
|
14
|
-
#
|
23
|
+
# Calls the completion handler for the child. At this stage, the process is
|
24
|
+
# not around anymore, but we still need to do some tracking.
|
15
25
|
#
|
16
26
|
def call_completion_handlers
|
17
|
-
result.process_died if result
|
18
27
|
handler.call if handler
|
19
28
|
end
|
29
|
+
|
30
|
+
# Notifies the childs result value that the child has died and that no
|
31
|
+
# more messages will be read. If this is the only notification, the result
|
32
|
+
# will yield a ChildDeath exception.
|
33
|
+
#
|
34
|
+
def notify_result
|
35
|
+
result.process_died if result
|
36
|
+
end
|
20
37
|
|
21
38
|
# Handles incoming messages from the tasks process.
|
22
39
|
#
|
data/lib/procrastinate/proxy.rb
CHANGED
@@ -17,7 +17,8 @@ class Procrastinate::Proxy
|
|
17
17
|
|
18
18
|
def method_missing(name, *args, &block)
|
19
19
|
if respond_to? name
|
20
|
-
task = Procrastinate::Task::
|
20
|
+
task = Procrastinate::Task::Callable.new(
|
21
|
+
lambda { @worker.send(name, *args, &block) })
|
21
22
|
@scheduler.schedule(task)
|
22
23
|
|
23
24
|
return task.result
|
@@ -57,12 +57,22 @@ class Procrastinate::Scheduler
|
|
57
57
|
# Called by the proxy to schedule work. You can implement your own Task
|
58
58
|
# classes; the relevant interface consists of only a #run method.
|
59
59
|
#
|
60
|
-
def schedule(task)
|
60
|
+
def schedule(task=nil, &block)
|
61
61
|
fail "Shutting down..." if @state != :running
|
62
|
+
|
63
|
+
fail ArgumentError, "Either task or block must be given." \
|
64
|
+
if !task && !block
|
65
|
+
|
66
|
+
if block
|
67
|
+
task = Procrastinate::Task::Callable.new(block)
|
68
|
+
end
|
69
|
+
|
62
70
|
task_queue << task
|
63
71
|
|
64
72
|
# Create an occasion for spawning
|
65
73
|
manager.wakeup
|
74
|
+
|
75
|
+
task.result
|
66
76
|
end
|
67
77
|
|
68
78
|
# Waits for the currently queued work to complete. This can be used at the
|
@@ -90,14 +100,14 @@ class Procrastinate::Scheduler
|
|
90
100
|
#
|
91
101
|
def shutdown(hard=false)
|
92
102
|
join unless hard
|
93
|
-
|
103
|
+
|
94
104
|
# Set the flag that will provoke shutdown
|
95
105
|
@state = :real_shutdown
|
96
106
|
# Wake the manager up, making it check the flag
|
97
107
|
manager.wakeup
|
98
108
|
# Wait for the manager to finish its work. This waits for child processes
|
99
109
|
# and then reaps their result, avoiding zombies.
|
100
|
-
@thread.join
|
110
|
+
@thread.join if @thread
|
101
111
|
end
|
102
112
|
|
103
113
|
private
|
data/lib/procrastinate/task.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
require 'procrastinate/task/result'
|
3
|
+
|
4
|
+
module Procrastinate::Task
|
5
|
+
# Calls the block and returns the result of execution.
|
6
|
+
#
|
7
|
+
class Callable
|
8
|
+
attr_reader :block
|
9
|
+
attr_reader :result
|
10
|
+
|
11
|
+
def initialize(block)
|
12
|
+
@b = block
|
13
|
+
@result = Result.new
|
14
|
+
end
|
15
|
+
|
16
|
+
# Runs this task. Gets passed an endpoint that can be used to communicate
|
17
|
+
# values back to the master. Every time you write a value to that endpoint
|
18
|
+
# (using #send), the server will call #incoming_message on the task object
|
19
|
+
# in the master process. This allows return values and other communication
|
20
|
+
# from children to the master (and to the caller in this case).
|
21
|
+
#
|
22
|
+
def run(endpoint)
|
23
|
+
r = @b.call
|
24
|
+
endpoint.send r if endpoint
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -15,7 +15,8 @@ class Procrastinate::Task::Result
|
|
15
15
|
# *control thread*
|
16
16
|
#
|
17
17
|
def incoming_message(obj)
|
18
|
-
|
18
|
+
fail "Read child answer too late, already marked dead." if ready? && @exception
|
19
|
+
fail "Double child answer." if ready?
|
19
20
|
|
20
21
|
@value = obj
|
21
22
|
@value_ready.set
|
@@ -1,3 +1,17 @@
|
|
1
|
+
|
2
|
+
# A flag that will allow threads to wait until it is set. Once it is set, it
|
3
|
+
# cannot be unset.
|
4
|
+
#
|
5
|
+
# Guarantees that this class tries to make:
|
6
|
+
#
|
7
|
+
# 1) No thread will start waiting for the flag once it is already set. There
|
8
|
+
# no set-hole, meaning that no thread goes to sleep while we're waking up
|
9
|
+
# threads because the flag has been set.
|
10
|
+
#
|
11
|
+
# Candidate stdlib classes violate some of these guarantees, here are some
|
12
|
+
# candidates:
|
13
|
+
# * ConditionVariable - violates 1)
|
14
|
+
#
|
1
15
|
class Procrastinate::Utils::OneTimeFlag
|
2
16
|
def initialize
|
3
17
|
@waiting = []
|
@@ -12,7 +26,9 @@ class Procrastinate::Utils::OneTimeFlag
|
|
12
26
|
|
13
27
|
@waiting_m.synchronize do
|
14
28
|
@waiting << Thread.current
|
15
|
-
|
29
|
+
until set?
|
30
|
+
@waiting_m.sleep(0.001)
|
31
|
+
end
|
16
32
|
end
|
17
33
|
end
|
18
34
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: procrastinate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-12-16 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: state_machine
|
17
|
-
requirement: &
|
17
|
+
requirement: &70314600072220 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: 0.9.4
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70314600072220
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: rspec
|
28
|
-
requirement: &
|
28
|
+
requirement: &70314600070260 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70314600070260
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: flexmock
|
39
|
-
requirement: &
|
39
|
+
requirement: &70314600069360 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,7 +44,7 @@ dependencies:
|
|
44
44
|
version: '0'
|
45
45
|
type: :development
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *70314600069360
|
48
48
|
description:
|
49
49
|
email:
|
50
50
|
- kaspar.schiess@absurd.li
|
@@ -54,6 +54,7 @@ extensions: []
|
|
54
54
|
extra_rdoc_files:
|
55
55
|
- README
|
56
56
|
files:
|
57
|
+
- HISTORY.txt
|
57
58
|
- LICENSE
|
58
59
|
- Rakefile
|
59
60
|
- README
|
@@ -71,13 +72,17 @@ files:
|
|
71
72
|
- lib/procrastinate/spawn_strategy/simple.rb
|
72
73
|
- lib/procrastinate/spawn_strategy/throttled.rb
|
73
74
|
- lib/procrastinate/spawn_strategy.rb
|
74
|
-
- lib/procrastinate/task/
|
75
|
+
- lib/procrastinate/task/callable.rb
|
75
76
|
- lib/procrastinate/task/result.rb
|
76
77
|
- lib/procrastinate/task.rb
|
77
78
|
- lib/procrastinate/utils/one_time_flag.rb
|
78
79
|
- lib/procrastinate/utils/one_time_flag_ruby18_shim.rb
|
79
80
|
- lib/procrastinate/utils.rb
|
80
81
|
- lib/procrastinate.rb
|
82
|
+
- examples/locking.rb
|
83
|
+
- examples/pascal.rb
|
84
|
+
- examples/simple.rb
|
85
|
+
- examples/throttled.rb
|
81
86
|
homepage: http://github.com/kschiess/procrastinate
|
82
87
|
licenses: []
|
83
88
|
post_install_message:
|
@@ -92,6 +97,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
92
97
|
- - ! '>='
|
93
98
|
- !ruby/object:Gem::Version
|
94
99
|
version: '0'
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
hash: 4532369798177350543
|
95
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
104
|
none: false
|
97
105
|
requirements:
|
@@ -1,35 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'procrastinate/task/result'
|
3
|
-
|
4
|
-
# Constructs an object of type +klass+ and calls a method on it.
|
5
|
-
#
|
6
|
-
class Procrastinate::Task::MethodCall
|
7
|
-
include Procrastinate::Task
|
8
|
-
|
9
|
-
attr_reader :i
|
10
|
-
attr_reader :m
|
11
|
-
attr_reader :a
|
12
|
-
attr_reader :b
|
13
|
-
|
14
|
-
def initialize(instance, method, arguments, block)
|
15
|
-
@i = instance
|
16
|
-
@m = method
|
17
|
-
@a = arguments
|
18
|
-
@b = block
|
19
|
-
end
|
20
|
-
|
21
|
-
# Runs this task. Gets passed an endpoint that can be used to communicate
|
22
|
-
# values back to the master. Every time you write a value to that endpoint
|
23
|
-
# (using #send), the server will call #incoming_message on the task object
|
24
|
-
# in the master process. This allows return values and other communication
|
25
|
-
# from children to the master (and to the caller in this case).
|
26
|
-
#
|
27
|
-
def run(endpoint)
|
28
|
-
r = @i.send(@m, *@a, &@b)
|
29
|
-
endpoint.send r if endpoint
|
30
|
-
end
|
31
|
-
|
32
|
-
def result
|
33
|
-
@result ||= Result.new
|
34
|
-
end
|
35
|
-
end
|