We have a client server application that is writing files to a mapped storage system. Sometimes renaming or removing a file failes, because despite fclose'ing it, the OS shows it as opened by that process.

I wrote a test tool that mimics our servers multithreaded filehandling behaviour. However, I probably have some stupid bug in it, because it fails even on my local harddisc frequently with either a "bad file descriptor" or "permission denied". I don't know how my own disc can find out I don't have permissions after the fact that I already opened and wrote almost half the file ( flushing after each record included ).

Anyway, here's the code. It's as ANSI as possible, threading is windows specific. I compiled it on VC6 and will try on VC7 today.

Run it with C:\Test or something as parameter ( make sure the folder exists ). Depending on the number of threads it will crash after about 10-50, 100-200 ( 3,2 threads ). With only one thread it did not yet fail, so maybe it's MT related.

PHP Code:
#include <stdio.h>
#include <windows.h>

volatile int        g_nThreadsRunning    0;
const 
char*            g_lpszPath            NULL;
CRITICAL_SECTION    g_CriticalSection;    

#define RECORD_COUNT            (1024 * 10)
#define RECORD_SIZE                (1024)
#define PATH_SIZE                (1024 + 1)
#define CONCURRENT_THREADS        3

// thread:
// open file a
// write to a
// close file a
// open a for reading
// open b for writing
// write a to b
// close a
// close b
// remove a
// rename b to a
// ( remove a to clean up )
DWORD WINAPI ThreadFunctionvoidpThreadnumber )
{
    
char szFileInputPATH_SIZE ];
    
char szFileOutputPATH_SIZE ];
    
char szBufferRECORD_SIZE ];
    
int  nBlock;

    
ZeroMemoryszBufferRECORD_SIZE );

    
// zero data to be written

    
sprintfszFileInput,  "%s\\%d.a"g_lpszPath, (int)pThreadnumber );
    
sprintfszFileOutput"%s\\%d.b"g_lpszPath, (int)pThreadnumber );

    
printf"%d: %s -> %s.\n", (int)pThreadnumberszFileInputszFileOutput );

    
FILEfpInput  NULL;
    
FILEfpOutput NULL;

    
fpInput fopenszFileInput"w" );

    if( 
fpInput == NULL )
    {
        
printf"Thread %d: error opening input file for initial writing: \n%s\n%s", (int)pThreadnumberstrerrorerrno ),strerror_doserrno ) );
        exit( 
);
    }

    for( 
nBlock nBlock RECORD_COUNT nBlock++ )
    {
        if( 
fwriteszBufferRECORD_SIZE1fpInput ) != )
        {
            
printf"Thread %d: error writing initial input file: \n%s\n%s", (int)pThreadnumberstrerrorerrno ),strerror_doserrno ) );
            exit( 
);
        }

        if( 
fflushfpInput ) != )
        {
            
printf"Thread %d: error flushing input file: \n%s\n%s", (int)pThreadnumberstrerrorerrno ),strerror_doserrno ) );
            exit( 
);
        }
    }

    
fclosefpInput );

    
fpInput fopenszFileInput"r" );

    if( 
fpInput == NULL )
    {
        
printf"Thread %d: error opening input file for reading: \n%s\n%s", (int)pThreadnumberstrerrorerrno ),strerror_doserrno ) );
        exit( 
);
    }

    
fpOutput fopenszFileOutput"w" );

    if( 
fpOutput == NULL )
    {
        
printf"Thread %d: error opening output file for writing: \n%s\n%s", (int)pThreadnumberstrerrorerrno ),strerror_doserrno ) );
        exit( 
);
    }

    for( 
nBlock nBlock RECORD_COUNT nBlock++ )
    {
        if( 
freadszBufferRECORD_SIZE1fpInput ) != )
        {
            
printf"Thread %d: error reading input file: \n%s\n%s", (int)pThreadnumberstrerrorerrno ),strerror_doserrno ) );
            exit( 
);
        }

        if( 
fwriteszBufferRECORD_SIZE1fpOutput ) != )
        {
            
printf"Thread %d: error writing output file: \n%s\n%s", (int)pThreadnumberstrerrorerrno ),strerror_doserrno ) );
            exit( 
);
        }

        if( 
fflushfpOutput ) != )
        {
            
printf"Thread %d: error flushing output file: \n%s\n%s", (int)pThreadnumberstrerrorerrno ),strerror_doserrno ) );
            exit( 
);
        }
    }

    
fclosefpInput );
    
fclosefpOutput );

    if( 
removeszFileInput ) != )
    {
        
printf"Thread %d: error removing input file: \n%s\n%s", (int)pThreadnumberstrerrorerrno ),strerror_doserrno ) );
        exit( 
);
    }

    if( 
renameszFileOutputszFileInput ) != )
    {
        
printf"Thread %d: error renaming files: \n%s\n%s", (int)pThreadnumberstrerrorerrno ),strerror_doserrno ) );
        exit( 
);
    }

    if( 
removeszFileInput ) != )
    {
        
printf"Thread %d: error removing renamed file: \n%s\n%s", (int)pThreadnumberstrerrorerrno ),strerror_doserrno ) );
        exit( 
);
    }
            
    
EnterCriticalSection( &g_CriticalSection );

    
g_nThreadsRunning--;

    
LeaveCriticalSection( &g_CriticalSection );

    return 
0;
}

int mainint argccharargv[] )
{
    
// Initialize the critical section one time only.
    
InitializeCriticalSection( &g_CriticalSection ); 
    
    if( 
argc != )
    {
        
printf"Usage: %s Path"argv[0] );
        return 
0;
    }

    
g_lpszPath argv[1];

    
int nCount 0;

    while( 
true )
    {
        
EnterCriticalSection( &g_CriticalSection );

        if( 
g_nThreadsRunning CONCURRENT_THREADS )
        {
            
DWORD dwThreadID;
            
            
g_nThreadsRunning++;
            
nCount++;
            
            
CreateThreadNULL0ThreadFunction, (void*)(nCount), 0, &dwThreadID );
        }

        
LeaveCriticalSection( &g_CriticalSection );

        
Sleep100 );
    }

    
// Release resources used by the critical section object.
    
DeleteCriticalSection( &g_CriticalSection );
    
    return 
0;