22
33import datadog .trace .api .config .ProfilingConfig ;
44import datadog .trace .bootstrap .config .provider .ConfigProvider ;
5+ import datadog .trace .util .AgentTaskScheduler ;
56import datadog .trace .util .PidHelper ;
67import java .io .IOException ;
78import java .nio .file .FileVisitResult ;
1516import java .time .Instant ;
1617import java .time .temporal .ChronoUnit ;
1718import java .util .Set ;
18- import java .util .concurrent .CompletableFuture ;
19- import java .util .concurrent .ExecutionException ;
19+ import java .util .concurrent .CountDownLatch ;
2020import java .util .concurrent .TimeUnit ;
21- import java .util .concurrent .TimeoutException ;
21+ import java .util .regex .Pattern ;
22+ import java .util .stream .Stream ;
2223import org .slf4j .Logger ;
2324import org .slf4j .LoggerFactory ;
2425
3031 */
3132public final class TempLocationManager {
3233 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_" ;
3337
3438 private static final class SingletonHolder {
3539 private static final TempLocationManager INSTANCE = new TempLocationManager ();
@@ -62,7 +66,7 @@ default FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOE
6266 default void onCleanupStart (boolean selfCleanup , long timeout , TimeUnit unit ) {}
6367 }
6468
65- private class CleanupVisitor implements FileVisitor <Path > {
69+ private final class CleanupVisitor implements FileVisitor <Path > {
6670 private boolean shouldClean ;
6771
6872 private final Set <String > pidSet = PidHelper .getJavaPids ();
@@ -98,14 +102,19 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
98102 terminated = true ;
99103 return FileVisitResult .TERMINATE ;
100104 }
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+
101110 cleanupTestHook .preVisitDirectory (dir , attrs );
102111
103112 if (dir .equals (baseTempDir )) {
104113 return FileVisitResult .CONTINUE ;
105114 }
106115 String fileName = dir .getFileName ().toString ();
107116 // 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 ;
109118 boolean isSelfPid = pid != null && pid .equals (PidHelper .getPid ());
110119 shouldClean |= cleanSelf ? isSelfPid : !isSelfPid && !pidSet .contains (pid );
111120 if (shouldClean ) {
@@ -167,18 +176,43 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx
167176 }
168177 String fileName = dir .getFileName ().toString ();
169178 // reset the flag only if we are done cleaning the top-level directory
170- shouldClean = !fileName .startsWith ("pid_" );
179+ shouldClean = !fileName .startsWith (TEMPDIR_PREFIX );
171180 }
172181 return FileVisitResult .CONTINUE ;
173182 }
174183 }
175184
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+
176211 private final Path baseTempDir ;
177212 private final Path tempDir ;
178213 private final long cutoffSeconds ;
179214
180- private final CompletableFuture <Void > cleanupTask ;
181-
215+ private final CleanupTask cleanupTask = new CleanupTask ();
182216 private final CleanupHook cleanupTestHook ;
183217
184218 /**
@@ -200,11 +234,7 @@ public static TempLocationManager getInstance() {
200234 static TempLocationManager getInstance (boolean waitForCleanup ) {
201235 TempLocationManager instance = SingletonHolder .INSTANCE ;
202236 if (waitForCleanup ) {
203- try {
204- instance .waitForCleanup (5 , TimeUnit .SECONDS );
205- } catch (TimeoutException ignored ) {
206-
207- }
237+ instance .waitForCleanup (5 , TimeUnit .SECONDS );
208238 }
209239 return instance ;
210240 }
@@ -214,10 +244,11 @@ private TempLocationManager() {
214244 }
215245
216246 TempLocationManager (ConfigProvider configProvider ) {
217- this (configProvider , CleanupHook .EMPTY );
247+ this (configProvider , true , CleanupHook .EMPTY );
218248 }
219249
220- TempLocationManager (ConfigProvider configProvider , CleanupHook testHook ) {
250+ TempLocationManager (
251+ ConfigProvider configProvider , boolean runStartupCleanup , CleanupHook testHook ) {
221252 cleanupTestHook = testHook ;
222253
223254 // In order to avoid racy attempts to clean up files which are currently being processed in a
@@ -255,21 +286,21 @@ private TempLocationManager() {
255286 baseTempDir = configuredTempDir .resolve ("ddprof" );
256287 baseTempDir .toFile ().deleteOnExit ();
257288
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+ }
260294
261295 Thread selfCleanup =
262296 new Thread (
263297 () -> {
264- try {
265- waitForCleanup (1 , TimeUnit .SECONDS );
266- } catch (TimeoutException e ) {
298+ if (!waitForCleanup (1 , TimeUnit .SECONDS )) {
267299 log .info (
268300 "Cleanup task timed out. {} temp directory might not have been cleaned up properly" ,
269301 tempDir );
270- } finally {
271- cleanup (true );
272302 }
303+ cleanup (true );
273304 },
274305 "Temp Location Manager Cleanup" );
275306 Runtime .getRuntime ().addShutdownHook (selfCleanup );
@@ -344,6 +375,19 @@ boolean cleanup(boolean cleanSelf) {
344375 */
345376 boolean cleanup (boolean cleanSelf , long timeout , TimeUnit unit ) {
346377 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+ }
347391 cleanupTestHook .onCleanupStart (cleanSelf , timeout , unit );
348392 CleanupVisitor visitor = new CleanupVisitor (cleanSelf , timeout , unit );
349393 Files .walkFileTree (baseTempDir , visitor );
@@ -359,21 +403,24 @@ boolean cleanup(boolean cleanSelf, long timeout, TimeUnit unit) {
359403 }
360404
361405 // accessible for tests
362- void waitForCleanup (long timeout , TimeUnit unit ) throws TimeoutException {
406+ boolean waitForCleanup (long timeout , TimeUnit unit ) {
363407 try {
364- cleanupTask .get (timeout , unit );
408+ return cleanupTask .await (timeout , unit );
365409 } catch (InterruptedException e ) {
366- cleanupTask . cancel ( true );
410+ log . debug ( "Temp directory cleanup was interrupted" );
367411 Thread .currentThread ().interrupt ();
368- } catch (TimeoutException e ) {
369- cleanupTask .cancel (true );
370- throw e ;
371- } catch (ExecutionException e ) {
412+ } catch (Throwable t ) {
372413 if (log .isDebugEnabled ()) {
373- log .debug ("Failed to cleanup temp directory: {}" , tempDir , e );
414+ log .debug ("Failed to cleanup temp directory: {}" , tempDir , t );
374415 } else {
375416 log .debug ("Failed to cleanup temp directory: {}" , tempDir );
376417 }
377418 }
419+ return false ;
420+ }
421+
422+ // accessible for tests
423+ void createDirStructure () throws IOException {
424+ Files .createDirectories (baseTempDir );
378425 }
379426}
0 commit comments