2
2
3
3
import datadog .trace .api .config .ProfilingConfig ;
4
4
import datadog .trace .bootstrap .config .provider .ConfigProvider ;
5
+ import datadog .trace .util .AgentTaskScheduler ;
5
6
import datadog .trace .util .PidHelper ;
6
7
import java .io .IOException ;
7
8
import java .nio .file .FileVisitResult ;
15
16
import java .time .Instant ;
16
17
import java .time .temporal .ChronoUnit ;
17
18
import java .util .Set ;
18
- import java .util .concurrent .CompletableFuture ;
19
- import java .util .concurrent .ExecutionException ;
19
+ import java .util .concurrent .CountDownLatch ;
20
20
import java .util .concurrent .TimeUnit ;
21
- import java .util .concurrent .TimeoutException ;
21
+ import java .util .regex .Pattern ;
22
+ import java .util .stream .Stream ;
22
23
import org .slf4j .Logger ;
23
24
import org .slf4j .LoggerFactory ;
24
25
30
31
*/
31
32
public final class TempLocationManager {
32
33
private static final Logger log = LoggerFactory .getLogger (TempLocationManager .class );
34
+ private static final Pattern JFR_DIR_PATTERN =
35
+ Pattern .compile ("\\ d{4}_\\ d{2}_\\ d{2}_\\ d{2}_\\ d{2}_\\ d{2}_\\ d{6}" );
36
+ private static final String TEMPDIR_PREFIX = "pid_" ;
33
37
34
38
private static final class SingletonHolder {
35
39
private static final TempLocationManager INSTANCE = new TempLocationManager ();
@@ -62,7 +66,7 @@ default FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOE
62
66
default void onCleanupStart (boolean selfCleanup , long timeout , TimeUnit unit ) {}
63
67
}
64
68
65
- private class CleanupVisitor implements FileVisitor <Path > {
69
+ private final class CleanupVisitor implements FileVisitor <Path > {
66
70
private boolean shouldClean ;
67
71
68
72
private final Set <String > pidSet = PidHelper .getJavaPids ();
@@ -98,14 +102,19 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
98
102
terminated = true ;
99
103
return FileVisitResult .TERMINATE ;
100
104
}
105
+ if (cleanSelf && JFR_DIR_PATTERN .matcher (dir .getFileName ().toString ()).matches ()) {
106
+ // do not delete JFR repository on 'self-cleanup' - it conflicts with the JFR's own cleanup
107
+ return FileVisitResult .SKIP_SUBTREE ;
108
+ }
109
+
101
110
cleanupTestHook .preVisitDirectory (dir , attrs );
102
111
103
112
if (dir .equals (baseTempDir )) {
104
113
return FileVisitResult .CONTINUE ;
105
114
}
106
115
String fileName = dir .getFileName ().toString ();
107
116
// the JFR repository directories are under <basedir>/pid_<pid>
108
- String pid = fileName .startsWith ("pid_" ) ? fileName .substring (4 ) : null ;
117
+ String pid = fileName .startsWith (TEMPDIR_PREFIX ) ? fileName .substring (4 ) : null ;
109
118
boolean isSelfPid = pid != null && pid .equals (PidHelper .getPid ());
110
119
shouldClean |= cleanSelf ? isSelfPid : !isSelfPid && !pidSet .contains (pid );
111
120
if (shouldClean ) {
@@ -167,18 +176,43 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx
167
176
}
168
177
String fileName = dir .getFileName ().toString ();
169
178
// reset the flag only if we are done cleaning the top-level directory
170
- shouldClean = !fileName .startsWith ("pid_" );
179
+ shouldClean = !fileName .startsWith (TEMPDIR_PREFIX );
171
180
}
172
181
return FileVisitResult .CONTINUE ;
173
182
}
174
183
}
175
184
185
+ private final class CleanupTask implements Runnable {
186
+ private final CountDownLatch latch = new CountDownLatch (1 );
187
+ private volatile Throwable throwable = null ;
188
+
189
+ @ Override
190
+ public void run () {
191
+ try {
192
+ cleanup (false );
193
+ } catch (OutOfMemoryError oom ) {
194
+ throw oom ;
195
+ } catch (Throwable t ) {
196
+ throwable = t ;
197
+ } finally {
198
+ latch .countDown ();
199
+ }
200
+ }
201
+
202
+ boolean await (long timeout , TimeUnit unit ) throws Throwable {
203
+ boolean ret = latch .await (timeout , unit );
204
+ if (throwable != null ) {
205
+ throw throwable ;
206
+ }
207
+ return ret ;
208
+ }
209
+ }
210
+
176
211
private final Path baseTempDir ;
177
212
private final Path tempDir ;
178
213
private final long cutoffSeconds ;
179
214
180
- private final CompletableFuture <Void > cleanupTask ;
181
-
215
+ private final CleanupTask cleanupTask = new CleanupTask ();
182
216
private final CleanupHook cleanupTestHook ;
183
217
184
218
/**
@@ -200,11 +234,7 @@ public static TempLocationManager getInstance() {
200
234
static TempLocationManager getInstance (boolean waitForCleanup ) {
201
235
TempLocationManager instance = SingletonHolder .INSTANCE ;
202
236
if (waitForCleanup ) {
203
- try {
204
- instance .waitForCleanup (5 , TimeUnit .SECONDS );
205
- } catch (TimeoutException ignored ) {
206
-
207
- }
237
+ instance .waitForCleanup (5 , TimeUnit .SECONDS );
208
238
}
209
239
return instance ;
210
240
}
@@ -214,10 +244,11 @@ private TempLocationManager() {
214
244
}
215
245
216
246
TempLocationManager (ConfigProvider configProvider ) {
217
- this (configProvider , CleanupHook .EMPTY );
247
+ this (configProvider , true , CleanupHook .EMPTY );
218
248
}
219
249
220
- TempLocationManager (ConfigProvider configProvider , CleanupHook testHook ) {
250
+ TempLocationManager (
251
+ ConfigProvider configProvider , boolean runStartupCleanup , CleanupHook testHook ) {
221
252
cleanupTestHook = testHook ;
222
253
223
254
// In order to avoid racy attempts to clean up files which are currently being processed in a
@@ -255,21 +286,21 @@ private TempLocationManager() {
255
286
baseTempDir = configuredTempDir .resolve ("ddprof" );
256
287
baseTempDir .toFile ().deleteOnExit ();
257
288
258
- tempDir = baseTempDir .resolve ("pid_" + pid );
259
- cleanupTask = CompletableFuture .runAsync (() -> cleanup (false ));
289
+ tempDir = baseTempDir .resolve (TEMPDIR_PREFIX + pid );
290
+ if (runStartupCleanup ) {
291
+ // do not execute the background cleanup task when running in tests
292
+ AgentTaskScheduler .INSTANCE .execute (() -> cleanup (false ));
293
+ }
260
294
261
295
Thread selfCleanup =
262
296
new Thread (
263
297
() -> {
264
- try {
265
- waitForCleanup (1 , TimeUnit .SECONDS );
266
- } catch (TimeoutException e ) {
298
+ if (!waitForCleanup (1 , TimeUnit .SECONDS )) {
267
299
log .info (
268
300
"Cleanup task timed out. {} temp directory might not have been cleaned up properly" ,
269
301
tempDir );
270
- } finally {
271
- cleanup (true );
272
302
}
303
+ cleanup (true );
273
304
},
274
305
"Temp Location Manager Cleanup" );
275
306
Runtime .getRuntime ().addShutdownHook (selfCleanup );
@@ -344,6 +375,19 @@ boolean cleanup(boolean cleanSelf) {
344
375
*/
345
376
boolean cleanup (boolean cleanSelf , long timeout , TimeUnit unit ) {
346
377
try {
378
+ if (!Files .exists (baseTempDir )) {
379
+ // not event the main temp location exists; nothing to clean up
380
+ return true ;
381
+ }
382
+ try (Stream <Path > paths = Files .walk (baseTempDir )) {
383
+ if (paths .noneMatch (
384
+ path ->
385
+ Files .isDirectory (path )
386
+ && path .getFileName ().toString ().startsWith (TEMPDIR_PREFIX ))) {
387
+ // nothing to clean up; bail out early
388
+ return true ;
389
+ }
390
+ }
347
391
cleanupTestHook .onCleanupStart (cleanSelf , timeout , unit );
348
392
CleanupVisitor visitor = new CleanupVisitor (cleanSelf , timeout , unit );
349
393
Files .walkFileTree (baseTempDir , visitor );
@@ -359,21 +403,24 @@ boolean cleanup(boolean cleanSelf, long timeout, TimeUnit unit) {
359
403
}
360
404
361
405
// accessible for tests
362
- void waitForCleanup (long timeout , TimeUnit unit ) throws TimeoutException {
406
+ boolean waitForCleanup (long timeout , TimeUnit unit ) {
363
407
try {
364
- cleanupTask .get (timeout , unit );
408
+ return cleanupTask .await (timeout , unit );
365
409
} catch (InterruptedException e ) {
366
- cleanupTask . cancel ( true );
410
+ log . debug ( "Temp directory cleanup was interrupted" );
367
411
Thread .currentThread ().interrupt ();
368
- } catch (TimeoutException e ) {
369
- cleanupTask .cancel (true );
370
- throw e ;
371
- } catch (ExecutionException e ) {
412
+ } catch (Throwable t ) {
372
413
if (log .isDebugEnabled ()) {
373
- log .debug ("Failed to cleanup temp directory: {}" , tempDir , e );
414
+ log .debug ("Failed to cleanup temp directory: {}" , tempDir , t );
374
415
} else {
375
416
log .debug ("Failed to cleanup temp directory: {}" , tempDir );
376
417
}
377
418
}
419
+ return false ;
420
+ }
421
+
422
+ // accessible for tests
423
+ void createDirStructure () throws IOException {
424
+ Files .createDirectories (baseTempDir );
378
425
}
379
426
}
0 commit comments