"Robert G. Hoover" <
XXXX@XXXXX.COM >wrote in message
Quote
Hi,
What's the best way to guard against one program writing to a file when
another
program is reading the same file?
In my app, I use TStringList::LoadFromFile() to read in the contents of a
particular file. I manipulate the contents to my liking and then save the
file
back to the disk using TStringList::SaveToFile(). In another app, I also
read in
the contents of the same file, append additional contents to it, and then
save
the amended file to the disk under the same name. I need to make sure that
no
additions are lost because it happens at just the wrong time after my
first app
reads the file but before my first app saves the revised file. I'm pretty
sure I
can use a marker file, but I was hoping for a more elegant method.
Reading and appending are two separate tasks, and the time between their
invocation is the time during which you don't want to allow reading by your
other app. So, instead of using two separate tasks, you effectively need
one task -- one that will prevent all access to the file beginning at the
time it is opened, and ending only after it has been closed.
Remy suggested that you have the applications communicate with each other to
coordinate access. One way you could do that (as you've already hinted by
your subject line) is to use a named semaphore.
Another way to accomplish the same task without a semaphore would be to let
the file system do the work for you, if you don't mind doing a little
polling. For your read/append process, instead of using TStringList's
ReadFromFile and SaveToFile methods (which is two tasks, separated by some
period of time, during which your other process could lock the file), use a
TFileStream to open and close the file with the appropriate access, and do
your reading and appending with the TFileStream:
1) Open the file with exclusive access (TFileStream *fs = new TFileStream(
filename, fmOpenReadWrite | fmShareExclusive ) )
1a) If the file cannot be opened, the reading process is using it (see
blow). Sleep for a short time (50ms?) and try again until access is
granted.
2) Read the file into the stream (TFileStream::ReadBuffer())
3) Load your stringlist from the stream (TStringList::LoadFromStream())
4) Manipulate your stringlist as necessary
5) Write the stringlist back to the stream (TStringList::SaveToStream())
6) Save the file back to disk (TFileStream::WriteBuffer())
7) Close the file (delete fs)
Then, in your reading process, try to open the file. If it fails to open
(an exception will be thrown), sleep for a short time (say 50ms), then try
again, repeating until the file can be opened (the following snippet is
copied from one of my apps):
while ( true )
{
try
{
pFileStream.reset( new TFileStream( FileName, fmOpenRead |
fmShareExclusive ) );
// if the file can be opened for exclusive access, the writer has
// finished writing to it and closed it; break out of the while
loop
// and allow processing to begin
break;
}
catch(...)
{
// exception will be thrown if the file could not be opened for
// exclusive access; wait a bit and try again
::Sleep( 50 );
}
}
Hope this helps.
- Dennis
{smallsort}