C++/Read/Write to a file simultaneously from different users
Expert: Zlatko - 9/7/2010
QuestionHey Zlatko,
i am writting an application, where many users will try and
have access to the same file, at the same time, so that they
can read/write to it. Can i use, in some way, the xStream
family of functions in C++ to do so? I've also read about
the lockf and flock functions in C. Some even suggest the
use of an extra file which will be locked/unlocked each time
some user wants access to the data file.
Coould you give me some insight on the way multiple file
access works and what would be the best approach to me
problem? Do you think the problem would be best tackled with
the use of a database implementation such as SQLite?
thanx in advance.
AnswerHello Kostas. I think there is no problem with using the C++ iostream classes with shared files but the iostream classes don't have locking methods. You will need to open the file twice, once for locking purposes, and once through the iostream. Although the iostream uses a FILE* deep down, that is private data and you would not be able to access it. Also, depending on your operating system, a FILE* may not be so useful for locking. How the locking is done depends on the operating system you use.
On Windows, use the LockFile or LockFileEx functions. Windows will enforce the locking, meaning that the operating system will not allow access to the file region by processes not holding the lock. The Windows file locking will work even for shared drives. To use LockFile, you will need to get a file handle from the CreateFile function.
On UNIX systems, you can use flock, lockf, or fcntl. The lockf function is a POSIX function so it should be more portable across UNIX systems. On UNIX, locking is advisory, meaning that the operating system does not enforce the lock. An uncooperative process can access a file region locked by another, so it is important that all processes are coded correctly. Files on NFS systems may not get locked, so you would have to read about the behavior of your system, and do some experiments.
If your program needs to be portable, you should encapsulate the locking functions into your own "FileLocker" class. This is actually a good idea in any case because there is always the danger that in a function, a lock will be obtained, but the unlocking be missed. This can happen if the function has many return points or throws an exception. Your "FileLocker" class would automatically release the lock in its destructor. Such a class will automatically release the lock in case of a return from a function or an exception. This technique is called "Resource Acquisition is Initialization" It is a common technique which you can read about at
http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
You don't need a separate or extra file on which to do the locking. That method is used when you want exclusive access to some other kind of resource.
I don't know how complex your data operations are, so I cannot say if the database solution is good or not. Of course if you are using NFS, and the locking does not work, the database solution might be the right one. Otherwise you might need to write some sort of server to serialize file access for all the clients.
Since this is an area with some unknowns, I'd advise you to do some experimenting on your system, especially if you are using NFS.
If you think you may need to go to the database option in the future, you should encapsulate all your data storage operations behind some interface so that you can switch between simple files and a database engine without affecting the rest of the code. Encapsulating what is likely to change is a good design principle.
One final point, don't forget to flush your ostream output buffers before releasing the lock which is protecting the ostream write.
I hope that something that I've written helps you.
Best regards
Zlatko