af15e0698b
git-svn-id: svn://10.65.10.50/trunk@4679 c028cbd2-c16b-5b4b-a496-9718f37d4682
553 lines
18 KiB
C
Executable File
553 lines
18 KiB
C
Executable File
/* 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 <sys\locking.h>
|
|
#define S4LOCKING
|
|
#endif
|
|
#ifdef _MSC_VER
|
|
#include <sys\types.h>
|
|
#include <sys\locking.h>
|
|
#endif
|
|
#endif
|
|
#ifndef S4OFF_OPTIMIZE
|
|
#ifdef E4ANALYZE_ALL
|
|
#include <sys\stat.h>
|
|
#include <share.h>
|
|
#include <fcntl.h>
|
|
#endif
|
|
#endif
|
|
|
|
/* #include <sys\stat.h>*/
|
|
/* #include <share.h>*/
|
|
#endif
|
|
|
|
/*#include <fcntl.h>*/
|
|
|
|
/* 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 */
|