-
Notifications
You must be signed in to change notification settings - Fork 362
Expand file tree
/
Copy pathDeferredProfilingContextIntegration.java
More file actions
200 lines (174 loc) · 6.46 KB
/
Copy pathDeferredProfilingContextIntegration.java
File metadata and controls
200 lines (174 loc) · 6.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package datadog.trace.bootstrap;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import datadog.context.Context;
import datadog.trace.api.EndpointTracker;
import datadog.trace.api.Stateful;
import datadog.trace.api.profiling.ProfilingContextAttribute;
import datadog.trace.api.profiling.ProfilingScope;
import datadog.trace.api.profiling.Timing;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.ProfilerContext;
import datadog.trace.bootstrap.instrumentation.api.ProfilingContextIntegration;
import datadog.trace.util.AgentTaskScheduler;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A {@link ProfilingContextIntegration} handed out synchronously during {@code premain} while the
* real ddprof-based integration is constructed later, off the premain thread, to avoid loading the
* ddprof native library (and touching {@code java.nio.file}) before {@code main} gets a chance to
* set its own {@code java.nio.file.spi.DefaultFileSystemProvider}. Delegates to {@link
* ProfilingContextIntegration.NoOp} until the swap happens; stays a no-op forever if construction
* fails.
*/
final class DeferredProfilingContextIntegration implements ProfilingContextIntegration {
private static final Logger log =
LoggerFactory.getLogger(DeferredProfilingContextIntegration.class);
/**
* Delay before the deferred construction runs, giving {@code main} a chance to install its own
* {@code java.nio.file.spi.DefaultFileSystemProvider} first; not user-tunable since losing the
* first second of context exposure is not observable.
*/
private static final long INITIALIZATION_DELAY_MILLIS = 1_000;
private final String name;
private final Callable<ProfilingContextIntegration> factory;
/**
* Swapped to the real integration once construction succeeds; volatile since scopes may already
* be running when the swap happens.
*/
private volatile ProfilingContextIntegration delegate = ProfilingContextIntegration.NoOp.INSTANCE;
/**
* Callbacks queued via {@link #whenAvailable(Runnable)} before the swap; guarded by {@code this}
* together with the {@link #delegate} write so none is run twice or dropped.
*/
private final List<Runnable> pendingAvailabilityCallbacks = new ArrayList<>(1);
/**
* @param name the name reported by {@link #name()}, i.e. the name of the integration being
* deferred.
* @param factory creates the real integration; invoked at most once, off the premain thread.
*/
DeferredProfilingContextIntegration(
final String name, final Callable<ProfilingContextIntegration> factory) {
this.name = name;
this.factory = factory;
}
/**
* Schedules the deferred construction to run off this (premain) thread, after {@link
* #INITIALIZATION_DELAY_MILLIS}.
*/
void scheduleInitialization() {
AgentTaskScheduler.get().schedule(this::initialize, INITIALIZATION_DELAY_MILLIS, MILLISECONDS);
}
/**
* Runs the deferred construction; called exactly once per instance, from {@link
* #scheduleInitialization()}. On failure this instance keeps behaving as {@link
* ProfilingContextIntegration.NoOp} forever; a background failure must never propagate.
*/
void initialize() {
try {
final ProfilingContextIntegration integration = factory.call();
if (integration == null) {
return;
}
final List<Runnable> callbacks;
synchronized (this) {
delegate = integration;
callbacks = new ArrayList<>(pendingAvailabilityCallbacks);
pendingAvailabilityCallbacks.clear();
}
for (final Runnable callback : callbacks) {
try {
callback.run();
} catch (final Throwable t) {
log.debug("Availability callback for {} profiling context failed.", name, t);
}
}
} catch (final Throwable t) {
// toString() because failures here (UnsatisfiedLinkError etc.) often carry no message.
log.info("Deferred {} profiling context labeling not available. {}", name, t.toString());
}
}
/**
* Runs {@code callback} once the real integration is swapped in, or immediately if it already is;
* never runs it if the deferred construction failed.
*/
@Override
public void whenAvailable(final Runnable callback) {
// double-checked: delegate is volatile, so a post-swap caller never takes the lock
if (delegate == ProfilingContextIntegration.NoOp.INSTANCE) {
synchronized (this) {
if (delegate == ProfilingContextIntegration.NoOp.INSTANCE) {
pendingAvailabilityCallbacks.add(callback);
return;
}
}
}
callback.run();
}
/**
* The name of the deferred integration, not of the current delegate: read once at tracer build
* time, possibly before the deferred construction completes.
*/
@Override
public String name() {
return name;
}
@Override
public void onStart() {
delegate.onStart();
}
@Override
public void onAttach() {
delegate.onAttach();
}
@Override
public void onDetach() {
delegate.onDetach();
}
@Override
public boolean isThreadContextBindingRequired() {
return delegate.isThreadContextBindingRequired();
}
@Override
public void setContext(final Context context) {
delegate.setContext(context);
}
@Override
public Stateful newScopeState(final ProfilerContext profilerContext) {
return delegate.newScopeState(profilerContext);
}
@Override
public int encode(final CharSequence constant) {
return delegate.encode(constant);
}
@Override
public int encodeOperationName(final CharSequence constant) {
return delegate.encodeOperationName(constant);
}
@Override
public int encodeResourceName(final CharSequence constant) {
return delegate.encodeResourceName(constant);
}
@Override
public ProfilingContextAttribute createContextAttribute(final String attribute) {
return delegate.createContextAttribute(attribute);
}
@Override
public ProfilingScope newScope() {
return delegate.newScope();
}
@Override
public void onRootSpanFinished(final AgentSpan rootSpan, final EndpointTracker tracker) {
delegate.onRootSpanFinished(rootSpan, tracker);
}
@Override
public EndpointTracker onRootSpanStarted(final AgentSpan rootSpan) {
return delegate.onRootSpanStarted(rootSpan);
}
@Override
public Timing start(final TimerType type) {
return delegate.start(type);
}
}