mailbox 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/README +5 -2
- data/Rakefile +6 -0
- data/VERSION.yml +1 -1
- data/example/channel_based_log_example.rb +28 -0
- data/example/i_can_has_cheese_burger_example.rb +42 -0
- data/example/log_example.rb +23 -0
- data/example/parallel_each_example.rb +30 -0
- data/example/ping_pong_example.rb +36 -0
- data/lib/mailbox.rb +61 -30
- data/lib/synchronized.rb +57 -0
- data/mailbox.gemspec +18 -5
- data/mailbox.iml +13 -0
- data/mailbox.ipr +254 -0
- data/test/mailbox_test.rb +64 -24
- data/test/synchronized_test.rb +88 -0
- data/test/test_helper.rb +9 -0
- metadata +15 -2
data/.gitignore
ADDED
data/README
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
== About Mailbox
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
"Unobtrusive concurrency for a multicore world"
|
4
|
+
|
5
|
+
Mailbox is a JRuby mixin that simplifies JVM thread backed message passing concurrency.
|
6
|
+
|
7
|
+
Mailbox is Alpha software under active development. Future releases may include backward compatibility breaking changes.
|
5
8
|
|
6
9
|
* jretlang gem is required to use mailbox
|
7
10
|
|
data/Rakefile
CHANGED
@@ -11,6 +11,12 @@ Rake::TestTask.new do |t|
|
|
11
11
|
t.verbose = true
|
12
12
|
end
|
13
13
|
|
14
|
+
Rake::TestTask.new("example") do |t|
|
15
|
+
t.libs << "example"
|
16
|
+
t.test_files = FileList['example/**/*_example.rb']
|
17
|
+
t.verbose = true
|
18
|
+
end
|
19
|
+
|
14
20
|
Jeweler::Tasks.new do |gemspec|
|
15
21
|
gemspec.name = "mailbox"
|
16
22
|
gemspec.summary = "Mailbox is a JRuby module that simplifies concurrency and is backed by JVM threads."
|
data/VERSION.yml
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + "/../lib/mailbox"
|
5
|
+
|
6
|
+
class ChannelBasedLogger
|
7
|
+
include Mailbox
|
8
|
+
|
9
|
+
def initialize(log_channel)
|
10
|
+
register_channel :log_channel, log_channel
|
11
|
+
end
|
12
|
+
|
13
|
+
mailslot :channel => :log_channel
|
14
|
+
def log(message)
|
15
|
+
p "Logging on Thread #{Thread.current.object_id} - #{message}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class ChannelBasedLogExample < Test::Unit::TestCase
|
20
|
+
|
21
|
+
def test_log_example
|
22
|
+
channel = JRL::Channel.new
|
23
|
+
logger = ChannelBasedLogger.new channel
|
24
|
+
p "Current Thread is #{Thread.current.object_id}"
|
25
|
+
channel.publish "some log message"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + "/../lib/mailbox"
|
5
|
+
|
6
|
+
class ICanHasCheeseBurger
|
7
|
+
include Mailbox
|
8
|
+
|
9
|
+
def initialize(channel)
|
10
|
+
register_channel :request_channel, channel
|
11
|
+
end
|
12
|
+
|
13
|
+
mailslot :channel => :request_channel, :replyable => true
|
14
|
+
def can_you_has?
|
15
|
+
return "ya you can has!"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class ICanHasCheeseBurgerExample < Test::Unit::TestCase
|
21
|
+
|
22
|
+
def test_cheeseburgerz
|
23
|
+
i_can_has_channel = org.jetlang.channels.MemoryRequestChannel.new
|
24
|
+
i_can_has_cheeseburger = ICanHasCheeseBurger.new(i_can_has_channel)
|
25
|
+
|
26
|
+
sync_executor = org.jetlang.core.SynchronousDisposingExecutor.new
|
27
|
+
sync_fiber = org.jetlang.fibers.ThreadFiber.new(sync_executor, nil, false)
|
28
|
+
question = "can i has?"
|
29
|
+
puts question
|
30
|
+
|
31
|
+
latch = JRL::Concurrent::Latch.new(1)
|
32
|
+
answer = "no response!"
|
33
|
+
org.jetlang.channels.AsyncRequest.with_one_reply(sync_fiber, i_can_has_channel, question) do |message|
|
34
|
+
answer = message
|
35
|
+
latch.count_down
|
36
|
+
end
|
37
|
+
|
38
|
+
latch.await(1)
|
39
|
+
puts answer
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + "/../lib/mailbox"
|
5
|
+
|
6
|
+
class Logger
|
7
|
+
include Mailbox
|
8
|
+
|
9
|
+
mailslot
|
10
|
+
def log(message)
|
11
|
+
p "Logging on Thread #{Thread.current.object_id} - #{message}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class LogExample < Test::Unit::TestCase
|
16
|
+
|
17
|
+
def test_log_example
|
18
|
+
logger = Logger.new
|
19
|
+
p "Current Thread is #{Thread.current.object_id}"
|
20
|
+
logger.log "some log message"
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + "/../lib/mailbox"
|
5
|
+
|
6
|
+
class DataProcessor
|
7
|
+
include Mailbox
|
8
|
+
|
9
|
+
mailslot
|
10
|
+
def process(data)
|
11
|
+
p "#{data} was processed by Thread# #{Thread.current.object_id}"
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class ParallelEachExample < Test::Unit::TestCase
|
17
|
+
|
18
|
+
def test_a_simple_parallel_each
|
19
|
+
|
20
|
+
items = ["item1", "item2", "item3", "item4", "item5"]
|
21
|
+
|
22
|
+
processors = [DataProcessor.new, DataProcessor.new, DataProcessor.new]
|
23
|
+
|
24
|
+
items.each_with_index do |item, index|
|
25
|
+
processors[index % processors.length].process item
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + "/../lib/mailbox"
|
5
|
+
|
6
|
+
class Player
|
7
|
+
include Mailbox
|
8
|
+
|
9
|
+
def initialize(sound, send_channel, recieve_channel)
|
10
|
+
@sound = sound
|
11
|
+
@send_channel = send_channel
|
12
|
+
register_channel :recieve_channel, recieve_channel
|
13
|
+
end
|
14
|
+
|
15
|
+
mailslot :channel => :recieve_channel
|
16
|
+
def play(last_move)
|
17
|
+
p "Thread [#{Thread.current.object_id}] - #{@sound} for #{last_move}"
|
18
|
+
@send_channel.publish @sound
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class PingPongExample < Test::Unit::TestCase
|
24
|
+
|
25
|
+
def test_ping_pong
|
26
|
+
ping_channel = JRL::Channel.new
|
27
|
+
pong_channel = JRL::Channel.new
|
28
|
+
|
29
|
+
pinger = Player.new "ping", ping_channel, pong_channel
|
30
|
+
ponger = Player.new "pong", pong_channel, ping_channel
|
31
|
+
|
32
|
+
ping_channel.publish "ping"
|
33
|
+
sleep 0.001
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/mailbox.rb
CHANGED
@@ -1,23 +1,37 @@
|
|
1
|
+
# Author:: Joel Friedman and Patrick Farley
|
2
|
+
#
|
3
|
+
# This module is used to simplify concurrency
|
4
|
+
# in your application. JVM threads and JRetlang are
|
5
|
+
# used to provide Actor model style asynchronous
|
6
|
+
# message passing via method calls. Named channel based
|
7
|
+
# message passing is also supported via +register_channel+ and
|
8
|
+
# the <tt>:channel</tt> parameter on +mailslot+.
|
9
|
+
|
1
10
|
require 'rubygems'
|
2
11
|
require 'jretlang'
|
3
12
|
|
4
|
-
|
13
|
+
require File.dirname(__FILE__) + '/synchronized'
|
5
14
|
|
6
|
-
# This module is used to simplify the using concurrency
|
7
|
-
# in your application. Using JVM threads as the backing
|
8
|
-
# a method can set to become an asynchronous method
|
9
|
-
# to be used in a actor-model method. Or a method
|
10
|
-
# can be set to be the backing of a named channel
|
11
|
-
# (jretlang channels are used here).
|
12
15
|
module Mailbox
|
16
|
+
include Synchronized
|
13
17
|
|
14
18
|
# Register your jretlang channel as a named channel
|
15
19
|
def register_channel(channel_name, channel)
|
16
20
|
channel_registry = self.class.__channel_registry__
|
17
|
-
channel_registry.
|
21
|
+
channel_registry.select { |k,v| v[:channel] == channel_name }.each do |k,v|
|
22
|
+
v[:replyable] ? __subscribe_with_single_reply__(channel, k) : __subscribe__(channel, k)
|
23
|
+
end
|
18
24
|
end
|
19
25
|
|
20
|
-
|
26
|
+
class << self
|
27
|
+
# Used to tell +Mailbox+ that all +mailslot+
|
28
|
+
# methods should be run on the calling thread.
|
29
|
+
#
|
30
|
+
# <b>*** Intended for synchronus unit testing of concurrent apps***</b>
|
31
|
+
attr_accessor :synchronous
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
21
35
|
|
22
36
|
def self.included(base)
|
23
37
|
base.extend(Mailbox::ClassMethods)
|
@@ -27,65 +41,82 @@ module Mailbox
|
|
27
41
|
channel.subscribe_on_fiber(__fiber__) { |*args| self.send(method, *args) }
|
28
42
|
end
|
29
43
|
|
44
|
+
def __subscribe_with_single_reply__(channel, method)
|
45
|
+
channel.subscribe(__fiber__) do |message|
|
46
|
+
message.reply(self.send(method))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def __synchronous_fiber__
|
51
|
+
executor = JRL::SynchronousDisposingExecutor.new
|
52
|
+
fiber = JRL::Fibers::ThreadFiber.new executor, "synchronous_thread", true
|
53
|
+
end
|
54
|
+
|
30
55
|
def __started_fiber__
|
31
|
-
fiber = JRL::
|
56
|
+
fiber = Mailbox.synchronous == true ? __synchronous_fiber__ : JRL::Fibers::ThreadFiber.new
|
32
57
|
fiber.start
|
33
58
|
fiber
|
34
59
|
end
|
35
60
|
|
36
61
|
def __fiber__
|
37
62
|
@fiber ||= __started_fiber__
|
38
|
-
end
|
39
|
-
|
40
|
-
module ClassMethods
|
41
|
-
|
42
|
-
|
63
|
+
end
|
64
|
+
|
65
|
+
module ClassMethods
|
66
|
+
include Synchronized::ClassMethods
|
67
|
+
|
68
|
+
# Used within +Mailbox+ module
|
69
|
+
attr_accessor :__channel_registry__
|
43
70
|
|
44
71
|
# Notifies Mailbox that the next method added
|
45
|
-
# will be a
|
46
|
-
#
|
72
|
+
# will be a +mailslot+. If <tt>:channel</tt> is provided
|
73
|
+
# the next method will become a subscriber on the channel.
|
74
|
+
# Channel based +mailslot+ methods are also made private
|
75
|
+
# to discourage direct invocation
|
47
76
|
def mailslot(params={})
|
48
77
|
@next_channel_name = params[:channel]
|
78
|
+
@replyable = params[:replyable]
|
49
79
|
@mailslot = true
|
50
80
|
end
|
51
81
|
|
52
82
|
private
|
53
83
|
|
54
84
|
def method_added(method_name, &block)
|
55
|
-
return
|
56
|
-
|
57
|
-
return unless @mailslot == true
|
58
|
-
|
85
|
+
return super unless @mailslot == true
|
59
86
|
@mailslot = false
|
60
87
|
|
61
88
|
if @next_channel_name.nil?
|
62
89
|
__setup_on_fiber__(method_name)
|
63
90
|
else
|
64
|
-
__setup_on_channel__(method_name)
|
91
|
+
__setup_on_channel__(method_name, @replyable)
|
65
92
|
end
|
93
|
+
|
94
|
+
super
|
66
95
|
|
67
96
|
end
|
68
97
|
|
69
98
|
def __setup_on_fiber__(method_name)
|
99
|
+
return super if @is_adding_mailbox_to_method
|
100
|
+
|
70
101
|
alias_method :"__#{method_name}__", method_name
|
102
|
+
@is_adding_mailbox_to_method = true
|
71
103
|
|
72
|
-
|
73
|
-
|
74
|
-
define_method( method_name, lambda do |*args|
|
75
|
-
__fiber__.execute { self.send(:"__#{method_name}__", *args ) }
|
104
|
+
define_method( method_name, lambda do |*args|
|
105
|
+
__fiber__.execute { self.send(:"__#{method_name}__", *args ) }
|
76
106
|
end )
|
77
107
|
|
78
|
-
@
|
108
|
+
@is_adding_mailbox_to_method = false
|
79
109
|
end
|
80
110
|
|
81
|
-
def __setup_on_channel__(method_name)
|
111
|
+
def __setup_on_channel__(method_name, replyable)
|
82
112
|
private method_name
|
83
113
|
@__channel_registry__ ||= {}
|
84
|
-
__channel_registry__[method_name] = @next_channel_name
|
114
|
+
__channel_registry__[method_name] = { :channel => @next_channel_name, :replyable => replyable }
|
115
|
+
@replyable = nil
|
85
116
|
@next_channel_name = nil
|
86
117
|
end
|
87
118
|
|
88
|
-
end
|
119
|
+
end
|
89
120
|
|
90
121
|
end
|
91
122
|
|
data/lib/synchronized.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Author:: Joel Friedman and Patrick Farley
|
2
|
+
#
|
3
|
+
# This module goes hand in hand with +Mailbox+.
|
4
|
+
# It simplifies concurrecncy within your
|
5
|
+
# JRuby applications.
|
6
|
+
module Synchronized
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def __mutex__
|
11
|
+
@mutex ||= Mutex.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.included(base)
|
15
|
+
base.extend(Synchronized::ClassMethods)
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
|
20
|
+
# Notify +Mailbox+ that the next method added
|
21
|
+
# will be +synchronized+.
|
22
|
+
#
|
23
|
+
# This guarentees:
|
24
|
+
# 1. Two invocations of this method will not interleave and
|
25
|
+
# 2. a happens-before relationship is established with any subsequent invocation.
|
26
|
+
# http://java.sun.com/docs/books/tutorial/essential/concurrency/syncmeth.html
|
27
|
+
def synchronized
|
28
|
+
@synchronized = true
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def method_added(method_name, &block)
|
34
|
+
return super unless @synchronized == true
|
35
|
+
@synchronized = false
|
36
|
+
__synchronize__(method_name)
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def __synchronize__(method_name)
|
42
|
+
return super if @is_adding_synchronized_to_method
|
43
|
+
|
44
|
+
alias_method :"__#{method_name}__", method_name
|
45
|
+
@is_adding_synchronized_to_method = true
|
46
|
+
|
47
|
+
|
48
|
+
define_method( method_name, lambda do |*args|
|
49
|
+
__mutex__.synchronize { self.send(:"__#{method_name}__", *args ) }
|
50
|
+
end )
|
51
|
+
|
52
|
+
@is_adding_synchronized_to_method = false
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
data/mailbox.gemspec
CHANGED
@@ -5,23 +5,34 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{mailbox}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Joel Friedman", "Patrick Farley"]
|
12
|
-
s.date = %q{2009-
|
12
|
+
s.date = %q{2009-11-04}
|
13
13
|
s.description = %q{Mailbox is a JRuby module that simplifies concurrency and is backed by JVM threads.}
|
14
14
|
s.email = %q{asher.friedman@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"README"
|
17
17
|
]
|
18
18
|
s.files = [
|
19
|
-
"
|
19
|
+
".gitignore",
|
20
|
+
"README",
|
20
21
|
"Rakefile",
|
21
22
|
"VERSION.yml",
|
23
|
+
"example/channel_based_log_example.rb",
|
24
|
+
"example/i_can_has_cheese_burger_example.rb",
|
25
|
+
"example/log_example.rb",
|
26
|
+
"example/parallel_each_example.rb",
|
27
|
+
"example/ping_pong_example.rb",
|
22
28
|
"lib/mailbox.rb",
|
29
|
+
"lib/synchronized.rb",
|
23
30
|
"mailbox.gemspec",
|
24
|
-
"
|
31
|
+
"mailbox.iml",
|
32
|
+
"mailbox.ipr",
|
33
|
+
"test/mailbox_test.rb",
|
34
|
+
"test/synchronized_test.rb",
|
35
|
+
"test/test_helper.rb"
|
25
36
|
]
|
26
37
|
s.homepage = %q{http://joelash.github.com/mailbox}
|
27
38
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -30,7 +41,9 @@ Gem::Specification.new do |s|
|
|
30
41
|
s.rubygems_version = %q{1.3.3}
|
31
42
|
s.summary = %q{Mailbox is a JRuby module that simplifies concurrency and is backed by JVM threads.}
|
32
43
|
s.test_files = [
|
33
|
-
"test/mailbox_test.rb"
|
44
|
+
"test/mailbox_test.rb",
|
45
|
+
"test/synchronized_test.rb",
|
46
|
+
"test/test_helper.rb"
|
34
47
|
]
|
35
48
|
|
36
49
|
if s.respond_to? :specification_version then
|
data/mailbox.iml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<module relativePaths="true" type="RUBY_MODULE" version="4">
|
3
|
+
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
4
|
+
<exclude-output />
|
5
|
+
<content url="file://$MODULE_DIR$" />
|
6
|
+
<orderEntry type="jdk" jdkName="JRuby SDK 1.3.1" jdkType="JRUBY_SDK" />
|
7
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
8
|
+
</component>
|
9
|
+
<component name="RModuleSettingsStorage">
|
10
|
+
<RMODULE_SETTINGS_STORAGE_ID NAME="NUMBER" VALUE="0" />
|
11
|
+
</component>
|
12
|
+
</module>
|
13
|
+
|
data/mailbox.ipr
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project relativePaths="false" version="4">
|
3
|
+
<component name="AntConfiguration">
|
4
|
+
<defaultAnt bundledAnt="true" />
|
5
|
+
</component>
|
6
|
+
<component name="BuildJarProjectSettings">
|
7
|
+
<option name="BUILD_JARS_ON_MAKE" value="false" />
|
8
|
+
</component>
|
9
|
+
<component name="CodeStyleSettingsManager">
|
10
|
+
<option name="PER_PROJECT_SETTINGS" />
|
11
|
+
<option name="USE_PER_PROJECT_SETTINGS" value="false" />
|
12
|
+
</component>
|
13
|
+
<component name="CompilerConfiguration">
|
14
|
+
<option name="DEFAULT_COMPILER" value="Javac" />
|
15
|
+
<option name="DEPLOY_AFTER_MAKE" value="0" />
|
16
|
+
<resourceExtensions>
|
17
|
+
<entry name=".+\.(properties|xml|html|dtd|tld)" />
|
18
|
+
<entry name=".+\.(gif|png|jpeg|jpg)" />
|
19
|
+
</resourceExtensions>
|
20
|
+
<wildcardResourcePatterns>
|
21
|
+
<entry name="?*.properties" />
|
22
|
+
<entry name="?*.xml" />
|
23
|
+
<entry name="?*.gif" />
|
24
|
+
<entry name="?*.png" />
|
25
|
+
<entry name="?*.jpeg" />
|
26
|
+
<entry name="?*.jpg" />
|
27
|
+
<entry name="?*.html" />
|
28
|
+
<entry name="?*.dtd" />
|
29
|
+
<entry name="?*.tld" />
|
30
|
+
<entry name="?*.ftl" />
|
31
|
+
</wildcardResourcePatterns>
|
32
|
+
</component>
|
33
|
+
<component name="CopyrightManager" default="">
|
34
|
+
<module2copyright />
|
35
|
+
</component>
|
36
|
+
<component name="DependencyValidationManager">
|
37
|
+
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
|
38
|
+
</component>
|
39
|
+
<component name="EclipseCompilerSettings">
|
40
|
+
<option name="DEBUGGING_INFO" value="true" />
|
41
|
+
<option name="GENERATE_NO_WARNINGS" value="true" />
|
42
|
+
<option name="DEPRECATION" value="false" />
|
43
|
+
<option name="ADDITIONAL_OPTIONS_STRING" value="" />
|
44
|
+
<option name="MAXIMUM_HEAP_SIZE" value="128" />
|
45
|
+
</component>
|
46
|
+
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
47
|
+
<component name="IdProvider" IDEtalkID="2A4BC5ABDEB6DBFA33020D902AA1570C" />
|
48
|
+
<component name="InspectionProjectProfileManager">
|
49
|
+
<option name="PROJECT_PROFILE" value="Project Default" />
|
50
|
+
<option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
|
51
|
+
<scopes />
|
52
|
+
<profiles>
|
53
|
+
<profile version="1.0" is_locked="false">
|
54
|
+
<option name="myName" value="Project Default" />
|
55
|
+
<option name="myLocal" value="false" />
|
56
|
+
</profile>
|
57
|
+
</profiles>
|
58
|
+
<list size="0" />
|
59
|
+
</component>
|
60
|
+
<component name="JavacSettings">
|
61
|
+
<option name="DEBUGGING_INFO" value="true" />
|
62
|
+
<option name="GENERATE_NO_WARNINGS" value="false" />
|
63
|
+
<option name="DEPRECATION" value="true" />
|
64
|
+
<option name="ADDITIONAL_OPTIONS_STRING" value="" />
|
65
|
+
<option name="MAXIMUM_HEAP_SIZE" value="128" />
|
66
|
+
</component>
|
67
|
+
<component name="JavadocGenerationManager">
|
68
|
+
<option name="OUTPUT_DIRECTORY" />
|
69
|
+
<option name="OPTION_SCOPE" value="protected" />
|
70
|
+
<option name="OPTION_HIERARCHY" value="true" />
|
71
|
+
<option name="OPTION_NAVIGATOR" value="true" />
|
72
|
+
<option name="OPTION_INDEX" value="true" />
|
73
|
+
<option name="OPTION_SEPARATE_INDEX" value="true" />
|
74
|
+
<option name="OPTION_DOCUMENT_TAG_USE" value="false" />
|
75
|
+
<option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" />
|
76
|
+
<option name="OPTION_DOCUMENT_TAG_VERSION" value="false" />
|
77
|
+
<option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" />
|
78
|
+
<option name="OPTION_DEPRECATED_LIST" value="true" />
|
79
|
+
<option name="OTHER_OPTIONS" value="" />
|
80
|
+
<option name="HEAP_SIZE" />
|
81
|
+
<option name="LOCALE" />
|
82
|
+
<option name="OPEN_IN_BROWSER" value="true" />
|
83
|
+
</component>
|
84
|
+
<component name="JikesSettings">
|
85
|
+
<option name="JIKES_PATH" value="" />
|
86
|
+
<option name="DEBUGGING_INFO" value="true" />
|
87
|
+
<option name="DEPRECATION" value="true" />
|
88
|
+
<option name="GENERATE_NO_WARNINGS" value="false" />
|
89
|
+
<option name="IS_EMACS_ERRORS_MODE" value="true" />
|
90
|
+
<option name="ADDITIONAL_OPTIONS_STRING" value="" />
|
91
|
+
</component>
|
92
|
+
<component name="Palette2">
|
93
|
+
<group name="Swing">
|
94
|
+
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
95
|
+
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
96
|
+
</item>
|
97
|
+
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
98
|
+
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
99
|
+
</item>
|
100
|
+
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
101
|
+
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
102
|
+
</item>
|
103
|
+
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
104
|
+
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
105
|
+
</item>
|
106
|
+
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
107
|
+
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
108
|
+
<initial-values>
|
109
|
+
<property name="text" value="Button" />
|
110
|
+
</initial-values>
|
111
|
+
</item>
|
112
|
+
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
113
|
+
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
114
|
+
<initial-values>
|
115
|
+
<property name="text" value="RadioButton" />
|
116
|
+
</initial-values>
|
117
|
+
</item>
|
118
|
+
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
119
|
+
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
120
|
+
<initial-values>
|
121
|
+
<property name="text" value="CheckBox" />
|
122
|
+
</initial-values>
|
123
|
+
</item>
|
124
|
+
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
125
|
+
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
126
|
+
<initial-values>
|
127
|
+
<property name="text" value="Label" />
|
128
|
+
</initial-values>
|
129
|
+
</item>
|
130
|
+
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
131
|
+
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
132
|
+
<preferred-size width="150" height="-1" />
|
133
|
+
</default-constraints>
|
134
|
+
</item>
|
135
|
+
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
136
|
+
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
137
|
+
<preferred-size width="150" height="-1" />
|
138
|
+
</default-constraints>
|
139
|
+
</item>
|
140
|
+
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
141
|
+
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
142
|
+
<preferred-size width="150" height="-1" />
|
143
|
+
</default-constraints>
|
144
|
+
</item>
|
145
|
+
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
146
|
+
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
147
|
+
<preferred-size width="150" height="50" />
|
148
|
+
</default-constraints>
|
149
|
+
</item>
|
150
|
+
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
151
|
+
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
152
|
+
<preferred-size width="150" height="50" />
|
153
|
+
</default-constraints>
|
154
|
+
</item>
|
155
|
+
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
156
|
+
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
157
|
+
<preferred-size width="150" height="50" />
|
158
|
+
</default-constraints>
|
159
|
+
</item>
|
160
|
+
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
161
|
+
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
162
|
+
</item>
|
163
|
+
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
164
|
+
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
165
|
+
<preferred-size width="150" height="50" />
|
166
|
+
</default-constraints>
|
167
|
+
</item>
|
168
|
+
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
169
|
+
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
170
|
+
<preferred-size width="150" height="50" />
|
171
|
+
</default-constraints>
|
172
|
+
</item>
|
173
|
+
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
174
|
+
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
175
|
+
<preferred-size width="150" height="50" />
|
176
|
+
</default-constraints>
|
177
|
+
</item>
|
178
|
+
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
179
|
+
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
180
|
+
<preferred-size width="200" height="200" />
|
181
|
+
</default-constraints>
|
182
|
+
</item>
|
183
|
+
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
184
|
+
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
185
|
+
<preferred-size width="200" height="200" />
|
186
|
+
</default-constraints>
|
187
|
+
</item>
|
188
|
+
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
189
|
+
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
190
|
+
</item>
|
191
|
+
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
192
|
+
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
193
|
+
</item>
|
194
|
+
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
195
|
+
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
196
|
+
</item>
|
197
|
+
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
198
|
+
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
199
|
+
</item>
|
200
|
+
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
201
|
+
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
202
|
+
<preferred-size width="-1" height="20" />
|
203
|
+
</default-constraints>
|
204
|
+
</item>
|
205
|
+
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
206
|
+
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
207
|
+
</item>
|
208
|
+
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
209
|
+
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
210
|
+
</item>
|
211
|
+
</group>
|
212
|
+
</component>
|
213
|
+
<component name="ProjectDetails">
|
214
|
+
<option name="projectName" value="mailbox" />
|
215
|
+
</component>
|
216
|
+
<component name="ProjectFileVersion" converted="true">
|
217
|
+
<converter id="javaee-modules" />
|
218
|
+
<converter id="ror-modules" />
|
219
|
+
<converter id="SelenaRunConfToDiana" />
|
220
|
+
</component>
|
221
|
+
<component name="ProjectKey">
|
222
|
+
<option name="state" value="project:///Users/garethjones/sandbox/mailbox/mailbox.ipr" />
|
223
|
+
</component>
|
224
|
+
<component name="ProjectModuleManager">
|
225
|
+
<modules>
|
226
|
+
<module fileurl="file://$PROJECT_DIR$/mailbox.iml" filepath="$PROJECT_DIR$/mailbox.iml" />
|
227
|
+
</modules>
|
228
|
+
</component>
|
229
|
+
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_5" assert-keyword="true" jdk-15="true" project-jdk-name="JRuby SDK 1.3.1" project-jdk-type="JRUBY_SDK">
|
230
|
+
<output url="file://$PROJECT_DIR$/out" />
|
231
|
+
</component>
|
232
|
+
<component name="ResourceManagerContainer">
|
233
|
+
<option name="myResourceBundles">
|
234
|
+
<value>
|
235
|
+
<list size="0" />
|
236
|
+
</value>
|
237
|
+
</option>
|
238
|
+
</component>
|
239
|
+
<component name="RmicSettings">
|
240
|
+
<option name="IS_EANABLED" value="false" />
|
241
|
+
<option name="DEBUGGING_INFO" value="true" />
|
242
|
+
<option name="GENERATE_NO_WARNINGS" value="false" />
|
243
|
+
<option name="GENERATE_IIOP_STUBS" value="false" />
|
244
|
+
<option name="ADDITIONAL_OPTIONS_STRING" value="" />
|
245
|
+
</component>
|
246
|
+
<component name="SvnBranchConfigurationManager">
|
247
|
+
<option name="mySupportsUserInfoFilter" value="true" />
|
248
|
+
</component>
|
249
|
+
<component name="VcsDirectoryMappings">
|
250
|
+
<mapping directory="" vcs="Git" />
|
251
|
+
</component>
|
252
|
+
<component name="WebServicesPlugin" addRequiredLibraries="true" />
|
253
|
+
</project>
|
254
|
+
|
data/test/mailbox_test.rb
CHANGED
@@ -1,11 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require 'test/unit'
|
3
|
-
|
4
|
-
require File.dirname(__FILE__) + "/../lib/mailbox"
|
5
|
-
|
6
|
-
module Latches
|
7
|
-
include_package 'java.util.concurrent'
|
8
|
-
end
|
1
|
+
require 'test_helper.rb'
|
9
2
|
|
10
3
|
class MailboxTest < Test::Unit::TestCase
|
11
4
|
|
@@ -15,36 +8,48 @@ class MailboxTest < Test::Unit::TestCase
|
|
15
8
|
include Mailbox
|
16
9
|
|
17
10
|
mailslot
|
18
|
-
def test_method(latch,
|
19
|
-
|
11
|
+
def test_method(latch, thread_info)
|
12
|
+
thread_info[:thread_id] = Thread.current.object_id
|
20
13
|
latch.count_down
|
21
14
|
end
|
22
15
|
end
|
23
16
|
|
24
|
-
|
17
|
+
thread_info = {}
|
25
18
|
latch = Latches::CountDownLatch.new( 1 )
|
26
|
-
klass.new.test_method(latch,
|
19
|
+
klass.new.test_method(latch, thread_info)
|
27
20
|
|
28
21
|
assert( latch.await( 1, Latches::TimeUnit::SECONDS ), "Timed out" )
|
29
|
-
assert_not_equal Thread.current.object_id,
|
22
|
+
assert_not_equal Thread.current.object_id, thread_info[:thread_id]
|
30
23
|
|
31
24
|
end
|
32
25
|
|
33
|
-
def
|
26
|
+
def test_default_is_run_asynchronously
|
27
|
+
assert Mailbox.synchronous == false, "Mailbox is defaulting to synchronous execution"
|
28
|
+
end
|
34
29
|
|
35
|
-
|
36
|
-
|
30
|
+
def test_can_set_mailslot_to_run_synchronously
|
31
|
+
begin
|
32
|
+
Mailbox.synchronous = true
|
33
|
+
klass = Class.new do
|
34
|
+
include Mailbox
|
35
|
+
|
36
|
+
mailslot
|
37
|
+
def test_method(thread_info)
|
38
|
+
thread_info[:thread_id] = Thread.current.object_id
|
39
|
+
end
|
37
40
|
|
38
|
-
def bar
|
39
|
-
"foo"
|
40
41
|
end
|
41
|
-
end
|
42
42
|
|
43
|
-
|
43
|
+
thread_info = {}
|
44
|
+
klass.new.test_method(thread_info)
|
45
|
+
assert_equal Thread.current.object_id, thread_info[:thread_id]
|
46
|
+
ensure
|
47
|
+
Mailbox.synchronous = false;
|
48
|
+
end
|
44
49
|
|
45
50
|
end
|
46
51
|
|
47
|
-
def
|
52
|
+
def test_should_support_channels
|
48
53
|
|
49
54
|
klass = Class.new do
|
50
55
|
include Mailbox
|
@@ -54,19 +59,54 @@ class MailboxTest < Test::Unit::TestCase
|
|
54
59
|
end
|
55
60
|
|
56
61
|
mailslot :channel => :test_channel
|
57
|
-
def test_method(
|
58
|
-
|
62
|
+
def test_method(message)
|
63
|
+
message[:thread_info][:thread_id] = Thread.current.object_id
|
64
|
+
message[:latch].count_down
|
59
65
|
end
|
60
66
|
end
|
61
67
|
|
68
|
+
thread_info = {}
|
62
69
|
latch = Latches::CountDownLatch.new 1
|
63
70
|
a_channel = JRL::Channel.new
|
64
71
|
|
65
72
|
klass.new(a_channel)
|
66
|
-
a_channel.publish latch
|
73
|
+
a_channel.publish :latch => latch, :thread_info => thread_info
|
67
74
|
|
68
75
|
assert latch.await( 1, Latches::TimeUnit::SECONDS ), "Timed out"
|
76
|
+
assert_not_equal Thread.current.object_id, thread_info[:thread_id]
|
69
77
|
|
70
78
|
end
|
71
79
|
|
80
|
+
def test_should_support_request_channels
|
81
|
+
|
82
|
+
klass = Class.new do
|
83
|
+
include Mailbox
|
84
|
+
|
85
|
+
def initialize(request_channel)
|
86
|
+
register_channel :test_channel, request_channel
|
87
|
+
end
|
88
|
+
|
89
|
+
mailslot :channel => :test_channel, :replyable => true
|
90
|
+
def test_method
|
91
|
+
return "ya_rly!"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
thread_info = {}
|
96
|
+
latch = Latches::CountDownLatch.new 1
|
97
|
+
request_channel = JRL::Channels::MemoryRequestChannel.new
|
98
|
+
|
99
|
+
klass.new(request_channel)
|
100
|
+
fiber = JRL::Fibers::ThreadFiber.new
|
101
|
+
fiber.start
|
102
|
+
|
103
|
+
response = "no response"
|
104
|
+
JRL::Channels::AsyncRequest.with_one_reply(fiber, request_channel, "orly?") do |message|
|
105
|
+
response = message
|
106
|
+
latch.count_down
|
107
|
+
end
|
108
|
+
|
109
|
+
assert latch.await(1, Latches::TimeUnit::SECONDS), "timed out"
|
110
|
+
assert_equal "ya_rly!", response
|
111
|
+
end
|
72
112
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'test_helper.rb'
|
2
|
+
|
3
|
+
class SynchronizedTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_supports_synchronized_access_of_methods_by_including_synchronized
|
6
|
+
|
7
|
+
klass = Class.new do
|
8
|
+
include Synchronized
|
9
|
+
|
10
|
+
attr_accessor :values
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@values = []
|
14
|
+
end
|
15
|
+
|
16
|
+
synchronized
|
17
|
+
def test_method( value )
|
18
|
+
@value = value
|
19
|
+
@values << @value
|
20
|
+
sleep 1
|
21
|
+
@values << @value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
clazz = klass.new
|
26
|
+
|
27
|
+
thread_1 = Thread.new do
|
28
|
+
clazz.test_method "thread 1"
|
29
|
+
end
|
30
|
+
|
31
|
+
sleep 0.3
|
32
|
+
|
33
|
+
thread_2 = Thread.new do
|
34
|
+
clazz.test_method "thread 2"
|
35
|
+
end
|
36
|
+
|
37
|
+
thread_1.join 1
|
38
|
+
thread_2.join 1
|
39
|
+
|
40
|
+
assert_equal "thread 1", clazz.values[0], "1st wrong"
|
41
|
+
assert_equal "thread 1", clazz.values[1], "2nd wrong"
|
42
|
+
assert_equal "thread 2", clazz.values[2], "3rd wrong"
|
43
|
+
assert_equal "thread 2", clazz.values[3], "4th wrong"
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_supports_synchronized_access_of_methods_by_including_mailbox
|
48
|
+
|
49
|
+
klass = Class.new do
|
50
|
+
include Mailbox
|
51
|
+
|
52
|
+
attr_accessor :values
|
53
|
+
|
54
|
+
def initialize
|
55
|
+
@values = []
|
56
|
+
end
|
57
|
+
|
58
|
+
synchronized
|
59
|
+
def test_method( value )
|
60
|
+
@value = value
|
61
|
+
@values << @value
|
62
|
+
sleep 1
|
63
|
+
@values << @value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
clazz = klass.new
|
68
|
+
|
69
|
+
thread_1 = Thread.new do
|
70
|
+
clazz.test_method "thread 1"
|
71
|
+
end
|
72
|
+
|
73
|
+
sleep 0.3
|
74
|
+
|
75
|
+
thread_2 = Thread.new do
|
76
|
+
clazz.test_method "thread 2"
|
77
|
+
end
|
78
|
+
|
79
|
+
thread_1.join 1
|
80
|
+
thread_2.join 1
|
81
|
+
|
82
|
+
assert_equal "thread 1", clazz.values[0], "1st wrong"
|
83
|
+
assert_equal "thread 1", clazz.values[1], "2nd wrong"
|
84
|
+
assert_equal "thread 2", clazz.values[2], "3rd wrong"
|
85
|
+
assert_equal "thread 2", clazz.values[3], "4th wrong"
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -29,12 +29,23 @@ description: Mailbox is a JRuby module that simplifies concurrency and is backed
|
|
29
29
|
specification_version: 3
|
30
30
|
default_executable:
|
31
31
|
files:
|
32
|
+
- .gitignore
|
32
33
|
- README
|
33
34
|
- Rakefile
|
34
35
|
- VERSION.yml
|
36
|
+
- example/channel_based_log_example.rb
|
37
|
+
- example/i_can_has_cheese_burger_example.rb
|
38
|
+
- example/log_example.rb
|
39
|
+
- example/parallel_each_example.rb
|
40
|
+
- example/ping_pong_example.rb
|
35
41
|
- lib/mailbox.rb
|
42
|
+
- lib/synchronized.rb
|
36
43
|
- mailbox.gemspec
|
44
|
+
- mailbox.iml
|
45
|
+
- mailbox.ipr
|
37
46
|
- test/mailbox_test.rb
|
47
|
+
- test/synchronized_test.rb
|
48
|
+
- test/test_helper.rb
|
38
49
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
50
|
requirements:
|
40
51
|
- - '>='
|
@@ -49,12 +60,14 @@ requirements: []
|
|
49
60
|
authors:
|
50
61
|
- Joel Friedman
|
51
62
|
- Patrick Farley
|
52
|
-
date: 2009-
|
63
|
+
date: 2009-11-04 06:00:00 +00:00
|
53
64
|
platform: ruby
|
54
65
|
test_files:
|
55
66
|
- test/mailbox_test.rb
|
67
|
+
- test/synchronized_test.rb
|
68
|
+
- test/test_helper.rb
|
56
69
|
version: !ruby/object:Gem::Version
|
57
|
-
version: 0.1.
|
70
|
+
version: 0.1.3
|
58
71
|
require_paths:
|
59
72
|
- lib
|
60
73
|
dependencies:
|