gvltools 0.4.0 → 0.5.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/.rubocop.yml +3 -1
- data/.ruby-version +1 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +28 -24
- data/README.md +11 -0
- data/ext/gvltools/instrumentation.c +22 -5
- data/lib/gvltools/version.rb +1 -1
- metadata +4 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 93f82e0950c9e2c02690e92984d20654d36dd91b3dffee80d0e85965a0bbc0b3
|
|
4
|
+
data.tar.gz: c6db2e33663b3c3fe290889eb91a71ac336d75a1005e90903b5c54e663446f70
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fcb609efc5575f85fd66fd57e223204a3206905b724940bf59dfc75e7421dab91faf0b9c7ed6e10083be728c17e5b083445ac59196fdfaa9ff629f4b05f53523
|
|
7
|
+
data.tar.gz: f71c390eaa0d6d72f8b06d58a70802f18ce7ec8347b84e42b4e5abcea5ec0c4a7bf598790080abd3b847e99537692caba4923651a2822d8cf1865a3588c5e962
|
data/.rubocop.yml
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
AllCops:
|
|
2
|
-
TargetRubyVersion: 3.0
|
|
3
2
|
NewCops: enable
|
|
4
3
|
SuggestExtensions: false
|
|
5
4
|
|
|
@@ -25,6 +24,9 @@ Style/GlobalVars:
|
|
|
25
24
|
Style/EmptyMethod:
|
|
26
25
|
Enabled: false
|
|
27
26
|
|
|
27
|
+
Naming/PredicateMethod:
|
|
28
|
+
Enabled: false
|
|
29
|
+
|
|
28
30
|
Style/IfUnlessModifier:
|
|
29
31
|
Enabled: false
|
|
30
32
|
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.3.0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.5.0] - 2026-06-15
|
|
4
|
+
|
|
5
|
+
- Fixed a `LocalTimer` deadlock with fiber schedulers by not allocating (and potentially triggering GC) during the `RESUMED` event (#34).
|
|
6
|
+
- Store the `LocalTimer` counter in a thread instance variable instead of fiber-local storage, so it is shared across fibers of the same thread (#37).
|
|
7
|
+
|
|
8
|
+
## [0.4.0] - 2024-01-19
|
|
9
|
+
|
|
3
10
|
- Fixed compatibility with Ruby 3.3.0.
|
|
4
11
|
- Added `GVLTools::LocalTimer.for(thread)` to access another thread counter (Ruby 3.3+ only).
|
|
5
12
|
|
data/Gemfile.lock
CHANGED
|
@@ -1,41 +1,45 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
gvltools (0.
|
|
4
|
+
gvltools (0.5.0)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
8
8
|
specs:
|
|
9
|
-
ast (2.4.
|
|
10
|
-
json (2.
|
|
11
|
-
language_server-protocol (3.17.0.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
ast (2.4.3)
|
|
10
|
+
json (2.19.8)
|
|
11
|
+
language_server-protocol (3.17.0.5)
|
|
12
|
+
lint_roller (1.1.0)
|
|
13
|
+
minitest (5.27.0)
|
|
14
|
+
parallel (2.1.0)
|
|
15
|
+
parser (3.3.11.1)
|
|
15
16
|
ast (~> 2.4.1)
|
|
16
17
|
racc
|
|
17
|
-
|
|
18
|
+
prism (1.9.0)
|
|
19
|
+
racc (1.8.1)
|
|
18
20
|
rainbow (3.1.1)
|
|
19
|
-
rake (13.
|
|
20
|
-
rake-compiler (1.
|
|
21
|
+
rake (13.4.2)
|
|
22
|
+
rake-compiler (1.3.1)
|
|
21
23
|
rake
|
|
22
|
-
regexp_parser (2.
|
|
23
|
-
|
|
24
|
-
rubocop (1.59.0)
|
|
24
|
+
regexp_parser (2.12.0)
|
|
25
|
+
rubocop (1.87.0)
|
|
25
26
|
json (~> 2.3)
|
|
26
|
-
language_server-protocol (
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
language_server-protocol (~> 3.17.0.2)
|
|
28
|
+
lint_roller (~> 1.1.0)
|
|
29
|
+
parallel (>= 1.10)
|
|
30
|
+
parser (>= 3.3.0.2)
|
|
29
31
|
rainbow (>= 2.2.2, < 4.0)
|
|
30
|
-
regexp_parser (>=
|
|
31
|
-
|
|
32
|
-
rubocop-ast (>= 1.30.0, < 2.0)
|
|
32
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
33
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
33
34
|
ruby-progressbar (~> 1.7)
|
|
34
|
-
unicode-display_width (>= 2.4.0, <
|
|
35
|
-
rubocop-ast (1.
|
|
36
|
-
parser (>= 3.
|
|
35
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
36
|
+
rubocop-ast (1.49.1)
|
|
37
|
+
parser (>= 3.3.7.2)
|
|
38
|
+
prism (~> 1.7)
|
|
37
39
|
ruby-progressbar (1.13.0)
|
|
38
|
-
unicode-display_width (2.
|
|
40
|
+
unicode-display_width (3.2.0)
|
|
41
|
+
unicode-emoji (~> 4.1)
|
|
42
|
+
unicode-emoji (4.2.0)
|
|
39
43
|
|
|
40
44
|
PLATFORMS
|
|
41
45
|
aarch64-linux
|
|
@@ -50,4 +54,4 @@ DEPENDENCIES
|
|
|
50
54
|
rubocop (~> 1.21)
|
|
51
55
|
|
|
52
56
|
BUNDLED WITH
|
|
53
|
-
2.3
|
|
57
|
+
2.5.3
|
data/README.md
CHANGED
|
@@ -44,6 +44,8 @@ It is particularly useful to detect wether an application use too many threads,
|
|
|
44
44
|
For instance as a Rack middleware:
|
|
45
45
|
|
|
46
46
|
```ruby
|
|
47
|
+
# lib/gvl_instrumentation_middleware.rb
|
|
48
|
+
|
|
47
49
|
class GVLInstrumentationMiddleware
|
|
48
50
|
def initialize(app)
|
|
49
51
|
@app = app
|
|
@@ -59,6 +61,15 @@ class GVLInstrumentationMiddleware
|
|
|
59
61
|
end
|
|
60
62
|
```
|
|
61
63
|
|
|
64
|
+
```ruby
|
|
65
|
+
# config/initializers/gvl_instrumentation.rb
|
|
66
|
+
|
|
67
|
+
GVLTools::LocalTimer.enable
|
|
68
|
+
|
|
69
|
+
require "gvl_instrumentation_middleware"
|
|
70
|
+
config.middleware.use GVLInstrumentationMiddleware
|
|
71
|
+
```
|
|
72
|
+
|
|
62
73
|
Starting from Ruby 3.3, a thread local timer can be accessed from another thread:
|
|
63
74
|
|
|
64
75
|
```ruby
|
|
@@ -26,6 +26,7 @@ typedef struct {
|
|
|
26
26
|
|
|
27
27
|
#ifdef HAVE_RB_INTERNAL_THREAD_SPECIFIC_GET // 3.3+
|
|
28
28
|
static int thread_storage_key = 0;
|
|
29
|
+
static ID id_local_state;
|
|
29
30
|
|
|
30
31
|
static size_t thread_local_state_memsize(const void *data) {
|
|
31
32
|
return sizeof(thread_local_state);
|
|
@@ -45,7 +46,8 @@ static inline thread_local_state *GT_LOCAL_STATE(VALUE thread, bool allocate) {
|
|
|
45
46
|
thread_local_state *state = rb_internal_thread_specific_get(thread, thread_storage_key);
|
|
46
47
|
if (!state && allocate) {
|
|
47
48
|
VALUE wrapper = TypedData_Make_Struct(rb_cLocalTimer, thread_local_state, &thread_local_state_type, state);
|
|
48
|
-
|
|
49
|
+
// Anchor to the thread object, not fiber-local storage, so it outlives the allocating fiber.
|
|
50
|
+
rb_ivar_set(thread, id_local_state, wrapper);
|
|
49
51
|
RB_GC_GUARD(wrapper);
|
|
50
52
|
rb_internal_thread_specific_set(thread, thread_storage_key, state);
|
|
51
53
|
}
|
|
@@ -99,6 +101,20 @@ static VALUE gt_enable_metric(VALUE module, VALUE metric) {
|
|
|
99
101
|
RUBY_INTERNAL_THREAD_EVENT_STARTED | RUBY_INTERNAL_THREAD_EVENT_EXITED | RUBY_INTERNAL_THREAD_EVENT_READY | RUBY_INTERNAL_THREAD_EVENT_RESUMED,
|
|
100
102
|
NULL
|
|
101
103
|
);
|
|
104
|
+
|
|
105
|
+
#ifdef HAVE_RB_INTERNAL_THREAD_SPECIFIC_GET
|
|
106
|
+
// Eagerly allocate the per-thread state for every thread that already
|
|
107
|
+
// exists at enable time. Threads created later get their state allocated from the
|
|
108
|
+
// STARTED event (also GVL-held). This guarantees the RESUMED event always finds an
|
|
109
|
+
// existing state and never has to allocate. Allocating from RESUMED is
|
|
110
|
+
// illegal because it doesn't necessarily run with the GVL.
|
|
111
|
+
VALUE threads = rb_funcall(rb_const_get(rb_cObject, rb_intern("Thread")), rb_intern("list"), 0);
|
|
112
|
+
long count = RARRAY_LEN(threads);
|
|
113
|
+
for (long i = 0; i < count; i++) {
|
|
114
|
+
GT_LOCAL_STATE(RARRAY_AREF(threads, i), true);
|
|
115
|
+
}
|
|
116
|
+
RB_GC_GUARD(threads);
|
|
117
|
+
#endif
|
|
102
118
|
}
|
|
103
119
|
return Qtrue;
|
|
104
120
|
}
|
|
@@ -138,7 +154,7 @@ static VALUE local_timer_m_reset(VALUE module) {
|
|
|
138
154
|
#ifdef HAVE_RB_INTERNAL_THREAD_SPECIFIC_GET
|
|
139
155
|
static VALUE local_timer_for(VALUE module, VALUE thread) {
|
|
140
156
|
GT_LOCAL_STATE(thread, true);
|
|
141
|
-
return
|
|
157
|
+
return rb_ivar_get(thread, id_local_state);
|
|
142
158
|
}
|
|
143
159
|
|
|
144
160
|
static VALUE local_timer_monotonic_time(VALUE timer) {
|
|
@@ -207,9 +223,9 @@ static void gt_thread_callback(rb_event_flag_t event, const rb_internal_thread_e
|
|
|
207
223
|
}
|
|
208
224
|
}
|
|
209
225
|
break;
|
|
210
|
-
case RUBY_INTERNAL_THREAD_EVENT_RESUMED: {
|
|
211
|
-
thread_local_state *state = GT_EVENT_LOCAL_STATE(event_data,
|
|
212
|
-
if (!state->was_ready) {
|
|
226
|
+
case RUBY_INTERNAL_THREAD_EVENT_RESUMED: { // Must not allocate
|
|
227
|
+
thread_local_state *state = GT_EVENT_LOCAL_STATE(event_data, false);
|
|
228
|
+
if (!state || !state->was_ready) {
|
|
213
229
|
break; // In case we registered the hook while some threads were already waiting on the GVL
|
|
214
230
|
}
|
|
215
231
|
|
|
@@ -240,6 +256,7 @@ static void gt_thread_callback(rb_event_flag_t event, const rb_internal_thread_e
|
|
|
240
256
|
void Init_instrumentation(void) {
|
|
241
257
|
#ifdef HAVE_RB_INTERNAL_THREAD_SPECIFIC_GET // 3.3+
|
|
242
258
|
thread_storage_key = rb_internal_thread_specific_key_create();
|
|
259
|
+
id_local_state = rb_intern("__gvltools_local_state");
|
|
243
260
|
#endif
|
|
244
261
|
|
|
245
262
|
VALUE rb_mGVLTools = rb_const_get(rb_cObject, rb_intern("GVLTools"));
|
data/lib/gvltools/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gvltools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jean Boussier
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
|
-
description:
|
|
14
12
|
email:
|
|
15
13
|
- jean.boussier@gmail.com
|
|
16
14
|
executables: []
|
|
@@ -19,6 +17,7 @@ extensions:
|
|
|
19
17
|
extra_rdoc_files: []
|
|
20
18
|
files:
|
|
21
19
|
- ".rubocop.yml"
|
|
20
|
+
- ".ruby-version"
|
|
22
21
|
- CHANGELOG.md
|
|
23
22
|
- Gemfile
|
|
24
23
|
- Gemfile.lock
|
|
@@ -38,7 +37,6 @@ metadata:
|
|
|
38
37
|
homepage_uri: https://github.com/Shopify/gvltools
|
|
39
38
|
source_code_uri: https://github.com/Shopify/gvltools
|
|
40
39
|
changelog_uri: https://github.com/Shopify/gvltools/blob/master/CHANGELOG.md
|
|
41
|
-
post_install_message:
|
|
42
40
|
rdoc_options: []
|
|
43
41
|
require_paths:
|
|
44
42
|
- lib
|
|
@@ -53,8 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
53
51
|
- !ruby/object:Gem::Version
|
|
54
52
|
version: '0'
|
|
55
53
|
requirements: []
|
|
56
|
-
rubygems_version:
|
|
57
|
-
signing_key:
|
|
54
|
+
rubygems_version: 4.0.10
|
|
58
55
|
specification_version: 4
|
|
59
56
|
summary: Set of GVL instrumentation tools
|
|
60
57
|
test_files: []
|