United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-4742723 File.mkdirs() fails due to race condition
JDK-4742723 : File.mkdirs() fails due to race condition

Details
Type:
Bug
Submit Date:
2002-09-06
Status:
Closed
Updated Date:
2007-10-05
Project Name:
JDK
Resolved Date:
2011-05-18
Component:
core-libs
OS:
generic,windows_2000
Sub-Component:
java.io
CPU:
x86,generic
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.3.0,1.4.1,5.0u11,6
Fixed Versions:

Related Reports
Backport:
Duplicate:
Duplicate:
Relates:

Sub Tasks

Description
Name: nt126004			Date: 09/05/2002


FULL PRODUCT VERSION :
java version "1.4.1-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-rc-b19)
Java HotSpot(TM) Client VM (build 1.4.1-rc-b19, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

ADDITIONAL OPERATING SYSTEMS :
All others probably



A DESCRIPTION OF THE PROBLEM :
File.mkdirs fails to create the entire hierarchy of
directories if another application or thread creates
one of the intervening directories while mkdirs is
running.

An example: We have two threads, A and B.
A calls new File("c:\base\a").mkdirs();
B calls new File("c:\base\b").mkdirs();

Pseudo-trace (follow along in File.mkdirs):
A: if (exists())     // c:\base\a doesn't exist
A: if (mkdir())      // fails to create c:\base\a
A: parent = getParent();  // c:\base
A: new File(parent).mkdirs()
B: if (exists())     // c:\base\b doesn't exist
B: if (mkdir())      // fails to create c:\base\b
B: parent = getParent();  // c:\base
B: new File(parent).mkdirs()
A: if (exists())     // c:\base doesn't exist
A: if (mkdir())      // creates c:\base
A: return true;
B: if (exists())     // c:\base does exist
B: return false;
A: mkdir()           // creates c:\base\a and returns true
B: // parent.mkdirs returned false so doesn't create
c:\base\b. Returns false.

I can see no reasonable excuse for thread B's mkdirs to
fail just because someone else happened to create one of
the directories in the desired path.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile and run the sample code
2. Check whether both directories were created

It's timing-dependant so it might not happen every time.



EXPECTED VERSUS ACTUAL BEHAVIOR :
Two directories, c:\base\a and c:\base\b, should be created.
Instead, just one of them gets created.


REPRODUCIBILITY :
This bug can be reproduced often.

---------- BEGIN SOURCE ----------
import java.io.File;

public class MkdirsBug {
   public static void main(String[] args)
   {
      Thread A = new Thread() {
         public void run()
         {
            new File("c:\\base\\a").mkdirs();
         }
      };
      Thread B = new Thread() {
         public void run()
         {
            new File("c:\\base\\b").mkdirs();
         }
      };

      A.start();
      B.start();
   }
}


---------- END SOURCE ----------

CUSTOMER WORKAROUND :
Write your own mkdirs.
(Review ID: 164083) 
======================================================================

                                    

Comments
SUGGESTED FIX

One of my fellow programmers reported that this bug was a serious problem for their commercial server and they eventually had to make a workaround.

This race condition can be fixed using the following change, which recovers the thread B after unsuccessful mkdir():

    public boolean mkdirs() {
	if (exists()) {
	    return false;
	}
	if (mkdir()) {
 	    return true;
 	}
        File canonFile = null;
        try {
            canonFile = getCanonicalFile();
        } catch (IOException e) {
            return false;
        }
<	String parent = canonFile.getParent();
<       return (parent != null) && 
<              (new File(parent, fs.prefixLength(parent)).mkdirs() &&
                                    canonFile.mkdir());
---
>       File parentFile = canonFile.getParentFile();
>       return (parentFile != null) &&
>              (parentFile.mkdirs() || parentFile.exists()) && 
                                    canonFile.mkdir();
    }
                                     
2006-10-11
EVALUATION

There is indeed a possible race condition in mkdirs() and the suggested fix seems
to make it go away in this particular situation. But the reality is that as long as
the mkdirs() is not an "atomic" operation it's impossible to "completely" fix this
bug at java lib level (for example, there is no way you can prevent another thread,
or other process, from deleting your parent dir after you successful created it, right
before you go into to invoke mkdir()...), the only thing we can do is to "improve the
situation" to reduce the race condition window, the suggested one is something worth
doing.
                                     
2007-02-08



Hardware and Software, Engineered to Work Together