InterprocessLock not working on macOS?


#1

Using InterprocessLock, enter() seems to always return true for me regardless of how many instances have supposedly locked it. Can anyone confirm that InterprocessLock is working on macOS 10.12?


InterprocessLock not working on macOS?
#2

Testing this more, it works correctly on Windows, but the same code on Mac acquires the lock every time.


#3

seems to always return true for me regardless of how many instances have supposedly locked it

but the same code on Mac acquires the lock every time

I could be wrong but this makes sense to me as an IP lock is ref-counted…


#4

Both the Mac and Windows versions are ref-counted, though, so I’m not sure how that would account for the discrepancy we’re seeing.


#5

InterprocessLock should be used to synchronise between different processes and not instances. If you want to synchronise between instances then you should be using a normal CriticalSection - maybe wrapped in a singleton.


#6

Thanks Fabian. We do need the lock across processes. Would the recommendation be to use a critical section AND an interprocess lock if we don’t know if we’re being placed in the same process or not?

Any reason this is different on Windows?


#7

Yes, I would think that this is the best approach.

I guess that the underlying native API has different behaviour when two InterProcessLocks are held by the same process. Obviously, we should abstract this difference in behaviour away. I’ve added this to our backlog.


#8

Thanks for the info!

Do you know if the current OS X implementation will gracefully deal with an abandoned lock? I know the Windows version has WAIT_ABANDONED for this situation. Either way, it might be worth mentioning in the docs since the abandoned situation can be important for interprocess use.


#9

Does this look sound, and does anyone know about the question above “OS X implementation will gracefully deal with an abandoned lock?”?

cheers!

class InterIntraProcessLock
{
public:
    InterIntraProcessLock(const String & lockname) : lockname(lockname) { success = sessionLock->enter(lockname); }
    ~InterIntraProcessLock() { if (success) sessionLock->exit(lockname); }
    bool isLocked() const { return success; }

private:
    /**
     * A shared object within each process that maintains a list of locks.
     */
    class SessionLock
    {
    public:
        bool enter(const String & lockname)
        {
            ScopedLock scopedMapLock(mapLock);

            auto it = locks.find(lockname);

            if (it != locks.end())
                return false; // we already have the lock 

            auto newLock = std::make_unique<InterProcessLock>(lockname);
            auto haveInterprocessLock = newLock->enter(0);

            if (!haveInterprocessLock)
                return false; // another process has the lock

            locks[lockname] = std::move(newLock);

            return true;
        }

        void exit(const String & lockname)
        {
            ScopedLock scopedMapLock(mapLock);

            auto it = locks.find(lockname);

            if (it != locks.end())
                locks.erase(lockname);
            else
                jassertfalse; // should not be possible to reach this line!
        }

        CriticalSection mapLock;
        std::unordered_map<String, std::unique_ptr<InterProcessLock>> locks;
    };

    bool success{ false };
    String lockname;
    SharedResourcePointer<SessionLock> sessionLock;
};