lockf.rb 1.0.0 → 2.0.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/README.md +40 -64
- data/Rakefile.rb +7 -18
- data/bin/test-runner +6 -0
- data/lib/lock/file/constants.rb +15 -0
- data/lib/lock/file/ffi.rb +36 -0
- data/lib/lock/file/version.rb +5 -0
- data/lib/lock/file.rb +114 -0
- data/lib/lockf.rb +1 -142
- data/lib/lockfile.rb +1 -0
- data/lockf.rb.gemspec +5 -5
- data/share/lockf.rb/examples/1_lockfile_blocking_variant.rb +6 -5
- data/share/lockf.rb/examples/2_lockfile_nonblocking_variant.rb +6 -5
- data/share/lockf.rb/examples/3_ffilockf_blocking_variant.rb +6 -4
- data/share/lockf.rb/examples/4_ffilockf_nonblocking_variant.rb +6 -4
- data/test/lock_file_test.rb +9 -7
- data/test/readme_test.rb +19 -17
- data/test/setup.rb +1 -1
- metadata +26 -24
- data/ext/lockf.rb/extconf.rb +0 -12
- data/ext/lockf.rb/lockf.c +0 -36
- data/lib/lockf/version.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a49042a4d79251c0a85dbe626fefaa323862ea30a45426012a1d8bab08f63fb
|
4
|
+
data.tar.gz: 57f2c700f963a299ce4502e3a7856cc75283ceb80d1c27864ea08438950047d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d5c94dbaa6ad0b2eff1fe114e902d7c49014f2bb0ce69b2c5512ae0d107274d7c3c8b6a2d688672faeb85c7b48510689952efd9c64dc4cc78616e3e566f2e8e
|
7
|
+
data.tar.gz: 27c55d59bbab2f8e76b5370b010b3541d65a346b88279361e7f64110b3e6a1fe3f1ddaf4677fbefcec7f2e6409d70483937298f47cda1e3d729c209122d821df
|
data/README.md
CHANGED
@@ -2,40 +2,36 @@
|
|
2
2
|
|
3
3
|
lockf.rb provides Ruby bindings for
|
4
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
5
|
|
10
6
|
## Examples
|
11
7
|
|
12
|
-
###
|
13
|
-
|
14
|
-
The
|
15
|
-
[`LockFile`](https://0x1eef.github.io/x/lockf.rb/LockFile.html)
|
16
|
-
class provides an abstract, Ruby-oriented interface to
|
17
|
-
[lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3).
|
8
|
+
### Lock::File
|
18
9
|
|
19
|
-
|
10
|
+
__Blocking__
|
20
11
|
|
21
|
-
|
22
|
-
|
12
|
+
[Lock::File#lock](http://0x1eef.github.io/x/lockf.rb/Lock/File.html#lock-instance_method)
|
13
|
+
can be used to acquire a lock.
|
14
|
+
[Lock::File.temporary_file](http://0x1eef.github.io/x/lockf.rb/Lock/File.html#temporary_file-class_method)
|
15
|
+
returns a lock for an unlinked temporary file.
|
16
|
+
[Lock::File#lock](http://0x1eef.github.io/x/lockf.rb/Lock/File.html#lock-instance_method)
|
17
|
+
will block when another
|
18
|
+
process has acquired a lock beforehand:
|
23
19
|
|
24
20
|
```ruby
|
25
|
-
|
21
|
+
#!/usr/bin/env ruby
|
22
|
+
require "lock/file"
|
26
23
|
|
27
|
-
lockf =
|
24
|
+
lockf = Lock::File.temporary_file
|
28
25
|
lockf.lock
|
29
26
|
print "Lock acquired by parent process (#{Time.now.utc})", "\n"
|
30
|
-
|
27
|
+
fork do
|
31
28
|
print "Child process waiting on lock (#{Time.now.utc})", "\n"
|
32
29
|
lockf.lock
|
33
30
|
print "Lock acquired by child process (#{Time.now.utc})", "\n"
|
34
31
|
end
|
35
32
|
sleep(3)
|
36
33
|
lockf.release
|
37
|
-
Process.wait
|
38
|
-
lockf.close
|
34
|
+
Process.wait
|
39
35
|
|
40
36
|
##
|
41
37
|
# Lock acquired by parent process (2023-02-11 16:43:15 UTC)
|
@@ -43,31 +39,31 @@ lockf.close
|
|
43
39
|
# Lock acquired by child process (2023-02-11 16:43:18 UTC)
|
44
40
|
```
|
45
41
|
|
46
|
-
__Non-
|
42
|
+
__Non-blocking__
|
47
43
|
|
48
|
-
|
49
|
-
without blocking. When it is found
|
50
|
-
the method will raise an
|
51
|
-
instead:
|
44
|
+
[Lock::File#lock_nonblock](http://0x1eef.github.io/x/lockf.rb/Lock/File.html#lock_nonblock-instance_method)
|
45
|
+
can be used to acquire a lock without blocking. When it is found
|
46
|
+
that acquiring a lock would block the method will raise an
|
47
|
+
exception (`Errno::EAGAIN` / `Errno::EWOULDBLOCK`) instead:
|
52
48
|
|
53
49
|
```ruby
|
54
|
-
|
50
|
+
#!/usr/bin/env ruby
|
51
|
+
require "lock/file"
|
55
52
|
|
56
|
-
lockf =
|
53
|
+
lockf = Lock::File.temporary_file
|
57
54
|
lockf.lock_nonblock
|
58
55
|
print "Lock acquired by parent process (#{Time.now.utc})", "\n"
|
59
|
-
|
56
|
+
fork do
|
60
57
|
lockf.lock_nonblock
|
61
58
|
print "Lock acquired by child process (#{Time.now.utc})", "\n"
|
62
59
|
rescue Errno::EWOULDBLOCK
|
63
|
-
print "Lock would block", "\n"
|
64
60
|
sleep 1
|
61
|
+
print "Lock would block", "\n"
|
65
62
|
retry
|
66
63
|
end
|
67
64
|
sleep 3
|
68
65
|
lockf.release
|
69
|
-
Process.wait
|
70
|
-
lockf.close
|
66
|
+
Process.wait
|
71
67
|
|
72
68
|
##
|
73
69
|
# Lock acquired by parent process (2023-02-11 19:03:05 UTC)
|
@@ -77,43 +73,25 @@ lockf.close
|
|
77
73
|
# Lock acquired by child process (2023-02-11 19:03:08 UTC)
|
78
74
|
```
|
79
75
|
|
80
|
-
###
|
76
|
+
### Lock::File::FFI
|
77
|
+
|
78
|
+
__lockf__
|
81
79
|
|
82
|
-
|
83
|
-
|
84
|
-
method provides a direct interface to
|
80
|
+
[Lock::File::FFI.lockf](http://0x1eef.freebsd.home.network/x/lockf.rb/Lock/File/FFI.html#lockf-instance_method)
|
81
|
+
provides a direct interface to
|
85
82
|
[lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3)
|
86
83
|
that is more or less equivalent to how the function would be called
|
87
|
-
from C
|
88
|
-
|
89
|
-
__Blocking lock__
|
90
|
-
|
91
|
-
```ruby
|
92
|
-
require "lockf"
|
93
|
-
require "tempfile"
|
94
|
-
|
95
|
-
file = Tempfile.new("lockf-ffi").tap(&:unlink)
|
96
|
-
LockFile.lockf(file.fileno, LockFile::F_LOCK, 0)
|
97
|
-
print "Lock acquired", "\n"
|
98
|
-
LockFile.lockf(file.fileno, LockFile::F_ULOCK, 0)
|
99
|
-
print "Lock released", "\n"
|
100
|
-
file.close
|
101
|
-
|
102
|
-
##
|
103
|
-
# Lock acquired
|
104
|
-
# Lock released
|
105
|
-
```
|
106
|
-
|
107
|
-
__Non-blocking lock__
|
84
|
+
from C:
|
108
85
|
|
109
86
|
```ruby
|
110
|
-
|
87
|
+
#!/usr/bin/env ruby
|
88
|
+
require "lock/file"
|
111
89
|
require "tempfile"
|
112
90
|
|
113
91
|
file = Tempfile.new("lockf-ffi").tap(&:unlink)
|
114
|
-
|
92
|
+
Lock::File::FFI.lockf(file, Lock::File::F_LOCK, 0)
|
115
93
|
print "Lock acquired", "\n"
|
116
|
-
|
94
|
+
Lock::File::FFI.lockf(file, Lock::File::F_ULOCK, 0)
|
117
95
|
print "Lock released", "\n"
|
118
96
|
file.close
|
119
97
|
|
@@ -124,14 +102,12 @@ file.close
|
|
124
102
|
|
125
103
|
## Documentation
|
126
104
|
|
127
|
-
A complete API reference is available at
|
128
|
-
[0x1eef.github.io/x/lockf.rb](https://0x1eef.github.io/x/lockf.rb)
|
105
|
+
A complete API reference is available at
|
106
|
+
[0x1eef.github.io/x/lockf.rb](https://0x1eef.github.io/x/lockf.rb)
|
129
107
|
|
130
108
|
## Install
|
131
109
|
|
132
|
-
|
133
|
-
|
134
|
-
lockf.rb can be installed via rubygems.org.
|
110
|
+
lockf.rb can be installed via rubygems.org:
|
135
111
|
|
136
112
|
gem install lockf.rb
|
137
113
|
|
@@ -142,7 +118,7 @@ lockf.rb can be installed via rubygems.org.
|
|
142
118
|
|
143
119
|
## License
|
144
120
|
|
145
|
-
[BSD Zero Clause](https://choosealicense.com/licenses/0bsd/)
|
121
|
+
[BSD Zero Clause](https://choosealicense.com/licenses/0bsd/)
|
146
122
|
<br>
|
147
|
-
See [LICENSE](./LICENSE)
|
123
|
+
See [LICENSE](./LICENSE)
|
148
124
|
|
data/Rakefile.rb
CHANGED
@@ -1,26 +1,15 @@
|
|
1
1
|
require "bundler/setup"
|
2
|
-
require "rake/extensiontask"
|
3
|
-
require "rake/testtask"
|
4
2
|
|
5
|
-
namespace :
|
6
|
-
desc "Run clang-format"
|
7
|
-
task :format do
|
8
|
-
sh "clang-format -style=file:.clang-format -i ext/lockf.rb/*.c"
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
namespace :ruby do
|
3
|
+
namespace :format do
|
13
4
|
desc "Run rubocop"
|
14
|
-
task :
|
5
|
+
task :ruby do
|
15
6
|
sh "bundle exec rubocop -A"
|
16
7
|
end
|
17
8
|
end
|
18
|
-
task format: %w[
|
9
|
+
task format: %w[format:ruby]
|
19
10
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
t.verbose = true
|
24
|
-
t.warning = false
|
11
|
+
desc "Run tests"
|
12
|
+
task :test do
|
13
|
+
sh "bin/test-runner"
|
25
14
|
end
|
26
|
-
task default: %w[
|
15
|
+
task default: %w[test]
|
data/bin/test-runner
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Lock::File
|
4
|
+
# The constants found in this module are defined
|
5
|
+
# by unistd.h. Their documentation can be found in
|
6
|
+
# the
|
7
|
+
# [lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3)
|
8
|
+
# man page
|
9
|
+
module Constants
|
10
|
+
F_ULOCK = 0x0
|
11
|
+
F_LOCK = 0x1
|
12
|
+
F_TLOCK = 0x2
|
13
|
+
F_TEST = 0x3
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Lock::File
|
4
|
+
module FFI
|
5
|
+
require "fiddle"
|
6
|
+
include Fiddle::Types
|
7
|
+
extend self
|
8
|
+
|
9
|
+
##
|
10
|
+
# Provides a Ruby interface for lockf(3)
|
11
|
+
#
|
12
|
+
# @see https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3 lockf(3)
|
13
|
+
# @param [Integer, #fileno] fd
|
14
|
+
# @param [Integer] function
|
15
|
+
# @param [Integer] size
|
16
|
+
# @raise [SystemCallError]
|
17
|
+
# Might raise a subclass of SystemCallError
|
18
|
+
# @return [Boolean]
|
19
|
+
# Returns true when successful
|
20
|
+
def lockf(fd, function, size = 0)
|
21
|
+
fileno = fd.respond_to?(:fileno) ? fd.fileno : fd
|
22
|
+
Fiddle::Function.new(
|
23
|
+
libc["lockf"],
|
24
|
+
[INT, INT, INT],
|
25
|
+
INT
|
26
|
+
).call(fileno, function, size)
|
27
|
+
.zero? || raise(SystemCallError.new("lockf", Fiddle.last_error))
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def libc
|
33
|
+
@libc ||= Fiddle.dlopen Dir["/lib/libc.*"].first
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/lock/file.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
module Lock
|
2
|
+
end unless defined?(Lock)
|
3
|
+
|
4
|
+
##
|
5
|
+
# {Lock::File Lock::File} provides an object-oriented
|
6
|
+
# [lockf(3)](https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3)
|
7
|
+
# interface
|
8
|
+
class Lock::File
|
9
|
+
require "tmpdir"
|
10
|
+
require_relative "file/version"
|
11
|
+
require_relative "file/ffi"
|
12
|
+
require_relative "file/constants"
|
13
|
+
|
14
|
+
include FFI
|
15
|
+
include Constants
|
16
|
+
|
17
|
+
##
|
18
|
+
# @example
|
19
|
+
# lockf = Lock::File.temporary_file
|
20
|
+
# lockf.lock
|
21
|
+
# # ...
|
22
|
+
#
|
23
|
+
# @param [String] basename
|
24
|
+
# @param [String] tmpdir
|
25
|
+
# @return [Lock::File]
|
26
|
+
# Returns a {Lock::File Lock::File} for a random,
|
27
|
+
# unlinked temporary file
|
28
|
+
def self.from_temporary_file(basename: "lockf", tmpdir: Dir.tmpdir)
|
29
|
+
require "tempfile" unless defined?(Tempfile)
|
30
|
+
file = Tempfile.new(basename, tmpdir:).tap(&:unlink)
|
31
|
+
Lock::File.new(file)
|
32
|
+
end
|
33
|
+
class << self
|
34
|
+
alias_method :temporary_file, :from_temporary_file
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# @return [<#fileno>]
|
39
|
+
# Returns a file handle
|
40
|
+
attr_reader :file
|
41
|
+
|
42
|
+
##
|
43
|
+
# @param [<#fileno>] file
|
44
|
+
# @param [Integer] size
|
45
|
+
# @return [Lock::File]
|
46
|
+
# Returns an instance of {Lock::File Lock::File}
|
47
|
+
def initialize(file, size = 0)
|
48
|
+
@file = file
|
49
|
+
@size = size
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Acquire lock (blocking)
|
54
|
+
#
|
55
|
+
# @see https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3 lockf(3)
|
56
|
+
# @raise [SystemCallError]
|
57
|
+
# Might raise a subclass of SystemCallError
|
58
|
+
# @return [Boolean]
|
59
|
+
# Returns true when successful
|
60
|
+
def lock
|
61
|
+
tries ||= 0
|
62
|
+
lockf(@file, F_LOCK, @size)
|
63
|
+
rescue Errno::EINTR => ex
|
64
|
+
tries += 1
|
65
|
+
tries == 3 ? raise(ex) : retry
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Acquire lock (non-blocking)
|
70
|
+
#
|
71
|
+
# @see https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3 lockf(3)
|
72
|
+
# @raise [SystemCallError]
|
73
|
+
# Might raise a subclass of SystemCallError
|
74
|
+
# @return [Boolean]
|
75
|
+
# Returns true when successful
|
76
|
+
def lock_nonblock
|
77
|
+
lockf(@file, F_TLOCK, @size)
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Release lock
|
82
|
+
#
|
83
|
+
# @see https://man.freebsd.org/cgi/man.cgi?query=lockf&sektion=3 lockf(3)
|
84
|
+
# @raise [SystemCallError]
|
85
|
+
# Might raise a subclass of SystemCallError
|
86
|
+
# @return [Boolean]
|
87
|
+
# Returns true when successful
|
88
|
+
def release
|
89
|
+
lockf(@file, F_ULOCK, @size)
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# @return [Boolean]
|
94
|
+
# Returns true when lock is held by another process
|
95
|
+
def locked?
|
96
|
+
lockf(@file, F_TEST, @size)
|
97
|
+
false
|
98
|
+
rescue Errno::EACCES, Errno::EAGAIN
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Closes {Lock::File#file Lock::File#file}
|
104
|
+
#
|
105
|
+
# @example
|
106
|
+
# # Equivalent to:
|
107
|
+
# lockf = Lock::File.temporary_file
|
108
|
+
# lockf.file.close
|
109
|
+
# @return [void]
|
110
|
+
def close
|
111
|
+
return unless @file.respond_to?(:close)
|
112
|
+
@file.close
|
113
|
+
end
|
114
|
+
end
|
data/lib/lockf.rb
CHANGED
@@ -1,142 +1 @@
|
|
1
|
-
|
2
|
-
# The
|
3
|
-
# [LockFile](https://0x1eef.github.io/x/lockf.rb/LockFile.html)
|
4
|
-
# class provides a Ruby interface to
|
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
|
-
|
10
|
-
##
|
11
|
-
# @!method self.lockf(fd, cmd, len)
|
12
|
-
# @example
|
13
|
-
# LockFile.lockf(5, LockFile::F_LOCK, 0)
|
14
|
-
#
|
15
|
-
# @param [Integer] fd
|
16
|
-
# A number that represents a file descriptor.
|
17
|
-
#
|
18
|
-
# @param [Integer] cmd
|
19
|
-
# {LockFile::F_LOCK}, {LockFile::F_TLOCK}, {LockFile::F_ULOCK}, or
|
20
|
-
# {LockFile::F_TEST}.
|
21
|
-
#
|
22
|
-
# @param [Integer] len
|
23
|
-
# The number of bytes to place a lock on.
|
24
|
-
# A value of "0" covers the entire file.
|
25
|
-
#
|
26
|
-
# @raise [SystemCallError]
|
27
|
-
# Might raise a number of Errno exception classes.
|
28
|
-
#
|
29
|
-
# @return [Integer]
|
30
|
-
# Returns 0 on success.
|
31
|
-
#
|
32
|
-
# @see (https://man7.org/linux/man-pages/man3/lockf.3.html) lockf man page (Linux)
|
33
|
-
# @see (https://man.openbsd.org/lockf.3) lockf man page (OpenBSD)
|
34
|
-
# @see (https://www.freebsd.org/cgi/man.cgi?query=lockf) lockf man page (FreeBSD)
|
35
|
-
|
36
|
-
##
|
37
|
-
# @example
|
38
|
-
# lockf = LockFile.temporary_file
|
39
|
-
# lockf.lock
|
40
|
-
# lockf.release
|
41
|
-
# lockf.file.close
|
42
|
-
#
|
43
|
-
# @param [String] basename
|
44
|
-
# The basename of the temporary file.
|
45
|
-
#
|
46
|
-
# @param [String] tmpdir
|
47
|
-
# The path to the parent directory of the temporary file.
|
48
|
-
#
|
49
|
-
# @return [LockFile]
|
50
|
-
# Returns an instance of {LockFile LockFile} backed by an
|
51
|
-
# unlinked instance of Tempfile.
|
52
|
-
def self.from_temporary_file(basename: "lockf", tmpdir: Dir.tmpdir)
|
53
|
-
require "tempfile" unless defined?(Tempfile)
|
54
|
-
file = Tempfile.new(basename, tmpdir:).tap(&:unlink)
|
55
|
-
LockFile.new(file)
|
56
|
-
end
|
57
|
-
class << self
|
58
|
-
alias_method :temporary_file, :from_temporary_file
|
59
|
-
end
|
60
|
-
|
61
|
-
##
|
62
|
-
# @return [<File, Tempfile, #fileno>]
|
63
|
-
# Returns a file object.
|
64
|
-
attr_reader :file
|
65
|
-
|
66
|
-
##
|
67
|
-
# @param [<File, TempFile, String, #fileno>] file
|
68
|
-
# The file to place a lock on.
|
69
|
-
#
|
70
|
-
# @param [Integer] len
|
71
|
-
# The number of bytes to place a lock on.
|
72
|
-
# A value of "0" covers the entire file.
|
73
|
-
#
|
74
|
-
# @return [LockFile]
|
75
|
-
# Returns an instance of {LockFile LockFile}.
|
76
|
-
def initialize(file, len = 0)
|
77
|
-
@file = String === file ? File.open(file, "r+") : file
|
78
|
-
@len = len
|
79
|
-
end
|
80
|
-
|
81
|
-
##
|
82
|
-
# Acquire a lock (blocking).
|
83
|
-
#
|
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
|
-
#
|
100
|
-
# @raise [Errno::EAGAIN]
|
101
|
-
# @raise [Errno::EBADF]
|
102
|
-
# @raise [Errno::ENOLCK]
|
103
|
-
# @raise [Errno::EINVAL]
|
104
|
-
# @return [Integer]
|
105
|
-
def lock_nonblock
|
106
|
-
LockFile.lockf(@file.fileno, F_TLOCK, @len)
|
107
|
-
end
|
108
|
-
|
109
|
-
##
|
110
|
-
# Release a lock.
|
111
|
-
#
|
112
|
-
# @raise [Errno::EBADF]
|
113
|
-
# @raise [Errno::ENOLCK]
|
114
|
-
# @return [Integer]
|
115
|
-
def release
|
116
|
-
LockFile.lockf(@file.fileno, F_ULOCK, @len)
|
117
|
-
end
|
118
|
-
|
119
|
-
##
|
120
|
-
# @return [Boolean]
|
121
|
-
# Returns true when a lock has been acquired by another process.
|
122
|
-
def locked?
|
123
|
-
LockFile.lockf(@file.fileno, F_TEST, @len)
|
124
|
-
false
|
125
|
-
rescue Errno::EACCES, Errno::EAGAIN
|
126
|
-
true
|
127
|
-
end
|
128
|
-
|
129
|
-
##
|
130
|
-
# Closes {LockFile#file LockFile#file}.
|
131
|
-
#
|
132
|
-
# @example
|
133
|
-
# # Equivalent to:
|
134
|
-
# lockf = LockFile.temporary_file
|
135
|
-
# lockf.file.close
|
136
|
-
#
|
137
|
-
# @return [void]
|
138
|
-
def close
|
139
|
-
return unless @file.respond_to?(:close)
|
140
|
-
@file.close
|
141
|
-
end
|
142
|
-
end
|
1
|
+
require_relative "lock/file"
|
data/lib/lockfile.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "lock/file"
|
data/lockf.rb.gemspec
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
|
1
|
+
require_relative "lib/lockfile"
|
2
2
|
|
3
3
|
Gem::Specification.new do |gem|
|
4
4
|
gem.name = "lockf.rb"
|
5
5
|
gem.authors = ["0x1eef"]
|
6
6
|
gem.email = ["0x1eef@protonmail.com"]
|
7
7
|
gem.homepage = "https://github.com/0x1eef/lockf.rb#readme"
|
8
|
-
gem.version =
|
8
|
+
gem.version = Lock::File::VERSION
|
9
9
|
gem.licenses = ["0BSD"]
|
10
10
|
gem.files = `git ls-files`.split($/)
|
11
11
|
gem.require_paths = ["lib"]
|
12
|
-
gem.extensions = %w[ext/lockf.rb/extconf.rb]
|
13
12
|
gem.summary = "Ruby bindings for lockf(3)"
|
14
13
|
gem.description = gem.summary
|
14
|
+
|
15
|
+
gem.add_runtime_dependency "fiddle", "~> 1.1"
|
15
16
|
gem.add_development_dependency "yard", "~> 0.9"
|
16
17
|
gem.add_development_dependency "standard", "~> 1.12"
|
17
18
|
gem.add_development_dependency "rubocop", "~> 1.29"
|
18
19
|
gem.add_development_dependency "test-unit", "~> 3.5"
|
19
|
-
gem.add_development_dependency "
|
20
|
-
gem.add_development_dependency "test-cmd.rb", "~> 0.6"
|
20
|
+
gem.add_development_dependency "test-cmd.rb", "~> 0.12"
|
21
21
|
end
|
@@ -1,17 +1,18 @@
|
|
1
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
|
4
|
+
require "lock/file"
|
5
|
+
lockf = Lock::File.temporary_file
|
4
6
|
lockf.lock
|
5
7
|
print "Lock acquired by parent process (#{Time.now.utc})", "\n"
|
6
|
-
|
8
|
+
fork do
|
7
9
|
print "Child process waiting on lock (#{Time.now.utc})", "\n"
|
8
10
|
lockf.lock
|
9
11
|
print "Lock acquired by child process (#{Time.now.utc})", "\n"
|
10
12
|
end
|
11
13
|
sleep(3)
|
12
14
|
lockf.release
|
13
|
-
Process.wait
|
14
|
-
lockf.close
|
15
|
+
Process.wait
|
15
16
|
|
16
17
|
##
|
17
18
|
# Lock acquired by parent process (2023-02-11 16:43:15 UTC)
|
@@ -1,9 +1,11 @@
|
|
1
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
|
4
|
+
require "lock/file"
|
5
|
+
lockf = Lock::File.temporary_file
|
4
6
|
lockf.lock_nonblock
|
5
7
|
print "Lock acquired by parent process (#{Time.now.utc})", "\n"
|
6
|
-
|
8
|
+
fork do
|
7
9
|
lockf.lock_nonblock
|
8
10
|
print "Lock acquired by child process (#{Time.now.utc})", "\n"
|
9
11
|
rescue Errno::EWOULDBLOCK
|
@@ -13,8 +15,7 @@ rescue Errno::EWOULDBLOCK
|
|
13
15
|
end
|
14
16
|
sleep 3
|
15
17
|
lockf.release
|
16
|
-
Process.wait
|
17
|
-
lockf.close
|
18
|
+
Process.wait
|
18
19
|
|
19
20
|
##
|
20
21
|
# Lock acquired by parent process (2023-02-11 19:03:05 UTC)
|
@@ -1,10 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "lock/file"
|
5
|
+
require "tempfile"
|
4
6
|
file = Tempfile.new("lockf-ffi").tap(&:unlink)
|
5
|
-
|
7
|
+
Lock::File::FFI.lockf(file, Lock::File::F_LOCK, 0)
|
6
8
|
print "Lock acquired", "\n"
|
7
|
-
|
9
|
+
Lock::File::FFI.lockf(file, Lock::File::F_ULOCK, 0)
|
8
10
|
print "Lock released", "\n"
|
9
11
|
file.close
|
10
12
|
|
@@ -1,10 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "lock/file"
|
5
|
+
require "tempfile"
|
4
6
|
file = Tempfile.new("lockf-ffi").tap(&:unlink)
|
5
|
-
|
7
|
+
Lock::File::FFI.lockf(file, Lock::File::F_TLOCK, 0)
|
6
8
|
print "Lock acquired", "\n"
|
7
|
-
|
9
|
+
Lock::File::FFI.lockf(file, Lock::File::F_ULOCK, 0)
|
8
10
|
print "Lock released", "\n"
|
9
11
|
file.close
|
10
12
|
|
data/test/lock_file_test.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require_relative "setup"
|
2
|
-
|
3
|
+
|
4
|
+
class Lock::File::Test < Test::Unit::TestCase
|
3
5
|
attr_reader :file
|
4
6
|
attr_reader :lockf
|
5
7
|
|
6
8
|
def setup
|
7
9
|
@file = Tempfile.new("lockf-test").tap(&:unlink)
|
8
|
-
@lockf =
|
10
|
+
@lockf = Lock::File.new(file)
|
9
11
|
end
|
10
12
|
|
11
13
|
def teardown
|
@@ -15,7 +17,7 @@ class LockFile::Test < Test::Unit::TestCase
|
|
15
17
|
##
|
16
18
|
# LockFile#lock
|
17
19
|
def test_lock
|
18
|
-
assert_equal
|
20
|
+
assert_equal true, lockf.lock
|
19
21
|
ensure
|
20
22
|
lockf.release
|
21
23
|
end
|
@@ -32,7 +34,7 @@ class LockFile::Test < Test::Unit::TestCase
|
|
32
34
|
##
|
33
35
|
# LockFile#lock_nonblock
|
34
36
|
def test_lock_nonblock
|
35
|
-
assert_equal
|
37
|
+
assert_equal true, lockf.lock_nonblock
|
36
38
|
ensure
|
37
39
|
lockf.release
|
38
40
|
end
|
@@ -60,9 +62,9 @@ class LockFile::Test < Test::Unit::TestCase
|
|
60
62
|
##
|
61
63
|
# LockFile.temporary_file
|
62
64
|
def test_temporary_file
|
63
|
-
lockf =
|
64
|
-
assert_equal
|
65
|
-
assert_equal
|
65
|
+
lockf = Lock::File.temporary_file
|
66
|
+
assert_equal true, lockf.lock
|
67
|
+
assert_equal true, lockf.release
|
66
68
|
ensure
|
67
69
|
lockf.file.close
|
68
70
|
end
|
data/test/readme_test.rb
CHANGED
@@ -3,38 +3,40 @@
|
|
3
3
|
require_relative "setup"
|
4
4
|
require "test/cmd"
|
5
5
|
|
6
|
-
class
|
6
|
+
class Lock::File::ReadmeTest < Test::Unit::TestCase
|
7
7
|
def test_lockfile_blocking_variant
|
8
|
-
r =
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
r = ruby(readme_example("1_lockfile_blocking_variant.rb"))
|
9
|
+
["Lock acquired by parent process \(.+\)\n",
|
10
|
+
"Child process waiting on lock \(.+\)\n",
|
11
|
+
"Lock acquired by child process \(.+\)\n"
|
12
|
+
].each { assert_match Regexp.new(_1), r.stdout }
|
13
13
|
end
|
14
14
|
|
15
15
|
def test_lockfile_nonblocking_variant
|
16
|
-
r =
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
r = ruby(readme_example("2_lockfile_nonblocking_variant.rb"))
|
17
|
+
["Lock acquired by parent process \(.+\)\n",
|
18
|
+
"(Lock would block\n){3,4}",
|
19
|
+
"Lock acquired by child process \(.+\)\n"
|
20
|
+
].each { assert_match Regexp.new(_1), r.stdout }
|
21
21
|
end
|
22
22
|
|
23
23
|
def test_ffi_lockf_blocking_variant
|
24
24
|
assert_equal "Lock acquired\nLock released\n",
|
25
|
-
readme_example("3_ffilockf_blocking_variant.rb").stdout
|
25
|
+
ruby(readme_example("3_ffilockf_blocking_variant.rb")).stdout
|
26
26
|
end
|
27
27
|
|
28
28
|
def test_ffi_lockf_nonblocking_variant
|
29
29
|
assert_equal "Lock acquired\nLock released\n",
|
30
|
-
readme_example("4_ffilockf_nonblocking_variant.rb").stdout
|
30
|
+
ruby(readme_example("4_ffilockf_nonblocking_variant.rb")).stdout
|
31
31
|
end
|
32
32
|
|
33
33
|
private
|
34
34
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
def ruby(*argv)
|
36
|
+
cmd("ruby", *argv)
|
37
|
+
end
|
38
|
+
|
39
|
+
def readme_example(example_name)
|
40
|
+
File.join(__dir__, "..", "share", "lockf.rb", "examples", example_name)
|
39
41
|
end
|
40
42
|
end
|
data/test/setup.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lockf.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- '0x1eef'
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: fiddle
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: yard
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,40 +80,25 @@ dependencies:
|
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '3.5'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rake-compiler
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '1.2'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '1.2'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: test-cmd.rb
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0.
|
89
|
+
version: '0.12'
|
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.12'
|
97
97
|
description: Ruby bindings for lockf(3)
|
98
98
|
email:
|
99
99
|
- 0x1eef@protonmail.com
|
100
100
|
executables: []
|
101
|
-
extensions:
|
102
|
-
- ext/lockf.rb/extconf.rb
|
101
|
+
extensions: []
|
103
102
|
extra_rdoc_files: []
|
104
103
|
files:
|
105
104
|
- ".bundle/config"
|
@@ -114,10 +113,13 @@ files:
|
|
114
113
|
- LICENSE
|
115
114
|
- README.md
|
116
115
|
- Rakefile.rb
|
117
|
-
-
|
118
|
-
-
|
116
|
+
- bin/test-runner
|
117
|
+
- lib/lock/file.rb
|
118
|
+
- lib/lock/file/constants.rb
|
119
|
+
- lib/lock/file/ffi.rb
|
120
|
+
- lib/lock/file/version.rb
|
119
121
|
- lib/lockf.rb
|
120
|
-
- lib/
|
122
|
+
- lib/lockfile.rb
|
121
123
|
- lockf.rb.gemspec
|
122
124
|
- share/lockf.rb/examples/1_lockfile_blocking_variant.rb
|
123
125
|
- share/lockf.rb/examples/2_lockfile_nonblocking_variant.rb
|
@@ -145,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
147
|
- !ruby/object:Gem::Version
|
146
148
|
version: '0'
|
147
149
|
requirements: []
|
148
|
-
rubygems_version: 3.5.
|
150
|
+
rubygems_version: 3.5.11
|
149
151
|
signing_key:
|
150
152
|
specification_version: 4
|
151
153
|
summary: Ruby bindings for lockf(3)
|
data/ext/lockf.rb/extconf.rb
DELETED
data/ext/lockf.rb/lockf.c
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
#include <ruby.h>
|
2
|
-
#include <unistd.h>
|
3
|
-
#include <errno.h>
|
4
|
-
|
5
|
-
static VALUE
|
6
|
-
lockf_lock(VALUE self, VALUE fd, VALUE cmd, VALUE len)
|
7
|
-
{
|
8
|
-
int result;
|
9
|
-
|
10
|
-
Check_Type(fd, T_FIXNUM);
|
11
|
-
Check_Type(cmd, T_FIXNUM);
|
12
|
-
Check_Type(len, T_FIXNUM);
|
13
|
-
errno = 0;
|
14
|
-
result = lockf(NUM2INT(fd), NUM2INT(cmd), NUM2INT(len));
|
15
|
-
if (result == 0)
|
16
|
-
{
|
17
|
-
return INT2NUM(result);
|
18
|
-
}
|
19
|
-
else
|
20
|
-
{
|
21
|
-
rb_syserr_fail(errno, "lockf");
|
22
|
-
}
|
23
|
-
}
|
24
|
-
|
25
|
-
void
|
26
|
-
Init_lockf(void)
|
27
|
-
{
|
28
|
-
VALUE cLockf;
|
29
|
-
|
30
|
-
cLockf = rb_define_class("LockFile", rb_cObject);
|
31
|
-
rb_define_const(cLockf, "F_LOCK", INT2NUM(F_LOCK));
|
32
|
-
rb_define_const(cLockf, "F_TLOCK", INT2NUM(F_TLOCK));
|
33
|
-
rb_define_const(cLockf, "F_ULOCK", INT2NUM(F_ULOCK));
|
34
|
-
rb_define_const(cLockf, "F_TEST", INT2NUM(F_TEST));
|
35
|
-
rb_define_singleton_method(cLockf, "lockf", lockf_lock, 3);
|
36
|
-
}
|
data/lib/lockf/version.rb
DELETED