Thread: Threads, Files and a Test

  1. #1
    the hat of redundancy hat nvoigt's Avatar
    Join Date
    Aug 2001
    Location
    Hannover, Germany
    Posts
    3,130

    Threads, Files and a Test

    Deja Vu. I think I did this at least once before, but I'll simply do it again *g* We have a client server system that has problems writing/reading files.

    I wrote a program that does the same file handling routine, without the processing stuff. Surprise, still the same errors. The errors are "Invalid parameter" or "Permission denied" and happen at random. Running the application on a local storage is fine. I had it running on a harddisk for 5 continuous days without a problem. Errors occur on networked file systems ( windows shares, storage solutions ) at least once a day.

    ( On a sidenote, I just thought of simply taking this application home and running it on some of my boxes. The network here is a very likely candidate. Anyway: )

    From my point of view, this means it's either our network, or the storage device. Neither is my responsibility, so I will give the source to the network tech and the storage device support. However, I don't like to give them faulty code, so if you see anything that might lead to errors, please point me to it.

    There is little comment and little consideration to design. This is purely for testing purposes and while I'm a big fan of coding standards and ANSI compatibility, I'm mostly interested in errors that might occur in *my* environment ( VC6, Windows XP ), not if it might throw errors in a hypothetical other environment

    Code:
    #include <stdio.h>
    #include <windows.h>
    #include <process.h>
    
    
    #ifndef _MT
    #error "Must link with /MT(d)
    #endif
    
    
    volatile int		g_nThreadsRunning	= 0;
    const char*			g_lpszPath			= NULL;
    CRITICAL_SECTION	g_CriticalSection;	
    
    #define RECORD_COUNT			(1024 * 100)
    #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 )
    unsigned int __stdcall ThreadFunction( void* pThreadnumber )
    {
    	char szFileInput[ PATH_SIZE ];
    	char szFileOutput[ PATH_SIZE ];
    	char szBuffer[ RECORD_SIZE ];
    	int  nBlock;
    
    	ZeroMemory( szBuffer, RECORD_SIZE );
    
    	// zero data to be written
    
    	sprintf( szFileInput,  "%s\\%d.a", g_lpszPath, (int)pThreadnumber );
    	sprintf( szFileOutput, "%s\\%d.b", g_lpszPath, (int)pThreadnumber );
    
    	printf( "%d: %s -> %s.\n", (int)pThreadnumber, szFileInput, szFileOutput );
    
    	FILE* fpInput  = NULL;
    	FILE* fpOutput = NULL;
    
    	fpInput = fopen( szFileInput, "w" );
    
    	if( fpInput == NULL )
    	{
    		printf( "Thread %d: error opening input file for initial writing: \n%s\n%s", (int)pThreadnumber, strerror( errno ),strerror( _doserrno ) );
    		exit( 0 );
    	}
    
    	for( nBlock = 0 ; nBlock < RECORD_COUNT ; nBlock++ )
    	{
    		if( fwrite( szBuffer, RECORD_SIZE, 1, fpInput ) != 1 )
    		{
    			printf( "Thread %d: error writing initial input file: \n%s\n%s", (int)pThreadnumber, strerror( errno ),strerror( _doserrno ) );
    			exit( 0 );
    		}
    
    		if( fflush( fpInput ) != 0 )
    		{
    			printf( "Thread %d: error flushing input file: \n%s\n%s", (int)pThreadnumber, strerror( errno ),strerror( _doserrno ) );
    			exit( 0 );
    		}
    	}
    
    	fclose( fpInput );
    
    	fpInput = fopen( szFileInput, "r" );
    
    	if( fpInput == NULL )
    	{
    		printf( "Thread %d: error opening input file for reading: \n%s\n%s", (int)pThreadnumber, strerror( errno ),strerror( _doserrno ) );
    		exit( 0 );
    	}
    
    	fpOutput = fopen( szFileOutput, "w" );
    
    	if( fpOutput == NULL )
    	{
    		printf( "Thread %d: error opening output file for writing: \n%s\n%s", (int)pThreadnumber, strerror( errno ),strerror( _doserrno ) );
    		exit( 0 );
    	}
    
    	for( nBlock = 0 ; nBlock < RECORD_COUNT ; nBlock++ )
    	{
    		if( fread( szBuffer, RECORD_SIZE, 1, fpInput ) != 1 )
    		{
    			printf( "Thread %d: error reading input file: \n%s\n%s", (int)pThreadnumber, strerror( errno ),strerror( _doserrno ) );
    			exit( 0 );
    		}
    
    		if( fwrite( szBuffer, RECORD_SIZE, 1, fpOutput ) != 1 )
    		{
    			printf( "Thread %d: error writing output file: \n%s\n%s", (int)pThreadnumber, strerror( errno ),strerror( _doserrno ) );
    			exit( 0 );
    		}
    
    		if( fflush( fpOutput ) != 0 )
    		{
    			printf( "Thread %d: error flushing output file: \n%s\n%s", (int)pThreadnumber, strerror( errno ),strerror( _doserrno ) );
    			exit( 0 );
    		}
    	}
    
    	fclose( fpInput );
    	fclose( fpOutput );
    
    	if( remove( szFileInput ) != 0 )
    	{
    		printf( "Thread %d: error removing input file: \n%s\n%s", (int)pThreadnumber, strerror( errno ),strerror( _doserrno ) );
    		exit( 0 );
    	}
    
    	if( rename( szFileOutput, szFileInput ) != 0 )
    	{
    		printf( "Thread %d: error renaming files: \n%s\n%s", (int)pThreadnumber, strerror( errno ),strerror( _doserrno ) );
    		exit( 0 );
    	}
    
    	if( remove( szFileInput ) != 0 )
    	{
    		printf( "Thread %d: error removing renamed file: \n%s\n%s", (int)pThreadnumber, strerror( errno ),strerror( _doserrno ) );
    		exit( 0 );
    	}
    			
    	EnterCriticalSection( &g_CriticalSection );
    
    	g_nThreadsRunning--;
    
    	LeaveCriticalSection( &g_CriticalSection );
    
    	_endthreadex( 0 );
    
    	return 0;
    }
    
    
    int main( int argc, char* argv[] )
    {
    	// Initialize the critical section one time only.
    	InitializeCriticalSection( &g_CriticalSection ); 
    	
    	if( argc != 2 )
    	{
    		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 )
    		{
    			unsigned int dwThreadID;
    			
    			g_nThreadsRunning++;
    			nCount++;
    			
    			_beginthreadex( NULL, 0, ThreadFunction, (void*)nCount, 0, &dwThreadID ); 
    		}
    
    		LeaveCriticalSection( &g_CriticalSection );
    
    		Sleep( 100 );
    	}
    
    	// Release resources used by the critical section object.
    	DeleteCriticalSection( &g_CriticalSection );
    	
    	return 0;
    }
    Grill me
    hth
    -nv

    She was so Blonde, she spent 20 minutes looking at the orange juice can because it said "Concentrate."

    When in doubt, read the FAQ.
    Then ask a smart question.

  2. #2
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    Maybe it will be third time lucky!

    You need to call CloseHandle on the return value of _beginthreadex.

    There is a strictly theoretical issue, if nCount overflowed, where you could have two threads try to work on the same number.

  3. #3
    the hat of redundancy hat nvoigt's Avatar
    Join Date
    Aug 2001
    Location
    Hannover, Germany
    Posts
    3,130
    Right, I'll look into the thread handles. Maybe I'll use the normal ( non-Ex ) versions of the thread functions, they close the handle automatically when the thread ends.

    The nCount overflow is theoretical. The application will stop with an error at a count of 50-200, but thanks for pointing it out
    hth
    -nv

    She was so Blonde, she spent 20 minutes looking at the orange juice can because it said "Concentrate."

    When in doubt, read the FAQ.
    Then ask a smart question.

Popular pages Recent additions subscribe to a feed