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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c7d43b57ed9b9c1976f33c1da3d39e9646bbb5e890594c339ca7178da830db81
4
- data.tar.gz: 7584b99738962f3526367c4310a5a626e8bcec599e5064350825f7a784ab0fac
3
+ metadata.gz: e19f93d6754a7535a3a7824b2ba55aa6fb8fcf00e63b04011af936865e26895f
4
+ data.tar.gz: da61f106a45c5da30d76891f88f3adf389c1610c852604326b3d182eadfe1215
5
5
  SHA512:
6
- metadata.gz: 8af3cb3b44c1e665d0a7744b5321b2084844d1b8c31b73cc37ea1abe47cc6e9c30ace004fd6abac823998a3584385ac18de237bebd53fc6702df186e2646b0bf
7
- data.tar.gz: c99a473b9ecf837cfc95d852ce2e5886b492ce19cba9929cc3dbaef70f80d8bccbb182f9ae08006f6e5ae35611af2819ef0ae4532e00670f6c91c75016a52c90
6
+ metadata.gz: d1adb27a04e721ff4ba1356afd44e19731b7193938b4fba03805eab9722c1b986a20bd4e5f0343de3f685e71b95b0d4e94f34213a49007839fc4243090cc87a0
7
+ data.tar.gz: 29748f308f7790b67d3e33cf1827bcaea200a2b2a9f7aa977db77f08e289176fc5f65b9927ef5f88309559f59886a414bc12a0988cc0a40a236ba52edc674cc5
data/.rubocop.yml CHANGED
@@ -2,6 +2,8 @@ AllCops:
2
2
  DisplayCopNames: true
3
3
  TargetRubyVersion: 2.3.3
4
4
 
5
+ Layout/EmptyLineAfterGuardClause:
6
+ Enabled: false
5
7
  Layout/MultilineMethodCallIndentation:
6
8
  Enabled: false
7
9
  Metrics/AbcSize:
data/.rultor.yml CHANGED
@@ -20,4 +20,7 @@ architect:
20
20
  - yegor256
21
21
  merge:
22
22
  commanders: []
23
+ script: |-
24
+ bundle install
25
+ rake
23
26
  deploy: {}
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
- File.write(f, 'Hello, world!')
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.3.1'
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', '~>5'
46
- s.add_development_dependency 'rake', '~>12'
47
- s.add_development_dependency 'rdoc', '~>4'
48
- s.add_development_dependency 'rubocop', '0.58.1'
49
- s.add_development_dependency 'rubocop-rspec', '~>1'
50
- s.add_development_dependency 'threads', '~>0'
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
- def open
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
- break if f.flock(File::LOCK_EX | File::LOCK_NB)
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.3.1
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-10-26 00:00:00.000000000 Z
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: '5'
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: '5'
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: '12'
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: '12'
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: '4'
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: '4'
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.58.1
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.58.1
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: '1'
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: '1'
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: '0'
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: '0'
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