comp_tree 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,38 @@
1
+
2
+ class Jumpstart
3
+ #
4
+ # Mixin for lazily-evaluated attributes.
5
+ #
6
+ module LazyAttribute
7
+ #
8
+ # &block is evaluated when this attribute is requested. The same
9
+ # result is returned for subsequent calls until the attribute is
10
+ # assigned a different value.
11
+ #
12
+ def attribute(reader, &block)
13
+ writer = "#{reader}="
14
+
15
+ singleton = (class << self ; self ; end)
16
+
17
+ define_evaluated_reader = lambda { |value|
18
+ singleton.class_eval {
19
+ remove_method(reader)
20
+ define_method(reader) { value }
21
+ }
22
+ }
23
+
24
+ singleton.class_eval {
25
+ define_method(reader) {
26
+ value = block.call
27
+ define_evaluated_reader.call(value)
28
+ value
29
+ }
30
+
31
+ define_method(writer) { |value|
32
+ define_evaluated_reader.call(value)
33
+ value
34
+ }
35
+ }
36
+ end
37
+ end
38
+ end
@@ -1,7 +1,7 @@
1
1
 
2
2
  require 'rbconfig'
3
3
 
4
- module Jumpstart
4
+ class Jumpstart
5
5
  module Ruby
6
6
  EXECUTABLE = lambda {
7
7
  name = File.join(
@@ -3,7 +3,7 @@ require 'rbconfig'
3
3
  require 'fileutils'
4
4
  require 'find'
5
5
 
6
- module Jumpstart
6
+ class Jumpstart
7
7
  class SimpleInstaller
8
8
  def initialize
9
9
  dest_root = Config::CONFIG["sitelibdir"]
data/lib/comp_tree.rb CHANGED
@@ -28,6 +28,8 @@ require 'comp_tree/driver'
28
28
  # See README.rdoc.
29
29
  #
30
30
  module CompTree
31
+ VERSION = "0.7.2"
32
+
31
33
  class << self
32
34
  #
33
35
  # :call-seq:
@@ -1,162 +1,81 @@
1
1
 
2
2
  module CompTree
3
3
  module Algorithm
4
- LEAVE = :__comp_tree_leave
5
- AGAIN = :__comp_tree_again
6
-
7
4
  module_function
8
5
 
9
- def loop_with(leave, again)
10
- catch(leave) {
11
- while true
12
- catch(again) {
13
- yield
14
- }
15
- end
16
- }
17
- end
18
-
19
6
  def compute_parallel(root, num_threads)
20
- #trace "Computing #{root.name} with #{num_threads} threads"
21
- finished = nil
22
- tree_mutex = Mutex.new
23
- condition = ConditionVariable.new
24
- num_threads_ready = 0
7
+ to_workers = Queue.new
8
+ from_workers = Queue.new
25
9
 
26
- threads = (0...num_threads).map { |thread_index|
27
- Thread.new {
28
- #
29
- # wait for main thread
30
- #
31
- tree_mutex.synchronize {
32
- #trace "Thread #{thread_index} waiting to start"
33
- num_threads_ready += 1
34
- condition.wait(tree_mutex)
35
- }
10
+ node_to_worker = nil
11
+ node_from_worker = nil
36
12
 
37
- loop_with(LEAVE, AGAIN) {
38
- node = tree_mutex.synchronize {
39
- #trace "Thread #{thread_index} acquired tree lock; begin search"
40
- if finished
41
- #trace "Thread #{thread_index} detected finish"
42
- num_threads_ready -= 1
43
- throw LEAVE
44
- else
45
- #
46
- # Find a node. The node we obtain, if any, will be locked.
47
- #
48
- node = find_node(root)
49
- if node
50
- #trace "Thread #{thread_index} found node #{node.name}"
51
- node
52
- else
53
- #trace "Thread #{thread_index}: no node found; sleeping."
54
- condition.wait(tree_mutex)
55
- throw AGAIN
56
- end
57
- end
58
- }
13
+ num_working = 0
14
+ finished = nil
59
15
 
60
- #trace "Thread #{thread_index} computing node"
61
- #debug {
62
- # node.trace_compute
63
- #}
16
+ workers = (1..num_threads).map {
17
+ Thread.new {
18
+ until (node = to_workers.pop) == :finished
64
19
  node.compute
65
- #trace "Thread #{thread_index} node computed; waiting for tree lock"
66
-
67
- tree_mutex.synchronize {
68
- #trace "Thread #{thread_index} acquired tree lock"
69
- #debug {
70
- # name = "#{node.name}" + ((node == root) ? " (ROOT NODE)" : "")
71
- # initial = "Thread #{thread_index} compute result for #{name}: "
72
- # status = node.computed.is_a?(Exception) ? "error" : "success"
73
- # trace initial + status
74
- # trace "Thread #{thread_index} node result: #{node.result}"
75
- #}
76
-
77
- if node.computed.is_a? Exception
78
- #
79
- # An error occurred; we are done.
80
- #
81
- finished = node.computed
82
- elsif node == root
83
- #
84
- # Root node was computed; we are done.
85
- #
86
- finished = true
87
- end
88
-
89
- #
90
- # remove locks for this node (shared lock and own lock)
91
- #
92
- node.unlock
93
-
94
- #
95
- # Tell the main thread that another node was computed.
96
- #
97
- condition.signal
98
- }
99
- }
100
- #trace "Thread #{thread_index} exiting"
20
+ from_workers.push node
21
+ end
101
22
  }
102
23
  }
103
24
 
104
- #trace "Main: waiting for threads to launch and block."
105
- until tree_mutex.synchronize { num_threads_ready == num_threads }
106
- Thread.pass
107
- end
108
-
109
- tree_mutex.synchronize {
110
- #trace "Main: entering main loop"
111
- until num_threads_ready == 0
112
- #trace "Main: waking threads"
113
- condition.broadcast
114
-
115
- if finished
116
- #trace "Main: detected finish."
25
+ while true
26
+ if num_working == num_threads or not (node_to_worker = find_node(root))
27
+ #
28
+ # max computations running or no nodes available -- wait for results
29
+ #
30
+ node_from_worker = from_workers.pop
31
+ node_from_worker.unlock
32
+ num_working -= 1
33
+ if node_from_worker == root or
34
+ node_from_worker.computed.is_a? Exception
35
+ finished = node_from_worker
117
36
  break
118
37
  end
119
-
120
- #trace "Main: waiting for a node"
121
- condition.wait(tree_mutex)
122
- #trace "Main: got a node"
38
+ elsif node_to_worker
39
+ #
40
+ # found a node
41
+ #
42
+ to_workers.push node_to_worker
43
+ num_working += 1
44
+ node_to_worker = nil
123
45
  end
124
- }
125
-
126
- #trace "Main: waiting for threads to finish."
127
- threads.each { |t| t.join }
128
-
129
- #trace "Main: computation done."
130
- if finished.is_a? Exception
131
- raise finished
46
+ end
47
+
48
+ num_threads.times { to_workers.push :finished }
49
+ workers.each { |t| t.join }
50
+
51
+ if finished.computed.is_a? Exception
52
+ raise finished.computed
132
53
  else
133
- root.result
54
+ finished.result
134
55
  end
135
56
  end
136
57
 
137
58
  def find_node(node)
138
- # --- only called inside shared tree mutex
139
- #trace "Looking for a node, starting with #{node.name}"
140
59
  if node.computed
141
60
  #
142
61
  # already computed
143
62
  #
144
- #trace "#{node.name} has been computed"
145
63
  nil
146
- elsif (children_results = node.find_children_results) and node.try_lock
64
+ elsif not node.locked? and node.children_results
147
65
  #
148
- # Node is not computed and its children are computed;
149
- # and we have the lock. Ready to compute.
66
+ # Node is not computed, not locked, and its children are
67
+ # computed; Ready to compute.
150
68
  #
151
- node.children_results = children_results
69
+ node.lock
152
70
  node
153
71
  else
154
72
  #
155
73
  # locked or children not computed; recurse to children
156
74
  #
157
- #trace "Checking #{node.name}'s children"
158
75
  node.each_child { |child|
159
- next_node = find_node(child) and return next_node
76
+ found = find_node(child) and (
77
+ return found
78
+ )
160
79
  }
161
80
  nil
162
81
  end
@@ -118,7 +118,10 @@ module CompTree
118
118
  # Any uncomputed children are computed first.
119
119
  #
120
120
  def compute(name, opts)
121
- threads = opts.is_a?(Hash) ? opts[:threads] : opts
121
+ threads = (opts.is_a?(Hash) ? opts[:threads] : opts).to_i
122
+ unless threads > 0
123
+ raise ArgumentError, "number of threads must be greater than zero"
124
+ end
122
125
  root = @nodes[name]
123
126
  if root.computed
124
127
  root.result
@@ -13,9 +13,7 @@ module CompTree
13
13
  attr_accessor :function #:nodoc:
14
14
  attr_accessor :result #:nodoc:
15
15
  attr_accessor :computed #:nodoc:
16
- attr_accessor :shared_lock #:nodoc:
17
-
18
- attr_writer :children_results #:nodoc:
16
+ attr_accessor :lock_level #:nodoc:
19
17
 
20
18
  #
21
19
  # Create a node
@@ -25,7 +23,6 @@ module CompTree
25
23
  @parents = []
26
24
  @children = []
27
25
  @function = nil
28
- @mutex = Mutex.new
29
26
  reset_self
30
27
  end
31
28
 
@@ -35,7 +32,7 @@ module CompTree
35
32
  def reset_self #:nodoc:
36
33
  @result = nil
37
34
  @computed = nil
38
- @shared_lock = 0
35
+ @lock_level = 0
39
36
  @children_results = nil
40
37
  end
41
38
 
@@ -85,12 +82,9 @@ module CompTree
85
82
  # If all children have been computed, return their results;
86
83
  # otherwise return nil.
87
84
  #
88
- # Do not assign to @children_results since own lock is not
89
- # necessarily aquired.
90
- #
91
- def find_children_results #:nodoc:
85
+ def children_results #:nodoc:
92
86
  @children_results or (
93
- @children.map { |child|
87
+ @children_results = @children.map { |child|
94
88
  unless child.computed
95
89
  return nil
96
90
  end
@@ -99,16 +93,6 @@ module CompTree
99
93
  )
100
94
  end
101
95
 
102
- #def trace_compute #:nodoc:
103
- # debug {
104
- # # --- own mutex
105
- # trace "Computing #{@name}"
106
- # raise AssertionFailedError if @computed
107
- # raise AssertionFailedError unless @mutex.locked?
108
- # raise AssertionFailedError unless @children_results
109
- # }
110
- #end
111
-
112
96
  #
113
97
  # Compute this node; children must be computed and lock must be
114
98
  # already acquired.
@@ -127,35 +111,20 @@ module CompTree
127
111
  @result
128
112
  end
129
113
 
130
- def try_lock #:nodoc:
131
- # --- shared tree mutex and own mutex
132
- if @shared_lock == 0 and @mutex.try_lock
133
- #trace "Locking #{@name}"
134
- each_upward { |node|
135
- node.shared_lock += 1
136
- #trace "#{node.name} locked by #{@name}: level: #{node.shared_lock}"
137
- }
138
- true
139
- else
140
- false
141
- end
114
+ def locked?
115
+ @lock_level != 0
116
+ end
117
+
118
+ def lock #:nodoc:
119
+ each_upward { |node|
120
+ node.lock_level += 1
121
+ }
142
122
  end
143
123
 
144
124
  def unlock #:nodoc:
145
- # --- shared tree mutex and own mutex
146
- #debug {
147
- # raise AssertionFailedError unless @mutex.locked?
148
- # trace "Unlocking #{@name}"
149
- #}
150
125
  each_upward { |node|
151
- node.shared_lock -= 1
152
- #debug {
153
- # if node.shared_lock == 0
154
- # trace "#{node.name} unlocked by #{@name}"
155
- # end
156
- #}
126
+ node.lock_level -= 1
157
127
  }
158
- @mutex.unlock
159
128
  end
160
129
  end
161
130
  end
data/test/common.rb CHANGED
@@ -2,7 +2,7 @@ $LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
2
2
 
3
3
  require 'test/unit'
4
4
  require 'comp_tree'
5
- require 'benchmark'
5
+ #require 'benchmark'
6
6
 
7
7
  module TestCommon
8
8
  if ARGV.include?("--bench")
data/test/test_drain.rb CHANGED
@@ -17,9 +17,10 @@ class TestDrain < Test::Unit::TestCase
17
17
  driver.define(:height, :border, &func)
18
18
  driver.define(:border, &func)
19
19
  driver.define(:offset, &func)
20
- bench_output "number of threads: #{threads}"
21
- bench = Benchmark.measure { driver.compute(:area, threads) }
22
- bench_output bench
20
+ #bench_output "number of threads: #{threads}"
21
+ #bench = Benchmark.measure { driver.compute(:area, threads) }
22
+ driver.compute(:area, threads)
23
+ #bench_output bench
23
24
  }
24
25
  end
25
26
 
@@ -4,45 +4,65 @@ class TestException < Test::Unit::TestCase
4
4
  def test_exception
5
5
  test_error = Class.new StandardError
6
6
  [true, false].each { |define_all|
7
- error = (
8
- begin
9
- CompTree.build { |driver|
10
- driver.define(:area, :width, :height, :offset) {
11
- |width, height, offset|
12
- width*height - offset
13
- }
14
-
15
- driver.define(:width, :border) { |border|
16
- 2 + border
17
- }
18
-
19
- driver.define(:height, :border) { |border|
20
- 3 + border
21
- }
22
-
23
- if define_all
24
- driver.define(:border) {
25
- raise test_error
7
+ [true, false].each { |abort_on_exception|
8
+ error = (
9
+ begin
10
+ CompTree.build { |driver|
11
+ driver.define(:area, :width, :height, :offset) {
12
+ |width, height, offset|
13
+ width*height - offset
14
+ }
15
+
16
+ driver.define(:width, :border) { |border|
17
+ 2 + border
26
18
  }
27
- end
28
-
29
- driver.define(:offset) {
30
- 7
19
+
20
+ driver.define(:height, :border) { |border|
21
+ 3 + border
22
+ }
23
+
24
+ if define_all
25
+ driver.define(:border) {
26
+ raise test_error
27
+ }
28
+ end
29
+
30
+ driver.define(:offset) {
31
+ 7
32
+ }
33
+
34
+ begin
35
+ previous = Thread.abort_on_exception
36
+ Thread.abort_on_exception = abort_on_exception
37
+ driver.compute(:area, 99)
38
+ ensure
39
+ Thread.abort_on_exception = previous
40
+ end
31
41
  }
32
-
33
- driver.compute(:area, 99)
34
- }
35
- nil
36
- rescue => e
37
- e
42
+ nil
43
+ rescue => e
44
+ e
45
+ end
46
+ )
47
+
48
+ if define_all
49
+ assert_block { error.is_a? test_error }
50
+ else
51
+ assert_block { error.is_a? CompTree::NoFunctionError }
38
52
  end
39
- )
40
-
41
- if define_all
42
- assert_block { error.is_a? test_error }
43
- else
44
- assert_block { error.is_a? CompTree::NoFunctionError }
45
- end
53
+ }
46
54
  }
55
+ end
56
+
57
+ def test_num_threads
58
+ CompTree.build do |driver|
59
+ driver.define(:root) { }
60
+ assert_raises(ArgumentError) { driver.compute(:root, 0) }
61
+ assert_raises(ArgumentError) { driver.compute(:root, :threads => 0) }
62
+ assert_raises(ArgumentError) { driver.compute(:root, -1) }
63
+ assert_raises(ArgumentError) { driver.compute(:root, :threads => -1) }
64
+ assert_raises(ArgumentError) { driver.compute(:root, -11) }
65
+ assert_raises(ArgumentError) { driver.compute(:root, :threads => -11) }
66
+ end
47
67
  end
48
- end
68
+ end