proper way to use lock file(s) as locks be

2019-03-16 21:37发布

问题:

I have a situation where 2 different processes(mine C++, other done by other people in JAVA) are a writer and a reader from some shared data file. So I was trying to avoid race condition by writing a class like this(EDIT:this code is broken, it was just an example)

class ReadStatus
{
    bool canRead;
public:
    ReadStatus()
    {
        if (filesystem::exists(noReadFileName))
        {
            canRead = false;
            return;
        }
        ofstream noWriteFile;
        noWriteFile.open (noWriteFileName.c_str());
        if ( ! noWriteFile.is_open())
        {
            canRead = false;
            return;
        }
        boost::this_thread::sleep(boost::posix_time::seconds(1));
        if (filesystem::exists(noReadFileName))
        {
            filesystem::remove(noWriteFileName);
            canRead= false;
            return;
        }
        canRead= true;
    }
    ~ReadStatus()
    {
        if (filesystem::exists(noWriteFileName))
            filesystem::remove(noWriteFileName);
    }
    inline bool OKToRead()
    {
        return canRead;
    }
};

usage:

ReadStatus readStatus; //RAII FTW
    if ( ! readStatus.OKToRead())
        return;

This is for one program ofc, other will have analogous class. Idea is: 1. check if other program created his "I'm owner file", if it has break else go to 2. 2. create my "I'm the owner" file, check again if other program created his own, if it has delete my file and break else go to 3. 3. do my reading, then delete mine "I'm the owner file".

Please note that rare occurences when they both dont read or write are OK, but the problem is that I still see a small chance of race conditions because theoretically other program can check for the existence of my lock file, see that there isnt one, then I create mine, other program creates his own, but before FS creates his file I check again, and it isnt there, then disaster occurs. This is why I added the one sec delay, but as a CS nerd I find it unnerving to have code like that running. Ofc I don't expect anybody here to write me a solution, but I would be happy if someone does know a link to a reliable code that I can use. P.S. It has to be files, cuz I'm not writing entire project and that is how it is arranged to be done.

P.P.S.: access to data file isn't reader,writer,reader,writer.... it can be reader,reader,writer,writer,writer,reader,writer....

P.P.S: other process is not written in C++ :(, so boost is out of the question.

回答1:

On Unices the traditional way of doing pure filesystem based locking is to use dedicated lockfiles with mkdir() and rmdir(), which can be created and removed atomically via single system calls. You avoid races by never explicitly testing for the existence of the lock --- instead you always try to take the lock. So:

lock:
    while mkdir(lockfile) fails
        sleep

unlock:
    rmdir(lockfile)

I believe this even works over NFS (which usually sucks for this sort of thing).

However, you probably also want to look into proper file locking, which is loads better; I use F_SETLK/F_UNLCK fcntl locks for this on Linux (note that these are different from flock locks, despite the name of the structure). This allows you to properly block until the lock is released. These locks also get automatically released if the app dies, which is usually a good thing. Plus, these will let you lock your shared file directly without having to have a separate lockfile. This, too, work on NFS.

Windows has very similar file locking functions, and it also has easy to use global named semaphores that are very convenient for synchronisation between processes.



回答2:

As far as I've seen it, you can't reliably use files as locks for multiple processes. The problem is, while you create the file in one thread, you might get an interrupt and the OS switches to another process because I/O is taking so long. The same holds true for deletion of the lock file.

If you can, take a look at Boost.Interprocess, under the synchronization mechanisms part.



回答3:

While I'm generally against making API calls which can throw from a constructor/destructor (see docs on boost::filesystem::remove) or making throwing calls without a catch block in general that's not really what you were asking about.

You could check out the Overlapped IO library if this is for windows. Otherwise have you considered using shared memory between the processes instead?

Edit: Just saw the other process was Java. You may still be able to create a named mutex that can be shared between processes and used that to create locks around the file IO bits so they have to take turns writing. Sorry I don't know Java so no I idea if that's more feasible than shared memory.