futex 0.3.1 → 0.4.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/.rultor.yml +3 -0
- data/README.md +16 -2
- data/Rakefile +7 -0
- data/appveyor.yml +38 -0
- data/futex.gemspec +7 -7
- data/lib/futex.rb +29 -11
- data/test/test_futex.rb +23 -0
- metadata +25 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e19f93d6754a7535a3a7824b2ba55aa6fb8fcf00e63b04011af936865e26895f
|
4
|
+
data.tar.gz: da61f106a45c5da30d76891f88f3adf389c1610c852604326b3d182eadfe1215
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1adb27a04e721ff4ba1356afd44e19731b7193938b4fba03805eab9722c1b986a20bd4e5f0343de3f685e71b95b0d4e94f34213a49007839fc4243090cc87a0
|
7
|
+
data.tar.gz: 29748f308f7790b67d3e33cf1827bcaea200a2b2a9f7aa977db77f08e289176fc5f65b9927ef5f88309559f59886a414bc12a0988cc0a40a236ba52edc674cc5
|
data/.rubocop.yml
CHANGED
data/.rultor.yml
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
[](http://www.elegantobjects.org)
|
1
2
|
[](http://www.rultor.com/p/yegor256/futex)
|
2
3
|
[](https://www.jetbrains.com/ruby/)
|
3
4
|
|
4
5
|
[](https://travis-ci.org/yegor256/futex)
|
6
|
+
[](https://ci.appveyor.com/project/yegor256/futex)
|
5
7
|
[](http://badge.fury.io/rb/futex)
|
6
8
|
[](https://codeclimate.com/github/yegor256/futex/maintainability)
|
9
|
+
[](http://rubydoc.info/github/yegor256/futex/master/frames)
|
7
10
|
|
8
11
|
Sometimes you need to synchronize your block of code, but `Mutex` is too coarse-grained,
|
9
12
|
because it _always locks_, no matter what objects your code accesses. The
|
@@ -21,12 +24,23 @@ Then, use it like this:
|
|
21
24
|
```ruby
|
22
25
|
require 'futex'
|
23
26
|
Futex.new('/tmp/my-file.txt').open |f|
|
24
|
-
|
27
|
+
IO.write(f, 'Hello, world!')
|
25
28
|
end
|
26
29
|
```
|
27
30
|
|
28
31
|
The file `/tmp/my-file.txt.lock` will be created and used as an entrance lock.
|
29
|
-
It will be deleted afterwards.
|
32
|
+
It <del>will</del> [won't](https://github.com/yegor256/futex/issues/5) be deleted afterwards.
|
33
|
+
|
34
|
+
If you are not planning to write to the file, it is recommended to get
|
35
|
+
a non-exclusive/shared access to it, by providing `false` to the method
|
36
|
+
`open()`:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
require 'futex'
|
40
|
+
Futex.new('/tmp/my-file.txt').open(false) |f|
|
41
|
+
IO.read(f)
|
42
|
+
end
|
43
|
+
```
|
30
44
|
|
31
45
|
For better traceability you can provide a few arguments to the
|
32
46
|
constructor of the `Futex` class, including:
|
data/Rakefile
CHANGED
@@ -44,6 +44,13 @@ Rake::TestTask.new(:test) do |test|
|
|
44
44
|
test.verbose = false
|
45
45
|
end
|
46
46
|
|
47
|
+
require 'rdoc/task'
|
48
|
+
RDoc::Task.new do |rdoc|
|
49
|
+
rdoc.main = 'README.md'
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.rdoc_files.include('README.md', 'lib/**/*.rb')
|
52
|
+
end
|
53
|
+
|
47
54
|
require 'rubocop/rake_task'
|
48
55
|
desc 'Run RuboCop on all directories'
|
49
56
|
RuboCop::RakeTask.new(:rubocop) do |task|
|
data/appveyor.yml
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
version: '{build}'
|
2
|
+
skip_tags: true
|
3
|
+
clone_depth: 10
|
4
|
+
branches:
|
5
|
+
only:
|
6
|
+
- master
|
7
|
+
except:
|
8
|
+
- gh-pages
|
9
|
+
os: Windows Server 2012
|
10
|
+
environment:
|
11
|
+
matrix:
|
12
|
+
- ruby_version: "23-x64"
|
13
|
+
- ruby_version: "24-x64"
|
14
|
+
- ruby_version: "25-x64"
|
15
|
+
install:
|
16
|
+
- ps: |
|
17
|
+
$Env:PATH = "C:\Ruby${Env:ruby_version}\bin;${Env:PATH}"
|
18
|
+
if ($Env:ruby_version -match "^23" ) {
|
19
|
+
# RubyInstaller; download OpenSSL headers from OpenKnapsack Project
|
20
|
+
$Env:openssl_dir = "C:\Ruby${Env:ruby_version}"
|
21
|
+
appveyor DownloadFile http://dl.bintray.com/oneclick/OpenKnapsack/x64/openssl-1.0.2j-x64-windows.tar.lzma
|
22
|
+
7z e openssl-1.0.2j-x64-windows.tar.lzma
|
23
|
+
7z x -y -oC:\Ruby${Env:ruby_version} openssl-1.0.2j-x64-windows.tar
|
24
|
+
} else {
|
25
|
+
# RubyInstaller2; openssl package seems to be installed already
|
26
|
+
$Env:openssl_dir = "C:\msys64\mingw64"
|
27
|
+
}
|
28
|
+
- bundle config --local path vendor/bundle
|
29
|
+
- bundle config build.openssl --with-openssl-dir=%openssl_dir%
|
30
|
+
- ruby -v
|
31
|
+
- bundle -v
|
32
|
+
build_script:
|
33
|
+
- bundle update
|
34
|
+
- bundle install
|
35
|
+
test_script:
|
36
|
+
- bundle exec rake --quiet
|
37
|
+
cache:
|
38
|
+
- vendor/bundle
|
data/futex.gemspec
CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
s.rubygems_version = '2.3.3'
|
32
32
|
s.required_ruby_version = '>=2.3'
|
33
33
|
s.name = 'futex'
|
34
|
-
s.version = '0.
|
34
|
+
s.version = '0.4.0'
|
35
35
|
s.license = 'MIT'
|
36
36
|
s.summary = 'File-based mutex'
|
37
37
|
s.description = 'Ruby Gem for file-based locking'
|
@@ -42,10 +42,10 @@ Gem::Specification.new do |s|
|
|
42
42
|
s.test_files = s.files.grep(%r{^(test)/})
|
43
43
|
s.rdoc_options = ['--charset=UTF-8']
|
44
44
|
s.extra_rdoc_files = ['README.md']
|
45
|
-
s.add_development_dependency 'minitest', '
|
46
|
-
s.add_development_dependency 'rake', '
|
47
|
-
s.add_development_dependency 'rdoc', '
|
48
|
-
s.add_development_dependency 'rubocop', '0.
|
49
|
-
s.add_development_dependency 'rubocop-rspec', '
|
50
|
-
s.add_development_dependency 'threads', '
|
45
|
+
s.add_development_dependency 'minitest', '5.11.3'
|
46
|
+
s.add_development_dependency 'rake', '12.3.1'
|
47
|
+
s.add_development_dependency 'rdoc', '4.3.0'
|
48
|
+
s.add_development_dependency 'rubocop', '0.60.0'
|
49
|
+
s.add_development_dependency 'rubocop-rspec', '1.30.1'
|
50
|
+
s.add_development_dependency 'threads', '0.3.0'
|
51
51
|
end
|
data/lib/futex.rb
CHANGED
@@ -25,37 +25,55 @@
|
|
25
25
|
require 'fileutils'
|
26
26
|
require 'time'
|
27
27
|
|
28
|
-
# Futex
|
28
|
+
# Futex (file mutex) is a fine-grained mutex that uses a file, not an entire
|
29
|
+
# thread, like <tt>Mutex</tt> does. Use it like this:
|
30
|
+
#
|
31
|
+
# require 'futex'
|
32
|
+
# Futex.new('/tmp/my-file.txt').open |f|
|
33
|
+
# IO.write(f, 'Hello, world!')
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# The file <tt>/tmp/my-file.txt.lock<tt> will be created and
|
37
|
+
# used as an entrance lock.
|
38
|
+
#
|
39
|
+
# If you are not planning to write to the file, to speed things up, you may
|
40
|
+
# want to get a non-exclusive access to it, by providing <tt>false</tt> to
|
41
|
+
# the method <tt>open()</tt>:
|
42
|
+
#
|
43
|
+
# require 'futex'
|
44
|
+
# Futex.new('/tmp/my-file.txt').open(false) |f|
|
45
|
+
# IO.read(f)
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# For more information read
|
49
|
+
# {README}[https://github.com/yegor256/futex/blob/master/README.md] file.
|
50
|
+
#
|
29
51
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
30
52
|
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
|
31
53
|
# License:: MIT
|
32
54
|
class Futex
|
55
|
+
# Creates a new instance of the class.
|
33
56
|
def initialize(path, log: STDOUT, timeout: 16, sleep: 0.005,
|
34
57
|
lock: path + '.lock', logging: false)
|
35
|
-
raise "File path can't be nil" if path.nil?
|
36
58
|
@path = path
|
37
|
-
raise "Log can't be nil" if log.nil?
|
38
59
|
@log = log
|
39
|
-
raise "Logging can't be nil" if logging.nil?
|
40
60
|
@logging = logging
|
41
|
-
raise "Timeout can't be nil" if timeout.nil?
|
42
|
-
raise "Timeout must be positive: #{timeout}" unless timeout.positive?
|
43
61
|
@timeout = timeout
|
44
|
-
raise "Sleep can't be nil" if sleep.nil?
|
45
|
-
raise "Sleep can't be negative or zero: #{sleep}" unless sleep.positive?
|
46
62
|
@sleep = sleep
|
47
|
-
raise "Lock path can't be nil" if lock.nil?
|
48
63
|
@lock = lock
|
49
64
|
end
|
50
65
|
|
51
|
-
|
66
|
+
# Open the file.
|
67
|
+
def open(exclusive = true)
|
52
68
|
FileUtils.mkdir_p(File.dirname(@lock))
|
53
69
|
step = (1 / @sleep).to_i
|
54
70
|
start = Time.now
|
55
71
|
File.open(@lock, File::CREAT | File::RDWR) do |f|
|
56
72
|
cycle = 0
|
57
73
|
loop do
|
58
|
-
|
74
|
+
if f.flock((exclusive ? File::LOCK_EX : File::LOCK_SH) | File::LOCK_NB)
|
75
|
+
break
|
76
|
+
end
|
59
77
|
sleep(@sleep)
|
60
78
|
cycle += 1
|
61
79
|
if Time.now - start > @timeout
|
data/test/test_futex.rb
CHANGED
@@ -25,6 +25,8 @@
|
|
25
25
|
require 'minitest/autorun'
|
26
26
|
require 'tmpdir'
|
27
27
|
require 'threads'
|
28
|
+
require 'digest'
|
29
|
+
require 'securerandom'
|
28
30
|
require_relative '../lib/futex'
|
29
31
|
|
30
32
|
# Futex test.
|
@@ -59,6 +61,27 @@ class FutexTest < Minitest::Test
|
|
59
61
|
end
|
60
62
|
end
|
61
63
|
|
64
|
+
def test_non_exclusive_locking
|
65
|
+
Dir.mktmpdir do |dir|
|
66
|
+
path = File.join(dir, 'g/e/f/file.txt')
|
67
|
+
Threads.new(20).assert(1000) do |_, r|
|
68
|
+
if (r % 50).zero?
|
69
|
+
Futex.new(path).open do |f|
|
70
|
+
text = SecureRandom.hex(1024)
|
71
|
+
hash = Digest::SHA256.hexdigest(text)
|
72
|
+
IO.write(f, text + ' ' + hash)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
Futex.new(path).open(false) do |f|
|
76
|
+
if File.exist?(f)
|
77
|
+
text, hash = IO.read(f, text).split(' ')
|
78
|
+
assert_equal(hash, Digest::SHA256.hexdigest(text))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
62
85
|
# This test doesn't work and I can't fix it. If I remove the file
|
63
86
|
# afterwards, the flock() logic gets broken. More about it here:
|
64
87
|
# https://stackoverflow.com/questions/53011200
|
metadata
CHANGED
@@ -1,99 +1,99 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: futex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 5.11.3
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 5.11.3
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 12.3.1
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 12.3.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rdoc
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 4.3.0
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 4.3.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rubocop
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - '='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
61
|
+
version: 0.60.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - '='
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
68
|
+
version: 0.60.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rubocop-rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - '='
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 1.30.1
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - '='
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 1.30.1
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: threads
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - '='
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 0.3.0
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - '='
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 0.3.0
|
97
97
|
description: Ruby Gem for file-based locking
|
98
98
|
email: yegor256@gmail.com
|
99
99
|
executables: []
|
@@ -110,6 +110,7 @@ files:
|
|
110
110
|
- Gemfile
|
111
111
|
- README.md
|
112
112
|
- Rakefile
|
113
|
+
- appveyor.yml
|
113
114
|
- futex.gemspec
|
114
115
|
- lib/futex.rb
|
115
116
|
- test/test_futex.rb
|