2019r2.1 Mutex not working on Mac

My 2019R1.1 apps use a mutex to prevent multiple copies of the app from running simultaneously. I know this worked from my own use because when developing on the Mac, I would sometimes accidentally leave the current built app running while attempting to test changes in the debugger. The debug version would give up the ghost.

Now with 2019R2.1 on Mojave, this code is not working. In the debugger, I can see the mutex is not nil, but the if statement is returning false and not displaying the message dialog.

Did something change in regard to mutex as part of 2019R2.1? I see nothing in the release notes regarding this.

Protected Sub CheckMutex() theMutex = new Mutex("BeyondCategory_SoundCatalog") if not theMutex.TryEnter then Dim d as New MessageDialog Dim b as MessageDialogButton d.icon=MessageDialog.GraphicStop d.ActionButton.Caption="OK" d.CancelButton.Visible=False d.AlternateActionButton.Visible=False d.Message= k_FAILED_MUTEX b=d.ShowModal Quit end End Sub

To add more confusion, the example project IS working. Why mine is not working is baffling as I am doing the same thing. Checking the mutex as first order of business in the App.Open event. I only made some simple menu changes to my app since upgrading to 2019R2.1.

Does it still work properly if you step back to 19r1.1?

Yes, it does.

I may have stumbled on what is wrong. I built the example app in 19r1.1. and then built it in 19r2.1. I was able to run both apps simultaneously. I used Find Any File to find the file created by the mutex and they are in slightly different locations. Could it be possible that 19R2.1 is looking in a different place for the mutex and not seeing the ones created by 19R1.1?

See image:

Yup. Confirmed. Seems to be another fallout from the temporary files problems.

Regression. Please report in Feedback to get attention from the r&d team.

Not sure what Xojo can do about this.

2019r2 & < 2019r2 use different file system APIs to request the temporary path. Both APIs are returning the correct path which unfortunately, is different.

The only solution might be to roll your own mutex solution that tests both the old & new paths.

This appears correct. I don’t know that a Feedback request will help in this case but I submitted it anyway. <https://xojo.com/issue/58484>

After a night’s sleep, I wonder if this is really a problem. Of course, as a developer, I am running multiple versions of my app and that is how I discovered this. But most users will only have one version of the app. On the Mac, any one version can’t be run twice simultaneously unless the user goes out of their way to make that happen.

On Windows, the mutex does work correctly across Xojo versions. That is where the mutex, for the purpose of policing multiple runs, it really needed.

Well - yes, if an .app is already running, double-clicking it again in Finder will just bring the app in front.
However: Just copy/duplicate the .app in Finder and launch the copy - there you go. I wouldn’t call that " the user goes out of their way".
Especially because…:

That would be the ideal. Probably works perfectly fine when using AppStore. But especially with “not so experienced users” I have seen various versions of .app’s all across the system. One in Applications, other/newer versions in Downloads folder and on Desktop. Or even still in the downloaded .dmg (“I wondered why I always have to open that first before I can launch it”).
That’s why I’m not quite sure about “most users will only have one version of the app” :wink:

When we have multiple apps working together, it’s an issue.
When an aux app, made with prior versions, consults the mutex to see if the main app is active, it will fail.
It seems a Xojo bug or OS SDK bug. Xojo needs to investigate.

[quote=465668:@Rick Araujo]When we have multiple apps working together, it’s an issue.
When an aux app, made with prior versions, consults the mutex to see if the main app is active, it will fail.[/quote]
Thanks, @Rick Araujo - This explains something that I ran into just this weekend. My main app, built with 18r3, was running, but when it tried to launch two of my helpers that I’d upgraded and rebuilt with 19r2.1, they failed with an error indicating that the parent application wasn’t running. I forgot that I was using a MUTEX for this check. My fix will be easy since the helpers are not dependent on any new compiler features and I can rebuild with 19r1.1.

[quote=465668:@Rick Araujo]When we have multiple apps working together, it’s an issue.
When an aux app, made with prior versions, consults the mutex to see if the main app is active, it will fail.
It seems a Xojo bug or OS SDK bug. Xojo needs to investigate.[/quote]

I imagine it more of a limitation caused by switching the underlying file system APIs. The old APIs provide one path and the newer APIs provide another path. Since the Xojo mutex is really just a file written to the temporary path you get side effects like the one encountered.
Unless Xojo can write the mutex file to the old temporary path it probably cannot be fixed (easily).

if you need to mix apps from different versions of Xojo then you’ll need to probably do some work to resolve the file path differences since Xojo uses different API’s depending on version
basically you’ll need to manually check the existence of the mutex file for yourself (possibly in both places) :frowning:

