0

I'm trying to synchronize reads and writes between processes(in the example I've used threads for convenience though) to a common file with c++ and I came up with this: the name of the file signals whose turn it is to write to the file. After one party is done writing, the file is renamed to signal that it's the other party's turn. This seems to work fine, however it seems that this is extremely slow(~0.5ms per write). Is there a better approach to this?

int main(int argc, char *argv[]) {
    //create common file
    std::ofstream file("buffTurnMain.txt");
    file.close();

    //thread writer
    std::thread th([](){
        for(int ii = 0; ii <10; ++ii) {
            while(true) {//loop till threads turn
                std::ifstream f("buffTurnThread.txt");
                if (!f.fail()) { //break if successfully found
                    break;
                }
            }

            std::ofstream fl;
            fl.open("buffTurnThread.txt", std::ios_base::app);
            fl << "hello from thread" << std::endl;
            fl.close();
            std::rename("buffTurnThread.txt", "buffTurnMain.txt");//after write rename to signal mains turn

        }
    });

    //main writer
    for(int ii = 0; ii < 10; ++ii) {
        while(true) {//loop till mains turn
            std::ifstream f("buffTurnMain.txt");
            if (!f.fail()) { //break if successfully found
                break;
            }
        }

        std::ofstream fl;
        fl.open("buffTurnMain.txt", std::ios_base::app);
        fl << "hello from main" << std::endl;
        fl.close();
        std::rename("buffTurnMain.txt", "buffTurnThread.txt");//after write rename to signal threads turn

    }
    th.join();
}
arc31
  • 127
  • 1
  • 5
  • You may be interested in O_EXCL and this other question: https://stackoverflow.com/q/15762752/1816262 – OMGtechy Apr 19 '22 at 16:02
  • What type of synchronisation do you want exactly? One writes then the other reads strictly alternating? Or unordered just not simultaneous? Why not use more conventional interprocess communication? – Mark Setchell Apr 19 '22 at 16:45
  • @MarkSetchell I want the order to be maintained and for it to be alternating. Both processes should be able to read(haven't implemented this in my code example) and write: As to why not something like pipes, I wanted to stay away from platform-specific functions because it would be nice to cross compile without any changes and I don't want to involve external dependencies like boost. – arc31 Apr 19 '22 at 17:00
  • Normally you synchronize threads with [mutexes](https://en.cppreference.com/w/cpp/thread/mutex), [condition variables](https://en.cppreference.com/w/cpp/thread/condition_variable), etc, which are much lighter weight than going through the filesystem. They are standard C++11 and should exist on any platform that supports threads, so no portability problems there. – Nate Eldredge Apr 19 '22 at 17:04
  • @NateEldredge Read the first sentence of the post, this was only an example to demonstrate the principle – arc31 Apr 19 '22 at 17:08
  • So, your title says "UNIX" and you are assuming your platform has the concept of "processes" (which do not exist in standard C++). Then I don't see why it is a big step to also assume it has semaphores or file locks (or pipes for that matter), all of which are promised by POSIX and don't need any external libraries. If you won't use the mechanisms that are designed to be fast, then it's to be expected that you'll end up with something slow. – Nate Eldredge Apr 19 '22 at 17:13
  • Many platforms guarantee that `rename` is atomic and persistent even in case of system crash, so that it has to do physical disk I/O before it can return. That could explain why it takes so long. – Nate Eldredge Apr 19 '22 at 17:16
  • @NateEldredge You're right, my use of the word unix was probably misleading, what I was trying to say was that I don't want write separate code for windows, which doesn't have the same functions even though it might have similar concepts. – arc31 Apr 19 '22 at 17:39
  • I can't come up with a portable way that doesn't include spinning (which consumes a lot of CPU). It switches a lot faster than you've described though. [Idea](https://godbolt.org/z/zz7q9E356) - You'd start it with `./program 1 2` and `./program 2 1` to ping-pong between the two. You can add more: `1 2`, `2 3` and `3 1` to get a round robin setup. I would never use this in real situations though. Better use boost to hide the platform specifics. – Ted Lyngmo Apr 19 '22 at 19:47

0 Answers0