27

Directory c:\test has 50 or so files in it, no subdirectories.

    If IO.Directory.Exists("C:\test") Then
        IO.Directory.Delete("C:\test", True)
    End If

    IO.Directory.CreateDirectory("C:\test")

Drive C is Intel's X25-M80 SSD drive, OS is Windows 7 64 bit with TRIM support, Visual Studio is 2008 with target framework 3.5. When above code is executed, CreateDirectory breaks code execution without an (visible) exception. After much headache I found that Delete is not yet done by the time code execution gets to CreateDirectory. If I modify my code like this:

    If IO.Directory.Exists("C:\test") Then
        IO.Directory.Delete("C:\test", True)
    End If
    Threading.Thread.Sleep(2000)
    IO.Directory.CreateDirectory("C:\test")

then everything works as expected.

My questions beside the obvious WTF here are:

  • shouldn't IO.Directory.Delete be a blocking function call no matter what the drive is
  • is SSD "cheating" on delete due to enabled TRIM support?
Machavity
  • 30,841
  • 27
  • 92
  • 100
Vnuk
  • 2,673
  • 1
  • 32
  • 49
  • Out of curiosity, what happens if you test `If IO.Directory.Exists("C:\test")` again immediately after deleting the directory? – Sean Edwards May 16 '11 at 20:43
  • If I replace Thread.Sleep with Debug.WriteLine(IO.Directory.Exists("C:\temp") it returns False and CreateDirectory succeeds. If I remove this Exists check CreateDirectory fails as described. – Vnuk May 16 '11 at 20:48
  • Works fine on my X25-M160 and 60 GB OCZ Vertex Turbo. Can you show a call stack with both threads? – Ritch Melton May 16 '11 at 20:50
  • Sure sounds like 'something' is lazy-writing, could be kernel, controller, or SSD itself! – n8wrl May 16 '11 at 20:50
  • 1
    As a work-around, what if you rename test to test-deleting, delete it, and call create? – n8wrl May 16 '11 at 20:51
  • As to your first point, `Directory.Delete` does in fact block until Windows is done deleting. Unfortunately Windows is relying on the drive to say its done and the drive is cheating. – Chris Haas May 16 '11 at 20:52
  • You may have better luck with this on serverfault -- those guys are SSD gurus by now. – Brian Webster May 16 '11 at 20:52
  • @n8wrl, I was thinking the same thing. This would be the approach that I would recommend trying. – Chris Haas May 16 '11 at 20:52
  • @Ritch Melton - if I place a breakpoint on CreateDirectory("c:\test") then IO.Directory.Delete will finish it's job. How do you suggest I get a call stack? – Vnuk May 16 '11 at 20:54
  • @Chris: I recall something on Raymon Chen's blog that leads me to think assuming .Delete blocks is a bad assumption. Just can't find the link – n8wrl May 16 '11 at 20:54
  • @Vnuk With the call stack window for both threads? – Ritch Melton May 16 '11 at 20:55
  • Of course who knows if .Rename blocks? :) – n8wrl May 16 '11 at 20:56
  • @Ritch Melton in order to see call stack I need to stop execution at some point, right? – Vnuk May 16 '11 at 20:59
  • It seems like there might be some laziness optimization going on. `IO.Directory.Exists("C:\test")` causes the `CreateDirectory` to work fine. I wonder if it has something to do with the fact that `Directory.Exists` actually requests data from the drive (ie, forces a sort of lazy evaluation of filesystem update instructions), whereas `CreateDirectory` doesn't. – Sean Edwards May 16 '11 at 20:59
  • @n8wrl IO.Directory doesn't have Rename – Vnuk May 16 '11 at 21:00
  • @Vnuk - Usually breaking process execution will break all threads. – Ritch Melton May 16 '11 at 21:04
  • To get a stack trace, you might try calling `StackTrace.GetFrames()`. http://msdn.microsoft.com/en-us/library/system.diagnostics.stacktrace.getframes.aspx – Jim Mischel May 16 '11 at 21:10
  • @Ritch Melton breaking process execution will give Directory.Delete enough time to finish and whatever stack trace shows will not represent stack trace of the actual problem. – Vnuk May 16 '11 at 21:13
  • @Jim Mischel after adding debug.writeline for each of stackframe returned by StackTrace.GetFrames CreateDirectory succeeds. It looks like there is delay in nanoseconds here that makes a difference. – Vnuk May 16 '11 at 21:19

6 Answers6

17

I've had problems with this before, but this is not specific to SSD drives. You would be far better off doing a move then delete:

if(Directory.Exists(dirpath))
{
    string temppath = dirpath + ".deleted";
    Directory.Move(dirpath, temppath);
    Directory.Delete(temppath, true);
}
Directory.Create(dirpath);

