tiamat 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/CHANGES.rdoc +7 -0
  2. data/MANIFEST +42 -0
  3. data/README.rdoc +194 -0
  4. data/Rakefile +19 -0
  5. data/bin/tiamat-server +31 -0
  6. data/devel/jumpstart.rb +987 -0
  7. data/install.rb +2 -0
  8. data/lib/tiamat.rb +43 -0
  9. data/lib/tiamat/autoconfig.rb +8 -0
  10. data/lib/tiamat/child_server.rb +21 -0
  11. data/lib/tiamat/config/ruby_parser.rb +6 -0
  12. data/lib/tiamat/connector.rb +23 -0
  13. data/lib/tiamat/error.rb +24 -0
  14. data/lib/tiamat/farm.rb +32 -0
  15. data/lib/tiamat/local_child_farm.rb +23 -0
  16. data/lib/tiamat/local_child_server.rb +25 -0
  17. data/lib/tiamat/local_child_worker.rb +11 -0
  18. data/lib/tiamat/remote_farm.rb +11 -0
  19. data/lib/tiamat/remote_worker.rb +10 -0
  20. data/lib/tiamat/server.rb +42 -0
  21. data/lib/tiamat/tiamat.rb +49 -0
  22. data/lib/tiamat/tiamat_server.rb +48 -0
  23. data/lib/tiamat/util.rb +37 -0
  24. data/lib/tiamat/version.rb +4 -0
  25. data/lib/tiamat/worker.rb +61 -0
  26. data/spec/connector_spec.rb +11 -0
  27. data/spec/drb_connection_spec.rb +18 -0
  28. data/spec/local_child_farm_spec.rb +29 -0
  29. data/spec/local_child_server_path_spec.rb +37 -0
  30. data/spec/local_child_server_spec.rb +24 -0
  31. data/spec/local_child_worker_spec.rb +140 -0
  32. data/spec/pure_spec.rb +59 -0
  33. data/spec/readme_spec.rb +29 -0
  34. data/spec/remote_farm_spec.rb +36 -0
  35. data/spec/remote_worker_spec.rb +59 -0
  36. data/spec/server_spec.rb +48 -0
  37. data/spec/tiamat_open_local_spec.rb +77 -0
  38. data/spec/tiamat_open_remote_spec.rb +67 -0
  39. data/spec/tiamat_server_spec.rb +51 -0
  40. data/spec/tiamat_spec_base.rb +36 -0
  41. data/spec/util_spec.rb +29 -0
  42. data/spec/worker_spec.rb +19 -0
  43. metadata +209 -0
