lockf.rb 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cc7a6787621983c5bcea9dc35a899eb064d8d09f20d905b90b8391e8fc018f64
4
+ data.tar.gz: e32dcc7d52786b2c1c2acdb652264b450506debd976583a2ff8f46a367b2efe9
5
+ SHA512:
6
+ metadata.gz: b275ee5e97c06f6dfcfbf32074139b1514b3fe4795ab1669bdbb188336f0039aee368846146b6e4b6ef15fc41d0077816844cfcf772e52039584b5816d5e8cbb
7
+ data.tar.gz: ffab08187b049fd42ad5549e060c0ba5da31228212c1e5419c3c9640a8842c96617a20cd54ee6f14edc265a5b28ddf50bc00e8dc343493fce0ec6a96a388a3cb
data/.bundle/config ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_PATH: ".gems"
@@ -0,0 +1,23 @@
1
+ name: lockf.rb
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ specs:
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ os: [ubuntu-latest]
15
+ ruby: [3.2]
16
+ runs-on: ${{ matrix.os }}
17
+ steps:
18
+ - uses: actions/checkout@v2
19
+ - uses: ruby/setup-ruby@v1
20
+ with:
21
+ ruby-version: ${{ matrix.ruby }}
22
+ - run: bundle install
23
+ - run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *.so
2
+ *.o
3
+ *.lock
4
+ Makefile
5
+ tmp/
6
+ pkg/
7
+ *~
8
+ .yardoc/
9
+ .gems/
10
+ doc/
data/.projectile ADDED
@@ -0,0 +1,3 @@
1
+ +./
2
+ +.github/
3
+ -.gems/
data/.rubocop.yml ADDED
@@ -0,0 +1,21 @@
1
+ ##
2
+ # Plugins
3
+ require:
4
+ - standard
5
+
6
+ ##
7
+ # Defaults: standard-rb
8
+ inherit_gem:
9
+ standard: config/base.yml
10
+
11
+ ##
12
+ # Disabled cops
13
+ Layout/MultilineMethodCallIndentation:
14
+ Enabled: false
15
+ Layout/ArgumentAlignment:
16
+ Enabled: false
17
+
18
+ ##
19
+ # Options for all cops.
20
+ AllCops:
21
+ TargetRubyVersion: 2.7
data/.uncrustify.cfg ADDED
@@ -0,0 +1,2 @@
1
+ indent_with_tabs = 0
2
+ indent_columns = 4
data/.yardopts ADDED
@@ -0,0 +1,4 @@
1
+ -m markdown -M redcarpet --no-private
2
+ -
3
+ README.md
4
+ LICENSE
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+ gemspec
3
+ gem "test-cmd.rb", github: "0x1eef/test-cmd.rb", tag: "v0.4.0"
data/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Copyright (C) 2023 by 0x1eef <0x1eef@protonmail.com>
2
+
3
+ Permission to use, copy, modify, and/or distribute this
4
+ software for any purpose with or without fee is hereby
5
+ granted.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
8
+ ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
9
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
10
+ EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
12
+ RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
14
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
15
+ OF THIS SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,165 @@
1
+ ## About
2
+
3
+ lockf.rb is a C extension that provides a Ruby interface to
4
+ [lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3).
5
+ The
6
+ [lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3)
7
+ function implements an advisory-mode lock that can be placed on select
8
+ regions of a file, or on the entire contents of a file.
9
+
10
+ [lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3)
11
+ can be used to synchronize access to a file between multiple
12
+ processes, or be used more generally to synchronize access to a shared
13
+ resource being accessed by multiple processes at the same time. When used
14
+ generally,
15
+ [lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3)
16
+ can provide something similar to a mutex that works across multiple
17
+ processes rather than multiple threads.
18
+
19
+ ## Examples
20
+
21
+ ### LockFile
22
+
23
+ The
24
+ [`LockFile`](https://0x1eef.github.io/x/lockf.rb/LockFile.html)
25
+ class provides an abstract, Ruby-oriented interface to
26
+ [lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3).
27
+
28
+ __Blocking lock__
29
+
30
+ The `LockFile#lock` method can be used to acquire a lock. The method will
31
+ block when another process has acquired a lock beforehand:
32
+
33
+ ```ruby
34
+ require "lockf"
35
+
36
+ lockf = LockFile.temporary_file
37
+ lockf.lock
38
+ print "Lock acquired by parent process (#{Time.now.utc})", "\n"
39
+ pid = fork do
40
+ print "Child process waiting on lock (#{Time.now.utc})", "\n"
41
+ lockf.lock
42
+ print "Lock acquired by child process (#{Time.now.utc})", "\n"
43
+ end
44
+ sleep(3)
45
+ lockf.release
46
+ Process.wait(pid)
47
+ lockf.file.close
48
+
49
+ ##
50
+ # Lock acquired by parent process (2023-02-11 16:43:15 UTC)
51
+ # Child process waiting on lock (2023-02-11 16:43:15 UTC)
52
+ # Lock acquired by child process (2023-02-11 16:43:18 UTC)
53
+ ```
54
+
55
+ __Non-blocking lock__
56
+
57
+ The `LockFile#lock_nonblock` method can be used to acquire a lock
58
+ without blocking. When it is found that acquiring a lock would block
59
+ the method will raise an exception (ie `Errno::EAGAIN` /`Errno::EWOULDBLOCK`)
60
+ instead:
61
+
62
+ ```ruby
63
+ require "lockf"
64
+
65
+ lockf = LockFile.temporary_file
66
+ lockf.lock_nonblock
67
+ print "Lock acquired by parent process (#{Time.now.utc})", "\n"
68
+ pid = fork do
69
+ lockf.lock_nonblock
70
+ print "Lock acquired by child process (#{Time.now.utc})", "\n"
71
+ rescue Errno::EWOULDBLOCK
72
+ print "Lock would block", "\n"
73
+ sleep 1
74
+ retry
75
+ end
76
+ sleep 3
77
+ lockf.release
78
+ Process.wait(pid)
79
+ lockf.file.close
80
+
81
+ ##
82
+ # Lock acquired by parent process (2023-02-11 19:03:05 UTC)
83
+ # Lock would block
84
+ # Lock would block
85
+ # Lock would block
86
+ # Lock acquired by child process (2023-02-11 19:03:08 UTC)
87
+ ```
88
+
89
+ ### LockFile.lockf
90
+
91
+ The
92
+ [`LockFile.lockf`](https://0x1eef.github.io/x/lockf.rb/LockFile.html#lockf-class_method)
93
+ method provides a direct interface to
94
+ [lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3)
95
+ that is more or less equivalent to how the function would be called
96
+ from C.
97
+
98
+ __Blocking lock__
99
+
100
+ ```ruby
101
+ require "lockf"
102
+ require "tempfile"
103
+
104
+ file = Tempfile.new("lockf-ffi").tap(&:unlink)
105
+ LockFile.lockf(file.fileno, LockFile::F_LOCK, 0)
106
+ print "Lock acquired", "\n"
107
+ LockFile.lockf(file.fileno, LockFile::F_ULOCK, 0)
108
+ print "Lock released", "\n"
109
+ file.close
110
+
111
+ ##
112
+ # Lock acquired
113
+ # Lock released
114
+ ```
115
+
116
+ __Non-blocking lock__
117
+
118
+ ```ruby
119
+ require "lockf"
120
+ require "tempfile"
121
+
122
+ file = Tempfile.new("lockf-ffi").tap(&:unlink)
123
+ LockFile.lockf(file.fileno, LockFile::F_TLOCK, 0)
124
+ print "Lock acquired", "\n"
125
+ LockFile.lockf(file.fileno, LockFile::F_ULOCK, 0)
126
+ print "Lock released", "\n"
127
+ file.close
128
+
129
+ ##
130
+ # Lock acquired
131
+ # Lock released
132
+ ```
133
+
134
+ ## Sources
135
+
136
+ * [Source code (GitHub)](https://github.com/0x1eef/lockf.rb#readme)
137
+ * [Source code (GitLab)](https://gitlab.com/0x1eef/lockf.rb#about)
138
+
139
+ ## Install
140
+
141
+ **Git**
142
+
143
+ lockf.rb is distributed as a RubyGem through its git repositories. <br>
144
+ [GitHub](https://github.com/0x1eef/lockf.rb),
145
+ and
146
+ [GitLab](https://gitlab.com/0x1eef/lockf.rb)
147
+ are available as sources.
148
+
149
+ ``` ruby
150
+ # Gemfile
151
+ gem "lock.fb", github: "0x1eef/lockf.rb", tag: "v0.12.0"
152
+ ```
153
+
154
+ **Rubygems.org**
155
+
156
+ lock.rb can also be installed via rubygems.org.
157
+
158
+ gem install lockf.rb
159
+
160
+ ## License
161
+
162
+ [BSD Zero Clause](https://choosealicense.com/licenses/0bsd/).
163
+ <br>
164
+ See [LICENSE](./LICENSE).
165
+
data/Rakefile.rb ADDED
@@ -0,0 +1,24 @@
1
+ require "rake/extensiontask"
2
+ require "rake/testtask"
3
+
4
+ namespace :linters do
5
+ desc "Run the C linter"
6
+ task :c do
7
+ sh "uncrustify -c .uncrustify.cfg --no-backup --replace ext/lockf.rb/*.c"
8
+ end
9
+
10
+ desc "Run the Ruby linter"
11
+ task :ruby do
12
+ sh "bundle exec rubocop -A Rakefile.rb lib/**/*.rb spec/**/*.rb"
13
+ end
14
+ end
15
+ task lint: ["linters:c", "linters:ruby"]
16
+
17
+ Rake::ExtensionTask.new("lockf.rb")
18
+
19
+ Rake::TestTask.new do |t|
20
+ t.test_files = FileList["test/*_test.rb"]
21
+ t.verbose = true
22
+ t.warning = false
23
+ end
24
+ task default: %w[clobber compile test]
@@ -0,0 +1,12 @@
1
+ require "mkmf"
2
+
3
+ [
4
+ "INSTALL_DATA", "INSTALL_SCRIPT",
5
+ "INSTALL_PROGRAM", "INSTALL_DATA",
6
+ "INSTALL"
7
+ ].each do
8
+ CONFIG[_1].sub!(/-o [a-zA-Z0-9]+/, "")
9
+ CONFIG[_1].sub!(/-g [a-zA-Z0-9]+/, "")
10
+ end
11
+
12
+ create_makefile "lockf.rb"
@@ -0,0 +1,36 @@
1
+ #include <ruby.h>
2
+ #include <unistd.h>
3
+ #include <errno.h>
4
+ #include "lockf.h"
5
+
6
+ static VALUE
7
+ lockf_lock(VALUE self, VALUE fd, VALUE cmd, VALUE len)
8
+ {
9
+ int result;
10
+
11
+ Check_Type(fd, T_FIXNUM);
12
+ Check_Type(cmd, T_FIXNUM);
13
+ Check_Type(len, T_FIXNUM);
14
+ errno = 0;
15
+ result = lockf(NUM2INT(fd), NUM2INT(cmd), NUM2INT(len));
16
+ if (result == -1) {
17
+ rb_syserr_fail(errno, "lockf");
18
+ } else {
19
+ return INT2NUM(result);
20
+ }
21
+ }
22
+
23
+ void
24
+ Init_lockf(void)
25
+ {
26
+ VALUE cLockf, mFcntl;
27
+
28
+ rb_require("fcntl");
29
+ cLockf = rb_define_class("LockFile", rb_cObject);
30
+ mFcntl = rb_const_get(rb_cObject, rb_intern("Fcntl"));
31
+ rb_define_const(mFcntl, "F_LOCK", INT2NUM(F_LOCK));
32
+ rb_define_const(mFcntl, "F_TLOCK", INT2NUM(F_TLOCK));
33
+ rb_define_const(mFcntl, "F_ULOCK", INT2NUM(F_ULOCK));
34
+ rb_define_const(mFcntl, "F_TEST", INT2NUM(F_TEST));
35
+ rb_define_singleton_method(cLockf, "lockf", lockf_lock, 3);
36
+ }
@@ -0,0 +1 @@
1
+ void Init_lockf(void);
@@ -0,0 +1,3 @@
1
+ class LockFile
2
+ VERSION = "0.12.0"
3
+ end
data/lib/lockf.rb ADDED
@@ -0,0 +1,140 @@
1
+ ##
2
+ # The
3
+ # [`LockFile`](https://0x1eef.github.io/x/lockf.rb/LockFile.html)
4
+ # class provides a Ruby-oriented interface to the C function
5
+ # [lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3).
6
+ class LockFile
7
+ require "tmpdir"
8
+ require_relative "lockf.rb.so"
9
+ include Fcntl
10
+
11
+ ##
12
+ # @!method self.lockf(fd, cmd, len)
13
+ # @example
14
+ # LockFile.lockf(5, LockFile::F_LOCK, 0)
15
+ #
16
+ # @param [Integer] fd
17
+ # A number that represents a file descriptor.
18
+ #
19
+ # @param [Integer] cmd
20
+ # {LockFile::F_LOCK}, {LockFile::F_TLOCK}, {LockFile::F_ULOCK}, or
21
+ # {LockFile::F_TEST}.
22
+ #
23
+ # @param [Integer] len
24
+ # The number of bytes to place a lock on.
25
+ # A value of "0" covers the entire file.
26
+ #
27
+ # @raise [SystemCallError]
28
+ # Might raise a number of Errno exception classes.
29
+ #
30
+ # @return [Integer]
31
+ # Returns 0 on success.
32
+ #
33
+ # @see (https://man7.org/linux/man-pages/man3/lockf.3.html) lockf man page (Linux)
34
+ # @see (https://man.openbsd.org/lockf.3) lockf man page (OpenBSD)
35
+ # @see (https://www.freebsd.org/cgi/man.cgi?query=lockf) lockf man page (FreeBSD)
36
+
37
+ ##
38
+ # @example
39
+ # lockf = LockFile.temporary_file
40
+ # lockf.lock
41
+ # lockf.release
42
+ # lockf.file.close
43
+ #
44
+ # @param [String] basename
45
+ # The basename of the temporary file.
46
+ #
47
+ # @param [String] tmpdir
48
+ # The path to the parent directory of the temporary file.
49
+ #
50
+ # @return [LockFile]
51
+ # Returns an instance of {LockFile LockFile} backed by an
52
+ # unlinked instance of Tempfile.
53
+ def self.from_temporary_file(basename: "lockf", tmpdir: Dir.tmpdir)
54
+ require "tempfile" unless defined?(Tempfile)
55
+ file = Tempfile.new(basename, tmpdir:).tap(&:unlink)
56
+ LockFile.new(file)
57
+ end
58
+ class << self
59
+ alias_method :temporary_file, :from_temporary_file
60
+ end
61
+
62
+ ##
63
+ # @return [<File, Tempfile, #fileno>]
64
+ # Returns a file object.
65
+ attr_reader :file
66
+
67
+ ##
68
+ # @param [<File, TempFile, String, #fileno>] file
69
+ # The file to place a lock on.
70
+ #
71
+ # @param [Integer] len
72
+ # The number of bytes to place a lock on.
73
+ # A value of "0" covers the entire file.
74
+ #
75
+ # @return [LockFile]
76
+ # Returns an instance of {LockFile LockFile}.
77
+ def initialize(file, len = 0)
78
+ @file = String === file ? File.open(file, "r+") : file
79
+ @len = len
80
+ end
81
+
82
+ ##
83
+ # Acquire a lock (blocking)
84
+ # @raise [Errno::EBADF]
85
+ # @raise [Errno::EDEADLK]
86
+ # @raise [Errno::EINTR]
87
+ # @raise [Errno::ENOLCK]
88
+ # @return [Integer]
89
+ def lock
90
+ attempts ||= 0
91
+ LockFile.lockf(@file.fileno, F_LOCK, @len)
92
+ rescue Errno::EINTR => ex
93
+ attempts += 1
94
+ attempts == 3 ? raise(ex) : retry
95
+ end
96
+
97
+ ##
98
+ # Acquire a lock (non-blocking)
99
+ # @raise [Errno::EAGAIN]
100
+ # @raise [Errno::EBADF]
101
+ # @raise [Errno::ENOLCK]
102
+ # @raise [Errno::EINVAL]
103
+ # @return [Integer]
104
+ def lock_nonblock
105
+ LockFile.lockf(@file.fileno, F_TLOCK, @len)
106
+ end
107
+
108
+ ##
109
+ # Release a lock
110
+ # @raise [Errno::EBADF]
111
+ # @raise [Errno::ENOLCK]
112
+ # @return [Integer]
113
+ def release
114
+ LockFile.lockf(@file.fileno, F_ULOCK, @len)
115
+ end
116
+
117
+ ##
118
+ # @return [Boolean]
119
+ # Returns true when a lock has been acquired by another process.
120
+ def locked?
121
+ LockFile.lockf(@file.fileno, F_TEST, @len)
122
+ false
123
+ rescue Errno::EACCES, Errno::EAGAIN
124
+ true
125
+ end
126
+
127
+ ##
128
+ # Closes {LockFile#file LockFile#file}.
129
+ #
130
+ # @example
131
+ # # Equivalent to:
132
+ # lockf = LockFile.temporary_file
133
+ # lockf.file.close
134
+ #
135
+ # @return [void]
136
+ def close
137
+ return unless @file.respond_to?(:close)
138
+ @file.close
139
+ end
140
+ end
data/lockf.rb.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ require "./lib/lockf/version"
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = "lockf.rb"
5
+ gem.authors = ["0x1eef"]
6
+ gem.email = ["0x1eef@protonmail.com"]
7
+ gem.homepage = "https://github.com/0x1eef/lockf.rb#readme"
8
+ gem.version = LockFile::VERSION
9
+ gem.licenses = ["0BSD"]
10
+ gem.files = `git ls-files`.split($/)
11
+ gem.require_paths = ["lib"]
12
+ gem.extensions = %w[ext/lockf.rb/extconf.rb]
13
+ gem.summary = "A Ruby interface for lockf(3)"
14
+ gem.description = gem.summary
15
+ gem.add_development_dependency "yard", "~> 0.9"
16
+ gem.add_development_dependency "redcarpet", "~> 3.5"
17
+ gem.add_development_dependency "standard", "= 1.12.1"
18
+ gem.add_development_dependency "rubocop", "= 1.29.1"
19
+ gem.add_development_dependency "test-unit", "~> 3.5.7"
20
+ gem.add_development_dependency "rake-compiler", "= 1.2.0"
21
+ gem.add_development_dependency "rack", "~> 3.0"
22
+ gem.add_development_dependency "rackup", "~> 2.1"
23
+ gem.add_development_dependency "test-cmd.rb", "~> 0.4"
24
+ end
@@ -0,0 +1,19 @@
1
+ require "lockf"
2
+
3
+ lockf = LockFile.from_temporary_file
4
+ lockf.lock
5
+ print "Lock acquired by parent process (#{Time.now.utc})", "\n"
6
+ pid = fork do
7
+ print "Child process waiting on lock (#{Time.now.utc})", "\n"
8
+ lockf.lock
9
+ print "Lock acquired by child process (#{Time.now.utc})", "\n"
10
+ end
11
+ sleep(3)
12
+ lockf.release
13
+ Process.wait(pid)
14
+ lockf.file.close
15
+
16
+ ##
17
+ # Lock acquired by parent process (2023-02-11 16:43:15 UTC)
18
+ # Child process waiting on lock (2023-02-11 16:43:15 UTC)
19
+ # Lock acquired by child process (2023-02-11 16:43:18 UTC)
@@ -0,0 +1,24 @@
1
+ require "lockf"
2
+
3
+ lockf = LockFile.from_temporary_file
4
+ lockf.lock_nonblock
5
+ print "Lock acquired by parent process (#{Time.now.utc})", "\n"
6
+ pid = fork do
7
+ lockf.lock_nonblock
8
+ print "Lock acquired by child process (#{Time.now.utc})", "\n"
9
+ rescue Errno::EWOULDBLOCK
10
+ sleep 1
11
+ print "Lock would block", "\n"
12
+ retry
13
+ end
14
+ sleep 3
15
+ lockf.release
16
+ Process.wait(pid)
17
+ lockf.file.close
18
+
19
+ ##
20
+ # Lock acquired by parent process (2023-02-11 19:03:05 UTC)
21
+ # Lock would block
22
+ # Lock would block
23
+ # Lock would block
24
+ # Lock acquired by child process (2023-02-11 19:03:08 UTC)
@@ -0,0 +1,13 @@
1
+ require "lockf"
2
+ require "tempfile"
3
+
4
+ file = Tempfile.new("lockf-ffi").tap(&:unlink)
5
+ LockFile.lockf(file.fileno, LockFile::F_LOCK, 0)
6
+ print "Lock acquired", "\n"
7
+ LockFile.lockf(file.fileno, LockFile::F_ULOCK, 0)
8
+ print "Lock released", "\n"
9
+ file.close
10
+
11
+ ##
12
+ # Lock acquired
13
+ # Lock released
@@ -0,0 +1,13 @@
1
+ require "lockf"
2
+ require "tempfile"
3
+
4
+ file = Tempfile.new("lockf-ffi").tap(&:unlink)
5
+ LockFile.lockf(file.fileno, LockFile::F_TLOCK, 0)
6
+ print "Lock acquired", "\n"
7
+ LockFile.lockf(file.fileno, LockFile::F_ULOCK, 0)
8
+ print "Lock released", "\n"
9
+ file.close
10
+
11
+ ##
12
+ # Lock acquired
13
+ # Lock released
@@ -0,0 +1,92 @@
1
+ require_relative "setup"
2
+ class LockFile::Test < Test::Unit::TestCase
3
+ include Timeout
4
+ include FileUtils
5
+ attr_reader :lockf
6
+
7
+ def setup
8
+ @lockf = LockFile.new(file)
9
+ end
10
+
11
+ def teardown
12
+ file.close
13
+ end
14
+
15
+ ##
16
+ # Lock::File#lock tests
17
+ def test_lock
18
+ assert_equal 0, lockf.lock
19
+ ensure
20
+ lockf.release
21
+ end
22
+
23
+ def test_lock_in_fork
24
+ Process.wait fork {
25
+ lockf.lock
26
+ Process.kill("SIGINT", Process.ppid)
27
+ sleep(1)
28
+ }
29
+ rescue Interrupt
30
+ assert_raises(Timeout::Error) { timeout(0.5) { lockf.lock } }
31
+ ensure
32
+ lockf.release
33
+ end
34
+
35
+ ##
36
+ # Lock::File#lock_nonblock tests
37
+ def test_lock_nonblock
38
+ assert_equal 0, lockf.lock_nonblock
39
+ ensure
40
+ lockf.release
41
+ end
42
+
43
+ def test_lock_nonblock_in_fork
44
+ Process.wait fork {
45
+ lockf.lock_nonblock
46
+ Process.kill("SIGINT", Process.ppid)
47
+ sleep(1)
48
+ }
49
+ rescue Interrupt
50
+ assert_raises(Errno::EAGAIN) { lockf.lock_nonblock }
51
+ ensure
52
+ lockf.release
53
+ end
54
+
55
+ ##
56
+ # Lock::File#locked? tests
57
+ def test_locked?
58
+ lockf.lock
59
+ Process.wait fork { lockf.locked? ? exit(0) : exit(1) }
60
+ assert_equal 0, $?.exitstatus
61
+ ensure
62
+ lockf.release
63
+ end
64
+
65
+ ##
66
+ # LockFile#initialize
67
+ def test_initialize_with_str_path
68
+ path = File.join(Dir.getwd, "test", "tmp.txt")
69
+ touch(path)
70
+ lockf = LockFile.new(path)
71
+ assert_equal 0, lockf.lock
72
+ assert_equal 0, lockf.release
73
+ ensure
74
+ lockf.file.close
75
+ rm(path)
76
+ end
77
+
78
+ ##
79
+ # LockFile.from_temporary_file
80
+ def test_from_temporary_file
81
+ lockf = LockFile.from_temporary_file
82
+ assert_equal 0, lockf.lock
83
+ assert_equal 0, lockf.release
84
+ lockf.file.close
85
+ end
86
+
87
+ private
88
+
89
+ def file
90
+ @file ||= Tempfile.new("lockf-test").tap(&:unlink)
91
+ end
92
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "setup"
4
+ require "test/cmd"
5
+
6
+ class LockFile::ReadmeTest < Test::Unit::TestCase
7
+ include Test::Cmd
8
+
9
+ def test_lockfile_blocking_variant
10
+ r = 'Lock acquired by parent process \(.+\)\s*' \
11
+ 'Child process waiting on lock \(.+\)\s*' \
12
+ 'Lock acquired by child process \(.+\)\s*'
13
+ assert_match Regexp.new(r),
14
+ readme_example("1_lockfile_blocking_variant.rb").stdout
15
+ end
16
+
17
+ def test_lockfile_nonblocking_variant
18
+ r = 'Lock acquired by parent process \(.+\)\s*' \
19
+ '(Lock would block\s*){3,4}' \
20
+ 'Lock acquired by child process \(.+\)\s*'
21
+ assert_match Regexp.new(r),
22
+ readme_example("2_lockfile_nonblocking_variant.rb").stdout
23
+ end
24
+
25
+ def test_ffi_lockf_blocking_variant
26
+ assert_equal "Lock acquired\nLock released\n",
27
+ readme_example("3_ffilockf_blocking_variant.rb").stdout
28
+ end
29
+
30
+ def test_ffi_lockf_nonblocking_variant
31
+ assert_equal "Lock acquired\nLock released\n",
32
+ readme_example("4_ffilockf_nonblocking_variant.rb").stdout
33
+ end
34
+
35
+ private
36
+
37
+ def readme_example(path)
38
+ examples_dir = File.join(Dir.getwd, "share", "lockf.rb", "examples")
39
+ example = File.join(examples_dir, path)
40
+ cmd "bundle exec ruby #{example}"
41
+ end
42
+ end
data/test/setup.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/setup"
2
+ require "test/unit"
3
+ require "lockf"
4
+ require "timeout"
5
+ require "fileutils"
6
+ require "tempfile"
metadata ADDED
@@ -0,0 +1,194 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lockf.rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.12.0
5
+ platform: ruby
6
+ authors:
7
+ - '0x1eef'
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-01-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: yard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: redcarpet
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: standard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.12.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.12.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.29.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 1.29.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: test-unit
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.5.7
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.5.7
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake-compiler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 1.2.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 1.2.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rackup
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.1'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.1'
125
+ - !ruby/object:Gem::Dependency
126
+ name: test-cmd.rb
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.4'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.4'
139
+ description: A Ruby interface for lockf(3)
140
+ email:
141
+ - 0x1eef@protonmail.com
142
+ executables: []
143
+ extensions:
144
+ - ext/lockf.rb/extconf.rb
145
+ extra_rdoc_files: []
146
+ files:
147
+ - ".bundle/config"
148
+ - ".github/workflows/tests.yml"
149
+ - ".gitignore"
150
+ - ".projectile"
151
+ - ".rubocop.yml"
152
+ - ".uncrustify.cfg"
153
+ - ".yardopts"
154
+ - Gemfile
155
+ - LICENSE
156
+ - README.md
157
+ - Rakefile.rb
158
+ - ext/lockf.rb/extconf.rb
159
+ - ext/lockf.rb/lockf.c
160
+ - ext/lockf.rb/lockf.h
161
+ - lib/lockf.rb
162
+ - lib/lockf/version.rb
163
+ - lockf.rb.gemspec
164
+ - share/lockf.rb/examples/1_lockfile_blocking_variant.rb
165
+ - share/lockf.rb/examples/2_lockfile_nonblocking_variant.rb
166
+ - share/lockf.rb/examples/3_ffilockf_blocking_variant.rb
167
+ - share/lockf.rb/examples/4_ffilockf_nonblocking_variant.rb
168
+ - test/lock_file_test.rb
169
+ - test/readme_test.rb
170
+ - test/setup.rb
171
+ homepage: https://github.com/0x1eef/lockf.rb#readme
172
+ licenses:
173
+ - 0BSD
174
+ metadata: {}
175
+ post_install_message:
176
+ rdoc_options: []
177
+ require_paths:
178
+ - lib
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ requirements: []
190
+ rubygems_version: 3.5.3
191
+ signing_key:
192
+ specification_version: 4
193
+ summary: A Ruby interface for lockf(3)
194
+ test_files: []