2

I am creating my own IOsystem, which is using fstream. There will be many threads and my question is how can I keep it safe which means to allow multiple read from one file but keep writing exclusive. So for example if there are two threads that want to write to file I want the second one to wait until first one close its fstream. Here Accessing a single file with multiple threads it was proposed to use TMultiReadExclusiveWriteSynchronizer but it am not sure if it's the best option. One my idea is to just keep the map and check it manually before opening any file if the file can be safely open, if not make the thread wait until the file is released.

EDIT: And is there any way to open fstream in exclusive mode? If You think that fstream is the worst option use in many threads enviroments what are other possibilities ?

Community
  • 1
  • 1
rank1
  • 1,018
  • 4
  • 16
  • 37

2 Answers2

2

Synchronization primitives such as mutex are helpful. For example C++11 standard library introduced std::mutex and std::lock_guard.

One possibility is to write a thread-safe wrapper around operator<< :

class FStreamWriter
{
    std::fstream *f;
    std::mutex mtx;
public:
    FStreamWriter(std::fstream *f) : f(f) {}

    template <typename T>
    FStreamWriter &operator<<(const T &x)
    {
        std::lock_guard<std::mutex> lock(mtx); // <- Mutex to make it safe-thread
        (*f) << x;
        return *this;
    }
   // ...
};

fstream file("my_file.txt");

// ...

FStreamWriter fwriter(&file);

and then use fwriter object in various threads:

fwriter << "Hello" << 1 << 2 << 3;

If you doesn't use C++11, there is many alternatives such as Boost, Qt, POSIX, Windows-API, ... which have mutex. But the main structure is same in all of them

masoud
  • 55,379
  • 16
  • 141
  • 208
  • 1
    I particularly agree with this answer because it is _not_ allowing multiple readers simultaneously. I can't think of any reasonable way to let multiple readers safely simultaneously access an `fstream`. An `fstream` is (usually) buffered and so performing a read operation mutates the buffers and buffer pointers. – Wandering Logic Apr 12 '13 at 14:32
  • You are right. Perhaps I should forget fstream and should use any other (windows) IO , any ideas ? – rank1 Apr 12 '13 at 20:59
  • Btw, is here any advantage of using pointer to fstream than straight fstream? – rank1 Apr 15 '13 at 08:07
  • You need it, it's better to pass a pointer instead of deep copy of `fstream`, in addition I suspect it's possible to copy it. – masoud Apr 15 '13 at 08:26
  • 1
    OK, but does this code really helps ? What if I create two objects of FstreamWriter using different fstreams with the same path. Each of these objects has its own mutex so they could possible read in exactly the same time when there will be many threads. – rank1 Apr 17 '13 at 09:33
  • This code assumed you opened `"my_file.txt"` once and wrapped its `fstream` object in a single `FStreamWriter` then you're using `FStreamWriter` in multi threads. – masoud Apr 17 '13 at 10:04
  • That's what I thought so. But I need to have sth more general. I keep the map of and everyone can use the file using fileDescriptor. (This is self generated file descriptor which works just as an id of a file). So maybe I should keep also the path of opened files and before opening them check if they are not open already (and lock the open operation if necessary) – rank1 Apr 17 '13 at 10:10
  • @rank1 - but someone might bypass you by using the same path, but written differently (mixing in some .. or else). This kind of check does not seem good. But I believe flock would suffice as it's internal kernel mechanism. – zoska Mar 03 '14 at 13:19
  • 1
    If the output was line oriented then couldn't the data get intermingled this way? The lock only exists for the processing of a single argument in the chain. For example, thread #1: out << "a" << "b" << "c" << endl; thread #2: out << "x" << "y" << "z" << endl; // couldn't you end up with axbycz? – David H. Bennett Aug 20 '15 at 00:43
1

Instead of using mutexes, you should use locking mechanism from the low-level IO library. So, pass to the the stream class you're implementing the file name, and use the file name to make your own fstream object, but in the implementation of the operator<< use the lock, so this will always work, not necessary thread only.

Constructor:

open the file to get the file descriptor, pass the descriptor to the fstream constructor

operator<<:

use the ::flock(fileDescriptor, LOCK_EX); before fstream<<, and do not forget to unlock it with the ::flock(fileDecriptor, LOCK_UN);

doc_ds
  • 241
  • 3
  • 4