@@ -71,6 +71,7 @@ public class BoxStore implements Closeable {
7171
7272 /** Currently used DB dirs with values from {@link #getCanonicalPath(File)}. */
7373 private static final Set <String > openFiles = new HashSet <>();
74+ private static volatile Thread openFilesCheckerThread ;
7475
7576 /**
7677 * Convenience singleton instance which gets set up using {@link BoxStoreBuilder#buildDefault()}.
@@ -260,7 +261,7 @@ static String getCanonicalPath(File directory) {
260261 }
261262 }
262263
263- private static void verifyNotAlreadyOpen (String canonicalPath ) {
264+ static void verifyNotAlreadyOpen (String canonicalPath ) {
264265 synchronized (openFiles ) {
265266 isFileOpen (canonicalPath ); // for retries
266267 if (!openFiles .add (canonicalPath )) {
@@ -271,15 +272,44 @@ private static void verifyNotAlreadyOpen(String canonicalPath) {
271272 }
272273
273274 /** Also retries up to 500ms to improve GC race condition situation. */
274- private static boolean isFileOpen (String canonicalPath ) {
275+ static boolean isFileOpen (final String canonicalPath ) {
276+ synchronized (openFiles ) {
277+ if (!openFiles .contains (canonicalPath )) return false ;
278+ }
279+ if (openFilesCheckerThread == null || !openFilesCheckerThread .isAlive ()) {
280+ // Use a thread to avoid finalizers that block us
281+ openFilesCheckerThread = new Thread () {
282+ @ Override
283+ public void run () {
284+ isFileOpenSync (canonicalPath , true );
285+ openFilesCheckerThread = null ; // Clean ref to itself
286+ }
287+ };
288+ openFilesCheckerThread .setDaemon (true );
289+ openFilesCheckerThread .start ();
290+ try {
291+ openFilesCheckerThread .join (500 );
292+ } catch (InterruptedException e ) {
293+ e .printStackTrace ();
294+ }
295+ } else {
296+ // Waiting for finalizers are blocking; only do that in the thread ^
297+ return isFileOpenSync (canonicalPath , false );
298+ }
299+ synchronized (openFiles ) {
300+ return openFiles .contains (canonicalPath );
301+ }
302+ }
303+
304+ static boolean isFileOpenSync (String canonicalPath , boolean runFinalization ) {
275305 synchronized (openFiles ) {
276306 int tries = 0 ;
277307 while (tries < 5 && openFiles .contains (canonicalPath )) {
278308 tries ++;
279309 System .gc ();
280- System .runFinalization ();
310+ if ( runFinalization && tries > 1 ) System .runFinalization ();
281311 System .gc ();
282- System .runFinalization ();
312+ if ( runFinalization && tries > 1 ) System .runFinalization ();
283313 try {
284314 openFiles .wait (100 );
285315 } catch (InterruptedException e ) {
@@ -513,7 +543,6 @@ public static boolean deleteAllFiles(File objectStoreDirectory) {
513543 * @return true if the directory 1) was deleted successfully OR 2) did not exist in the first place.
514544 * Note: If false is returned, any number of files may have been deleted before the failure happened.
515545 * @throws IllegalStateException if the given name is still used by a open {@link BoxStore}.
516- *
517546 */
518547 public static boolean deleteAllFiles (Object androidContext , @ Nullable String customDbNameOrNull ) {
519548 File dbDir = BoxStoreBuilder .getAndroidDbDir (androidContext , customDbNameOrNull );
0 commit comments