mega_mutex 0.1.0 → 0.2.0
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.
- data/Rakefile +11 -1
- data/VERSION +1 -1
- data/lib/mega_mutex/cross_process_mutex.rb +5 -23
- data/lib/mega_mutex.rb +48 -3
- data/mega_mutex.gemspec +7 -5
- data/spec/lib/mega_mutex_spec.rb +21 -23
- data/spec/spec_helper.rb +32 -0
- metadata +5 -3
data/Rakefile
CHANGED
@@ -19,6 +19,16 @@ rescue LoadError
|
|
19
19
|
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
20
20
|
end
|
21
21
|
|
22
|
+
namespace :github do
|
23
|
+
task :push do
|
24
|
+
remotes = `git remote`.split("\n")
|
25
|
+
unless remotes.include?('github')
|
26
|
+
sh('git remote add github git@github.com:songkick/mega_mutex.git')
|
27
|
+
end
|
28
|
+
sh('git push github master')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
22
32
|
require 'spec/rake/spectask'
|
23
33
|
Spec::Rake::SpecTask.new(:spec) do |spec|
|
24
34
|
spec.libs << 'lib' << 'spec'
|
@@ -33,7 +43,7 @@ end
|
|
33
43
|
|
34
44
|
task :spec => :check_dependencies
|
35
45
|
|
36
|
-
task :default => :spec
|
46
|
+
task :default => [:spec, 'github:push', 'gemcutter:release']
|
37
47
|
|
38
48
|
require 'rake/rdoctask'
|
39
49
|
Rake::RDocTask.new do |rdoc|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -6,24 +6,6 @@ module MegaMutex
|
|
6
6
|
|
7
7
|
class CrossProcessMutex
|
8
8
|
|
9
|
-
class Configuration
|
10
|
-
attr_accessor :memcache_servers
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
@memcache_servers = 'localhost'
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class << self
|
18
|
-
def configure
|
19
|
-
yield configuration
|
20
|
-
end
|
21
|
-
|
22
|
-
def configuration
|
23
|
-
@configuration ||= Configuration.new
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
9
|
def initialize(key, timeout = nil)
|
28
10
|
@key = key
|
29
11
|
@timeout = timeout
|
@@ -45,6 +27,10 @@ module MegaMutex
|
|
45
27
|
log "Unlocking Mutex."
|
46
28
|
end
|
47
29
|
|
30
|
+
def current_lock
|
31
|
+
cache.get(@key)
|
32
|
+
end
|
33
|
+
|
48
34
|
private
|
49
35
|
|
50
36
|
def timeout?
|
@@ -81,10 +67,6 @@ module MegaMutex
|
|
81
67
|
current_lock == my_lock_id
|
82
68
|
end
|
83
69
|
|
84
|
-
def current_lock
|
85
|
-
cache.get(@key)
|
86
|
-
end
|
87
|
-
|
88
70
|
def set_current_lock(new_lock)
|
89
71
|
cache.add(@key, my_lock_id)
|
90
72
|
end
|
@@ -94,7 +76,7 @@ module MegaMutex
|
|
94
76
|
end
|
95
77
|
|
96
78
|
def cache
|
97
|
-
@cache ||= MemCache.new
|
79
|
+
@cache ||= MemCache.new MegaMutex.configuration.memcache_servers, :namespace => MegaMutex.configuration.namespace
|
98
80
|
end
|
99
81
|
end
|
100
82
|
end
|
data/lib/mega_mutex.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
$:.push File.expand_path(File.dirname(__FILE__))
|
3
|
-
|
2
|
+
$:.push File.expand_path(File.dirname(__FILE__)) unless $:.include?(File.expand_path(File.dirname(__FILE__)))
|
4
3
|
require 'mega_mutex/cross_process_mutex'
|
5
4
|
|
6
5
|
# == Why
|
@@ -41,6 +40,10 @@ require 'mega_mutex/cross_process_mutex'
|
|
41
40
|
# config.memcache_servers = ['mc1', 'mc2']
|
42
41
|
# end
|
43
42
|
module MegaMutex
|
43
|
+
|
44
|
+
def self.get_current_lock(mutex_id)
|
45
|
+
CrossProcessMutex.new(mutex_id).current_lock
|
46
|
+
end
|
44
47
|
|
45
48
|
##
|
46
49
|
# Wraps code that should only be run when the mutex has been obtained.
|
@@ -55,7 +58,49 @@ module MegaMutex
|
|
55
58
|
# end
|
56
59
|
def with_cross_process_mutex(mutex_id, options = {}, &block)
|
57
60
|
mutex = CrossProcessMutex.new(mutex_id, options[:timeout])
|
58
|
-
|
61
|
+
begin
|
62
|
+
mutex.run(&block)
|
63
|
+
rescue Object => e
|
64
|
+
mega_mutex_insert_into_backtrace(
|
65
|
+
e,
|
66
|
+
/mega_mutex\.rb.*with_cross_process_mutex/,
|
67
|
+
"MegaMutex lock #{mutex_id}"
|
68
|
+
)
|
69
|
+
raise e
|
70
|
+
end
|
59
71
|
end
|
60
72
|
|
73
|
+
# inserts a line into a backtrace at the correct location
|
74
|
+
def mega_mutex_insert_into_backtrace(exception, re, newline)
|
75
|
+
loc = nil
|
76
|
+
exception.backtrace.each_with_index do |line, index|
|
77
|
+
if line =~ re
|
78
|
+
loc = index
|
79
|
+
break
|
80
|
+
end
|
81
|
+
end
|
82
|
+
if loc
|
83
|
+
exception.backtrace.insert(loc, newline)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Configuration
|
88
|
+
attr_accessor :memcache_servers, :namespace
|
89
|
+
|
90
|
+
def initialize
|
91
|
+
@memcache_servers = 'localhost'
|
92
|
+
@namespace = 'mega_mutex'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class << self
|
97
|
+
def configure
|
98
|
+
yield configuration
|
99
|
+
end
|
100
|
+
|
101
|
+
def configuration
|
102
|
+
@configuration ||= Configuration.new
|
103
|
+
end
|
104
|
+
end
|
61
105
|
end
|
106
|
+
|
data/mega_mutex.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{mega_mutex}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Matt Johnson", "Matt Wynne"]
|
12
|
-
s.date = %q{2009-
|
12
|
+
s.date = %q{2009-10-26}
|
13
13
|
s.description = %q{Cross-process mutex using MemCache}
|
14
14
|
s.email = %q{developers@songkick.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -26,15 +26,17 @@ Gem::Specification.new do |s|
|
|
26
26
|
"lib/mega_mutex.rb",
|
27
27
|
"lib/mega_mutex/cross_process_mutex.rb",
|
28
28
|
"mega_mutex.gemspec",
|
29
|
-
"spec/lib/mega_mutex_spec.rb"
|
29
|
+
"spec/lib/mega_mutex_spec.rb",
|
30
|
+
"spec/spec_helper.rb"
|
30
31
|
]
|
31
32
|
s.homepage = %q{http://github.com/songkick/mega_mutex}
|
32
33
|
s.rdoc_options = ["--charset=UTF-8"]
|
33
34
|
s.require_paths = ["lib"]
|
34
|
-
s.rubygems_version = %q{1.3.
|
35
|
+
s.rubygems_version = %q{1.3.5}
|
35
36
|
s.summary = %q{Cross-process mutex using MemCache}
|
36
37
|
s.test_files = [
|
37
|
-
"spec/
|
38
|
+
"spec/spec_helper.rb",
|
39
|
+
"spec/lib/mega_mutex_spec.rb"
|
38
40
|
]
|
39
41
|
|
40
42
|
if s.respond_to? :specification_version then
|
data/spec/lib/mega_mutex_spec.rb
CHANGED
@@ -1,20 +1,12 @@
|
|
1
|
-
require File.
|
2
|
-
|
3
|
-
# Logging::Logger[:root].add_appenders(Logging::Appenders.stdout)
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
4
2
|
|
5
3
|
module MegaMutex
|
6
4
|
describe MegaMutex do
|
7
5
|
def logger
|
8
6
|
Logging::Logger['Specs']
|
9
7
|
end
|
10
|
-
|
11
|
-
|
12
|
-
@old_abort_on_exception_value = Thread.abort_on_exception
|
13
|
-
Thread.abort_on_exception = true
|
14
|
-
end
|
15
|
-
after(:all) do
|
16
|
-
Thread.abort_on_exception = @old_abort_on_exception_value
|
17
|
-
end
|
8
|
+
|
9
|
+
abort_on_thread_exceptions
|
18
10
|
|
19
11
|
describe "two blocks, one fast, one slow" do
|
20
12
|
before(:each) do
|
@@ -29,10 +21,9 @@ module MegaMutex
|
|
29
21
|
|
30
22
|
describe "with no lock" do
|
31
23
|
it "trying to run the block twice should raise an error" do
|
32
|
-
threads = []
|
33
24
|
threads << Thread.new(&@mutually_exclusive_block)
|
34
25
|
threads << Thread.new(&@mutually_exclusive_block)
|
35
|
-
|
26
|
+
wait_for_threads_to_finish
|
36
27
|
@errors.should_not be_empty
|
37
28
|
end
|
38
29
|
end
|
@@ -51,11 +42,10 @@ module MegaMutex
|
|
51
42
|
[2, 20].each do |n|
|
52
43
|
describe "when #{n} blocks try to run at the same instant in the same process" do
|
53
44
|
it "should run each in turn" do
|
54
|
-
threads = []
|
55
45
|
n.times do
|
56
46
|
threads << Thread.new{ with_cross_process_mutex(mutex_id, &@mutually_exclusive_block) }
|
57
47
|
end
|
58
|
-
|
48
|
+
wait_for_threads_to_finish
|
59
49
|
@errors.should be_empty
|
60
50
|
end
|
61
51
|
end
|
@@ -110,19 +100,27 @@ module MegaMutex
|
|
110
100
|
|
111
101
|
describe "with a timeout" do
|
112
102
|
include MegaMutex
|
103
|
+
|
113
104
|
it "should raise an error if the code blocks for longer than the timeout" do
|
114
|
-
@
|
115
|
-
|
116
|
-
threads << Thread.new{ with_cross_process_mutex('foo'){ sleep 2 } }
|
105
|
+
@exception = nil
|
106
|
+
@first_thread_has_started = false
|
117
107
|
threads << Thread.new do
|
108
|
+
with_cross_process_mutex('foo') do
|
109
|
+
@first_thread_has_started = true
|
110
|
+
sleep 0.2
|
111
|
+
end
|
112
|
+
end
|
113
|
+
threads << Thread.new do
|
114
|
+
sleep 0.1 until @first_thread_has_started
|
118
115
|
begin
|
119
|
-
with_cross_process_mutex('foo', :timeout => 1 )
|
120
|
-
|
121
|
-
|
116
|
+
with_cross_process_mutex('foo', :timeout => 0.1 ) do
|
117
|
+
raise 'this code should never run'
|
118
|
+
end
|
119
|
+
rescue Exception => @exception
|
122
120
|
end
|
123
121
|
end
|
124
|
-
|
125
|
-
@
|
122
|
+
wait_for_threads_to_finish
|
123
|
+
assert @exception.is_a?(MegaMutex::TimeoutError), "Expected TimeoutError to be raised, but wasn't"
|
126
124
|
end
|
127
125
|
end
|
128
126
|
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/mega_mutex')
|
2
|
+
require 'test/unit/assertions'
|
3
|
+
|
4
|
+
# Logging::Logger[:root].add_appenders(Logging::Appenders.stdout)
|
5
|
+
|
6
|
+
module ThreadHelper
|
7
|
+
def abort_on_thread_exceptions
|
8
|
+
before(:all) do
|
9
|
+
@old_abort_on_exception_value = Thread.abort_on_exception
|
10
|
+
Thread.abort_on_exception = true
|
11
|
+
end
|
12
|
+
after(:all) do
|
13
|
+
Thread.abort_on_exception = @old_abort_on_exception_value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ThreadExampleHelper
|
19
|
+
def threads
|
20
|
+
@threads ||= []
|
21
|
+
end
|
22
|
+
|
23
|
+
def wait_for_threads_to_finish
|
24
|
+
threads.each{ |t| t.join }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Spec::Runner.configure do |config|
|
29
|
+
config.extend ThreadHelper
|
30
|
+
config.include ThreadExampleHelper
|
31
|
+
config.include Test::Unit::Assertions
|
32
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mega_mutex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Johnson
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-
|
13
|
+
date: 2009-10-26 00:00:00 +00:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -53,6 +53,7 @@ files:
|
|
53
53
|
- lib/mega_mutex/cross_process_mutex.rb
|
54
54
|
- mega_mutex.gemspec
|
55
55
|
- spec/lib/mega_mutex_spec.rb
|
56
|
+
- spec/spec_helper.rb
|
56
57
|
has_rdoc: true
|
57
58
|
homepage: http://github.com/songkick/mega_mutex
|
58
59
|
licenses: []
|
@@ -77,9 +78,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
78
|
requirements: []
|
78
79
|
|
79
80
|
rubyforge_project:
|
80
|
-
rubygems_version: 1.3.
|
81
|
+
rubygems_version: 1.3.5
|
81
82
|
signing_key:
|
82
83
|
specification_version: 3
|
83
84
|
summary: Cross-process mutex using MemCache
|
84
85
|
test_files:
|
86
|
+
- spec/spec_helper.rb
|
85
87
|
- spec/lib/mega_mutex_spec.rb
|