1

Context: I'm currently writing a simple library for sharing memory between Linux and Wine processes. The only way I found so far to synchronize access is locking the file, since Wine's implementation of LockFileEx uses F_SETLK.


The FCNTL(3P) man page specifies that the behavior of fcntl(/*...*/, F_SETLK, /*...*/) for shared memory is unspecified:

When the file descriptor fildes refers to a shared memory object, the behavior of fcntl() shall be the same as for a regular file except the effect of the following values for the argument cmd shall be unspecified: F_SETFL, F_GETLK, F_SETLK, and F_SETLKW.

However, FCNTL(2) doesn't seem to mention the issue, so to test it, I wrote the following C++ program (compile with g++ -lrt test.cxx):


#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <iostream>

#define SHM_NAME "thisisatest"
#define BUF_SIZE 16

int main()
{
    int mytest = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0600);
    if (mytest < 0)
    {
        std::cerr << "failed to open shared memory\n";
        return 1;
    }

    if (ftruncate(mytest, BUF_SIZE) < 0)
    {
        std::cerr << "failed to open shared memory\n";
        close(mytest);
        shm_unlink(SHM_NAME);
        return 1;
    }

    volatile void* mytestMem = mmap(nullptr, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mytest, 0);
    if (mytestMem == MAP_FAILED)
    {
        std::cerr << "failed to map shared memory\n";
        close(mytest);
        shm_unlink(SHM_NAME);
        return 1;
    }

    std::clog << "Trying to lock first byte...\n";

    struct flock lock;
    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 1;
    if (fcntl(mytest, F_SETLKW, &lock) < 0)
    {
        std::clog << "Failed to lock.\n";
    }
    else
    {
        std::clog << "Locked.\n";
    }

    // Do stuff with the shared memory...

    // Wait until enter is pressed
    std::cin.get();

    lock.l_type = F_UNLCK;
    if (fcntl(mytest, F_SETLKW, &lock) < 0)
    {
        std::clog << "Failed to unlock.\n";
    }
    else
    {
        std::clog << "Unlocked.\n";
    }

    munmap(const_cast<void*>(mytestMem), BUF_SIZE);

    close(mytest);
    shm_unlink(SHM_NAME);
}

And it works: if I run the program twice, the second one will stop until the first unlocks the file. So I should be able to use this.


My issue is: I was unable to find confirmation anywhere whether or not this will always work. Can I rely on this behavior on Linux?

LHLaurini
  • 1,737
  • 17
  • 31
  • [WINE is effectively a pseudo-virtual machine](https://stackoverflow.com/a/32333479), so I'm not sure how this would work using only shared memory. If it only works on Linux and that's fine with you, then that's your choice (you can't support every system available). However, I know for certain locking a shared memory file description doesn't work on FreeBSD, and I wouldn't be surprised if it didn't work on macOS, Illumos, or the other BSDs. Also, `SHM_NAME` should begin with a `/` as suggested by POSIX, else `shm_open` will fail on some platforms. – MemReflect Jun 25 '20 at 19:39
  • @MemReflect Yes, I'm aware. I'm just aiming at x64 Linux, so I don't care much about portability (I guess I should remove the `posix` tag). Thanks for the info about the preceding slash though, guess I overlooked that (Linux doesn't seem to mind, but it's good to know). – LHLaurini Jun 25 '20 at 22:22

0 Answers0