data/install.rb ADDED
@@ -0,0 +1,2 @@
1
+ load './devel/jumpstart.rb'
2
+ Jumpstart::SimpleInstaller.new.run
data/lib/tiamat.rb ADDED
@@ -0,0 +1,43 @@
1
+ #
2
+ # Copyright (c) 2009 James M. Lawrence. All rights reserved.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person
5
+ # obtaining a copy of this software and associated documentation files
6
+ # (the "Software"), to deal in the Software without restriction,
7
+ # including without limitation the rights to use, copy, modify, merge,
8
+ # publish, distribute, sublicense, and/or sell copies of the Software,
9
+ # and to permit persons to whom the Software is furnished to do so,
10
+ # subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19
+ # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20
+ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+ #
24
+
25
+ require 'rbconfig'
26
+ require 'drb'
27
+
28
+ require 'pure'
29
+
30
+ require 'tiamat/error'
31
+ require 'tiamat/version'
32
+ require 'tiamat/util'
33
+ require 'tiamat/connector'
34
+ require 'tiamat/server'
35
+ require 'tiamat/farm'
36
+ require 'tiamat/worker'
37
+ require 'tiamat/remote_farm'
38
+ require 'tiamat/remote_worker'
39
+ require 'tiamat/child_server'
40
+ require 'tiamat/local_child_server'
41
+ require 'tiamat/local_child_farm'
42
+ require 'tiamat/local_child_worker'
43
+ require 'tiamat/tiamat'
@@ -0,0 +1,8 @@
1
+ #
2
+ # Upon <tt>require 'tiamat/autoconfig'</tt>, a search will ensue to
3
+ # find the optimal (Pure.parser, Tiamat.compiler) pair.
4
+ #
5
+ # There is currently only one available implementation:
6
+ # (RubyParser, Ruby2Ruby).
7
+ #
8
+ require 'tiamat/config/ruby_parser'
@@ -0,0 +1,21 @@
1
+
2
+ module Tiamat
3
+ class ChildServer < Server
4
+ def initialize(uri, *args)
5
+ @thread = Thread.new {
6
+ launch(uri, *args)
7
+ }
8
+ super(uri)
9
+ end
10
+
11
+ def close
12
+ begin
13
+ @drb_object.close
14
+ rescue DRb::DRbConnError
15
+ nil # rcov workaround
16
+ end
17
+ super
18
+ @thread.join
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+
2
+ require 'tiamat'
3
+ require 'pure/parser/ruby_parser'
4
+
5
+ Pure.parser = Pure::Parser::RubyParser
6
+ Tiamat.compiler = Pure.parser.compiler
@@ -0,0 +1,23 @@
1
+
2
+ module Tiamat
3
+ module Connector
4
+ module_function
5
+
6
+ def connect(wait_interval, timeout)
7
+ begin
8
+ yield
9
+ rescue DRb::DRbConnError
10
+ start = Time.now
11
+ begin
12
+ Kernel.sleep(wait_interval)
13
+ yield
14
+ rescue DRb::DRbConnError
15
+ if Time.now - start > timeout
16
+ raise
17
+ end
18
+ retry
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+
2
+ module Tiamat
3
+ class Error < StandardError
4
+ end
5
+
6
+ class AlreadyOpenError < Error
7
+ def initialize(object)
8
+ @object = object
9
+ super("open called on object which is already open: #{@object.inspect}")
10
+ end
11
+
12
+ attr_reader :object
13
+ end
14
+
15
+ class RunRubyError < Error
16
+ def initialize(command, status)
17
+ @command, @status = command, status
18
+ command_str = command.map { |a| "'#{a}'" }.join(", ")
19
+ super("system(#{command_str}) failed with status #{@status}")
20
+ end
21
+
22
+ attr_reader :command, :status
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+
2
+ module Tiamat
3
+ class Farm
4
+ def initialize(servers)
5
+ @servers = servers
6
+ @available = Queue.new
7
+
8
+ @servers.each { |server|
9
+ @available.push(server)
10
+ }
11
+ end
12
+
13
+ def num_servers
14
+ @servers.size
15
+ end
16
+
17
+ def lend_server
18
+ server = @available.pop
19
+ begin
20
+ yield server
21
+ ensure
22
+ @available.push(server)
23
+ end
24
+ end
25
+
26
+ def close
27
+ @servers.each { |server|
28
+ server.close
29
+ }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,23 @@
1
+
2
+ module Tiamat
3
+ class LocalChildFarm < Farm
4
+ def initialize(num_servers, *args)
5
+ base_uri = self.class.base_uri
6
+ port_begin = self.class.port_begin
7
+ servers = (0...num_servers).map { |n|
8
+ LocalChildServer.new("#{base_uri}:#{port_begin + n}", *args)
9
+ }
10
+ super(servers)
11
+ end
12
+
13
+ class << self
14
+ def base_uri
15
+ "druby://localhost"
16
+ end
17
+
18
+ def port_begin
19
+ 48727
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+
2
+ module Tiamat
3
+ class LocalChildServer < ChildServer
4
+ def launch(uri, compiler_name, *requires)
5
+ args = [compiler_name] + requires
6
+ Util.run_ruby(self.class.server_path, uri, *args)
7
+ end
8
+
9
+ class << self
10
+ def server_basename
11
+ "tiamat-server"
12
+ end
13
+
14
+ def server_path
15
+ if server = Util.find_executable(server_basename)
16
+ server
17
+ else
18
+ File.expand_path(
19
+ File.dirname(__FILE__) + "/../../bin/" + server_basename
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+
2
+ module Tiamat
3
+ class LocalChildWorker < Worker
4
+ class << self
5
+ def open(num_servers, *args, &block)
6
+ farm = LocalChildFarm.new(num_servers, *args)
7
+ super(farm, &block)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+
2
+ module Tiamat
3
+ class RemoteFarm < Farm
4
+ def initialize(*uris)
5
+ servers = uris.map { |uri|
6
+ Server.new(uri)
7
+ }
8
+ super(servers)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+
2
+ module Tiamat
3
+ class RemoteWorker < Worker
4
+ class << self
5
+ def open(*uris, &block)
6
+ super(RemoteFarm.new(*uris), &block)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,42 @@
1
+
2
+ module Tiamat
3
+ class Server
4
+ include Connector
5
+
6
+ def initialize(uri)
7
+ @uri = uri
8
+ @drb_object = DRbObject.new_with_uri(uri)
9
+ connect {
10
+ @drb_object.ping
11
+ }
12
+ end
13
+
14
+ attr_reader :uri
15
+
16
+ def close
17
+ end
18
+
19
+ def evaluate_function(*args)
20
+ @drb_object.evaluate_function(*args)
21
+ end
22
+
23
+ private
24
+
25
+ def connect(&block)
26
+ super(self.class.wait_interval, self.class.timeout, &block)
27
+ end
28
+
29
+ class << self
30
+ attr_writer :wait_interval
31
+ attr_writer :timeout
32
+
33
+ def wait_interval
34
+ @wait_interval ||= 0.02
35
+ end
36
+
37
+ def timeout
38
+ @timeout ||= 3
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,49 @@
1
+
2
+ module Tiamat
3
+ class << self
4
+ #
5
+ # Launch _num_parallel_ local Ruby interpreters.
6
+ #
7
+ # If a block is given:
8
+ # * Pure.worker is replaced for the duration of the block.
9
+ # * The return value is the block result.
10
+ # * The interpreters are stopped when the block finishes.
11
+ #
12
+ # If no block is given, an opened worker is returned. You are
13
+ # responsible for closing it.
14
+ #
15
+ def open_local(num_parallel, *requires, &block)
16
+ args = [num_parallel] + Tiamat.compiler.reverse + requires
17
+ open_worker(LocalChildWorker, *args, &block)
18
+ end
19
+
20
+ #
21
+ # Connect to the already-running tiamat servers given by the URIs,
22
+ # e.g. druby://192.168.4.1:27272.
23
+ #
24
+ # If a block is given:
25
+ # * Pure.worker is replaced for the duration of the block.
26
+ # * The return value is the block result.
27
+ #
28
+ # If no block is given, an opened worker is returned. You are
29
+ # responsible for closing it.
30
+ #
31
+ def open_remote(*uris, &block)
32
+ open_worker(RemoteWorker, *uris, &block)
33
+ end
34
+
35
+ attr_accessor :compiler
36
+
37
+ private
38
+
39
+ def open_worker(worker, *args, &block)
40
+ result = worker.open(*args, &block)
41
+ if block
42
+ result
43
+ else
44
+ worker
45
+ end
46
+ end
47
+ end
48
+ @compiler = nil
49
+ end
@@ -0,0 +1,48 @@
1
+ #
2
+ # This is the core of bin/tiamat-server, the code which runs on the
3
+ # remote Ruby interpreter.
4
+ #
5
+ # This file is not required by the top-level tiamat.rb.
6
+ #
7
+
8
+ module Tiamat
9
+ class TiamatServer
10
+ FINISH = Queue.new
11
+
12
+ def initialize(compiler_name, *requires)
13
+ @compiler_name = compiler_name
14
+
15
+ requires.each { |path| require path }
16
+
17
+ @compiler = compiler_name.split("::").inject(Object) { |acc, name|
18
+ acc.const_get(name)
19
+ }.new
20
+
21
+ nil # rcov workaround
22
+ end
23
+
24
+ attr_reader :compiler_name
25
+
26
+ def evaluate_function(spec, *args)
27
+ @compiler.evaluate_function(spec, *args)
28
+ end
29
+
30
+ def ping
31
+ end
32
+
33
+ def close
34
+ FINISH.push nil
35
+ end
36
+
37
+ class << self
38
+ def run(uri, *args)
39
+ DRb.start_service(uri, new(*args))
40
+ begin
41
+ FINISH.pop
42
+ ensure
43
+ DRb.stop_service
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,37 @@
1
+
2
+ module Tiamat
3
+ module Util
4
+ module_function
5
+
6
+ def ruby_executable
7
+ require 'rbconfig'
8
+ name = File.join(
9
+ Config::CONFIG["bindir"],
10
+ Config::CONFIG["RUBY_INSTALL_NAME"]
11
+ )
12
+ if Config::CONFIG["host"] =~ %r!(mswin|cygwin|mingw)! and
13
+ File.basename(name) !~ %r!\.(exe|com|bat|cmd)\Z!i
14
+ name + Config::CONFIG["EXEEXT"]
15
+ else
16
+ name
17
+ end
18
+ end
19
+
20
+ def run_ruby(*args)
21
+ cmd = [ruby_executable, *args]
22
+ unless system(*cmd)
23
+ raise RunRubyError.new(cmd, $?.exitstatus)
24
+ end
25
+ end
26
+
27
+ def find_executable(basename)
28
+ ENV["PATH"].split(File::PATH_SEPARATOR).each { |path|
29
+ candidate = path + "/" + basename
30
+ if File.exist?(candidate)
31
+ return candidate
32
+ end
33
+ }
34
+ nil
35
+ end
36
+ end
37
+ end