futex 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![EO principles respected here](http://www.elegantobjects.org/badge.svg)](http://www.elegantobjects.org)
|
1
2
|
[![DevOps By Rultor.com](http://www.rultor.com/b/yegor256/futex)](http://www.rultor.com/p/yegor256/futex)
|
2
3
|
[![We recommend RubyMine](http://www.elegantobjects.org/rubymine.svg)](https://www.jetbrains.com/ruby/)
|
3
4
|
|
4
5
|
[![Build Status](https://travis-ci.org/yegor256/futex.svg)](https://travis-ci.org/yegor256/futex)
|
6
|
+
[![Build status](https://ci.appveyor.com/api/projects/status/po1mn8ca96jk0llr?svg=true)](https://ci.appveyor.com/project/yegor256/futex)
|
5
7
|
[![Gem Version](https://badge.fury.io/rb/futex.svg)](http://badge.fury.io/rb/futex)
|
6
8
|
[![Maintainability](https://api.codeclimate.com/v1/badges/5528e182bb5e4a2ecc1f/maintainability)](https://codeclimate.com/github/yegor256/futex/maintainability)
|
9
|
+
[![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](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
|