pidfd 1.0.0 → 1.0.1
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/CHANGELOG.md +5 -0
- data/README.md +10 -8
- data/lib/pidfd/errors.rb +4 -0
- data/lib/pidfd/version.rb +2 -1
- data/lib/pidfd.rb +16 -7
- data/pidfd.gemspec +18 -17
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7c498e89e5d07cb2d256846016a70bf0aece20a8bf145664cf6516637b7b4b84
|
|
4
|
+
data.tar.gz: 45ef88b384618b3d83b9fcc147b582154e01dd05fad36875e529d56cbe964d97
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e0454d5fe4fefd3a5095a63a79666e1d7ea794ce8b4cfd5b96475c79bab981ddc82a750fb77a26ae8fb04fbf5416ffda053eca8efa64b0fa4d049b5c2cc0f544
|
|
7
|
+
data.tar.gz: 3b034e4305df0eba9ccd3e718a32e6a47632602a1176064f36e9684b626e406922538687065c5f32dc005e7f94f9508038a9a8018b8719bfadb66c6942917e2b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Pidfd Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.1 (2026-11-02)
|
|
4
|
+
- [Breaking] Drop support for Ruby 3.2 (EOL March 2026). Minimum required Ruby is now 3.3.
|
|
5
|
+
- [Enhancement] Use dynamic Ruby warning category opt-in in test helpers
|
|
6
|
+
- [Change] Document Linux 5.4 as the minimum kernel. `#cleanup` uses `waitid(P_PIDFD)`, which Linux supports since 5.4. The 0.5.0 entry stated 5.3.
|
|
7
|
+
|
|
3
8
|
## 1.0.0 (2025-09-05)
|
|
4
9
|
- [Change] No changes. Just versioning update.
|
|
5
10
|
|
data/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Pidfds provide a **stable reference** to a process that remains valid even if th
|
|
|
24
24
|
- **Race-free signal delivery** - Signals always go to the intended process
|
|
25
25
|
- **Reliable process monitoring** - Know definitively when a process exits
|
|
26
26
|
- **Thread-safe operations** - No TOCTTOU races
|
|
27
|
-
- **Pollable file descriptors** -
|
|
27
|
+
- **Pollable file descriptors** - The kernel marks a pidfd readable when its process exits
|
|
28
28
|
|
|
29
29
|
## When to Use This Gem
|
|
30
30
|
|
|
@@ -38,7 +38,7 @@ Use pidfd when you need:
|
|
|
38
38
|
|
|
39
39
|
Don't use pidfd for:
|
|
40
40
|
- macOS or Windows (Linux-only feature)
|
|
41
|
-
- Kernels older than Linux 5.
|
|
41
|
+
- Kernels older than Linux 5.4
|
|
42
42
|
- Simple parent-child relationships (traditional wait works fine)
|
|
43
43
|
|
|
44
44
|
## Installation
|
|
@@ -63,8 +63,8 @@ gem install pidfd
|
|
|
63
63
|
|
|
64
64
|
## Requirements
|
|
65
65
|
|
|
66
|
-
- Linux kernel 5.
|
|
67
|
-
- Ruby 3.
|
|
66
|
+
- Linux kernel 5.4 or newer
|
|
67
|
+
- Ruby 3.3 or newer
|
|
68
68
|
- FFI gem
|
|
69
69
|
|
|
70
70
|
## Usage
|
|
@@ -115,13 +115,13 @@ pidfd = Pidfd.new(pid)
|
|
|
115
115
|
pidfd.signal('TERM') # Guaranteed to hit the right process or fail safely
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
-
###
|
|
118
|
+
### Waiting for a Process to Exit
|
|
119
|
+
|
|
120
|
+
`Pidfd` does not expose its file descriptor, so you cannot pass a `Pidfd` to `IO.select` or an event loop. `alive?` checks the pidfd with a zero timeout, so the check does not wait for the process to exit. To wait for a process to exit, poll `alive?`:
|
|
119
121
|
|
|
120
122
|
```ruby
|
|
121
|
-
# Pidfd provides a pollable file descriptor
|
|
122
123
|
pidfd = Pidfd.new(child_pid)
|
|
123
124
|
|
|
124
|
-
# Use with IO.select for non-blocking monitoring
|
|
125
125
|
loop do
|
|
126
126
|
if pidfd.alive?
|
|
127
127
|
# Process still running
|
|
@@ -146,6 +146,8 @@ else
|
|
|
146
146
|
end
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
+
`Pidfd.supported?` tests only `pidfd_open`, so it also returns `true` on Linux 5.3. On 5.3, `#cleanup` does not reap the process, because `waitid(P_PIDFD)` needs Linux 5.4. If your code can run on 5.3, check the kernel version yourself.
|
|
150
|
+
|
|
149
151
|
### Fallback Strategy
|
|
150
152
|
|
|
151
153
|
```ruby
|
|
@@ -202,7 +204,7 @@ end
|
|
|
202
204
|
- **Efficient**: Pidfd operations are kernel-level, very fast
|
|
203
205
|
- **Low overhead**: Minimal memory usage per pidfd
|
|
204
206
|
- **Scalable**: Handles thousands of processes efficiently
|
|
205
|
-
- **Non-blocking**:
|
|
207
|
+
- **Non-blocking**: The `alive?` check uses a zero timeout and does not wait for the process to exit
|
|
206
208
|
|
|
207
209
|
## Security
|
|
208
210
|
|
data/lib/pidfd/errors.rb
CHANGED
|
@@ -2,14 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
class Pidfd
|
|
4
4
|
# Namespace for all the pidfd errors
|
|
5
|
+
# @api public
|
|
5
6
|
module Errors
|
|
6
7
|
# Base error class for all pidfd errors
|
|
8
|
+
# @api public
|
|
7
9
|
BaseError = Class.new(StandardError)
|
|
8
10
|
|
|
9
11
|
# Error raised when pidfd_open syscall fails
|
|
12
|
+
# @api public
|
|
10
13
|
PidfdOpenFailedError = Class.new(BaseError)
|
|
11
14
|
|
|
12
15
|
# Error raised when pidfd_signal syscall fails
|
|
16
|
+
# @api public
|
|
13
17
|
PidfdSignalFailedError = Class.new(BaseError)
|
|
14
18
|
end
|
|
15
19
|
end
|
data/lib/pidfd/version.rb
CHANGED
data/lib/pidfd.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "ffi"
|
|
4
4
|
|
|
5
5
|
# Main class that wraps Linux pidfd functionality for Ruby
|
|
6
|
+
# @api public
|
|
6
7
|
class Pidfd
|
|
7
8
|
extend FFI::Library
|
|
8
9
|
|
|
@@ -29,29 +30,33 @@ class Pidfd
|
|
|
29
30
|
# Wait for child processes that have exited
|
|
30
31
|
WEXITED = 4
|
|
31
32
|
|
|
32
|
-
# Default syscall numbers for x86_64 Linux
|
|
33
|
-
#
|
|
34
|
-
#
|
|
33
|
+
# Default syscall numbers for x86_64 Linux. These can be overridden if needed for different
|
|
34
|
+
# architectures. Pidfd open call number
|
|
35
|
+
# @api public
|
|
35
36
|
DEFAULT_PIDFD_OPEN_SYSCALL = 434
|
|
36
37
|
|
|
37
38
|
# Pidfd signal call number
|
|
39
|
+
# @api public
|
|
38
40
|
DEFAULT_PIDFD_SIGNAL_SYSCALL = 424
|
|
39
41
|
|
|
40
42
|
private_constant :P_PIDFD, :WEXITED
|
|
41
43
|
|
|
42
44
|
class << self
|
|
43
45
|
# @return [Integer] syscall number for pidfd_open
|
|
46
|
+
# @api public
|
|
44
47
|
attr_accessor :pidfd_open_syscall
|
|
45
48
|
|
|
46
49
|
# @return [Integer] syscall number for pidfd_signal
|
|
50
|
+
# @api public
|
|
47
51
|
attr_accessor :pidfd_signal_syscall
|
|
48
52
|
|
|
49
53
|
# @return [Boolean] true if syscall is supported via FFI
|
|
54
|
+
# @api public
|
|
50
55
|
def supported?
|
|
51
56
|
# If we were not even able to load the FFI C lib, it won't be supported
|
|
52
57
|
return false unless API_SUPPORTED
|
|
53
58
|
# Won't work on macOS because it does not support pidfd
|
|
54
|
-
return false if RUBY_DESCRIPTION.include?(
|
|
59
|
+
return false if RUBY_DESCRIPTION.include?("darwin")
|
|
55
60
|
# Won't work on Windows for the same reason as on macOS
|
|
56
61
|
return false if RUBY_DESCRIPTION.match?(/mswin|ming|cygwin/)
|
|
57
62
|
|
|
@@ -71,6 +76,7 @@ class Pidfd
|
|
|
71
76
|
self.pidfd_signal_syscall = DEFAULT_PIDFD_SIGNAL_SYSCALL
|
|
72
77
|
|
|
73
78
|
# @param pid [Integer] pid of the node we want to work with
|
|
79
|
+
# @api public
|
|
74
80
|
def initialize(pid)
|
|
75
81
|
@mutex = Mutex.new
|
|
76
82
|
|
|
@@ -80,6 +86,7 @@ class Pidfd
|
|
|
80
86
|
end
|
|
81
87
|
|
|
82
88
|
# @return [Boolean] true if given process is alive, false if no longer
|
|
89
|
+
# @api public
|
|
83
90
|
def alive?
|
|
84
91
|
@pidfd_select ||= [@pidfd_io]
|
|
85
92
|
|
|
@@ -98,6 +105,7 @@ class Pidfd
|
|
|
98
105
|
|
|
99
106
|
# Cleans the zombie process
|
|
100
107
|
# @note This should run **only** on processes that exited, otherwise will wait
|
|
108
|
+
# @api public
|
|
101
109
|
def cleanup
|
|
102
110
|
@mutex.synchronize do
|
|
103
111
|
return if @cleaned
|
|
@@ -117,6 +125,7 @@ class Pidfd
|
|
|
117
125
|
# @return [Boolean] true if signal was sent, otherwise false or error raised. `false`
|
|
118
126
|
# returned when we attempt to send a signal to a dead process
|
|
119
127
|
# @note It will not send signals to dead processes
|
|
128
|
+
# @api public
|
|
120
129
|
def signal(sig_name)
|
|
121
130
|
@mutex.synchronize do
|
|
122
131
|
return false if @cleaned
|
|
@@ -155,5 +164,5 @@ class Pidfd
|
|
|
155
164
|
end
|
|
156
165
|
end
|
|
157
166
|
|
|
158
|
-
require_relative
|
|
159
|
-
require_relative
|
|
167
|
+
require_relative "pidfd/version"
|
|
168
|
+
require_relative "pidfd/errors"
|
data/pidfd.gemspec
CHANGED
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
3
|
+
require_relative "lib/pidfd/version"
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name =
|
|
6
|
+
spec.name = "pidfd"
|
|
7
7
|
spec.version = Pidfd::VERSION
|
|
8
|
-
spec.authors = [
|
|
9
|
-
spec.email = [
|
|
8
|
+
spec.authors = ["Maciej Mensfeld"]
|
|
9
|
+
spec.email = ["maciej@mensfeld.pl"]
|
|
10
10
|
|
|
11
|
-
spec.summary =
|
|
12
|
-
spec.homepage =
|
|
13
|
-
spec.license =
|
|
11
|
+
spec.summary = "Ruby wrapper for Linux pidfd system calls"
|
|
12
|
+
spec.homepage = "https://github.com/mensfeld/pidfd"
|
|
13
|
+
spec.license = "MIT"
|
|
14
14
|
spec.description = <<~DESC
|
|
15
15
|
Provides race-free process management using Linux pidfd (process file descriptors) for safer
|
|
16
16
|
signal delivery and process monitoring
|
|
17
17
|
DESC
|
|
18
18
|
|
|
19
|
-
spec.required_ruby_version =
|
|
19
|
+
spec.required_ruby_version = ">= 3.3.0"
|
|
20
20
|
|
|
21
|
-
spec.metadata[
|
|
22
|
-
spec.metadata[
|
|
23
|
-
spec.metadata[
|
|
24
|
-
spec.metadata[
|
|
25
|
-
spec.metadata[
|
|
26
|
-
spec.metadata[
|
|
21
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
22
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
23
|
+
spec.metadata["source_code_uri"] = "https://github.com/mensfeld/pidfd"
|
|
24
|
+
spec.metadata["changelog_uri"] = "https://github.com/mensfeld/pidfd/blob/master/CHANGELOG.md"
|
|
25
|
+
spec.metadata["documentation_uri"] = "https://github.com/mensfeld/pidfd#readme"
|
|
26
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/mensfeld/pidfd/issues"
|
|
27
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
27
28
|
|
|
28
29
|
# Specify which files should be added to the gem when it is released.
|
|
29
|
-
spec.files = Dir.glob(
|
|
30
|
-
spec.require_paths = [
|
|
30
|
+
spec.files = Dir.glob("{lib}/**/*") + %w[LICENSE CHANGELOG.md README.md pidfd.gemspec]
|
|
31
|
+
spec.require_paths = ["lib"]
|
|
31
32
|
|
|
32
|
-
spec.add_dependency
|
|
33
|
+
spec.add_dependency "ffi", ">= 1.15"
|
|
33
34
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pidfd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Maciej Mensfeld
|
|
@@ -46,8 +46,9 @@ metadata:
|
|
|
46
46
|
homepage_uri: https://github.com/mensfeld/pidfd
|
|
47
47
|
allowed_push_host: https://rubygems.org
|
|
48
48
|
source_code_uri: https://github.com/mensfeld/pidfd
|
|
49
|
-
changelog_uri: https://github.com/mensfeld/pidfd/blob/
|
|
49
|
+
changelog_uri: https://github.com/mensfeld/pidfd/blob/master/CHANGELOG.md
|
|
50
50
|
documentation_uri: https://github.com/mensfeld/pidfd#readme
|
|
51
|
+
bug_tracker_uri: https://github.com/mensfeld/pidfd/issues
|
|
51
52
|
rubygems_mfa_required: 'true'
|
|
52
53
|
rdoc_options: []
|
|
53
54
|
require_paths:
|
|
@@ -56,14 +57,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
56
57
|
requirements:
|
|
57
58
|
- - ">="
|
|
58
59
|
- !ruby/object:Gem::Version
|
|
59
|
-
version: 3.
|
|
60
|
+
version: 3.3.0
|
|
60
61
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
62
|
requirements:
|
|
62
63
|
- - ">="
|
|
63
64
|
- !ruby/object:Gem::Version
|
|
64
65
|
version: '0'
|
|
65
66
|
requirements: []
|
|
66
|
-
rubygems_version:
|
|
67
|
+
rubygems_version: 4.0.20
|
|
67
68
|
specification_version: 4
|
|
68
69
|
summary: Ruby wrapper for Linux pidfd system calls
|
|
69
70
|
test_files: []
|