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