1 Attachment(s)
CMutexEx Class for Safer & Cooperative Mutex Handling
CMutexEx — A Safe, Cooperative, Cancellable Win32 Mutex Wrapper for VB6 - cross-process coordination made simple
CMutexEx is a small, production-ready class that wraps Windows named mutexes with a clean VB6/twinBASIC API: blocking or cancellable waits, timeouts, short-circuit detection, abandoned owner detection, and optional SDDL-based security. Namespaces map to the real Win32 object namespaces: Global (system-wide) and Local (session-scoped).
Why this class?
Win32 mutex APIs are powerful but tricky (namespaces, UI pumping, abandoned states, security). CMutexEx gives you a small, well-documented surface that “does the right thing” by default—and still lets you opt into advanced features when you need them.
Highlights
- Namespaces: Global (machine-wide) or Local (current logon session)
- Wait modes: blocking (classic) or cancellable (UI stays responsive via message pump)
- Timeouts: INFINITE, finite ms, or zero-wait (“try once”)
- Short-circuits: abort acquisition if any named mutex from a list exists (e.g., “Shutdown”)
- Abandoned detection: signals when prior owner crashed (WAIT_ABANDONED_0)
- Security (optional): pass SDDL to build a DACL (e.g., allow all users to synchronize only)
- Events: Acquiring(ByRef Cancel As Boolean), Acquired(IsAbandoned As Boolean), Failed(IsExpected As Boolean)
- Performance: ~70k acquire/release cycles/sec compiled (fast path)
When to use System/Global vs Session/Local Mutexes
- Global\ (System Mutex): coordinate across all users/processes on the machine (installers, shared files, single-instance app across users).
- Local\ (Session Mutex): coordinate inside the current logon session (per-user single-instance, per-session helpers).
Mutex Factory Helpers You'll Use
- Critical: infinite, blocking — database writes, installers, migrations
- Reactive: infinite, cancellable — user-initiated tasks that should politely yield
- ZeroWait: instant decision — single-instance, opportunistic background tasks
- Timed: finite wait — retry loops, background syncs that should skip when busy
- Perpetual: Zero-wait, acquired permanently for the app process lifetime.
? Quick Start
Blocking, infinite wait with short-circuit (critical section):
Code:
Sub Main()
If DbMigrationNeeded Then
With NewCriticalSystemMutex("Database_Migration", "Database_Migration_Complete") ' Get Database Migration mutex or short-circuit if another process performs the migration
If .IsShortCircuited Then
' Database migration completed successfully by another process, continue as normal
ElseIf .IsAcquired Then
' Mutex acquired - perform database migration
MigrateDatabase
Else
' Something else happened! Should abort!!
Err.Raise vbObjectError, , "Migration error!"
End If
End With ' auto-release mutex
End If
End Sub
Private Sub MigrateDatabase()
' DoDatabaseMigrationWorkHere
If NewPerpetualSystemMutex("Database_Migration_Completed") Then ' Perpetual mutexes will NEVER be released until app process terminates
' Migration Completed OK and we acquired a perpetual mutex to signal that no further migrations should be attempted
Else
' Uhoh! Could not acquire mutex!!
Err.Raise vbObjectError, , "Could not acquire migration complete mutex!!
End If
End Sub
Cancellable wait (responsive UI):
Code:
Private WithEvents m As CMutexEx
Private Sub StartWork()
Set m = NewReactiveSystemMutex("Report_Generation")
If m.IsAcquired Then RunReport
End Sub
Private Sub m_Acquiring(Cancel As Boolean)
If UserPressedCancel Then Cancel = True
End Sub
Zero-wait “try once” (single-instance guard):
Code:
Private Sub Form_Load()
With NewZeroWaitSessionMutex("App_Main")
If Not .IsAcquired Then
MsgBox "Already running!", vbExclamation
Unload Me
End If
End With
End Sub
Short-circuit if “Shutdown” mutex exists:
Code:
With NewCriticalSystemIndex("Reindex", Array("Global\Shutdown"))
If .IsShortCircuited Then Exit Sub
If .IsAcquired Then DoReindex
End With
API Overview
- Prepare(Name, [Namespace], [ShortCircuits], [SDDL], [ErrorPolicy])
- Acquire([TimeoutMs], [WaitBehavior], [ErrorPolicy])
- PrepareAndAcquire(Name, [Timeout], [Behavior], [Namespace], [ShortCircuits], [SDDL], [ErrorPolicy])
- Release
— drop ownership but keep handle prepared for re-acquire - Cancel
— cooperative cancel during cancellable waits - State helpers: IsAcquired, IsTimedOut, IsCancelled, IsShortCircuited, IsFailed, IsAbandoned
Other Notes
- Mutex names are case-sensitive.
- In cancellable mode, the class uses MsgWaitForMultipleObjects to keep the UI responsive.
- On any non-success path, resources are cleaned and state is set consistently.
- You can attach a list of “short-circuit” names to abort acquisition if those mutexes show up.
Feedback and bug reports are very welcome!
Changelog
- v1.0 — Initial public release. Single-use lifecycle, cancellable waits, short-circuits, abandoned detection, bitmask error policy, Global/Local helpers, SDDL support.
- v1.01 — Fixed a bug in short-circuiting logic (using wrong Mutex Name variable).
- v1.02 — Fixed a bug with too early LocalFree call - would obfuscate Err.LastDllError result of CreateMutex call and erroneous report error creating mutex instead of waiting for acquire.
- v1.03 — Improvements to the demo app to prevent re-entrancy in Cancellable mode. Also add improved comments and messages to the demo.
- v2.0 — Major refactor with lots of changes to the available methods, and new CMutexEx factory with helpers for common use cases, improvements to the demo, and some bug fixes.
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
Quote:
Private Sub mnuExtrasPerfTest_Click()
Dim tt As Currency
Dim ii As Long
Dim l_MutexName As String
' Unique name per run so we don? hit existing OS objects
l_MutexName = "22f94c11-66d0-48d4-8322-a6ac6a2de95b." & TickCount
Screen.MousePointer = vbHourglass
tt = TickCount ' TickCount returns seconds (see below)
' Count how many acquire/release cycles we can do in ~3 seconds
Do
ii = ii + 1
' Each instance is single-use; Acquire returns same object; scope end triggers Dispose
With New CMutexEx
With .Acquire(l_MutexName, , , mxep_None)
If Not .IsAcquired Then Debug.Assert False
End With
End With
Loop While TickCount - tt < 3 ' run for ~3 seconds
Screen.MousePointer = vbDefault
' Integer division gives rough ?er-second?throughput
MsgBox "Acquired and Disposed approximately " & ii \ 3 & " mutexes per second."
End Sub
I'm relatively new to mutex programming. In my past experience with mutexes, launching two instances of the same application would immediately trigger a lock warning. But with your implementation, duplicate processes seem to start without any mutex detection. What's the difference in the locking mechanism?
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
Quote:
Originally Posted by
xxdoc123
I'm relatively new to mutex programming. In my past experience with mutexes, launching two instances of the same application would immediately trigger a lock warning. But with your implementation, duplicate processes seem to start without any mutex detection. What's the difference in the locking mechanism?
My demo app doesn't attempt to acquire a mutex at startup (although it could be modified to do so) because it isn't trying to demonstrate that common usage of mutexes (that is, preventing multiple entire processes from running at the time). For that you could use a acquire mutex (in Sub Main() for example) and leave it alone to let it be released by the OS when your app closes - if the mutex can't be acquired, then you know another process is running and you can shutdown.
The idea behind this mutex class is for temporary blocking of code execution across processes using mutexes. So for example, say you are going to be modifying a shared resource in one process, but you don't want other processes modifying it until you are done - you can acquire/wait on a mutex and do you work safely. Even this is still within the usual use of a regular mutex, but what this mutex class adds to that are the following features:
- A bit safer to use (you can't accidentally forget to release the mutex) since cleanup happens automatically when the object terminates.
- Short-circuitable - For example, when you want processes to be able to jump out of waiting for a mutex and shutdown if your app installer starts running.
- UI friendly in that it can keep the UI alive (for example, progress marquee animations and user cancellation buttons can remain active) for non-critical operations that you want the user to be allowed to cancel if the mutex can't be acquired.
- Hides all the API stuff behind a simple class interface, which simplifies things a bit for end users.
Hope that clears things up a bit :)
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
If compile the demo and try running 2 copies of it, then create a mutex in one copy and try to create it in the other copy you will see the locking behaviour. If you then close the first copy, you will see the automatic acquiring behaviour in the waiting app.
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
Just fixed a bug in the Shortcircuiting implementation (was using the wrong variable for the Mutex name).
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
Just fixed another bug: too early LocalFree call - would obfuscate Err.LastDllError result of CreateMutex call and erroneously report error creating mutex in some circumstances instead of waiting for acquire.
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
(Hopefully) One last round of improvements to the demo app to prevent re-entrancy in Cancellable mode. Also added improved comments and messages to the demo.
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
Quote:
Originally Posted by
jpbro
If compile the demo and try running 2 copies of it, then create a mutex in one copy and try to create it in the other copy you will see the locking behaviour. If you then close the first copy, you will see the automatic acquiring behaviour in the waiting app.
I run ,but I ran it at the same time and didn't see what you said about locking it in advance. Can you send a picture to take a look?
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
I think that is a common misconception that mutexes only exist to prevent multiple instances of an application from running at once. That's a perfectly valid and common use case of Mutexes, but it's just one of many.
So, "Locking in advance" is not a requirement for mutexes - it's simply one way to use them. For example, if your goal is to ensure that only one instance of your app runs, it makes sense to attempt to acquire a mutex during startup. If you successfully acquire it, you know you're the first instance, but if not, you can assume another instance is already running and respond appropriately (for example, bring the existing instance to the foreground or show a message before exiting).
But that's only the tip of the iceberg. Mutexes can also be used as short-lived guards around specific critical operations that should *never* run concurrently across processes. For instance, imagine a multi-process application that interacts with a shared database file. The first time any process touches the database, it may need to create and initialize the schema. By acquiring a mutex before any database access, each process can safely check whether the database exists, create it if necessary, and then release the mutex once initialization is complete. This guarantees (within reason) that your database and schema are fully set up before any of your app processes attempts to use it.
A mutex is a fundamental building block/a primitive synchronization mechanism that you can use to build specialized synchronization tools and higher-level patterns. I'm currently refactoring this project to include a mutex factory module, which provides convenient helper methods that wrap and extend the basic mutex primitive into practical synchronization tools for cross-process coordination.
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
This could become part of TwinBasic and not be out of place. Mutex-es are provided by the o/s but there needs to be a language specific wrapper that makes it convenient and common for general use. You should raise this with Wayne JB.
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
I think this could be a good small project to try my hand at creating my first tB package.
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
New version in first post, major refactor with some bug fixes, more demo options, and a new MutexExFactory module that includes helper methods for creating Mutexes with various properties for the most common use cases. Helper methods are:
Code:
' Function Namespace Timeout Cancellable Lifetime
' ----------------------------------------------------------------------------------------
' NewCriticalSystemMutex() Global\ Infinite No Until Released
' NewCriticalSessionMutex() Local\ Infinite No Until Released
' NewReactiveSystemMutex() Global\ Infinite Yes Until Released
' NewReactiveSessionMutex() Local\ Infinite Yes Until Released
' NewZeroWaitSystemMutex() Global\ 0ms No Until Released
' NewZeroWaitSessionMutex() Local\ 0ms No Until Released
' NewPerpetualSystemMutex() Global\ 0ms No Process Lifetime
' NewPerpetualSessionMutex() Local\ 0ms No Process Lifetime
' NewTimedSystemMutex() Global\ Variable No Until Released
' NewTimedSessionMutex() Local\ Variable No Until Released
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
Quote:
Originally Posted by
xxdoc123
I run ,but I ran it at the same time and didn't see what you said about locking it in advance. Can you send a picture to take a look?
Try this in the latest version:
- Compile the demo app and run 1 instance of it.
- Run another instance of it - note that both instances are allowed to run.
- Close one of the instances
- In the remaining instance click the Extras menu, then click Block other app instances. This will acquire a perpetual mutex that will last for the rest of this app instances lifetime.
- Try to start another instance of the app - it will now show a message that another instance is running and give the option to Retry or Cancel.
- Click Retry a few times and notice that the app never starts, just keeps prompting you to Retry/Cancel.
- Close the fully running instance of the app
- Click Retry - the other instance of the app now starts.
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
Just a small change in the latest update - renamed the "This" property to "Self" (just preferred that name after some thought).
I think this project is essentially complete barring any bug reports, other demo requests, or new ideas for useful factory methods. Hope somebody finds it useful! :)
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
Quote:
Originally Posted by
jpbro
Try this in the latest version:
- Compile the demo app and run 1 instance of it.
- Run another instance of it - note that both instances are allowed to run.
- Close one of the instances
- In the remaining instance click the Extras menu, then click Block other app instances. This will acquire a perpetual mutex that will last for the rest of this app instances lifetime.
- Try to start another instance of the app - it will now show a message that another instance is running and give the option to Retry or Cancel.
- Click Retry a few times and notice that the app never starts, just keeps prompting you to Retry/Cancel.
- Close the fully running instance of the app
- Click Retry - the other instance of the app now starts.
It looks good, but many features are currently not usable. I hope there is a real use case where this module can actually be applied.thanks
Re: CMutexEx Class for Safer & Cooperative Mutex Handling
Quote:
Originally Posted by
xxdoc123
It looks good, but many features are currently not usable.
What could possibly look good about this if many features are not usable ;) Or put another way, what features are you looking for that are currently usable?
Quote:
I hope there is a real use case where this module can actually be applied.thanks
There is, else I wouldn't have posted it here.