/* f4write.c (c)Copyright Sequiter Software Inc., 1988-1996. All rights reserved. */ #include "d4all.h" #ifdef __TURBOC__ #pragma hdrstop #endif #ifdef S4TEMP #include "t4test.h" #endif #ifdef S4WINTEL #ifndef S4IBMOS2 #ifndef __TURBOC__ #include #define S4LOCKING #endif #ifdef _MSC_VER #include #include #endif #endif #ifndef S4OFF_OPTIMIZE #ifdef E4ANALYZE_ALL #include #include #include #endif #endif /* #include */ /* #include */ #endif /*#include */ /* returns the length written */ static unsigned file4writeLowDo( FILE4 *f4, const long pos, const void *ptr, const unsigned len ) { unsigned int urc ; /* */ /* */ /* */ #ifdef S4MULTI_THREAD EnterCriticalSection( &f4->critical4file ) ; #endif #ifdef S4WIN32 if ( SetFilePointer( (HANDLE)f4->hand, pos, NULL, FILE_BEGIN ) == (DWORD)-1 ) #else /* */ /* */ /* */ #ifdef S4WINDOWS if ( _llseek( f4->hand, pos, 0 ) != pos ) #else #ifdef S4LSEEK if ( f4lseek( f4->hand, pos, 0, 1 ) != pos ) #else if ( lseek( f4->hand, pos, 0 ) != pos ) #endif #endif /* */ #endif { #ifdef S4MULTI_THREAD LeaveCriticalSection( &f4->critical4file ) ; #endif return error4describe( f4->codeBase, e4write, E90615, f4->name, 0, 0 ) ; } #ifdef S4WIN32 WriteFile( (HANDLE)f4->hand, ptr, len, (unsigned long *)&urc, NULL ) ; #else /* */ /* */ /* */ /* */ /* */ /* */ #ifdef S4WINDOWS urc = (unsigned)_lwrite( f4->hand, (char *) ptr, len ) ; #else urc = (unsigned)write( f4->hand, ptr, len ) ; #endif /* */ #endif #ifdef S4MULTI_THREAD LeaveCriticalSection( &f4->critical4file ) ; #endif return urc ; } #ifdef P4ARGS_USED #pragma argsused #endif int file4writeLow( FILE4 *f4, const long pos, const void *ptr, const unsigned len, const int checkDelayList, const int checkAdvanceList ) { unsigned urc ; CODE4 *c4 ; #ifdef S4WRITE_DELAY FILE4WRITE_DELAY *writeDelay ; LINK4 *delayLink ; #endif c4 = f4->codeBase ; if ( f4->isReadOnly ) return error4( c4, e4write, E80606 ) ; #ifdef S4ADVANCE_READ /* take care of over-writing advance-read buffers where appropriate */ if ( c4->advanceReadsEnabled ) file4advanceReadWriteOver( f4, pos, len, ptr, 1 ) ; #endif #ifdef S4MULTI_THREAD EnterCriticalSection( &f4->critical4file ) ; #endif #ifdef S4WRITE_DELAY if ( checkDelayList ) /* check to see if write request covers delayed areas */ if ( l4numNodes( &f4->delayWriteFileList ) != 0 ) { for ( delayLink = (LINK4 *)l4first( &f4->delayWriteFileList ) ;; ) { if ( delayLink == 0 ) break ; writeDelay = (FILE4WRITE_DELAY *)(delayLink - 1 ) ; delayLink = (LINK4 *)l4next( &f4->delayWriteFileList, delayLink ) ; if ( pos >= ( writeDelay->pos + (long)writeDelay->len ) ) /* outside of block */ continue ; if ( ( pos + (long)len ) <= writeDelay->pos ) /* outside of block */ continue ; /* now, if the delay piece belongs in the buffer, then read all the info before the delay piece, copy the delay piece over, and read all the info after the delay piece */ while ( writeDelay->usageFlag == r4inUse ) /* is being written to disk, just wait until it is done... */ Sleep( 0 ) ; if ( writeDelay->usageFlag == r4finished ) /* is written to disk, so can just ignore */ continue ; /* if the entire block is within the range, then can just delete it, otherwise must write it now (note that, due to constant contents of the delay-write buffer, cannot copy into it directly */ if ( ( writeDelay->pos >= pos ) && ( pos + len >= writeDelay->pos + writeDelay->len ) ) /* remove */ writeDelay->status = 0 ; else /* otherwise must write the block now, and then continue */ writeDelay->status = file4writeLow( writeDelay->file, writeDelay->pos, writeDelay->data, writeDelay->len, 0, 1 ) ; writeDelay->usageFlag = r4finished ; /* outside of critical section, to allow a wait for completion while keeping the critical section */ l4remove( &c4->delayWriteList, writeDelay ) ; l4remove( &f4->delayWriteFileList, &writeDelay->fileLink ) ; writeDelay->completionRoutine( writeDelay ) ; mem4free( c4->delayWriteMemory, writeDelay ) ; } } #endif urc = file4writeLowDo( f4, pos, ptr, len ) ; #ifdef S4MULTI_THREAD LeaveCriticalSection( &f4->critical4file ) ; #endif if ( urc != len ) return error4describe( c4, e4write, E90615, f4->name, 0, 0 ) ; #ifndef S4OFF_MULTI if ( f4->codeBase->fileFlush != 0 ) file4flush( f4 ) ; #endif return 0 ; } #ifndef S4OFF_OPTIMIZE #ifdef P4ARGS_USED #pragma argsused #endif #ifdef S4WRITE_DELAY int file4writeOpt( FILE4 *f4, const long pos, const void *ptr, const unsigned len, int doDelay, S4DELAY_FUNCTION *completionRoutine, void *completionData ) #else int file4writeOpt( FILE4 *f4, const long pos, const void *ptr, const unsigned len, int doDelay, void *completionRoutine, void *completionData ) #endif { int rc ; CODE4 *c4 ; #ifndef S4OFF_OPTIMIZE unsigned urc ; #ifdef E4ANALYZE_ALL char buf[512] ; long bufWritePos, bufWriteLen ; #endif #ifdef S4OPTIMIZE_STATS DATA4 *stat ; #endif #endif c4 = f4->codeBase ; rc = 0 ; if ( f4->doBuffer ) { #ifdef S4OPTIMIZE_STATS stat = c4->statusDbf ; if ( stat != 0 ) /* track stats */ { if ( f4 != &stat->dataFile->file ) /* don't do for the stat file! */ { if ( d4appendStart( stat, 0 ) == 0 ) { f4assignChar( c4->typeFld, 'W' ) ; /* high-level */ f4assign( c4->fileNameFld, f4->name ) ; f4assignLong( c4->offsetFld, pos ) ; f4assignLong( c4->lengthFld, len ) ; d4append( stat ) ; } } } #endif urc = (unsigned int)opt4fileWrite( f4, pos, len, ptr, f4->bufferWrites ) ; if ( urc != len ) return error4describe( c4, e4write, E90619, f4->name, 0, 0 ) ; } if ( f4->doBuffer == 0 || f4->writeBuffer == 0 || f4->bufferWrites == 0 ) { if ( f4->fileCreated == 0 ) { c4->opt.forceCurrent = 1 ; #ifdef S4CB51 file4temp( f4, c4, (char *)f4->name, 1 ); #else file4tempLow( f4, c4, 1 ) ; #endif c4->opt.forceCurrent = 0 ; } #ifdef E4ANALYZE_ALL if ( f4->hasDup == 1 ) { if ( file4len( f4 ) < pos ) { /* need to null out any extra file contents in order for file verification for optimization to work properly */ memset( buf, 0, sizeof( buf ) ) ; while ( file4len( f4 ) < pos ) { bufWritePos = file4len( f4 ) ; bufWriteLen = (pos - bufWritePos) ; if ( bufWriteLen > (long)sizeof( buf ) ) bufWriteLen = (long)sizeof( buf ) ; if ( file4write( f4, bufWritePos, buf, (int)bufWriteLen ) != 0 ) break ; } } } #endif /* E4ANALYZE_ALL */ #ifdef S4OPTIMIZE_STATS stat = c4->statusDbf ; if ( stat != 0 ) /* track stats */ { if ( f4 != &stat->dataFile->file ) /* don't do for the stat file! */ { if ( d4appendStart( stat, 0 ) == 0 ) { f4assignChar( c4->typeFld, 'X' ) ; /* low-level */ f4assign( c4->fileNameFld, f4->name ) ; f4assignLong( c4->offsetFld, pos ) ; f4assignLong( c4->lengthFld, len ) ; d4append( stat ) ; } } } #endif #ifdef S4WRITE_DELAY if ( doDelay == 1 ) rc = file4writeDelay( f4, pos, ptr, len, completionRoutine, completionData ) ; else #endif rc = file4writeLow( f4, pos, ptr, len, 1, 1 ) ; } #ifdef E4ANALYZE_ALL if ( f4->hasDup == 1 ) if ( f4->doBuffer == 1 || f4->link.n == 0 ) if ( file4writePart( ptr, f4, pos, urc ) != 0 ) return error4( c4, e4opt, E80602 ) ; #endif return rc ; } #endif /* S4OFF_OPTIMIZE */ int S4FUNCTION file4write( FILE4 *f4, const long pos, const void *ptr, const unsigned len ) { CODE4 *c4 ; #ifdef E4PARM_HIGH if ( f4 == 0 ) return error4( 0, e4parm_null, E90619 ) ; if ( pos < 0 || ( ptr == 0 && len ) ) return error4( f4->codeBase, e4parm, E90619 ) ; if ( f4->hand < 0 ) return error4( f4->codeBase, e4parm, E90619 ) ; #endif c4 = f4->codeBase ; if ( error4code( c4 ) < 0 ) return e4codeBase ; if ( f4->isReadOnly ) return error4( c4, e4write, E80606 ) ; #ifndef S4OFF_OPTIMIZE #ifdef S4ADVANCE_READ /* take care of over-writing advance-read buffers where appropriate */ if ( c4->advanceReadsEnabled ) file4advanceReadWriteOver( f4, pos, len, ptr, 1 ) ; #endif return file4writeOpt( f4, pos, ptr, len, 0, 0, 0 ) ; #else /* file4writeLow() itself takes care of the advance-read condition... */ return file4writeLow( f4, pos, ptr, len, 1, 0 ) ; #endif } #ifdef S4WRITE_DELAY /* f4writed.c (c)Copyright Sequiter Software Inc., 1988-1996. All rights reserved. */ #define MEM4DELAY_START 10 #define MEM4DELAY_EXPAND 10 int file4writeDelay( FILE4 *f4, const long pos, const void *data, const unsigned int len, S4DELAY_FUNCTION *completionRoutine, void *completionData ) { FILE4WRITE_DELAY *writeDelay ; LINK4 *delayLink ; CODE4 *c4 ; int rc ; c4 = f4->codeBase ; /* first check to see if the current delay-write over-rides an existing one */ if ( l4numNodes( &f4->delayWriteFileList ) != 0 ) { EnterCriticalSection( &c4->critical4delayWriteList ) ; for ( delayLink = (LINK4 *)l4first( &f4->delayWriteFileList ) ;; ) { if ( delayLink == 0 ) break ; writeDelay = (FILE4WRITE_DELAY *)(delayLink - 1 ) ; delayLink = (LINK4 *)l4next( &f4->delayWriteFileList, delayLink ) ; if ( pos >= ( writeDelay->pos + (long)writeDelay->len ) ) /* outside of block */ continue ; if ( ( pos + (long)len ) <= writeDelay->pos ) /* outside of block */ continue ; while ( writeDelay->usageFlag == r4inUse ) /* is being written to disk, just wait until it is done... */ Sleep( 0 ) ; if ( writeDelay->usageFlag == r4finished ) /* is written to disk, so can just ignore */ continue ; /* we have a delay-write which overlaps the current request */ /* if the entire block is within the range, then can just delete it, otherwise must write it now (note that, due to constant contents of the delay-write buffer, cannot copy into it directly */ if ( ( writeDelay->pos >= pos ) && ( pos + len >= writeDelay->pos + writeDelay->len ) ) /* remove */ writeDelay->status = 0 ; else /* otherwise must write the block now, and then continue */ writeDelay->status = file4writeLow( writeDelay->file, writeDelay->pos, writeDelay->data, writeDelay->len, 0, 1 ) ; writeDelay->usageFlag = r4finished ; /* outside of critical section, to allow a wait for completion while keeping the critical section */ l4remove( &f4->codeBase->delayWriteList, writeDelay ) ; l4remove( &f4->delayWriteFileList, &writeDelay->fileLink ) ; writeDelay->completionRoutine( writeDelay ) ; mem4free( c4->delayWriteMemory, writeDelay ) ; } LeaveCriticalSection( &c4->critical4delayWriteList ) ; } if ( c4->delayWriteMemory == 0 ) writeDelay = (FILE4WRITE_DELAY *)mem4createAlloc( c4, &c4->delayWriteMemory, MEM4DELAY_START, sizeof( FILE4WRITE_DELAY ), MEM4DELAY_EXPAND, 0 ) ; else writeDelay = (FILE4WRITE_DELAY *)mem4alloc( c4->delayWriteMemory ) ; if ( writeDelay == 0 ) return error4( c4, e4memory, E90624 ) ; writeDelay->file = f4 ; writeDelay->data = (const char *)data ; writeDelay->len = len ; writeDelay->pos = pos ; writeDelay->usageFlag = r4queued ; writeDelay->completionRoutine = completionRoutine ; writeDelay->completionData = completionData ; if ( c4->delayWritesEnabled == 0 ) /* delay-writes not enabled, so just write */ { rc = file4writeLow( f4, pos, data, len, 1, 1 ) ; completionRoutine( writeDelay ) ; return rc ; } EnterCriticalSection( &c4->critical4delayWriteList ) ; l4add( &c4->delayWriteList, writeDelay ) ; l4add( &f4->delayWriteFileList, &writeDelay->fileLink ) ; LeaveCriticalSection( &c4->critical4delayWriteList ) ; SetEvent( c4->pendingWriteEvent ) ; /* notify the write thread */ Sleep( 0 ) ; return 0 ; } /* flush out any delayed-writes for the file in question */ /* if doWrite is false, then the blocks are just dumped (eg. temp. files) */ int file4writeDelayFlush( FILE4 *file, const int doWrite ) { FILE4WRITE_DELAY *writeDelay ; LINK4 *writeDelayLink, *saved ; CODE4 *c4 ; c4 = file->codeBase ; /* by obtaining the critical4delayWriteList critical section, we can guarantee that the other thread will be suspended. Therefore the flushes for this file will get high-priority treatment, which is what is desired */ EnterCriticalSection( &c4->critical4delayWriteList ) ; /* go through the list and manually flush each one belonging to our file */ for ( writeDelayLink = (LINK4 *)l4first( &file->delayWriteFileList ) ;; ) { if ( writeDelayLink == 0 ) break ; writeDelay = (FILE4WRITE_DELAY *)(writeDelayLink - 1) ; saved = (LINK4 *)l4next( &file->delayWriteFileList, writeDelayLink ) ; if ( writeDelay->usageFlag == r4queued ) /* do ourselves */ { l4remove( &file->delayWriteFileList, writeDelayLink ) ; l4remove( &c4->delayWriteList, writeDelay ) ; if ( doWrite == 1 ) writeDelay->status = file4writeLow( writeDelay->file, writeDelay->pos, writeDelay->data, writeDelay->len, 0, 1 ) ; else writeDelay->status = 0 ; writeDelay->usageFlag = r4finished ; writeDelay->completionRoutine( writeDelay ) ; mem4free( c4->delayWriteMemory, writeDelay ) ; } writeDelayLink = saved ; } LeaveCriticalSection( &c4->critical4delayWriteList ) ; for ( ;; ) { /* now verify that the checkInUse write gets completed */ if ( l4numNodes( &file->delayWriteFileList ) == 0 ) break ; #ifdef E4ANALYZE if ( l4numNodes( &file->delayWriteFileList ) > 1 ) /* in theory impossible, it means delay-write has 2 files writing at same time */ return error4( c4, e4struct, E90624 ) ; #endif SetEvent( c4->pendingWriteEvent ) ; /* notify the write thread */ Sleep( 0 ) ; /* give up our time slice to get the delay-write going */ } return 0 ; } /* main function for delay-write thread (i.e. paramater to CreateThread()) */ #ifdef S4USE_INT_DELAY int file4writeDelayMain( void *data ) #else void file4writeDelayMain( void *data ) #endif { CODE4 *c4 ; FILE4WRITE_DELAY *writeDelay ; c4 = (CODE4 *)data ; c4->delayWritesEnabled = 1 ; for ( ;; ) { if ( l4numNodes( &c4->delayWriteList ) == 0 ) { if ( c4->uninitializeDelayWrite == 1 ) /* shutdown */ { SetEvent( c4->initUndoDelayWrite ) ; #ifdef S4USE_INT_DELAY return 0 ; #else return ; #endif } else { WaitForSingleObject( c4->pendingWriteEvent, INFINITE ) ; ResetEvent( c4->pendingWriteEvent ) ; } } else /* perform a write on the first available block */ { EnterCriticalSection( &c4->critical4delayWriteList ) ; writeDelay = (FILE4WRITE_DELAY *)l4first( &c4->delayWriteList ) ; if ( writeDelay == 0 ) /* maybe got removed by main thread, so none to write... */ { LeaveCriticalSection( &c4->critical4delayWriteList ) ; Sleep( 0 ) ; continue ; } writeDelay->usageFlag = r4inUse ; LeaveCriticalSection( &c4->critical4delayWriteList ) ; writeDelay->status = file4writeLow( writeDelay->file, writeDelay->pos, writeDelay->data, writeDelay->len, 0, 1 ) ; writeDelay->usageFlag = r4finished ; /* outside of critical section, to allow a wait for completion while keeping the critical section */ EnterCriticalSection( &c4->critical4delayWriteList ) ; l4remove( &c4->delayWriteList, writeDelay ) ; l4remove( &writeDelay->file->delayWriteFileList, &writeDelay->fileLink ) ; LeaveCriticalSection( &c4->critical4delayWriteList ) ; writeDelay->completionRoutine( writeDelay ) ; mem4free( c4->delayWriteMemory, writeDelay ) ; } } } #endif /* S4WRITE_DELAY */