lockf.rb 0.12.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 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: []