filelock 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ca5c3766ead1d38142a6a07fff24bb2b84eb1bf8
4
+ data.tar.gz: 7fa07b19de6de7a852412b07a3425d6992a0fbb2
5
+ SHA512:
6
+ metadata.gz: cea750b7cf162bb5c7924a442a29acb02cb0ea7a2cd26baa29e3c3227e55a238bcf8ce6cc01bfca1d611f6b43aaa66481a6efeb4b00afea9e5389d62787997d7
7
+ data.tar.gz: 78f0673ea1fbadc2658c3e455b7d5eadb1eb5b6522313e6944bafc4314ec4d45fbcd5f2633cdefe1a36552d402c7fb06d1dc2a936ed7b4db257ccf22283e5aca
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ script: bundle exec rspec
3
+ rvm:
4
+ - "1.8.7"
5
+ - "1.9.3"
6
+ - "1.9.2"
7
+ - "2.0.0"
8
+ - jruby-19mode
9
+ - jruby-18mode
10
+ - rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in filelock.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Adam Stankiewicz
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,56 @@
1
+ # Filelock [![Build Status][travis-img-url]][travis-url]
2
+
3
+ [travis-img-url]: https://travis-ci.org/sheerun/filelock.png
4
+ [travis-url]: https://travis-ci-org/sheerun/filelock
5
+
6
+ Heavily tested, but simple filelocking solution using [flock](http://linux.die.net/man/2/flock) command. It guarantees unlocking of files.
7
+
8
+ It works for sure on MRI 1.8, 1.9, 2.0, JRuby in both 1.8 and 1.9 mode, and Rubinius.
9
+
10
+ ## Usage
11
+
12
+ ```ruby
13
+ Filelock '/tmp/path/to/lock' do
14
+ # do blocking operation
15
+ end
16
+ ```
17
+
18
+ You can also pass the timeout for blocking operation (default is 60 seconds):
19
+
20
+ ```ruby
21
+ Filelock '/tmp/path/to/lock', :timeout => 10 do
22
+ # do blocking operation
23
+ end
24
+ ```
25
+
26
+ Note that lock file directory must already exist, and lock file is not removed after unlock.
27
+
28
+ ## FAQ
29
+
30
+ *Does it support NFS?*
31
+
32
+ No. You can use more complex [lockfile](https://github.com/ahoward/lockfile) gem if you want to support NFS.
33
+
34
+ *The code is so short. Why shouln't I just copy-paste it?*
35
+
36
+ Because even such short code can have issues in future. File locking is very fragile operation. You may expect new releases of this gem fixing discovered bogus behavior (or introducing awesome features).
37
+
38
+ You are encouraged to use it if you develop gem that uses flock command, and care about running it on different ruby versions and platforms. Each has its own quirks with regard to flock command.
39
+
40
+ *How it's different from [lockfile](https://github.com/ahoward/lockfile) gem?*
41
+
42
+ Lockfile is filelocking solution handling NFS filesystems, based on homemade locking solution. Filelock uses [flock](http://linux.die.net/man/2/flock) UNIX command to handle filelocking on very low level. Also lockfile allows you to specify retry timeout. In case of Ruby's flock command this is hard-cored to 0.1 seconds.
43
+
44
+ *How it's different from [cleverua-lockfile](https://github.com/cleverua/lockfile) gem?*
45
+
46
+ Cleverua removes lockfile after unlocking it. Thas has been proven fatal both in my tests and in [filelocking advices from the Internet](http://world.std.com/~swmcd/steven/tech/flock.html). You could try find a way to remove lock file without breaking Filelock tests. I will be glad to accept such pull-request.
47
+
48
+ ## Challenge
49
+
50
+ Please try to break Filelock in some way (note it doesn't support NFS).
51
+
52
+ If you show at least one failing test, I'll put your name below:
53
+
54
+ ## License
55
+
56
+ Filelock is MIT-licensed. You are awesome.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'filelock/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "filelock"
8
+ spec.version = Filelock::VERSION
9
+ spec.authors = ["Adam Stankiewicz"]
10
+ spec.email = ["sheerun@sher.pl"]
11
+ spec.description = %q{Heavily tested yet simple filelocking solution using flock}
12
+ spec.summary = %q{Heavily tested yet simple filelocking solution using flock}
13
+ spec.homepage = "http://github.com/sheerun/filelock"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ end
@@ -0,0 +1,21 @@
1
+ require 'filelock/version'
2
+ require 'timeout'
3
+ require 'tempfile'
4
+
5
+ if RUBY_PLATFORM == "java"
6
+ def Filelock(lockname, options = {}, &block)
7
+ lockname = lockname.path if lockname.is_a?(Tempfile)
8
+ File.open(lockname, File::RDWR|File::CREAT, 0644) do |file|
9
+ Thread.pass until file.flock(File::LOCK_EX)
10
+ Timeout::timeout(options.fetch(:timeout, 60)) { yield }
11
+ end
12
+ end
13
+ else
14
+ def Filelock(lockname, options = {}, &block)
15
+ lockname = lockname.path if lockname.is_a?(Tempfile)
16
+ File.open(lockname, File::RDWR|File::CREAT, 0644) do |file|
17
+ file.flock(File::LOCK_EX)
18
+ Timeout::timeout(options.fetch(:timeout, 60)) { yield }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module Filelock
2
+ VERSION = "1.0.3"
3
+ end
@@ -0,0 +1,232 @@
1
+ require 'filelock'
2
+ require 'tempfile'
3
+ require 'timeout'
4
+
5
+ describe Filelock do
6
+
7
+ # Helper because File.write won't work for older Ruby
8
+ def write(filename, contents)
9
+ File.open(filename.to_s, 'w') { |f| f.write(contents) }
10
+ end
11
+
12
+ def parallel(n = 2, templock = nil, &block)
13
+ Timeout::timeout(5) do
14
+ templock ||= Tempfile.new(['sample', '.lock'])
15
+
16
+ (1..n).map do
17
+ Thread.new do
18
+ Filelock(templock, &block)
19
+ end
20
+ end.map(&:join)
21
+ end
22
+ end
23
+
24
+ def parallel_forks(n = 2, templock = nil, &block)
25
+ Timeout::timeout(5) do
26
+ templock ||= Tempfile.new(['sample', '.lock'])
27
+
28
+ (1..n).map do
29
+ fork {
30
+ Filelock(templock, &block)
31
+ }
32
+ end.map do |pid|
33
+ Process.waitpid(pid)
34
+ end
35
+ end
36
+ end
37
+
38
+ it 'runs simple ruby block as usual' do
39
+ Dir.mktmpdir do |dir|
40
+ lockpath = File.join(dir, 'sample.lock')
41
+ answer = 0
42
+
43
+ Filelock lockpath do
44
+ answer += 42
45
+ end
46
+
47
+ expect(answer).to eq(42)
48
+ end
49
+ end
50
+
51
+ it 'returns value returned by block' do
52
+ Dir.mktmpdir do |dir|
53
+ lockpath = File.join(dir, 'sample.lock')
54
+ answer = Filelock lockpath do
55
+ 42
56
+ end
57
+
58
+ expect(answer).to eq(42)
59
+ end
60
+ end
61
+
62
+ it 'runs in parallel without race condition' do
63
+ answer = 0
64
+
65
+ parallel(2) do
66
+ value = answer
67
+ sleep 0.1
68
+ answer = value + 21
69
+ end
70
+
71
+ expect(answer).to eq(42)
72
+ end
73
+
74
+ it 'handels high amount of concurrent tasks' do
75
+ answer = 0
76
+
77
+ parallel(100) do
78
+ value = answer
79
+ sleep 0.001
80
+ answer = value + 1
81
+ end
82
+
83
+ expect(answer).to eq(100)
84
+ end
85
+
86
+ it 'creates lock file on disk during block execution' do
87
+ Dir.mktmpdir do |dir|
88
+ lockpath = File.join(dir, 'sample.lock')
89
+ parallel(2, lockpath) do
90
+ expect(File.exist?(lockpath)).to eq(true)
91
+ end
92
+ end
93
+ end
94
+
95
+ it 'runs in parallel without race condition' do
96
+ Dir.mktmpdir do |dir|
97
+ lockpath = File.join(dir, 'sample.lock')
98
+
99
+ answer = 0
100
+
101
+ begin
102
+ Filelock(lockpath) do
103
+ raise '42'
104
+ end
105
+ rescue RuntimeError
106
+ end
107
+
108
+ Filelock(lockpath) do
109
+ answer += 42
110
+ end
111
+
112
+ expect(answer).to eq(42)
113
+ end
114
+ end
115
+
116
+ it 'times out after specified number of seconds' do
117
+ Dir.mktmpdir do |dir|
118
+ lockpath = File.join(dir, 'sample.lock')
119
+
120
+ answer = 42
121
+
122
+ begin
123
+ Filelock lockpath, :timeout => 1 do
124
+ sleep 2
125
+ answer = 0
126
+ end
127
+ rescue Timeout::Error
128
+ end
129
+
130
+ expect(answer).to eq(42)
131
+ end
132
+ end
133
+
134
+ # Java doesn't support forking
135
+ if RUBY_PLATFORM != 'java'
136
+
137
+ it 'should work for multiple processes' do
138
+ write('/tmp/number.txt', '0')
139
+
140
+ parallel_forks(6) do
141
+ number = File.read('/tmp/number.txt').to_i
142
+ sleep 0.3
143
+ write('/tmp/number.txt', (number + 7).to_s)
144
+ end
145
+
146
+ number = File.read('/tmp/number.txt').to_i
147
+
148
+ expect(number).to eq(42)
149
+ end
150
+
151
+ it 'should handle heavy forking' do
152
+ write('/tmp/number.txt', '0')
153
+
154
+ parallel_forks(100) do
155
+ number = File.read('/tmp/number.txt').to_i
156
+ sleep 0.001
157
+ write('/tmp/number.txt', (number + 1).to_s)
158
+ end
159
+
160
+ number = File.read('/tmp/number.txt').to_i
161
+
162
+ expect(number).to eq(100)
163
+ end
164
+
165
+ it 'should unblock files when killing processes' do
166
+ Dir.mktmpdir do |dir|
167
+ lockpath = File.join(dir, 'sample.lock')
168
+
169
+ Dir.mktmpdir do |dir|
170
+ pid = fork {
171
+ Filelock lockpath do
172
+ sleep 10
173
+ end
174
+ }
175
+
176
+ sleep 0.5
177
+
178
+ answer = 0
179
+
180
+ thread = Thread.new {
181
+ Filelock lockpath do
182
+ answer += 42
183
+ end
184
+ }
185
+
186
+ sleep 0.5
187
+
188
+ expect(answer).to eq(0)
189
+ Process.kill(9, pid)
190
+ thread.join
191
+
192
+ expect(answer).to eq(42)
193
+ end
194
+ end
195
+ end
196
+
197
+ it 'should handle Pathname as well as string path' do
198
+ Dir.mktmpdir do |dir|
199
+ lockpath = Pathname.new(File.join(dir, 'sample.lock'))
200
+
201
+ answer = 0
202
+ Filelock lockpath do
203
+ answer += 42
204
+ end
205
+
206
+ expect(answer).to eq(42)
207
+ end
208
+ end
209
+
210
+ end
211
+
212
+ # It failed for 1.8.7 (cannot convert to String)
213
+ it 'works for Tempfile' do
214
+ answer = 0
215
+
216
+ Filelock Tempfile.new(['sample', '.lock']) do
217
+ answer += 42
218
+ end
219
+
220
+ expect(answer).to eq(42)
221
+ end
222
+
223
+ # If implemented the wrong way lock is created elsewhere
224
+ it 'creates file with exact path provided' do
225
+ filename = "/tmp/awesome-lock-#{rand}.lock"
226
+
227
+ Filelock filename do
228
+ end
229
+
230
+ expect(File.exist?(filename)).to eq(true)
231
+ end
232
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: filelock
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Adam Stankiewicz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
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
+ description: Heavily tested yet simple filelocking solution using flock
56
+ email:
57
+ - sheerun@sher.pl
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .rspec
63
+ - .travis.yml
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - filelock.gemspec
69
+ - lib/filelock.rb
70
+ - lib/filelock/version.rb
71
+ - spec/filelock_spec.rb
72
+ homepage: http://github.com/sheerun/filelock
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.0.3
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Heavily tested yet simple filelocking solution using flock
96
+ test_files:
97
+ - spec/filelock_spec.rb
98
+ has_rdoc: