InterProcessLock doesn't work on the mac

InterProcessLock doesn’t work on the mac.

void* juce_getGlobalLock (const String& lockName, int timeoutMillis) { return (void*)1; }

Is there another way to get this functionality on the mac?

Yeah, I never figured out a good way of doing it. I think on the mac/linux you’d implement that kind of thing with a file lock, but have never had chance to investigate.

For linux (and probably mac as well), ACE uses a pthread mutex stored in a shared memory segment named after the mutex. I could probably take a crack at it if interested.

By the way, JUCE is pretty slick compared to the preprocessor nightmare of Qt. I’m still not sold on the “listener” concept vs explicit function callbacks a la GTK, but I’ve only been poking around for a few hours now.

[quote=“lewislp”]For linux (and probably mac as well), ACE uses a pthread mutex stored in a shared memory segment named after the mutex. I could probably take a crack at it if interested.

By the way, JUCE is pretty slick compared to the preprocessor nightmare of Qt. I’m still not sold on the “listener” concept vs explicit function callbacks a la GTK, but I’ve only been poking around for a few hours now.[/quote]

Cheers, if you had even a rough implementation that’d make it a lot easier for me to start from.

After digging through the code, I’ve noticed that you’re handling an InterProcessLock quite differently from a CriticalSection in that implementation for the CriticalSection class is provided in a platform-specific source file. Is there a reason you elected to use a set of global functions instead of following the same pattern with InterProcessLock?

My concern is the efficiency of creating/destroying the mutex every time a lock/unlock occurs. There’s a couple of ways to solve this:

  1. Move implementation of the InterProcessLock class to the platform-specific code.

  2. Create separate functions for creating/destroying the lock, and locking/unlocking the lock. In this scenario, the lock would be created/destroyed in the IPL constructor/destructor and locked/unlocked in enter/exit.

I should have something shortly.

[quote=“lewislp”]After digging through the code, I’ve noticed that you’re handling an InterProcessLock quite differently from a CriticalSection in that implementation for the CriticalSection class is provided in a platform-specific source file. Is there a reason you elected to use a set of global functions instead of following the same pattern with InterProcessLock?

My concern is the efficiency of creating/destroying the mutex every time a lock/unlock occurs. There’s a couple of ways to solve this:

  1. Move implementation of the InterProcessLock class to the platform-specific code.

  2. Create separate functions for creating/destroying the lock, and locking/unlocking the lock. In this scenario, the lock would be created/destroyed in the IPL constructor/destructor and locked/unlocked in enter/exit.

I should have something shortly.[/quote]

It’s old code and should certainly be reviewed, which I’ll do when I have a go at implementing it.

I threw together a quick implementation of InterProcessLock for Linux (and likely for Mac, although I don’t have one nor have I ever written a single line of code for Mac so I’d appreciate it if someone else could verify this). Instead of implementing the two global functions, I ifdef’ed the hell out of the InterProcessLock class as discussed in a previous post. There are some minor intialization/destruction race condtions that haven’t been addressed and the timeout is not in there yet, but it works pretty good as tested on my CentOS 4.4 system.

juce_InterProcessLock.h

juce_InterProcessLock.cpp

Jules, I’m planning on using JUCE for some cross-platform Linux/Windows applications, and am wondering if there’s other areas where the Linux implementation is not as mature as the Windows and Mac implementations?

Overall, the library is incredible in its scope, consistency and simplicity. Unfortunately, I just recently stumbled onto it after buying Tracktion.

Thanks - I’ll probably implement it slightly differently, putting it into platform-specific code areas, but that’s a great place to start from, and should save me a lot of time working out how the shared memory stuff is done.

There’s not many places left now where the linux version is missing functionality. I’ve got some http stream code ready for the next release so that’ll be up-to-date, which I think just leaves transparent windows. (There’s probably something else but I can’t think of anything offhand)

I’ve had a go at this, but the shared mem solution isn’t usable, because if an app crashes or gets killed, it leaks an object and then the app refuses to run any more. I’ve got a neater solution now using file locking. If you fancy trying it, here’s the code - let me know if it does anything silly…

[code]//==============================================================================
InterProcessLock::InterProcessLock (const String& name_)
: internal (0),
name (name_),
reentrancyLevel (0)
{
const File tempDir (File::getSpecialLocation (File::tempDirectory));
const File temp (tempDir.getChildFile (name));
temp.create();

internal = (void*) open (temp.getFullPathName().toUTF8(), 'a');

}

InterProcessLock::~InterProcessLock()
{
while (reentrancyLevel > 0)
this->exit();

close ((int) internal);

}

bool InterProcessLock::enter (int timeOutMillisecs)
{
if (internal == 0)
return false;

if (reentrancyLevel != 0)
    return true;

const int64 endTime 
    = timeOutMillisecs > 0 ? Time::currentTimeMillis() + timeOutMillisecs : 0;

for (;;)
{
    if (flock ((int) internal, LOCK_EX | LOCK_NB) == 0)
    {
        ++reentrancyLevel;
        return true;
    }

    if (timeOutMillisecs == 0 || (endTime != 0 && Time::currentTimeMillis() >= endTime))
        break;

    sleep (10);
}

return false;

}

void InterProcessLock::exit()
{
if (reentrancyLevel > 0 && internal != 0)
{
–reentrancyLevel;

    const int result = flock ((int) internal, LOCK_UN);
    (void) result;
    jassert (result == 0);
}

}[/code]

Seems to work quite nicely. I agree with your assessment of the shared memory mutex leaking if the program crashes or is terminated uncleanly. I’d suggest handling the special case of block forever with the blocking version of flock(), but I’m sure this is a rough implementation anyways.

Don’t forget about Linux’s sleep being in seconds (I assume you wanted usleep(10000) for 10 milliseconds). Does JUCE have a platform-independent wrapper for sleep?

Ah yes, I’d meant to type Thread::sleep instead of sleep()… As you say, this was just a quick first draft!

This doesn’t quite work.

If app 1 is in the lock, app 2 will block in the constructor.

[quote=“G-Mon”]This doesn’t quite work.

If app 1 is in the lock, app 2 will block in the constructor.[/quote]

really? I tried that situation and it seemed to work just fine. You’re saying the open() would block?

[quote=“jules”][quote=“G-Mon”]This doesn’t quite work.

If app 1 is in the lock, app 2 will block in the constructor.[/quote]

really? I tried that situation and it seemed to work just fine. You’re saying the open() would block?[/quote]

Yes, on a ppc mac running 10.4.8.

Ok, I’d not tried it on a mac yet, I was using linux.

Try throwing an O_NONBLOCK flag in there to prevent the open from blocking. I had no trouble constructing a second lock while the first one was locked in linux either.

ok, I’ll have a go of that. Ta.