Leopard multiple instance checker gone haywire

This is strange… We have these networked iMacs running Leopard and I recently compiled JUCE on one and linked to my program. However, the program immediately shuts down, but doesn’t crash. Opening up the debugger shows that JUCE gives the message “Another instance running - quitting…” (Note that I had allowMoreThanOneInstance returning false, and for returning true the problem naturally went away) Checked the process list, but there wasn’t anything in there running that could confuse it, and did restarts and everything, but no change.

After playing around with it, we found that when we ran the binary from an admin account, it would work, and when we ran it on a local guest account it worked, but whenever it was a user signed on, it somehow was thinking that there was another instance running already.

We then tried some other JUCE apps that had been built elsewhere and found that it wasn’t a JUCE pandemic, but my best guess is that it had to do with the JUCE library that I compiled in XCode. This happened on both the release and debug.

Anyone run into this before?

juce is really checking a lock file in ~/Library/Caches/YourAppName/juceAppLock_YourAppName

You might look there and see if that lock file is being created and removed as expected. Could there be permissions issues preventing it’s removal under certain circumstances?

Ok, after some more testing we found this difference. The programs that work correctly, do as you say and put an application lock in

~/Library/Caches/YourAppName/juceAppLock_YourAppName

However, the ones that are quitting immediately are putting there app lock in a JUCE subfolder, i.e.:

~/Library/Caches/Juce/YourAppName/juceAppLock_YourAppName

The first one looks like the temp folder, but the second one is the JUCE_MAC specific code:

[code]InterProcessLock::InterProcessLock (const String& name_) throw()
: internal (0),
name (name_),
reentrancyLevel (0)
{
#if JUCE_MAC
// (don’t use getSpecialLocation() to avoid the temp folder being different for each app)
const File temp (File (T("~/Library/Caches/Juce")).getChildFile (name));
#else
const File temp (File::getSpecialLocation (File::tempDirectory).getChildFile (name));
#endif

temp.create();

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

}[/code]

Is this meaningful at all?

It looks like the lock code for the mac was changed in the 1.46 version of juce. The current location shouldn’t use the app name as part of the lock path, and put everything inside ~/Library/Caches/Juce

Could the apps that are working have been compiled under an older version of juce?

But that seems tangential to your problem. Why is InterProcessLock::enter() failing when the lock is put in the Juce subdirectory? Can you break in that routine and see where things aren’t working?

I can’t see how the directory would make a difference as long as the folder isn’t somehow locked…?

igor, you are right, the “working” version was using 1.45, so this appears to be a 1.46 + Leopard + networking issue. The difference in the path of lock file isn’t the problem. I stepped into a bare minimum JUCE project in XCode and the place where it dies is:

const int result = fcntl (filedesc, F_SETLK, &fl);

in the InterProcessLock::enter method. fcntl returns -1 and then the reentrancy never goes up, and the method fails, which then causes the calling function to assume that there is another instance running.

The path of the lock file that JUCE is working with in this case was:
/Servers/camil.music.uiuc.edu/Volumes/Users/burnson2/Library/Caches/Juce/juceAppLock_test

I see that 1.45 used flock instead:

if (flock ((int) internal,
                   timeOutMillisecs < 0 ? LOCK_EX
                                        : (LOCK_EX | LOCK_NB)) == 0)

And this DOES work. I don’t know anything about these locking functions, but I wonder if one doesn’t work as well in restricted environments…

Again, the issue only arises with our networked users. Network admin and local accounts aren’t affected here.

I don’t know much about the flock and fcntl calls either, but my first google search on fcntl lead me here:
http://www.opengroup.org/onlinepubs/009695399/functions/fcntl.html

Not only is there a short section of code showing how that call should be made, but it also shows that you can examine errno to get more information about why failure might have occured.

My first guess would be permissions. How are the permissions in your /Servers/camil.music.uiuc.edu/Volumes/Users/burnson2/Library/Caches/Juce/ directory set when you’re writing the file locally, and when you’re trying to write the file from a second machine. Are you burson2 on both machines? Are those really the same account according to the 2 machines? If the names match, but the user ID numbers don’t match, you may not have permission to write into the Caches directory if you’re not an administrator. There’s some interesting discussion of a problem along those lines at: http://forums.macosxhints.com/archive/index.php/t-63897.html

I wonder if it’d be enough just to try to open the file with write-access, and hold the handle until the lock is released? That’d avoid any problems with the file becoming stuck if the app quits unexpectedly…

Ok, I was finally able to get to the errno for that fcntl call. It turns out that errno = 45 which is ENOTSUP //Operation not supported… Sure wasn’t expecting that one!

Oh, and to answer your question igor, I’m not the administrator so I can’t say for sure, but I’m pretty sure that his permissions are set correctly. JUCE 1.45 has no problem with any kind of user storing a lock file in the Library/Caches directory… In fact it’s not the creation of the file that is the problem… just this fcntl call, if that answers your question.

Here’s an interesting tidbit:
http://www.briandwells.com/main/Blog/Entries/2006/10/5_Subversion,_Mac_OS_X,_and_SMB.html

[quote]
This error occurs because FSFS implements file locking using the apr_file_lock function, which for Mac OS X (and most other forms of Unix) uses fcntl for file locking. This file locking mechanism does not support SMB filesystems on Mac OS X.[/quote]

There are other references saying that if you use flock instead of fcntl, you’ll get no errors, but that on AFP shares, the lock does nothing.
http://svn.haxx.se/users/archive-2005-04/0699.shtml

If I were you, I’d post a note on one of the apple developer lists:
http://lists.apple.com/mailman/listinfo, and see if anyone has a definitive answer, and perhaps a solution.

OS X isn’t my native tongue, so this is a bit over my head, but I will post on list and see if anything turns up. In the meantime, is there anything naive about doing something like this:

[code]class MyApplication : public JUCEApplication
{
NamedPipe* instanceChecker;

//Other members here…

void initialise(const String& commandLine)
{
instanceChecker = new NamedPipe;
if(!instanceChecker->createNewPipe(String(“MyApplicationPipe”)))
{
/Pipe already exists so we assume the program already has an
instance running.
/
quit();
return;
}

//Rest of initialization goes here...

}

void shutdown()
{
//Rest of shutdown goes here…

delete instanceChecker;

}

bool moreThanOneInstanceAllowed(void)
{
return true; //Circumvent built-in instance checking.
}
};
[/code]

This would obviate the need for file-based locks. It seems to work OK, but I haven’t tested it widely.