The other way to deal with it is to loop until complete:

if(Directory.Exists(dirpath))
{
    Directory.Delete(dirpath, true);
    int limit = 100;
    while(Directory.Exists(dirpath) && limit-- > 0)
        Thread.Sleep(0);
}
Directory.Create(dirpath);
csharptest.net
  • 62,602
  • 11
  • 71
  • 89
  • You can't put the Delete inside the loop as it will throw since the directory is already scheduled for deletion by the OS. Well, I guess you could try/catch around it, but what would be the point of that? – csharptest.net May 17 '11 at 00:19
  • 1
    I accepted your answer because I was thinking along the lines of your second solution while posting my question. Base line is that IO.Directory.Delete cannot be trusted on its own – Vnuk May 17 '11 at 06:45
  • I can confirm that non SSD drives are affected as well. Thanks for this answer. – user12861 Mar 09 '12 at 14:08
  • i up voted the first piece of code as it works perfectly and solves all the problems. However, you should update your code to Directory.CreateDirectory to ensure it's accurate. – MaxOvrdrv Aug 07 '14 at 14:53
  • nice, i'm gonna borrow your move then delete – kite Mar 20 '15 at 02:26
  • this is actually a very good workaround, MoveFile (underneath) seems to be handling things differently – NSGaga-mostly-inactive Apr 03 '16 at 11:56
5

After exploring System.IO.Directory with reflector, it looks like .Delete is just a wrapper around the FindFirstFile, FindNextFile, and RemoveDirectory Win API calls. There's nothing threaded or asynchronous about the .Net runtime's invokation of those API calls, or the API implementation themselves.

Now, supposing its somehow a TRIM issue, you can disable TRIM by opening an elevated command prompt and using fsutil:

fsutil behavior set disabledeletenotify 1

To enable, run the same command with 0 as parameter.

To query, use query as the command argument:

fsutil behavior query disabledeletenotify 
Ritch Melton
  • 11,498
  • 4
  • 41
  • 54
2

Yes, it has nothing to do with SSD drive. I had same problem but only on a client laptop. I am using .NET 3.5. In my case, the directory had a single file. CreateDirectory seems to be executing first internally before Delete is finished.

This was a code that worked well for three years on many computers. Client changed to a new laptop and the code consistently fails for him. I can not reproduce the scenario on development/test machines with same configuration.

Bharat
  • 21
  • 2
1

I had the same problem. I ended up deleting only the contents of the directory @"C:\test" and copying the new files to the directory. That was my problem:

Using Directory.Delete() and Directory.CreateDirectory() to overwrite a folder

Community
  • 1
  • 1
A-Sharabiani
  • 17,750
  • 17
  • 113
  • 128
0

I suspect that you've found a race condition in NTFS that was previously not exposed since drives didn't exist that were fast enough to hit it. I don't think that TRIM has anything to do with it (though I reserve the right to be wrong!)

Either way, the proper way to handle this is to put the code in a Retry loop:

int retries = 3;
while(true) {
    try {
        doTheOperation();
        break;
    } catch (Exception ex) {
        retries--;
        if (retries == 0) {
            throw;
        }

        Thread.Sleep(100);
        continue;
    }        
}
Ana Betts
  • 73,868
  • 16
  • 141
  • 209
  • race condition...but doesn't that seem odd? The directory doesn't finish deleting before the next line is called. That implies that the operation slower and not faster, which may or may not be true for an SSD erase/garbage collection. – Ritch Melton May 16 '11 at 21:41
  • @Ritch NTFS is a very, very complicated piece of software and it does things you would've never even thought of, like Tunneling (http://blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx). It wouldn't surprise me if it just "told you it was done", but didn't completely finish its bookkeeping – Ana Betts May 16 '11 at 21:43
  • Sure, I understand that the user->drive system is a complicated stack, but I just find the speed argument....difficult. – Ritch Melton May 16 '11 at 21:50
  • @Ritch Well, at the end of the day, the argument of *why* it doesn't work is academic, unless you've got a lot of money to buy a Microsoft Premier contract to file a Hotfix Request; you'll have to add the delay :) A lot of NTFS behavior is strange because of AppCompat (i.e. fixing something would break apps, even though it's not always NTFS's fault), so we have to live with it, at least until the next major revision of Windows. I know that's a lame answer though... – Ana Betts May 16 '11 at 21:57
-1

If that's really the application code, note that you are really trying to delete the directory named "est"!

Escape your path to be "c:\test" or use the @ operator @"c:\test".

Andrew
  • 1,606
  • 1
  • 12
  • 19