or switch to compiling all your apps from the same version so they use the same file system api’s

I can’t believe it’s an OS API thing, if it is, it’s an OS API BUG thing needing fix. Calling different interfaces for the SAME function should result in the same functionality. An OldLockMutex(“MyMutex”) As Boolean should return the same as NewLockMutex(“MyMutex”) As Boolean
Imagine things like an Adobe Bundle with 5 apps, and you “upgrade” later just Photoshop, now using the new “NewLockMutex” instead of the OldLockMutex recently deprecated, things MUST work seamlessly, You MUST GET a lock for that mutex or not, no one should even to know that exists a file in your system for that, because in some systems a mutex could be just an in memory data.
That’s a bug. We need to know if the culprit is Xojo or Apple. The research must start with Xojo. I hope that this is a Xojo bug, because the fix will be easier.

[quote=465734:@Rick Araujo]I can’t believe it’s an OS API thing, if it is, it’s an OS API BUG thing needing fix. Calling different interfaces for the SAME function should result in the same functionality. An OldLockMutex(“MyMutex”) As Boolean should return the same as NewLockMutex(“MyMutex”) As Boolean
Imagine things like an Adobe Bundle with 5 apps, and you “upgrade” later just Photoshop, now using the new “NewLockMutex” instead of the OldLockMutex recently deprecated, things MUST work seamlessly, You MUST GET a lock for that mutex or not, no one should even to know that exists a file in your system for that, because in some systems a mutex could be just an in memory data.
That’s a bug. We need to know if the culprit is Xojo or Apple. The research must start with Xojo. I hope that this is a Xojo bug, because the fix will be easier.[/quote]
The mutex is a Xojo feature and not an OS feature. Xojo decided to use the temporary folder for this which is why the problem is occurring.
The fact that the temporary folder path is different based on the API used doesn’t appear to be an OS bug either. I queried this recently with Apple while reporting a Catalina issue and they stated that the two APIs are supposed to use different temporary folders.

Apple’s standard style of response - “uh, yes. We MEANT for that to happen.” Fits right in with “That’s what customers expect.” and “It’s always worked that way.”

But, in this case, it IS the temporary folder and we all know what to NOT expect there. There should be a blessed location like “/var/run” for these types of filesystem uses.

You mean that Xojo opted to avoid the OS feature (because Mutex is a known OS feature) and invented their own “solution” using files and that created the problem? If so, yep, it’s broken by design. :smiley:

[code]
// Basic Interprocess Unix/MacOS mutex example code using shared memory
int key = ftok(NAMED_MEMORY, ID_TAG);
if (-1 == key)
{
printf("Unable to name shared memory
");
exit(1);
}

// Create the segment exclusively (if the segment already exists then a combination of IPC_CREAT | IPC_EXCL returns an error EEXIST)
int m_iShmid = shmget(key, TOTAL_SIZE, READ_WRITE_PERMISSIONS | IPC_CREAT | IPC_EXCL);
if (m_iShmid < 0)
{
if (EEXIST == errno)
{
// if the shared memory already exists we only fetch the id to that memory
m_iShmid = shmget(key, TOTAL_SIZE, READ_WRITE_PERMISSIONS);
}
if (m_iShmid < 0)
{
printf("Unable to create shared memory - %s
",strerror(errno));
exit(1);
}
else
printf("Attached to the existing shared memory
");
}
else
printf("Created new shared memory
");

// Now we attach the segment to our data space.
mutex = reinterpret_cast<pthread_mutex_t*>(shmat(m_iShmid, NULL, 0));
if (reinterpret_cast<pthread_mutex_t*>(-1) == mutex)
{
printf("Unable to attach shared memory to the process - %s
",strerror(errno));
exit(1);
}

// Now we can set this mutex to be shared between processes
pthread_mutex_t* mutex;
pthread_mutexattr_t mutexAttr;
ret = pthread_mutexattr_init(&mutexAttr);
if(ret != 0)
{
printf("pthread_mutexattr_init failed - err=%d
",ret);
exit(1);
}
ret = pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED);
if(ret != 0)
{
printf("pthread_mutexattr_setpshared failed - err=%d
",ret);
exit(1);
}
ret = pthread_mutexattr_setrobust_np(&mutexAttr, PTHREAD_MUTEX_ROBUST_NP);
if(ret != 0)
{
printf("pthread_mutexattr_setrobust_np failed - err=%d
",ret);
exit(1);
}
ret = pthread_mutex_init(mutex, &mutexAttr);
if(ret != 0)
{
printf("pthread_mutex_init failed - err=%d
",ret);
exit(1);
}
// ------ Use the mutex from here on between processes[/code]