Abundance 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/abundance.rb +94 -0
- data/lib/garden.rb +206 -0
- data/lib/gardener.rb +99 -0
- data/lib/seed.rb +42 -0
- data/lib/test.rb +121 -0
- data/lib/toolshed.rb +95 -0
- metadata +58 -0
data/lib/abundance.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# This class provides a mean to parallelize the execution of your program processes.
|
2
|
+
#
|
3
|
+
# Its caracteristics are:
|
4
|
+
# * concurent
|
5
|
+
# * non-blocking
|
6
|
+
# * simple
|
7
|
+
# * pure ruby
|
8
|
+
# * no dependency installation
|
9
|
+
#
|
10
|
+
# It:
|
11
|
+
# * scales to multi core
|
12
|
+
# * is intended for batch processing or other parallel ready operations
|
13
|
+
# * can boost you program's performance
|
14
|
+
#
|
15
|
+
# And not:
|
16
|
+
# * a replacement for Thread.new invocations
|
17
|
+
# * a replacement for Thread friendly programming languages like Erlang
|
18
|
+
#
|
19
|
+
#
|
20
|
+
# Based on Gardener,Garden,Seed natural design patern
|
21
|
+
#
|
22
|
+
# While there should be no use for this kind of class in most programs, there are some occasions where processes
|
23
|
+
# cannot live inside serialized execution without blocking. The built in threading model can save your execution in many occasions, but in many case green threading is not enough, or simply just won't work.
|
24
|
+
# For a nice explanation on the subject of ruby green threading, you can refer to: http://www.igvita.com/2008/11/13/concurrency-is-a-myth-in-ruby/
|
25
|
+
#
|
26
|
+
# Abundance is by no mean intended to equal or compete with the tools mentionned in the article, its rather a different
|
27
|
+
# approach on the same problem. I hope it will inspire some of you to hack out something that works even better,
|
28
|
+
# and hopefully, eventually this tool will become obsolete because Ruby will get concurency built in.
|
29
|
+
#
|
30
|
+
# So, the approach here is really simple, you require abundance in your program.
|
31
|
+
# Then ask the Abundance.gardener class method to build you a garden built with a garden patch block you provide as part of the invocation.
|
32
|
+
# This garden patch block includes an initialization block, which may be empty,
|
33
|
+
# and the invocation of the Abundance.grow class method, the perpetual seed ready patch garden.
|
34
|
+
# It becomes the threaded looping object, growing concurently on garden patch row forks.
|
35
|
+
#
|
36
|
+
# The gardener objected is then available to seeds and harvest the multiple garden patch row forks,
|
37
|
+
# allowing you to cultivate parallel garden rows where your seeds sprout till fruitful harvest time comes.
|
38
|
+
#
|
39
|
+
# Author:: lp (mailto:lp@spiralix.org)
|
40
|
+
# Copyright:: 2008 Louis-Philippe Perron - Released under the terms of the MIT license
|
41
|
+
#
|
42
|
+
# :title:Abundance
|
43
|
+
|
44
|
+
class Abundance
|
45
|
+
require 'garden'
|
46
|
+
require 'gardener'
|
47
|
+
require 'seed'
|
48
|
+
|
49
|
+
# The +gardener+ class method initializes a gardener instance
|
50
|
+
# with its garden supplied as a block. The invocation block must include
|
51
|
+
# the +grow+ class method and a preceeding optional initialisation section.
|
52
|
+
# === Parameters
|
53
|
+
# * :seed_size = allowed seed size in bytes
|
54
|
+
# * :rows = garden rows number, the number of concurent threads
|
55
|
+
# * :init_timeout = allow to pause execution to allow for larger gardens to initialize
|
56
|
+
# === Example
|
57
|
+
# gardener = Abundance.gardener( :block_size => 8192, :rows => 2, :init_timeout => 2) do
|
58
|
+
#
|
59
|
+
# processor = SpecialProcess.new
|
60
|
+
#
|
61
|
+
# Abundance.grow do |seed|
|
62
|
+
# command = seed.sprout
|
63
|
+
# results = processor.parse(command)
|
64
|
+
# seed.crop( true, results)
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# id1 = gardener.seed('command1')
|
69
|
+
# id2 = gardener.seed('command2')
|
70
|
+
#
|
71
|
+
# result1 = gardener.harvest(id1)
|
72
|
+
# result2 = gardener.harvest(id2)
|
73
|
+
#
|
74
|
+
# # with many more seeds over here
|
75
|
+
#
|
76
|
+
# gardener.close
|
77
|
+
|
78
|
+
def Abundance.gardener(options={:seed_size => 8192, :rows => 2, :init_timeout => 2},&gardener_block)
|
79
|
+
return Gardener.new(options,gardener_block)
|
80
|
+
end
|
81
|
+
|
82
|
+
# The +grow+ class method needs to be used inside the gardener invocation.
|
83
|
+
# A seed instance is given each time, acting as getter/setter for your queued seed commands
|
84
|
+
|
85
|
+
def Abundance.grow(&grow_block)
|
86
|
+
loop do
|
87
|
+
unless $seed.nil? || $seed.include?(:message)
|
88
|
+
grow_block.call(Seed.new)
|
89
|
+
end
|
90
|
+
Thread.stop
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
data/lib/garden.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
# These classes provides the garden part of the Gardener,Garden,Seed natural design patern
|
2
|
+
#
|
3
|
+
# The Garden is where the thread concurency is implemented,
|
4
|
+
# offering itself as a thread queue manager,
|
5
|
+
# dispatching seeds from the Gardener to its Rows child class and back again.
|
6
|
+
# Since Ruby doesn't implement Native Threads, and only Native Threads scales to multi-core execution,
|
7
|
+
# the way to implement concurent execution is through splitting the task at hand between multiple single threaded parallel executions.
|
8
|
+
# The Rows system does exactly that, using the Ruby fork function, then connecting the isolated running
|
9
|
+
# processes to the Garden, through a simple socket system provided by the Toolshed Module.
|
10
|
+
#
|
11
|
+
# Author:: lp (mailto:lp@spiralix.org)
|
12
|
+
# Copyright:: 2008 Louis-Philippe Perron - Released under the terms of the MIT license
|
13
|
+
#
|
14
|
+
# :title:Garden
|
15
|
+
|
16
|
+
class Garden
|
17
|
+
require 'toolshed'
|
18
|
+
include Toolshed
|
19
|
+
|
20
|
+
attr_reader :pid
|
21
|
+
|
22
|
+
# The +new+ class method initializes the Garden.
|
23
|
+
# As part of the Abundance lib, Garden is not initialized directly,
|
24
|
+
# but rather as a side effect of the Gardener's initialization.
|
25
|
+
# Its instance resides in the @garden Gardener's instance variable.
|
26
|
+
# Its real muscles are inaccessibles from instance method intervention,
|
27
|
+
# because of its nature as a forked Ruby process.
|
28
|
+
# === Example
|
29
|
+
# garden = Garden.new
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@pid = fork do
|
33
|
+
@quit = false
|
34
|
+
@harvest = []
|
35
|
+
@rows_port = []
|
36
|
+
@seeds = []; @sprouts = []; @crops = []; @id = 0
|
37
|
+
@socket_server = Toolshed.socket_server(Toolshed::garden_port)
|
38
|
+
@socket_client_temp = Toolshed.socket_client_temp
|
39
|
+
loop do
|
40
|
+
catch :fill_rows do
|
41
|
+
loop do
|
42
|
+
if ! @seeds.empty? && ! @rows_port.empty?
|
43
|
+
seed = @seeds.shift
|
44
|
+
@sprouts[seed[:id]] = seed
|
45
|
+
row_port = @rows_port.shift
|
46
|
+
socket_client_temp(:sprout,seed,row_port)
|
47
|
+
elsif @quit && ! @rows_port.empty?
|
48
|
+
seed = nil
|
49
|
+
row_port = @rows_port.shift
|
50
|
+
socket_client_temp(:quit,seed,row_port)
|
51
|
+
else
|
52
|
+
throw :fill_rows
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
command, data, clientport, clientname, clientaddr = socket_server_recv
|
57
|
+
case command
|
58
|
+
when :seed
|
59
|
+
@id += 1; @seeds << {:id => @id , :seed => data}
|
60
|
+
socket_server_send(command,@id,clientaddr,clientport)
|
61
|
+
when :row
|
62
|
+
if @quit
|
63
|
+
command = :quit
|
64
|
+
seed = nil
|
65
|
+
elsif @seeds.empty?
|
66
|
+
seed = nil
|
67
|
+
@rows_port << data
|
68
|
+
else
|
69
|
+
seed = @seeds.shift
|
70
|
+
@sprouts[seed[:id]] = seed
|
71
|
+
end
|
72
|
+
socket_server_send(command,seed,clientaddr,clientport)
|
73
|
+
when :crop
|
74
|
+
@sprouts[data[:id]] = nil
|
75
|
+
@crops[data[:id]] = data; socket_server_send(command,true,clientaddr,clientport)
|
76
|
+
socket_server_send(command,data, @harvest[data[:id]][:clientaddr], @harvest[data[:id]][:clientport]) if @harvest[data[:id]]
|
77
|
+
when :growth
|
78
|
+
case data
|
79
|
+
when :progress
|
80
|
+
progress = sprintf( "%.2f", @crops.size.to_f / (@crops.size + @sprouts.compact.size + @seeds.size))
|
81
|
+
socket_server_send(command,progress,clientaddr,clientport)
|
82
|
+
when :seed
|
83
|
+
socket_server_send(command,@seeds.size,clientaddr,clientport)
|
84
|
+
when :sprout
|
85
|
+
socket_server_send(command,@sprouts.compact.size,clientaddr,clientport)
|
86
|
+
when :crop
|
87
|
+
socket_server_send(command,@crops.size,clientaddr,clientport)
|
88
|
+
else
|
89
|
+
socket_server_send(command,false,clientaddr,clientport)
|
90
|
+
end
|
91
|
+
when :harvest
|
92
|
+
case data
|
93
|
+
when :all
|
94
|
+
socket_server_send(command,{:seeds => @seeds, :sprouts => @sprouts.compact, :crops => @crops},clientaddr,clientport)
|
95
|
+
when :seed
|
96
|
+
socket_server_send(command,@seeds,clientaddr,clientport)
|
97
|
+
when :sprout
|
98
|
+
socket_server_send(command,@sprouts.compact,clientaddr,clientport)
|
99
|
+
when :crop
|
100
|
+
socket_server_send(command,@crops,clientaddr,clientport)
|
101
|
+
else
|
102
|
+
if data.is_a? Integer
|
103
|
+
if @crops[data]
|
104
|
+
socket_server_send(command,@crops[data],clientaddr,clientport)
|
105
|
+
else
|
106
|
+
@harvest[data] = {:clientaddr => clientaddr, :clientport => clientport}
|
107
|
+
end
|
108
|
+
else
|
109
|
+
socket_server_send(command,false,clientaddr,clientport)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
when :close
|
113
|
+
if data[:level] == :garden
|
114
|
+
@seeds_pid = data[:pid]
|
115
|
+
@quit = true
|
116
|
+
@mem_addr = clientaddr; @mem_port = clientport
|
117
|
+
else
|
118
|
+
@seeds_pid.delete(data[:pid].to_i)
|
119
|
+
if @seeds_pid.empty?
|
120
|
+
socket_server_send(:close,{:seeds => @seeds, :sprouts => @sprouts.compact, :crops => @crops}, @mem_addr, @mem_port)
|
121
|
+
exit
|
122
|
+
end
|
123
|
+
end
|
124
|
+
else
|
125
|
+
socket_server_send(command,false,clientaddr,clientport)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
return pid
|
130
|
+
end
|
131
|
+
|
132
|
+
# The +rows+ method for the Garden instance allow instantiation of its child Rows.
|
133
|
+
# As part of the Abundance lib, Garden.rows is not invoked directly,
|
134
|
+
# but rather as a side effect of the Gardener's initialization.
|
135
|
+
# Its in reality an indirect initializer for the Rows class.
|
136
|
+
# === Parameter
|
137
|
+
# * _rows_ = garden rows number, the number of concurent threads
|
138
|
+
# * _init_timeout_ = allow to pause execution to allow for larger garden rows to initialize
|
139
|
+
# === Example
|
140
|
+
# rows = garden.rows(4,2) { grow_block }
|
141
|
+
#
|
142
|
+
|
143
|
+
def rows(rows,init_timeout,grow_block)
|
144
|
+
Rows.new(rows,init_timeout,grow_block)
|
145
|
+
end
|
146
|
+
|
147
|
+
# :title:Rows
|
148
|
+
|
149
|
+
class Rows
|
150
|
+
include Toolshed
|
151
|
+
attr_reader :pids
|
152
|
+
|
153
|
+
# The +new+ class method initializes the Rows.
|
154
|
+
# As part of the Abundance lib, Rows is not initialized directly,
|
155
|
+
# but rather as a side effect of the Gardener's initialization,
|
156
|
+
# through the +rows+ Garden instance method.
|
157
|
+
# Its instance resides in the @garden_rows Gardener's instance variable.
|
158
|
+
# Its real muscles are inaccessibles from instance method intervention,
|
159
|
+
# because of its nature as a forked Ruby process.
|
160
|
+
# === Parameter
|
161
|
+
# * _rows_ = garden rows number, the number of concurent threads
|
162
|
+
# * _init_timeout_ = allow to pause execution to allow for larger garden rows to initialize
|
163
|
+
# === Example
|
164
|
+
# rows = Rows.new(4,2) { grow_block }
|
165
|
+
|
166
|
+
def initialize(rows,init_timeout,gardener_block)
|
167
|
+
@pids = []
|
168
|
+
rows.times do
|
169
|
+
row_port = Toolshed.available_port
|
170
|
+
@pids << fork do
|
171
|
+
@socket_server = Toolshed.socket_server(row_port)
|
172
|
+
t1 = Thread.new do
|
173
|
+
gardener_block.call
|
174
|
+
end
|
175
|
+
|
176
|
+
t2 = Thread.new do
|
177
|
+
@socket_client_perm = Toolshed.socket_client_perm
|
178
|
+
loop do
|
179
|
+
if $seed.nil?
|
180
|
+
command, data = socket_client_perm_duplex(:row,row_port)
|
181
|
+
if command == :quit
|
182
|
+
pid = Process.pid
|
183
|
+
socket_client_perm_send(:close,{:level => :seed, :pid => pid})
|
184
|
+
exit
|
185
|
+
end
|
186
|
+
$seed = data
|
187
|
+
if $seed.nil?
|
188
|
+
command, data, clientport, clientname, clientaddr = socket_server_recv
|
189
|
+
$seed = data
|
190
|
+
end
|
191
|
+
elsif $seed.include?(:success)
|
192
|
+
command, data = socket_client_perm_duplex(:crop,$seed)
|
193
|
+
$seed = nil
|
194
|
+
else
|
195
|
+
t1.run
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
end
|
200
|
+
t2.join
|
201
|
+
end
|
202
|
+
end
|
203
|
+
sleep init_timeout
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
data/lib/gardener.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# This class provides the gardener part of the Gardener,Garden,Seed natural design patern
|
2
|
+
#
|
3
|
+
# The Gardener act as the client class for accessing and assessing the Garden ressources.
|
4
|
+
# Its initialization occurs through the Abundance.gardener class method.
|
5
|
+
# Its instance methods are fourthfold, following the 4 states of the garden.
|
6
|
+
# Like the 4 seasons northern hemisphere gardening cycles:
|
7
|
+
# * seed = the setting of your command cycle
|
8
|
+
# * growth = the evolution of your command growing period
|
9
|
+
# * harvest = the getting of your command results
|
10
|
+
# * close = the closing and dying cycle
|
11
|
+
#
|
12
|
+
# Author:: lp (mailto:lp@spiralix.org)
|
13
|
+
# Copyright:: 2008 Louis-Philippe Perron - Released under the terms of the MIT license
|
14
|
+
#
|
15
|
+
# :title:Gardener
|
16
|
+
|
17
|
+
class Gardener
|
18
|
+
require 'toolshed'
|
19
|
+
include Toolshed
|
20
|
+
|
21
|
+
# The +new+ class method initializes the class.
|
22
|
+
# As part of the Abundance lib, Gardener is not initialized directly,
|
23
|
+
# but rather through +Abundance.gardener+.
|
24
|
+
# === Parameters
|
25
|
+
# * :seed_size = allowed seed size in bytes
|
26
|
+
# * :rows = garden rows number, the number of concurent threads
|
27
|
+
# * :init_timeout = allow to pause execution to allow for larger gardens to initialize
|
28
|
+
# === Example
|
29
|
+
# gardener = Gardener.new({:seed_size => 1024, :rows => 6, :init_timeout => 1}) do
|
30
|
+
# Abundance.grow do |seed|
|
31
|
+
# seed.crop(`#{seed.sprout}`)
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
|
36
|
+
def initialize(options,gardener_block)
|
37
|
+
Toolshed::block_size = options[:seed_size]
|
38
|
+
Toolshed::garden_port = Toolshed.available_port
|
39
|
+
|
40
|
+
@garden = Garden.new
|
41
|
+
@garden_rows = @garden.rows(options[:rows], options[:init_timeout], gardener_block)
|
42
|
+
|
43
|
+
@socket_client_perm = Toolshed.socket_client_perm
|
44
|
+
end
|
45
|
+
|
46
|
+
# The +seed+ method for the Gardener instance allow to sow a command in the Gardener's Garden.
|
47
|
+
# === Parameter
|
48
|
+
# * _command_ = a ruby expression or object
|
49
|
+
# === Example
|
50
|
+
# id_seed_1 = gardener.seed('ruby -v')
|
51
|
+
|
52
|
+
def seed(command)
|
53
|
+
command, data = socket_client_perm_duplex(:seed,command)
|
54
|
+
return data
|
55
|
+
end
|
56
|
+
|
57
|
+
# The +growth+ method for the Gardener instance allow to get report of the growing process
|
58
|
+
# === Parameter
|
59
|
+
# The parameter given as a symbol specifies the level of growth report you wish to get:
|
60
|
+
# * :progress = return actual progress status, scaled between 0.00 and 1.00
|
61
|
+
# * :seed = return total seeds waiting to be processed
|
62
|
+
# * :sprout = return total seeds actually in process
|
63
|
+
# * :crop = return total seeds for which process has completed
|
64
|
+
# === Example
|
65
|
+
# progress = gardener.growth(:progress)
|
66
|
+
# puts "progress is now #{progress}" # => progress is now 0.75
|
67
|
+
|
68
|
+
def growth(crop=:progress)
|
69
|
+
command, data = socket_client_perm_duplex(:growth,crop)
|
70
|
+
return data
|
71
|
+
end
|
72
|
+
|
73
|
+
# The +harvest+ method for the Gardener instance allow to get arrays of results for each queue level
|
74
|
+
# === Parameter
|
75
|
+
# The parameter given as a symbol specifies the level of queue results you wish to get:
|
76
|
+
# * _seedID_ = return the result for a specific seed, if seed hasn't processed it wait until completed
|
77
|
+
# * :crop = return an array of seed for which process has completed
|
78
|
+
# * :sprout = return an array of seed actually processing
|
79
|
+
# * :seed = return an array of seed waiting to be processed
|
80
|
+
# * :all = return a hash of respective arrays for crops, sprouts and seeds
|
81
|
+
# === Example
|
82
|
+
# puts "result is: #{gardener.harvest(id_seed_1)} # => result is: ruby 1.8.6 (2007-09-24 patchlevel 111) [universal-darwin9.0]
|
83
|
+
|
84
|
+
def harvest(crop)
|
85
|
+
command, data = socket_client_perm_duplex(:harvest,crop)
|
86
|
+
return data
|
87
|
+
end
|
88
|
+
|
89
|
+
# The +close+ method for the Gardener instance allow to safely close the Garden and its Rows.
|
90
|
+
# It return a hash of respective arrays for crops, sprouts and seeds at the moment of closing.
|
91
|
+
# === Example
|
92
|
+
# final_harvest = gardener.close
|
93
|
+
|
94
|
+
def close
|
95
|
+
command, data = socket_client_perm_duplex(:close,{:level => :garden, :pid => @garden_rows.pids})
|
96
|
+
return data
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
data/lib/seed.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# This class provides the seed part of the Gardener,Garden,Seed natural design patern
|
2
|
+
#
|
3
|
+
# In nature, seed is usually small, and so is this class.
|
4
|
+
# No attributes/variables of itself, its only a kind of localized getter/setter class for
|
5
|
+
# the global $seed variable. Every garden row assess one fork-localized $seed variable, and
|
6
|
+
# you get access to it from a +seed+ instance passed to every Abundance.grow iteration.
|
7
|
+
#
|
8
|
+
# Author:: lp (mailto:lp@spiralix.org)
|
9
|
+
# Copyright:: 2008 Louis-Philippe Perron - Released under the terms of the MIT license
|
10
|
+
#
|
11
|
+
# :title:Seed
|
12
|
+
|
13
|
+
class Seed
|
14
|
+
|
15
|
+
# The +new+ class method initializes the class.
|
16
|
+
# You don't have to initialize it inside of Abundance,
|
17
|
+
# as it gets initialized automatically inside the Abundance.grow method
|
18
|
+
|
19
|
+
# The +sprout+ method for the Seed instance allow to get the passed command
|
20
|
+
# from the inside the Abundance.grow block.
|
21
|
+
# === Example
|
22
|
+
# system "#{seed.sprout}\n"
|
23
|
+
def sprout
|
24
|
+
return $seed[:seed]
|
25
|
+
end
|
26
|
+
|
27
|
+
# The +crop+ method for the Seed instance allow to set a success status and
|
28
|
+
# a return message from the inside the Abundance.grow block.
|
29
|
+
# === Parameter
|
30
|
+
# * _success_ = success of the iteration, may be true or false
|
31
|
+
# * _message_ = a ruby expression or object
|
32
|
+
# === Example
|
33
|
+
# if success
|
34
|
+
# seed.crop(true,results)
|
35
|
+
# else
|
36
|
+
# seed.crop(false,results)
|
37
|
+
# end
|
38
|
+
def crop(success,message)
|
39
|
+
$seed[:success] = success; $seed[:message] = message
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/lib/test.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'pty'
|
2
|
+
require 'expect'
|
3
|
+
require 'abundance'
|
4
|
+
|
5
|
+
# puts "Init pid #{Process.pid}"
|
6
|
+
#
|
7
|
+
# g = Abundance.gardener(:seed_size => 8192, :rows => 1, :init_timeout => 1) do
|
8
|
+
# puts "Garden init done, pid: #{Process.pid}"
|
9
|
+
# puts "@@ yield just before growing..."
|
10
|
+
# Abundance.grow do |seed|
|
11
|
+
# puts "@@ yield #{Time.now}: got in here"
|
12
|
+
# puts "@@ yield job: #{seed.sprout}"
|
13
|
+
# seed.crop(true, "Bravo!!gadigooo!!")
|
14
|
+
# end
|
15
|
+
# puts "Garden seeded"
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# g.seed("jobidoo")
|
19
|
+
# puts "First chore sent"
|
20
|
+
# g.seed("jobidaa")
|
21
|
+
# puts "second chore sent"
|
22
|
+
# g.seed("jobidob")
|
23
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
24
|
+
# g.seed("jobidoc")
|
25
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
26
|
+
# id = g.seed("jobidod")
|
27
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
28
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!!!!! waiting for harvest: #{id.class.to_s}"
|
29
|
+
# har = g.harvest(id)
|
30
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!!!!! famous harvest: #{har.inspect}"
|
31
|
+
# g.seed("jobidoe")
|
32
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
33
|
+
# g.seed("jobidof")
|
34
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
35
|
+
# g.seed("jobidog")
|
36
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
37
|
+
# g.seed("jobidoh")
|
38
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
39
|
+
# g.seed("jobidoi")
|
40
|
+
# puts "!!! some seeds: #{g.harvest(:seed).inspect}"
|
41
|
+
# puts "!!! some sprouts: #{g.harvest(:sprout).inspect}"
|
42
|
+
# puts "!!! some crops: #{g.harvest(:crop).inspect}"
|
43
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
44
|
+
# g.seed("jobidoj")
|
45
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
46
|
+
# g.seed("jobidok")
|
47
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
48
|
+
# g.seed("jobidol")
|
49
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
50
|
+
# g.seed("jobidom")
|
51
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
52
|
+
# g.seed("jobidon")
|
53
|
+
# g.seed("jobidop")
|
54
|
+
# g.seed("jobidoq")
|
55
|
+
# g.seed("jobidor")
|
56
|
+
# g.seed("jobidos")
|
57
|
+
# g.seed("jobidot")
|
58
|
+
# g.seed("jobidou")
|
59
|
+
# g.seed("jobidov")
|
60
|
+
# g.seed("jobidow")
|
61
|
+
# g.seed("jobidox")
|
62
|
+
# g.seed("jobidoy")
|
63
|
+
# g.seed("jobidoz")
|
64
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
65
|
+
# sleep 30
|
66
|
+
# puts "!!! some seeds: #{g.harvest(:seed).inspect}"
|
67
|
+
# puts "!!! some sprouts: #{g.harvest(:sprout).inspect}"
|
68
|
+
# puts "!!! some crops: #{g.harvest(:crop).inspect}"
|
69
|
+
# sleep 5
|
70
|
+
# puts "!!!!!!!!!!!!!!!!!!!!!!! growth: #{g.growth}"
|
71
|
+
# fin = g.close
|
72
|
+
# puts "on close #{fin.inspect}"
|
73
|
+
|
74
|
+
puts "Init pid #{Process.pid}"
|
75
|
+
|
76
|
+
g = Abundance.gardener(:seed_size => 8192, :rows => 1, :init_timeout => 3) do
|
77
|
+
puts "@@ yield just before growing..."
|
78
|
+
PTY.spawn('smbclient //spiralgemini/geminishare goLEELAubu -U leelasheel') do |r,w,pid|
|
79
|
+
w.sync = true
|
80
|
+
$expect_verbose = false
|
81
|
+
|
82
|
+
r.expect(/.*smb: \\>.*/) do |text|
|
83
|
+
puts "!!! starter: #{text}"
|
84
|
+
end
|
85
|
+
Abundance.grow do |seed|
|
86
|
+
neew = seed.sprout
|
87
|
+
puts "??? will go for: #{neew}"
|
88
|
+
w.print "#{seed.sprout}\n"
|
89
|
+
|
90
|
+
r.expect(/.*smb: \\>/) do |text|
|
91
|
+
puts "!!! got #{text}"
|
92
|
+
unless text
|
93
|
+
r.expect(/.*smb: \\>/) do |text|
|
94
|
+
puts "!!!2 got #{text}"
|
95
|
+
seed.crop(true, text)
|
96
|
+
end
|
97
|
+
else
|
98
|
+
seed.crop(true, text)
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
puts "Garden seeded"
|
108
|
+
|
109
|
+
g.seed("ls")
|
110
|
+
puts "First chore sent"
|
111
|
+
g.seed("pwd")
|
112
|
+
puts "second chore sent"
|
113
|
+
g.seed("volume")
|
114
|
+
g.seed("help")
|
115
|
+
g.seed("du")
|
116
|
+
g.seed("put inside.rb")
|
117
|
+
sleep 20
|
118
|
+
# r = g.harvest
|
119
|
+
# puts r.inspect
|
120
|
+
fin = g.close
|
121
|
+
puts fin.inspect
|
data/lib/toolshed.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# This module provides a toolkit of helper methods to Abundance
|
2
|
+
#
|
3
|
+
# It uses the Socket Ruby standard library to provide a communication mechanism
|
4
|
+
# between Abundance concurent processes. It's used as both a namespace for init variables of
|
5
|
+
# the different Abundance Classes, using Toolshed's Class Method like qualified names,
|
6
|
+
# and as mixins methods which access directly the instance variables of the client classes .
|
7
|
+
#
|
8
|
+
# Author:: lp (mailto:lp@spiralix.org)
|
9
|
+
# Copyright:: 2008 Louis-Philippe Perron - Released under the terms of the MIT license
|
10
|
+
#
|
11
|
+
# :title:Toolshed
|
12
|
+
|
13
|
+
module Toolshed
|
14
|
+
require 'socket'
|
15
|
+
UDP_HOST = 'localhost'
|
16
|
+
@@start_port = 50000
|
17
|
+
|
18
|
+
def Toolshed.available_port
|
19
|
+
port = @@start_port + 1
|
20
|
+
catch :scan_port do
|
21
|
+
loop do
|
22
|
+
begin
|
23
|
+
socket = UDPSocket.new
|
24
|
+
socket.connect(UDP_HOST,port)
|
25
|
+
socket.send('',0)
|
26
|
+
response,address = socket.recvfrom(1024)
|
27
|
+
rescue Errno::ECONNREFUSED
|
28
|
+
throw :scan_port
|
29
|
+
end
|
30
|
+
port += 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
@@start_port = port
|
34
|
+
return port
|
35
|
+
end
|
36
|
+
|
37
|
+
def Toolshed.socket_client_perm
|
38
|
+
socket = UDPSocket.new
|
39
|
+
socket.connect(UDP_HOST,@@garden_port)
|
40
|
+
return socket
|
41
|
+
end
|
42
|
+
|
43
|
+
def Toolshed.socket_client_temp
|
44
|
+
UDPSocket.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def Toolshed.socket_server(port)
|
48
|
+
socket_server = UDPSocket.new
|
49
|
+
socket_server.bind(nil,port)
|
50
|
+
return socket_server
|
51
|
+
end
|
52
|
+
|
53
|
+
def Toolshed::block_size=(block_size)
|
54
|
+
@@block_size = block_size
|
55
|
+
end
|
56
|
+
|
57
|
+
def Toolshed::block_size
|
58
|
+
@@block_size
|
59
|
+
end
|
60
|
+
|
61
|
+
def Toolshed::garden_port=(garden_port)
|
62
|
+
@@garden_port = garden_port
|
63
|
+
end
|
64
|
+
|
65
|
+
def Toolshed::garden_port
|
66
|
+
@@garden_port
|
67
|
+
end
|
68
|
+
|
69
|
+
def socket_client_perm_duplex(command,data)
|
70
|
+
@socket_client_perm.send(Marshal.dump([command,data]),0)
|
71
|
+
recv_block,address = @socket_client_perm.recvfrom(@@block_size)
|
72
|
+
return Marshal.load(recv_block)
|
73
|
+
end
|
74
|
+
|
75
|
+
def socket_client_perm_send(command,data)
|
76
|
+
@socket_client_perm.send(Marshal.dump([command,data]),0)
|
77
|
+
end
|
78
|
+
|
79
|
+
def socket_client_temp(command,data,port)
|
80
|
+
@socket_client_temp.connect(UDP_HOST,port)
|
81
|
+
@socket_client_temp.send(Marshal.dump([command,data]),0)
|
82
|
+
end
|
83
|
+
|
84
|
+
def socket_server_recv
|
85
|
+
block,address = @socket_server.recvfrom(@@block_size)
|
86
|
+
clientport = address[1]; clientname = address[2]; clientaddr = address[3]
|
87
|
+
command, data = Marshal.load(block)
|
88
|
+
return command, data, clientport, clientname, clientaddr
|
89
|
+
end
|
90
|
+
|
91
|
+
def socket_server_send(command,data,clientaddr,clientport)
|
92
|
+
@socket_server.send(Marshal.dump([command,data]), 0, clientaddr, clientport)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: Abundance
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Louis-Philippe Perron
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-12-03 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: lp@spiralix.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/abundance.rb
|
26
|
+
- lib/garden.rb
|
27
|
+
- lib/gardener.rb
|
28
|
+
- lib/seed.rb
|
29
|
+
- lib/test.rb
|
30
|
+
- lib/toolshed.rb
|
31
|
+
has_rdoc: true
|
32
|
+
homepage: http://abundance.rubyforge.org/
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: "0"
|
43
|
+
version:
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
requirements: []
|
51
|
+
|
52
|
+
rubyforge_project: Abundance
|
53
|
+
rubygems_version: 1.2.0
|
54
|
+
signing_key:
|
55
|
+
specification_version: 2
|
56
|
+
summary: Ruby Parallel Processing, Concurent Native Threads
|
57
|
+
test_files: []
|
58
|
+
|