2019r2.1 Mutex not working on Mac

  1. last week

    Steve A

    Dec 1 Little Falls, NJ

    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
  2. Steve A

    Dec 1 Little Falls, NJ

    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.

  3. Tim J

    Dec 1 Pre-Release Testers N. Phoenix, AZ

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

  4. Steve A

    Dec 1 Little Falls, NJ

    @Tim J 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:

    -image-

  5. Beatrix W

    Dec 1 Pre-Release Testers, Third Party Store Europe (Germany)

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

  6. Joost R

    Dec 1 Pre-Release Testers, Xojo Pro The Netherlands

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

  7. Kevin G

    Dec 1 Pre-Release Testers, Xojo Pro Gatesheed, England

    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.

  8. Steve A

    Dec 2 Little Falls, NJ

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

    This appears correct. I don't know that a Feedback request will help in this case but I submitted it anyway. Feedback Case #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.

  9. Jürg O

    Dec 2 Pre-Release Testers, Xojo Pro

    @Steve A On the Mac, any one version can't be run twice simultaneously unless the user goes out of their way to make that happen.

    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...:

    @Steve A But most users will only have one version of the app.

    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" ;)

  10. Rick A

    Dec 2 Pre-Release Testers (Brazil. UTC-3:00)
    Edited last week

    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.

  11. Tim J

    Dec 2 Pre-Release Testers N. Phoenix, AZ

    @Rick A 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.

    Thanks, @Rick A - 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.

  12. Kevin G

    Dec 2 Pre-Release Testers, Xojo Pro Gatesheed, England

    @Rick A 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.

    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).

  13. Norman P

    Dec 2 Pre-Release Testers, Xojo Pro under a bus

    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) :(

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

  14. Rick A

    Dec 2 Pre-Release Testers (Brazil. UTC-3:00)
    Edited last week

    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.

  15. Kevin G

    Dec 2 Pre-Release Testers, Xojo Pro Gatesheed, England

    @Rick A 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.

    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.

  16. Tim J

    Dec 2 Pre-Release Testers N. Phoenix, AZ

    @Kevin G ... 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.

  17. Rick A

    Dec 3 Pre-Release Testers (Brazil. UTC-3:00)
    Edited last week

    @Kevin G The mutex is a Xojo feature and not an OS feature.

    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. :D

    // 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\n");
        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\n",strerror(errno));
            exit(1);
        }
        else
            printf("Attached to the existing shared memory\n");
    }
    else
        printf("Created new shared memory\n");
    
    // 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\n",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\n",ret);
        exit(1);
    }
    ret = pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED);
    if(ret != 0)
    {
        printf("pthread_mutexattr_setpshared failed - err=%d\n",ret);
        exit(1);
    }
    ret = pthread_mutexattr_setrobust_np(&mutexAttr, PTHREAD_MUTEX_ROBUST_NP);
    if(ret != 0)
    {
        printf("pthread_mutexattr_setrobust_np failed - err=%d\n",ret);
        exit(1);
    }
    ret = pthread_mutex_init(mutex, &mutexAttr);
    if(ret != 0)
    {
        printf("pthread_mutex_init failed - err=%d\n",ret);
        exit(1);
    }
    // ------ Use the mutex from here on between processes

or Sign Up to reply!