safe_timeout 0.0.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.codeclimate.yml +29 -0
- data/.github/workflows/ci.yml +38 -0
- data/.rspec +1 -1
- data/.rubocop.yml +204 -0
- data/.ruby-version +1 -0
- data/.yardopts +6 -0
- data/CODE_OF_CONDUCT.md +133 -0
- data/{LICENSE.txt → LICENSE} +1 -2
- data/README.md +19 -7
- data/Rakefile +4 -1
- data/bin/safe_timeout +11 -0
- data/lib/safe_timeout/interrupting_child_process.rb +12 -27
- data/lib/safe_timeout/spawner.rb +38 -0
- data/lib/safe_timeout/version.rb +3 -1
- data/lib/safe_timeout.rb +18 -7
- data/safe_timeout.gemspec +24 -13
- data/spec/lib/safe_timeout_spec.rb +3 -6
- data/spec/spec_helper.rb +12 -11
- metadata +128 -22
- data/circle.yml +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 85774cf93e4a1a308a3f7bfe0d52b8659ab7eed9bd27ac6574ecf4f9fee948bb
|
4
|
+
data.tar.gz: e9c71448c0d4975bf580e273e659a7b25da4c6814956adc820f4e3235a0f810a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b45ddb641fb75815a74504e52554cae94eeee7b9e88590097756df1e283ab9116f2f35097b5f478ca38e55f27407734b43f4a0d52fa9487387f4818768188390
|
7
|
+
data.tar.gz: 88ab46e0059f2b78bd9d76d063d791557e28a2ffc27b4ac6a6ca181ca11675c11aa099d47a87a261ad30b34105db8c8f26b6011e55d8d590f912cb22591e31bb
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
---
|
2
|
+
engines:
|
3
|
+
duplication:
|
4
|
+
enabled: true
|
5
|
+
config:
|
6
|
+
languages:
|
7
|
+
- c
|
8
|
+
- ruby
|
9
|
+
checks:
|
10
|
+
Similar code:
|
11
|
+
enabled: false
|
12
|
+
fixme:
|
13
|
+
enabled: true
|
14
|
+
flog:
|
15
|
+
enabled: true
|
16
|
+
config:
|
17
|
+
score_threshold: 20.0
|
18
|
+
rubocop:
|
19
|
+
enabled: true
|
20
|
+
exclude_fingerprints:
|
21
|
+
- 4218049e28199ed950d3cd721df86dce
|
22
|
+
- c8179d0de3a9df18a2c45750d3f8647e
|
23
|
+
- 03f6eee11d86507da564695007106721
|
24
|
+
channel: rubocop-1-23-0
|
25
|
+
ratings:
|
26
|
+
paths:
|
27
|
+
- "**.rb"
|
28
|
+
exclude_paths:
|
29
|
+
- spec/
|
@@ -0,0 +1,38 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ main ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ main ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
|
12
|
+
runs-on: ubuntu-latest
|
13
|
+
strategy:
|
14
|
+
matrix:
|
15
|
+
ruby-version: ['2.6', '2.7', '3.0']
|
16
|
+
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v2
|
19
|
+
- name: Set up Ruby
|
20
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
21
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
22
|
+
# uses: ruby/setup-ruby@v1
|
23
|
+
uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
24
|
+
with:
|
25
|
+
ruby-version: ${{ matrix.ruby-version }}
|
26
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
27
|
+
- name: Run tests
|
28
|
+
run: bundle exec rake spec
|
29
|
+
- name: Rubocop
|
30
|
+
run: bundle exec rubocop
|
31
|
+
- name: Send coverage to Code Climate
|
32
|
+
if: ${{ matrix.ruby-version == '3.0' }}
|
33
|
+
uses: paambaati/codeclimate-action@v3.0.0
|
34
|
+
env:
|
35
|
+
CC_TEST_REPORTER_ID: 79c39bac6840a990e7dfd0950f282b08c1be9fb15e17718af730e524cf631855
|
36
|
+
with:
|
37
|
+
coverageLocations: ${{github.workspace}}/coverage/coverage.json:simplecov
|
38
|
+
coverageCommand: bundle exec rake coverage
|
data/.rspec
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- ext/**/*
|
4
|
+
- tmp/**/*
|
5
|
+
- vendor/**/*
|
6
|
+
Gemspec/DateAssignment:
|
7
|
+
Enabled: true
|
8
|
+
Gemspec/RequireMFA:
|
9
|
+
Enabled: true
|
10
|
+
Gemspec/RequiredRubyVersion:
|
11
|
+
Enabled: false
|
12
|
+
Layout/EmptyLinesAroundAttributeAccessor:
|
13
|
+
Enabled: true
|
14
|
+
Layout/EmptyLinesAroundClassBody:
|
15
|
+
EnforcedStyle: empty_lines_except_namespace
|
16
|
+
Layout/EmptyLinesAroundModuleBody:
|
17
|
+
EnforcedStyle: empty_lines_except_namespace
|
18
|
+
Layout/ExtraSpacing:
|
19
|
+
Enabled: false
|
20
|
+
Layout/HashAlignment:
|
21
|
+
EnforcedHashRocketStyle: table
|
22
|
+
EnforcedColonStyle: table
|
23
|
+
Layout/LineEndStringConcatenationIndentation:
|
24
|
+
Enabled: true
|
25
|
+
Layout/LineLength:
|
26
|
+
Max: 120
|
27
|
+
Enabled: false
|
28
|
+
Layout/SpaceAroundMethodCallOperator:
|
29
|
+
Enabled: true
|
30
|
+
Layout/SpaceBeforeBrackets:
|
31
|
+
Enabled: true
|
32
|
+
Lint/AmbiguousAssignment:
|
33
|
+
Enabled: true
|
34
|
+
Lint/AmbiguousOperatorPrecedence:
|
35
|
+
Enabled: true
|
36
|
+
Lint/AmbiguousRange:
|
37
|
+
Enabled: true
|
38
|
+
Lint/DeprecatedConstants:
|
39
|
+
Enabled: true
|
40
|
+
Lint/DeprecatedOpenSSLConstant:
|
41
|
+
Enabled: true
|
42
|
+
Lint/DuplicateBranch:
|
43
|
+
Enabled: true
|
44
|
+
Lint/DuplicateRegexpCharacterClassElement:
|
45
|
+
Enabled: true
|
46
|
+
Lint/EmptyBlock:
|
47
|
+
Enabled: true
|
48
|
+
Lint/EmptyClass:
|
49
|
+
Enabled: true
|
50
|
+
Lint/EmptyInPattern:
|
51
|
+
Enabled: true
|
52
|
+
Lint/IncompatibleIoSelectWithFiberScheduler:
|
53
|
+
Enabled: true
|
54
|
+
Lint/LambdaWithoutLiteralBlock:
|
55
|
+
Enabled: true
|
56
|
+
Lint/MixedRegexpCaptureTypes:
|
57
|
+
Enabled: true
|
58
|
+
Lint/NoReturnInBeginEndBlocks:
|
59
|
+
Enabled: true
|
60
|
+
Lint/NumberedParameterAssignment:
|
61
|
+
Enabled: true
|
62
|
+
Lint/OrAssignmentToConstant:
|
63
|
+
Enabled: true
|
64
|
+
Lint/RaiseException:
|
65
|
+
Enabled: true
|
66
|
+
Lint/RedundantDirGlobSort:
|
67
|
+
Enabled: true
|
68
|
+
Lint/RequireRelativeSelfPath:
|
69
|
+
Enabled: true
|
70
|
+
Lint/StructNewOverride:
|
71
|
+
Enabled: true
|
72
|
+
Lint/SymbolConversion:
|
73
|
+
Enabled: true
|
74
|
+
Lint/ToEnumArguments:
|
75
|
+
Enabled: true
|
76
|
+
Lint/TripleQuotes:
|
77
|
+
Enabled: true
|
78
|
+
Lint/UnexpectedBlockArity:
|
79
|
+
Enabled: true
|
80
|
+
Lint/UnmodifiedReduceAccumulator:
|
81
|
+
Enabled: true
|
82
|
+
Lint/UselessRuby2Keywords:
|
83
|
+
Enabled: true
|
84
|
+
Metrics/AbcSize:
|
85
|
+
Max: 50
|
86
|
+
Enabled: false
|
87
|
+
Metrics/BlockLength:
|
88
|
+
Max: 50
|
89
|
+
Enabled: false
|
90
|
+
Metrics/ClassLength:
|
91
|
+
Max: 50
|
92
|
+
Enabled: false
|
93
|
+
Metrics/CyclomaticComplexity:
|
94
|
+
Max: 30
|
95
|
+
Enabled: false
|
96
|
+
Metrics/MethodLength:
|
97
|
+
Max: 20
|
98
|
+
Enabled: false
|
99
|
+
Metrics/ModuleLength:
|
100
|
+
Max: 1000
|
101
|
+
Enabled: false
|
102
|
+
Metrics/PerceivedComplexity:
|
103
|
+
Max: 30
|
104
|
+
Enabled: false
|
105
|
+
Naming/BlockForwarding:
|
106
|
+
Enabled: true
|
107
|
+
Security/IoMethods:
|
108
|
+
Enabled: true
|
109
|
+
Security/MarshalLoad:
|
110
|
+
Enabled: false
|
111
|
+
Style/AndOr:
|
112
|
+
Enabled: false
|
113
|
+
Style/ArgumentsForwarding:
|
114
|
+
Enabled: true
|
115
|
+
Style/CaseEquality:
|
116
|
+
Enabled: false
|
117
|
+
Style/CollectionCompact:
|
118
|
+
Enabled: true
|
119
|
+
Style/DocumentDynamicEvalDefinition:
|
120
|
+
Enabled: true
|
121
|
+
Style/Documentation:
|
122
|
+
Enabled: false
|
123
|
+
Style/DoubleNegation:
|
124
|
+
Enabled: false
|
125
|
+
Style/EndlessMethod:
|
126
|
+
Enabled: true
|
127
|
+
Style/ExponentialNotation:
|
128
|
+
Enabled: true
|
129
|
+
Style/FileRead:
|
130
|
+
Enabled: true
|
131
|
+
Style/FileWrite:
|
132
|
+
Enabled: true
|
133
|
+
Style/FrozenStringLiteralComment:
|
134
|
+
Enabled: false
|
135
|
+
Style/GuardClause:
|
136
|
+
Enabled: false
|
137
|
+
Style/HashConversion:
|
138
|
+
Enabled: true
|
139
|
+
Style/HashEachMethods:
|
140
|
+
Enabled: true
|
141
|
+
Style/HashExcept:
|
142
|
+
Enabled: true
|
143
|
+
Style/HashSyntax:
|
144
|
+
Enabled: true
|
145
|
+
Style/HashTransformKeys:
|
146
|
+
Enabled: true
|
147
|
+
Style/HashTransformValues:
|
148
|
+
Enabled: true
|
149
|
+
Style/IfUnlessModifier:
|
150
|
+
Enabled: false
|
151
|
+
Style/IfWithBooleanLiteralBranches:
|
152
|
+
Enabled: true
|
153
|
+
Style/InPatternThen:
|
154
|
+
Enabled: true
|
155
|
+
Style/MapToHash:
|
156
|
+
Enabled: true
|
157
|
+
Style/MultilineBlockChain:
|
158
|
+
Enabled: false
|
159
|
+
Style/MultilineIfModifier:
|
160
|
+
Enabled: false
|
161
|
+
Style/MultilineInPatternThen:
|
162
|
+
Enabled: true
|
163
|
+
Style/MutableConstant:
|
164
|
+
Enabled: false
|
165
|
+
Style/NegatedIfElseCondition:
|
166
|
+
Enabled: true
|
167
|
+
Style/NilLambda:
|
168
|
+
Enabled: true
|
169
|
+
Style/NumberedParameters:
|
170
|
+
Enabled: true
|
171
|
+
Style/NumberedParametersLimit:
|
172
|
+
Enabled: true
|
173
|
+
Style/OpenStructUse:
|
174
|
+
Enabled: true
|
175
|
+
Style/QuotedSymbols:
|
176
|
+
Enabled: true
|
177
|
+
Style/RedundantArgument:
|
178
|
+
Enabled: true
|
179
|
+
Style/RedundantRegexpCharacterClass:
|
180
|
+
Enabled: true
|
181
|
+
Style/RedundantRegexpEscape:
|
182
|
+
Enabled: true
|
183
|
+
Style/RedundantSelfAssignmentBranch:
|
184
|
+
Enabled: true
|
185
|
+
Style/RescueModifier:
|
186
|
+
Enabled: false
|
187
|
+
Style/RescueStandardError:
|
188
|
+
Enabled: false
|
189
|
+
Style/SelectByRegexp:
|
190
|
+
Enabled: true
|
191
|
+
Style/SlicingWithRange:
|
192
|
+
Enabled: true
|
193
|
+
Style/StringChars:
|
194
|
+
Enabled: true
|
195
|
+
Style/SwapValues:
|
196
|
+
Enabled: true
|
197
|
+
Style/TrailingCommaInArguments:
|
198
|
+
EnforcedStyleForMultiline: comma
|
199
|
+
Style/TrailingCommaInArrayLiteral:
|
200
|
+
EnforcedStyleForMultiline: consistent_comma
|
201
|
+
Style/TrailingCommaInHashLiteral:
|
202
|
+
EnforcedStyleForMultiline: consistent_comma
|
203
|
+
Style/ZeroLengthPredicate:
|
204
|
+
Enabled: false
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.3
|
data/.yardopts
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
|
2
|
+
# Contributor Covenant Code of Conduct
|
3
|
+
|
4
|
+
## Our Pledge
|
5
|
+
|
6
|
+
We as members, contributors, and leaders pledge to make participation in our
|
7
|
+
community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
9
|
+
identity and expression, level of experience, education, socio-economic status,
|
10
|
+
nationality, personal appearance, race, caste, color, religion, or sexual
|
11
|
+
identity and orientation.
|
12
|
+
|
13
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
14
|
+
diverse, inclusive, and healthy community.
|
15
|
+
|
16
|
+
## Our Standards
|
17
|
+
|
18
|
+
Examples of behavior that contributes to a positive environment for our
|
19
|
+
community include:
|
20
|
+
|
21
|
+
* Demonstrating empathy and kindness toward other people
|
22
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
23
|
+
* Giving and gracefully accepting constructive feedback
|
24
|
+
* Accepting responsibility and apologizing to those affected by our mistakes,
|
25
|
+
and learning from the experience
|
26
|
+
* Focusing on what is best not just for us as individuals, but for the overall
|
27
|
+
community
|
28
|
+
|
29
|
+
Examples of unacceptable behavior include:
|
30
|
+
|
31
|
+
* The use of sexualized language or imagery, and sexual attention or advances of
|
32
|
+
any kind
|
33
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
34
|
+
* Public or private harassment
|
35
|
+
* Publishing others' private information, such as a physical or email address,
|
36
|
+
without their explicit permission
|
37
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
38
|
+
professional setting
|
39
|
+
|
40
|
+
## Enforcement Responsibilities
|
41
|
+
|
42
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
43
|
+
acceptable behavior and will take appropriate and fair corrective action in
|
44
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
45
|
+
or harmful.
|
46
|
+
|
47
|
+
Community leaders have the right and responsibility to remove, edit, or reject
|
48
|
+
comments, commits, code, wiki edits, issues, and other contributions that are
|
49
|
+
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
50
|
+
decisions when appropriate.
|
51
|
+
|
52
|
+
## Scope
|
53
|
+
|
54
|
+
This Code of Conduct applies within all community spaces, and also applies when
|
55
|
+
an individual is officially representing the community in public spaces.
|
56
|
+
Examples of representing our community include using an official e-mail address,
|
57
|
+
posting via an official social media account, or acting as an appointed
|
58
|
+
representative at an online or offline event.
|
59
|
+
|
60
|
+
## Enforcement
|
61
|
+
|
62
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
63
|
+
reported to the community leaders responsible for enforcement at
|
64
|
+
[INSERT CONTACT METHOD].
|
65
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
66
|
+
|
67
|
+
All community leaders are obligated to respect the privacy and security of the
|
68
|
+
reporter of any incident.
|
69
|
+
|
70
|
+
## Enforcement Guidelines
|
71
|
+
|
72
|
+
Community leaders will follow these Community Impact Guidelines in determining
|
73
|
+
the consequences for any action they deem in violation of this Code of Conduct:
|
74
|
+
|
75
|
+
### 1. Correction
|
76
|
+
|
77
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed
|
78
|
+
unprofessional or unwelcome in the community.
|
79
|
+
|
80
|
+
**Consequence**: A private, written warning from community leaders, providing
|
81
|
+
clarity around the nature of the violation and an explanation of why the
|
82
|
+
behavior was inappropriate. A public apology may be requested.
|
83
|
+
|
84
|
+
### 2. Warning
|
85
|
+
|
86
|
+
**Community Impact**: A violation through a single incident or series of
|
87
|
+
actions.
|
88
|
+
|
89
|
+
**Consequence**: A warning with consequences for continued behavior. No
|
90
|
+
interaction with the people involved, including unsolicited interaction with
|
91
|
+
those enforcing the Code of Conduct, for a specified period of time. This
|
92
|
+
includes avoiding interactions in community spaces as well as external channels
|
93
|
+
like social media. Violating these terms may lead to a temporary or permanent
|
94
|
+
ban.
|
95
|
+
|
96
|
+
### 3. Temporary Ban
|
97
|
+
|
98
|
+
**Community Impact**: A serious violation of community standards, including
|
99
|
+
sustained inappropriate behavior.
|
100
|
+
|
101
|
+
**Consequence**: A temporary ban from any sort of interaction or public
|
102
|
+
communication with the community for a specified period of time. No public or
|
103
|
+
private interaction with the people involved, including unsolicited interaction
|
104
|
+
with those enforcing the Code of Conduct, is allowed during this period.
|
105
|
+
Violating these terms may lead to a permanent ban.
|
106
|
+
|
107
|
+
### 4. Permanent Ban
|
108
|
+
|
109
|
+
**Community Impact**: Demonstrating a pattern of violation of community
|
110
|
+
standards, including sustained inappropriate behavior, harassment of an
|
111
|
+
individual, or aggression toward or disparagement of classes of individuals.
|
112
|
+
|
113
|
+
**Consequence**: A permanent ban from any sort of public interaction within the
|
114
|
+
community.
|
115
|
+
|
116
|
+
## Attribution
|
117
|
+
|
118
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
119
|
+
version 2.1, available at
|
120
|
+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
121
|
+
|
122
|
+
Community Impact Guidelines were inspired by
|
123
|
+
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
124
|
+
|
125
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
126
|
+
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
127
|
+
[https://www.contributor-covenant.org/translations][translations].
|
128
|
+
|
129
|
+
[homepage]: https://www.contributor-covenant.org
|
130
|
+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
131
|
+
[Mozilla CoC]: https://github.com/mozilla/diversity
|
132
|
+
[FAQ]: https://www.contributor-covenant.org/faq
|
133
|
+
[translations]: https://www.contributor-covenant.org/translations
|
data/{LICENSE.txt → LICENSE}
RENAMED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
MIT License
|
2
2
|
|
3
3
|
Copyright (c) 2015 David McCullars
|
4
4
|
|
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
21
|
SOFTWARE.
|
22
|
-
|
data/README.md
CHANGED
@@ -1,8 +1,23 @@
|
|
1
1
|
# SafeTimeout
|
2
2
|
|
3
|
+
* README: https://github.com/david-mccullars/safe_timeout
|
4
|
+
* Documentation: http://www.rubydoc.info/github/david-mccullars/safe_timeout
|
5
|
+
* Bug Reports: https://github.com/david-mccullars/safe_timeout/issues
|
6
|
+
|
7
|
+
|
8
|
+
## Status
|
9
|
+
|
10
|
+
[![Gem Version](https://badge.fury.io/rb/safe_timeout.svg)](https://badge.fury.io/rb/safe_timeout)
|
11
|
+
[![Build Status](https://github.com/david-mccullars/safe_timeout/workflows/CI/badge.svg)](https://github.com/david-mccullars/safe_timeout/actions?workflow=CI)
|
12
|
+
[![Code Climate](https://codeclimate.com/github/david-mccullars/safe_timeout/badges/gpa.svg)](https://codeclimate.com/github/david-mccullars/safe_timeout)
|
13
|
+
[![Test Coverage](https://codeclimate.com/github/david-mccullars/safe_timeout/badges/coverage.svg)](https://codeclimate.com/github/david-mccullars/safe_timeout/coverage)
|
14
|
+
[![MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
|
15
|
+
|
16
|
+
|
17
|
+
## Description
|
18
|
+
|
3
19
|
A safer alternative to Ruby's Timeout that uses unix processes instead of threads
|
4
20
|
|
5
|
-
[![Circle CI](https://circleci.com/gh/david-mccullars/safe_timeout/tree/master.svg?style=svg)](https://circleci.com/gh/david-mccullars/safe_timeout/tree/master)
|
6
21
|
|
7
22
|
## Installation
|
8
23
|
|
@@ -41,10 +56,7 @@ could add the following to the top of an application:
|
|
41
56
|
|
42
57
|
Timeout = SafeTimeout
|
43
58
|
|
44
|
-
## Contributing
|
45
59
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
50
|
-
5. Create a new Pull Request
|
60
|
+
## License
|
61
|
+
|
62
|
+
MIT. See the {file:LICENSE} file.
|
data/Rakefile
CHANGED
data/bin/safe_timeout
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
if ENV['COVERAGE']
|
3
|
+
require 'simplecov'
|
4
|
+
SimpleCov.command_name("#{Process.ppid}:#{Process.pid}")
|
5
|
+
SimpleCov.formatter = Class.new(SimpleCov::Formatter::HTMLFormatter) do
|
6
|
+
def output_message(_ignored); end
|
7
|
+
end
|
8
|
+
SimpleCov.start
|
9
|
+
end
|
10
|
+
require 'safe_timeout'
|
11
|
+
SafeTimeout::InterruptingChildProcess.new(*ARGV).wait_for_timeout
|
@@ -1,40 +1,25 @@
|
|
1
1
|
module SafeTimeout
|
2
2
|
class InterruptingChildProcess
|
3
3
|
|
4
|
-
def initialize(
|
5
|
-
@
|
6
|
-
@
|
7
|
-
end
|
4
|
+
def initialize(expiration)
|
5
|
+
@ppid = Process.ppid
|
6
|
+
@expiration = expiration.to_f
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
Kernel.fork { wait_for_expiration }
|
12
|
-
yield
|
13
|
-
ensure
|
14
|
-
stop rescue nil
|
8
|
+
abort "Invalid pid to monitor: #{@ppid}" if @ppid.to_i.zero?
|
9
|
+
abort "Invalid expiration: #{@expiration}" unless @expiration > 0.0
|
15
10
|
end
|
16
11
|
|
17
|
-
def
|
18
|
-
|
19
|
-
Process.kill("HUP", @child_pid)
|
12
|
+
def notify_parent_of_expiration
|
13
|
+
SafeTimeout.send_signal('TRAP', @ppid)
|
20
14
|
end
|
21
15
|
|
22
|
-
def
|
23
|
-
Signal.trap(
|
16
|
+
def wait_for_timeout
|
17
|
+
Signal.trap('HUP') { exit 0 }
|
18
|
+
|
19
|
+
sleep [@expiration - Time.now.to_f, 0.1].max
|
24
20
|
|
25
|
-
|
26
|
-
# stop, it becomes an orphan and is given to the init process (1)
|
27
|
-
# or worse yet it becomes a zombie with parent 0. In either case,
|
28
|
-
# stop interrupting!
|
29
|
-
while Process.ppid > 1
|
30
|
-
sleep 0.1
|
31
|
-
if Time.now.to_f > @expiration
|
32
|
-
Process.kill("TRAP", Process.ppid)
|
33
|
-
return
|
34
|
-
end
|
35
|
-
end
|
21
|
+
notify_parent_of_expiration
|
36
22
|
end
|
37
23
|
|
38
24
|
end
|
39
|
-
|
40
25
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'English'
|
2
|
+
module SafeTimeout
|
3
|
+
class Spawner
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@expiration = Time.now.to_f + options.fetch(:timeout)
|
7
|
+
@on_timeout = lambda do |*args|
|
8
|
+
@timed_out = true
|
9
|
+
options.fetch(:on_timeout).call(*args)
|
10
|
+
end
|
11
|
+
@timed_out = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
original = Signal.trap('TRAP', &@on_timeout) || 'DEFAULT'
|
16
|
+
spawn_interrupter
|
17
|
+
yield
|
18
|
+
ensure
|
19
|
+
Signal.trap('TRAP', original)
|
20
|
+
stop
|
21
|
+
end
|
22
|
+
|
23
|
+
def stop
|
24
|
+
# Tell that child to stop interrupting!
|
25
|
+
SafeTimeout.send_signal('HUP', @child_pid) unless @timed_out
|
26
|
+
Process.wait(@child_pid) rescue nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def spawn_interrupter
|
30
|
+
# Create a light-weight child process to notify this process if it is
|
31
|
+
# taking too long
|
32
|
+
bin = Gem.bin_path('safe_timeout', 'safe_timeout')
|
33
|
+
@child_pid = Process.spawn({ 'COVERAGE' => defined?(SimpleCov) }, bin, @expiration.to_s)
|
34
|
+
Process.detach(@child_pid)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/lib/safe_timeout/version.rb
CHANGED
data/lib/safe_timeout.rb
CHANGED
@@ -1,15 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Ruby's Timeout is broken and highly dangerous. To avoid this risk we instead
|
5
|
+
# use a child process to handle the timeout. We fork it and let it issue a SIGINT
|
6
|
+
# (Ctrl-C) to the parent if the timeout is reached.
|
7
|
+
#
|
4
8
|
module SafeTimeout
|
5
9
|
|
6
10
|
autoload :InterruptingChildProcess, 'safe_timeout/interrupting_child_process'
|
11
|
+
autoload :Spawner, 'safe_timeout/spawner'
|
7
12
|
|
8
|
-
def self.timeout(sec, klass=nil, &block)
|
9
|
-
|
10
|
-
:
|
11
|
-
:
|
13
|
+
def self.timeout(sec, klass = nil, &block)
|
14
|
+
Spawner.new(
|
15
|
+
timeout: sec,
|
16
|
+
on_timeout: ->(_) { raise(klass || Timeout::Error) },
|
12
17
|
).start(&block)
|
13
18
|
end
|
14
19
|
|
20
|
+
def self.send_signal(signal, pid)
|
21
|
+
Process.kill(signal, pid) if pid
|
22
|
+
rescue Errno::ESRCH
|
23
|
+
# do nothing
|
24
|
+
end
|
25
|
+
|
15
26
|
end
|
data/safe_timeout.gemspec
CHANGED
@@ -1,24 +1,35 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'safe_timeout/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
6
|
+
spec.name = 'safe_timeout'
|
8
7
|
spec.version = SafeTimeout::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
8
|
+
spec.authors = ['David McCullars']
|
9
|
+
spec.email = ['david.mccullars@gmail.com']
|
10
|
+
spec.summary = "A safer alternative to Ruby's Timeout that uses unix processes instead of threads."
|
11
|
+
spec.description = "A safer alternative to Ruby's Timeout that uses unix processes instead of threads."
|
12
|
+
spec.homepage = 'https://github.com/david-mccullars/safe_timeout'
|
13
|
+
spec.license = 'MIT'
|
15
14
|
|
16
15
|
spec.files = `git ls-files -z`.split("\x0")
|
17
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = [
|
18
|
+
spec.require_paths = ['lib']
|
20
19
|
|
21
|
-
spec.
|
22
|
-
|
23
|
-
spec.add_development_dependency
|
20
|
+
spec.required_ruby_version = '>= 2.6'
|
21
|
+
|
22
|
+
spec.add_development_dependency 'bundler'
|
23
|
+
spec.add_development_dependency 'github-markup'
|
24
|
+
spec.add_development_dependency 'rake'
|
25
|
+
spec.add_development_dependency 'redcarpet'
|
26
|
+
spec.add_development_dependency 'rspec'
|
27
|
+
spec.add_development_dependency 'rubocop'
|
28
|
+
spec.add_development_dependency 'rubocop-rake'
|
29
|
+
spec.add_development_dependency 'rubocop-rspec'
|
30
|
+
spec.add_development_dependency 'simplecov', '~> 0.17.0' # 0.18 not supported by code climate
|
31
|
+
spec.add_development_dependency 'yard'
|
32
|
+
spec.metadata = {
|
33
|
+
'rubygems_mfa_required' => 'true',
|
34
|
+
}
|
24
35
|
end
|
@@ -2,7 +2,6 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe SafeTimeout do
|
4
4
|
context '.timeout' do
|
5
|
-
|
6
5
|
it 'should process its block' do
|
7
6
|
result = nil
|
8
7
|
expect do
|
@@ -23,22 +22,21 @@ describe SafeTimeout do
|
|
23
22
|
child = fork do
|
24
23
|
SafeTimeout.timeout(5) { sleep 5 }
|
25
24
|
end
|
26
|
-
:loop until grand_child = all_processes[child].first
|
25
|
+
:loop until (grand_child = all_processes[child].first)
|
27
26
|
expect(grand_child).to be > child
|
28
27
|
|
29
28
|
Process.kill('TERM', child)
|
30
|
-
expect(is_process_still_running?
|
29
|
+
expect(is_process_still_running?(grand_child)).to be false
|
31
30
|
end
|
32
31
|
|
33
32
|
private
|
34
33
|
|
35
34
|
# Returns a hash of all running processes and their children
|
36
35
|
def all_processes
|
37
|
-
`ps -eo pid,ppid`.lines.
|
36
|
+
`ps -eo pid,ppid`.lines.each_with_object(Hash.new([])) do |line, hash|
|
38
37
|
pid, ppid = line.split.map(&:to_i)
|
39
38
|
hash[ppid] = [] unless hash.key?(ppid)
|
40
39
|
hash[ppid] << pid
|
41
|
-
hash
|
42
40
|
end
|
43
41
|
end
|
44
42
|
|
@@ -55,6 +53,5 @@ describe SafeTimeout do
|
|
55
53
|
end
|
56
54
|
true
|
57
55
|
end
|
58
|
-
|
59
56
|
end
|
60
57
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
-
require '
|
2
|
-
|
1
|
+
require 'English'
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
3
|
+
require 'simplecov'
|
3
4
|
|
4
|
-
|
5
|
+
FileUtils.rm_rf('coverage') # Prevent old runs from merging in
|
6
|
+
SimpleCov.command_name(Process.pid.to_s)
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
config.mock_with :rspec do |mocks|
|
11
|
-
mocks.verify_partial_doubles = true
|
12
|
-
mocks.syntax = :expect
|
13
|
-
end
|
8
|
+
SimpleCov.start do
|
9
|
+
add_filter '/spec/'
|
10
|
+
end
|
14
11
|
|
12
|
+
require 'safe_timeout'
|
13
|
+
# Ensure all files get loaded (for coverage sake)
|
14
|
+
Dir[File.expand_path('../lib/**/*.rb', __dir__)].each do |f|
|
15
|
+
require f[%r{lib/(.*)\.rb$}, 1]
|
15
16
|
end
|
metadata
CHANGED
@@ -1,74 +1,180 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: safe_timeout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David McCullars
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: github-markup
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rake
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
|
-
- - "
|
45
|
+
- - ">="
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
47
|
+
version: '0'
|
34
48
|
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
|
-
- - "
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: redcarpet
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
39
60
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: simplecov
|
43
127
|
requirement: !ruby/object:Gem::Requirement
|
44
128
|
requirements:
|
45
129
|
- - "~>"
|
46
130
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
131
|
+
version: 0.17.0
|
48
132
|
type: :development
|
49
133
|
prerelease: false
|
50
134
|
version_requirements: !ruby/object:Gem::Requirement
|
51
135
|
requirements:
|
52
136
|
- - "~>"
|
53
137
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
138
|
+
version: 0.17.0
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: yard
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
55
153
|
description: A safer alternative to Ruby's Timeout that uses unix processes instead
|
56
154
|
of threads.
|
57
155
|
email:
|
58
156
|
- david.mccullars@gmail.com
|
59
|
-
executables:
|
157
|
+
executables:
|
158
|
+
- safe_timeout
|
60
159
|
extensions: []
|
61
160
|
extra_rdoc_files: []
|
62
161
|
files:
|
162
|
+
- ".codeclimate.yml"
|
163
|
+
- ".github/workflows/ci.yml"
|
63
164
|
- ".gitignore"
|
64
165
|
- ".rspec"
|
166
|
+
- ".rubocop.yml"
|
167
|
+
- ".ruby-version"
|
168
|
+
- ".yardopts"
|
169
|
+
- CODE_OF_CONDUCT.md
|
65
170
|
- Gemfile
|
66
|
-
- LICENSE
|
171
|
+
- LICENSE
|
67
172
|
- README.md
|
68
173
|
- Rakefile
|
69
|
-
-
|
174
|
+
- bin/safe_timeout
|
70
175
|
- lib/safe_timeout.rb
|
71
176
|
- lib/safe_timeout/interrupting_child_process.rb
|
177
|
+
- lib/safe_timeout/spawner.rb
|
72
178
|
- lib/safe_timeout/version.rb
|
73
179
|
- safe_timeout.gemspec
|
74
180
|
- spec/lib/safe_timeout_spec.rb
|
@@ -76,8 +182,9 @@ files:
|
|
76
182
|
homepage: https://github.com/david-mccullars/safe_timeout
|
77
183
|
licenses:
|
78
184
|
- MIT
|
79
|
-
metadata:
|
80
|
-
|
185
|
+
metadata:
|
186
|
+
rubygems_mfa_required: 'true'
|
187
|
+
post_install_message:
|
81
188
|
rdoc_options: []
|
82
189
|
require_paths:
|
83
190
|
- lib
|
@@ -85,16 +192,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
192
|
requirements:
|
86
193
|
- - ">="
|
87
194
|
- !ruby/object:Gem::Version
|
88
|
-
version: '
|
195
|
+
version: '2.6'
|
89
196
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
197
|
requirements:
|
91
198
|
- - ">="
|
92
199
|
- !ruby/object:Gem::Version
|
93
200
|
version: '0'
|
94
201
|
requirements: []
|
95
|
-
|
96
|
-
|
97
|
-
signing_key:
|
202
|
+
rubygems_version: 3.2.32
|
203
|
+
signing_key:
|
98
204
|
specification_version: 4
|
99
205
|
summary: A safer alternative to Ruby's Timeout that uses unix processes instead of
|
100
206
|
threads.
|
data/circle.yml
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
dependencies:
|
2
|
-
override:
|
3
|
-
- 'rvm-exec 1.8.7 bundle install'
|
4
|
-
- 'rvm-exec 1.9.3 bundle install'
|
5
|
-
- 'rvm-exec 2.1.2 bundle install'
|
6
|
-
- 'rvm-exec 2.2.0 bundle install'
|
7
|
-
test:
|
8
|
-
override:
|
9
|
-
- 'rvm-exec 1.8.7 bundle exec rake'
|
10
|
-
- 'rvm-exec 1.9.3 bundle exec rake'
|
11
|
-
- 'rvm-exec 2.1.2 bundle exec rake'
|
12
|
-
- 'rvm-exec 2.2.0 bundle exec rake'
|