mega_mutex 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|