pwrake 0.9.3

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,171 @@
1
+ module Pwrake
2
+
3
+ InvocationChain = Rake::InvocationChain
4
+ TaskArguments = Rake::TaskArguments
5
+
6
+ module TaskAlgorithm
7
+
8
+ def location
9
+ @location ||= []
10
+ end
11
+
12
+ def location=(a)
13
+ @location = a
14
+ end
15
+
16
+
17
+ def invoke_modify(*args)
18
+ task_args = TaskArguments.new(arg_names, args)
19
+ flag = application.pwrake_options['HALT_QUEUE_WHILE_SEARCH']
20
+ start_time = Time.now
21
+ if flag
22
+ application.task_queue.enq_synchronize do
23
+ search_with_call_chain(self, task_args, InvocationChain::EMPTY)
24
+ end
25
+ else
26
+ search_with_call_chain(self, task_args, InvocationChain::EMPTY)
27
+ end
28
+ Log.info "-- search_tasks %.6fs" % (Time.now-start_time)
29
+
30
+ if conn = Pwrake.current_shell
31
+ @waiting_thread = nil
32
+ application.thread_loop(conn,self)
33
+ else
34
+ @waiting_thread = Thread.current
35
+ Log.debug "--- sleep in invoke_modify #{self.name} #{@waiting_thread.inspect}"
36
+ sleep
37
+ Log.debug "--- awake in invoke_modify"
38
+ pw_invoke
39
+ end
40
+ end
41
+
42
+
43
+ def pw_invoke
44
+ shell = Pwrake.current_shell
45
+ if shell
46
+ shell.current_task = self
47
+ log_host(shell.host)
48
+ end
49
+ @lock.synchronize do
50
+ return if @already_invoked
51
+ @already_invoked = true
52
+ end
53
+ pw_execute(@arg_data) if needed?
54
+ start_time = Time.now
55
+ pw_enq_subsequents
56
+ Log.info "-- pw_enq_subsequents(%s) %.6fs"%[self.name,Time.now-start_time]
57
+ shell.current_task = nil if shell
58
+ end
59
+
60
+
61
+ # Execute the actions associated with this task.
62
+ def pw_execute(args=nil)
63
+ args ||= Rake::EMPTY_TASK_ARGS
64
+ if application.options.dryrun
65
+ Log.info "** Execute (dry run) #{name}"
66
+ return
67
+ end
68
+ if application.options.trace
69
+ Log.info "** Execute #{name}"
70
+ end
71
+ application.enhance_with_matching_rule(name) if @actions.empty?
72
+ @actions.each do |act|
73
+ case act.arity
74
+ when 1
75
+ act.call(self)
76
+ else
77
+ act.call(self, args)
78
+ end
79
+ end
80
+ end
81
+
82
+ def pw_enq_subsequents
83
+ @lock.synchronize do
84
+ my_name = self.name
85
+ application.task_queue.enq_synchronize do
86
+ @subsequents.each do |t| # <<--- competition !!!
87
+ t && t.check_and_enq(my_name)
88
+ end
89
+ @already_finished = true # <<--- competition !!!
90
+ end
91
+ end
92
+ end
93
+
94
+ def check_and_enq(preq_name=nil)
95
+ @unfinished_prereq.delete(preq_name)
96
+ if @unfinished_prereq.empty?
97
+ if @waiting_thread
98
+ Log.debug "--- @waiting_thread.wakeup name=#{self.name} "
99
+ @waiting_thread.wakeup
100
+ else
101
+ Log.debug "--- check_and_enq enq name=#{self.name} "
102
+ application.task_queue.enq(self)
103
+ end
104
+ end
105
+ end
106
+
107
+ # Same as search, but explicitly pass a call chain to detect
108
+ # circular dependencies.
109
+ def search_with_call_chain(subseq, task_args, invocation_chain) # :nodoc:
110
+ new_chain = InvocationChain.append(self, invocation_chain)
111
+ @lock.synchronize do
112
+ if application.options.trace
113
+ Log.info "** Search #{name} #{format_search_flags}"
114
+ end
115
+
116
+ return true if @already_finished # <<--- competition !!!
117
+ @subsequents ||= []
118
+ @subsequents << subseq # <<--- competition !!!
119
+
120
+ if ! @already_searched
121
+ @already_searched = true
122
+ @arg_data = task_args
123
+ search_prerequisites(task_args, new_chain)
124
+ end
125
+ return false
126
+ end
127
+ rescue Exception => ex
128
+ add_chain_to(ex, new_chain)
129
+ raise ex
130
+ end
131
+
132
+ # Search all the prerequisites of a task.
133
+ def search_prerequisites(task_args, invocation_chain) # :nodoc:
134
+ @unfinished_prereq = @prerequisites.dup
135
+ prerequisite_tasks.each { |prereq|
136
+ #prereq_args = task_args.new_scope(prereq.arg_names) # in vain
137
+ if prereq.search_with_call_chain(self, task_args, invocation_chain)
138
+ @unfinished_prereq.delete(prereq.name)
139
+ end
140
+ }
141
+ check_and_enq
142
+ end
143
+
144
+ # Format the trace flags for display.
145
+ def format_search_flags
146
+ flags = []
147
+ flags << "finished" if @already_finished
148
+ flags << "first_time" unless @already_searched
149
+ flags << "not_needed" unless needed?
150
+ flags.empty? ? "" : "(" + flags.join(", ") + ")"
151
+ end
152
+ private :format_search_flags
153
+
154
+
155
+ def log_host(exec_host)
156
+ # exec_host = Pwrake.current_shell.host
157
+ prereq_name = @prerequisites[0]
158
+ if kind_of?(Rake::FileTask) and prereq_name
159
+ Pwrake.application.count( @location, exec_host )
160
+ if @location and @location.include? exec_host
161
+ compare = "=="
162
+ else
163
+ compare = "!="
164
+ end
165
+ Log.info "-- access to #{prereq_name}: file_host=#{@location.inspect} #{compare} exec_host=#{exec_host}"
166
+ end
167
+ end
168
+
169
+ end
170
+
171
+ end # module Pwrake
@@ -0,0 +1,167 @@
1
+ module Pwrake
2
+
3
+ class TaskQueue
4
+
5
+ def initialize(*args)
6
+ @finished = false
7
+ @halt = false
8
+ @mutex = Mutex.new
9
+ @cv = ConditionVariable.new
10
+ @th_end = []
11
+ @enable_steal = true
12
+ @q = []
13
+ @reservation = {}
14
+ @reserved_q = {}
15
+ end
16
+
17
+ attr_reader :mutex
18
+ attr_accessor :enable_steal
19
+
20
+ def reserve(item)
21
+ @reservation[item] = Thread.current
22
+ end
23
+
24
+ def halt
25
+ @mutex.synchronize do
26
+ @halt = true
27
+ end
28
+ end
29
+
30
+ def resume
31
+ @mutex.synchronize do
32
+ @halt = false
33
+ @cv.broadcast
34
+ end
35
+ end
36
+
37
+ def enq_synchronize
38
+ if @halt
39
+ yield
40
+ else
41
+ @mutex.synchronize do
42
+ yield
43
+ enq_finish
44
+ end
45
+ @cv.broadcast
46
+ end
47
+ @reserved_q.keys.each do |th|
48
+ Log.debug "--- run #{th}";
49
+ th.run
50
+ end
51
+ end
52
+
53
+ def enq_finish
54
+ end
55
+
56
+
57
+ def enq(item,hint=nil)
58
+ if th = @reservation[item]
59
+ @reserved_q[th] = item
60
+ else
61
+ enq_impl(item,hint)
62
+ end
63
+ end
64
+
65
+
66
+ def enq_bak(item,hint=nil)
67
+ # Log.debug "--- #{self.class}#enq #{item.inspect}"
68
+ th = nil
69
+ if @halt
70
+ if th = @reservation[item]
71
+ @reserved_q[th] = item
72
+ else
73
+ enq_impl(item,hint)
74
+ end
75
+ else
76
+ @mutex.synchronize do
77
+ if th = @reservation[item]
78
+ @reserved_q[th] = item
79
+ else
80
+ enq_impl(item,hint)
81
+ @cv.signal
82
+ end
83
+ end
84
+ end
85
+ @reserved_q.keys.each{|th|
86
+ Log.debug "--- run #{th}";
87
+ th.run
88
+ }
89
+ end
90
+
91
+ def enq_impl(item,hint)
92
+ @q.push(item) # FIFO Queue
93
+ end
94
+
95
+
96
+ def deq(hint=nil)
97
+ # Log.debug "--- #{self.class}#deq #{self.inspect}"
98
+ n = 0
99
+ loop do
100
+ @mutex.synchronize do
101
+ if @th_end.first == Thread.current
102
+ @th_end.shift
103
+ return false
104
+
105
+ elsif @halt
106
+ Log.debug "--- halt in #{self.class}#deq @q=#{@q.inspect}"
107
+ @cv.wait(@mutex)
108
+ n = 0
109
+
110
+ elsif item = @reserved_q.delete(Thread.current)
111
+ Log.debug "--- deq from reserved_q=#{item.inspect}"
112
+ return item
113
+
114
+ elsif empty? # no item in queue
115
+ Log.debug "--- empty=true in #{self.class}#deq @finished=#{@finished.inspect}"
116
+ if @finished
117
+ @cv.signal
118
+ return false
119
+ end
120
+ Log.debug "--- waiting in #{self.class}#deq @finished=#{@finished.inspect}"
121
+ @cv.wait(@mutex)
122
+ n = 0
123
+
124
+ else
125
+ if t = deq_impl(hint,n)
126
+ Log.debug "--- #{self.class}#deq #{t.inspect}"
127
+ return t
128
+ end
129
+ n += 1
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ def deq_impl(hint,n)
136
+ @q.shift # FIFO Queue
137
+ end
138
+
139
+ def clear
140
+ @q.clear
141
+ @reserved_q.clear
142
+ end
143
+
144
+ def empty?
145
+ @q.empty? && @reserved_q.empty?
146
+ end
147
+
148
+ def finish
149
+ Log.debug "--- #{self.class}#finish"
150
+ @finished = true
151
+ @cv.signal
152
+ end
153
+
154
+ def stop
155
+ @mutex.synchronize do
156
+ clear
157
+ finish
158
+ end
159
+ end
160
+
161
+ def thread_end(th)
162
+ @th_end.push(th)
163
+ @cv.broadcast
164
+ end
165
+ end
166
+
167
+ end
@@ -0,0 +1,22 @@
1
+ module Pwrake
2
+
3
+ class Timer
4
+
5
+ def initialize(prefix,*extra)
6
+ @prefix = prefix
7
+ @start_time = Time.now
8
+ str = "%s[start]:%s %s" %
9
+ [@prefix, Log.fmt_time(@start_time), extra.join(' ')]
10
+ Log.info(str)
11
+ end
12
+
13
+ def finish(*extra)
14
+ end_time = Time.now
15
+ elap_time = end_time - @start_time
16
+ str = "%s[end]:%s elap=%.3f %s" %
17
+ [@prefix, Log.fmt_time(end_time), elap_time, extra.join(' ')]
18
+ Log.info(str)
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module Pwrake
2
+ VERSION = "0.9.3"
3
+ end
data/lib/pwrake.rb ADDED
@@ -0,0 +1,18 @@
1
+ require "thread"
2
+ require "pathname"
3
+
4
+ require "pwrake/version"
5
+
6
+ require "pwrake/logger"
7
+ require "pwrake/timer"
8
+ require "pwrake/counter"
9
+ require "pwrake/file_utils"
10
+ require "pwrake/profiler"
11
+ require "pwrake/shell"
12
+ require "pwrake/task_algorithm"
13
+ require "pwrake/task_queue"
14
+ require "pwrake/option"
15
+ require "pwrake/master"
16
+ require "pwrake/application"
17
+
18
+ require "pwrake/rake_modify"
data/pwrake.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pwrake/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "pwrake"
8
+ gem.version = Pwrake::VERSION
9
+ gem.authors = ["Masahiro TANAKA"]
10
+ gem.email = ["masa16.tanaka@gmail.com"]
11
+ gem.description = %q{Parallel workflow extension for Rake}
12
+ gem.summary = %q{Adding Parallel and Distributed feature to Rake}
13
+ gem.homepage = "http://masa16.github.com/pwrake"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ end