hustle 0.0.1
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.
- checksums.yaml +7 -0
- data/lib/hustle/runner.rb +74 -0
- data/lib/hustle/version.rb +3 -0
- data/lib/hustle.rb +93 -0
- data/test/hustle_test.rb +41 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5d63d92d42cdec1d7a666e7357ab233f9455fa1c
|
4
|
+
data.tar.gz: e1b6c7cc34b13eb0ef287d30e51b046b3a8be9b5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5b11f41b9b01350abda0fb40458d7aefb927b5628705f14996b4cdb29def0f00889e7d6c3ea0149549e598c837c59391882dc32d75df5593b44d67f4a27efc20
|
7
|
+
data.tar.gz: b935918265827b097d6b6e6e9b9f802276d8301311c6fbac92ac95c9fb471002efddef4110b9b3da71e0aca74cc48ab49a1e461e793f3ada2eb461806cac0cbe
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Hustle
|
2
|
+
|
3
|
+
class Runner
|
4
|
+
attr_reader :uri, :pid, :value
|
5
|
+
attr_accessor :callback_thread
|
6
|
+
|
7
|
+
# methods to be run on the local instance
|
8
|
+
|
9
|
+
def initialize(uri)
|
10
|
+
@uri = uri
|
11
|
+
end
|
12
|
+
|
13
|
+
def remote_instance
|
14
|
+
DRbObject.new_with_uri(uri)
|
15
|
+
end
|
16
|
+
|
17
|
+
def remote_instance_started?
|
18
|
+
!pid.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
def remote_instance_ready?
|
22
|
+
begin
|
23
|
+
!remote_instance.uri.nil?
|
24
|
+
rescue DRb::DRbConnError
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def start_remote_instance
|
30
|
+
return if remote_instance_started?
|
31
|
+
@pid = fork do
|
32
|
+
DRb.start_service uri, self
|
33
|
+
DRb.thread.join
|
34
|
+
end
|
35
|
+
Process.detach pid
|
36
|
+
pid
|
37
|
+
end
|
38
|
+
|
39
|
+
def stop_remote_instance
|
40
|
+
remote_instance.stop
|
41
|
+
end
|
42
|
+
|
43
|
+
def run_remote(&block)
|
44
|
+
remote_instance.run(&block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def remote_instance_finished?
|
48
|
+
remote_instance.finished?
|
49
|
+
end
|
50
|
+
|
51
|
+
def remote_value
|
52
|
+
remote_instance.value
|
53
|
+
end
|
54
|
+
|
55
|
+
# methods to be run on the remote instance
|
56
|
+
|
57
|
+
def stop
|
58
|
+
DRb.stop_service
|
59
|
+
end
|
60
|
+
|
61
|
+
def run
|
62
|
+
Thread.new do
|
63
|
+
@value = yield
|
64
|
+
@finished = true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def finished?
|
69
|
+
!!@finished
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
data/lib/hustle.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require_relative "hustle/version"
|
2
|
+
require_relative "hustle/runner"
|
3
|
+
require "os"
|
4
|
+
require "socket"
|
5
|
+
require "drb"
|
6
|
+
require "thread"
|
7
|
+
require "singleton"
|
8
|
+
require "forwardable"
|
9
|
+
require "monitor"
|
10
|
+
|
11
|
+
module Hustle
|
12
|
+
class << self
|
13
|
+
extend Forwardable
|
14
|
+
def_delegators :"Hustle::Hustler.instance", :go, :wait
|
15
|
+
end
|
16
|
+
|
17
|
+
class Hustler
|
18
|
+
include Singleton
|
19
|
+
include MonitorMixin
|
20
|
+
|
21
|
+
def cores
|
22
|
+
@cores ||= OS.cpu_count
|
23
|
+
end
|
24
|
+
|
25
|
+
def active_runners
|
26
|
+
@active_runners ||= {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def start_drb
|
30
|
+
@drb ||= DRb.start_service
|
31
|
+
end
|
32
|
+
|
33
|
+
def stop_drb
|
34
|
+
synchronize do
|
35
|
+
if active_runners.empty?
|
36
|
+
DRb.stop_service
|
37
|
+
@drb = nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def go(callback: -> (val) {}, &block)
|
43
|
+
start_drb
|
44
|
+
sleep 0 while active_runners.size >= cores.size
|
45
|
+
uri = "druby://127.0.0.1:#{random_port}"
|
46
|
+
runner = Runner.new(uri)
|
47
|
+
runner.start_remote_instance
|
48
|
+
sleep 0 while !runner.remote_instance_ready?
|
49
|
+
runner.run_remote(&block)
|
50
|
+
finish runner, callback
|
51
|
+
end
|
52
|
+
|
53
|
+
def wait
|
54
|
+
active_runners.each do |_, runner|
|
55
|
+
runner.callback_thread.join
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def random_port
|
62
|
+
socket = Socket.new(:INET, :STREAM, 0)
|
63
|
+
socket.bind(Addrinfo.tcp("127.0.0.1", 0))
|
64
|
+
port = socket.local_address.ip_port
|
65
|
+
socket.close
|
66
|
+
port
|
67
|
+
end
|
68
|
+
|
69
|
+
def finish(runner, callback)
|
70
|
+
runner.callback_thread = Thread.new do
|
71
|
+
sleep 0.01 while !runner.remote_instance_finished?
|
72
|
+
value = runner.remote_value
|
73
|
+
runner.stop_remote_instance
|
74
|
+
stop_drb
|
75
|
+
synchronize do
|
76
|
+
active_runners.delete(runner.pid)
|
77
|
+
end
|
78
|
+
callback.call value
|
79
|
+
end
|
80
|
+
|
81
|
+
synchronize do
|
82
|
+
active_runners[runner.pid] = runner
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
Signal.trap(0) do
|
91
|
+
Hustle.wait
|
92
|
+
DRb.stop_service
|
93
|
+
end
|
data/test/hustle_test.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require "micro_test"
|
2
|
+
require "pry"
|
3
|
+
require_relative "../lib/hustle"
|
4
|
+
|
5
|
+
class HustleTest < MicroTest::Test
|
6
|
+
|
7
|
+
test "multiple processes spawned" do
|
8
|
+
4.times do
|
9
|
+
Hustle.go { sleep 0.1 }
|
10
|
+
end
|
11
|
+
|
12
|
+
assert Hustle::Hustler.instance.active_runners.size == 4
|
13
|
+
Hustle.wait
|
14
|
+
end
|
15
|
+
|
16
|
+
test "mutate state in primary process" do
|
17
|
+
data = { foo: nil, bar: nil }
|
18
|
+
|
19
|
+
asserts = -> (value) do
|
20
|
+
assert data[:foo] == 1
|
21
|
+
assert data[:bar] == 2
|
22
|
+
end
|
23
|
+
|
24
|
+
Hustle.go callback: asserts do
|
25
|
+
data[:foo] = 1
|
26
|
+
data[:bar] = 2
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
test "callback value" do
|
31
|
+
asserts = -> (value) do
|
32
|
+
assert Process.pid != value
|
33
|
+
end
|
34
|
+
|
35
|
+
Hustle.go(callback: asserts) do
|
36
|
+
Process.pid
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hustle
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nathan Hopkins
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: os
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.9.6
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.9.6
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: micro_test
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.4.4
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.4.4
|
83
|
+
description: Run Ruby blocks inside their own process.
|
84
|
+
email:
|
85
|
+
- natehop@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- lib/hustle.rb
|
91
|
+
- lib/hustle/runner.rb
|
92
|
+
- lib/hustle/version.rb
|
93
|
+
- test/hustle_test.rb
|
94
|
+
homepage: https://github.com/hopsoft/hustle
|
95
|
+
licenses:
|
96
|
+
- MIT
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.2.0
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: Run Ruby blocks inside their own process.
|
118
|
+
test_files:
|
119
|
+
- test/hustle_test.rb
|