-
-
Notifications
You must be signed in to change notification settings - Fork 60
Single Instance Mutexes
Several Sucrose processes must run as a single instance — you should never have two tray icons, two settings windows, or two background services fighting over the same state. Sucrose enforces this with named Windows mutexes through a deliberately strict gate, Instance.Basic, that requires both a freshly-created mutex and a process-count check. This page documents the gate, the full mutex-name table, which processes gate versus which are intentionally re-runnable, and what happens on a double launch.
- The
Instance.Basicrule - The
Instance.Singlealternative - Mutex names
- Which processes gate
- What happens when the gate fails
- Double-launch behavior
- See also
File: src/Shared/Sucrose.Shared.Space/Helper/Instance.cs.
Instance.Basic(name, application):
- Creates a named
Mutex(true, name, out createdNew). - Returns
createdNew && Processor.WorkCount(application) <= 1— i.e. the mutex must be newly created AND at most one process of that executable name may currently exist. - The whole thing is wrapped in a 3-attempt retry; on persistent failure it returns
false.
This is a deliberately strict double-check: a fresh mutex alone is not enough — the process-count safeguard catches edge cases where a mutex was abandoned but a copy of the process is still alive.
Instance.Basic(name, application):
Mutex(true, name, out createdNew)
return createdNew && WorkCount(application) <= 1
An alternate helper, Instance.Single(name, application), returns Skylark.Wing.Utility.SingleInstance.IsAppMutexRunning(name) && WorkCount(application) <= 1 — i.e. it pairs the Skylark.Wing mutex check with the same process-count safeguard used by Instance.Basic. Instance.Basic is the one used by the startup gates documented below.
Mutex names are well-known, brace-wrapped constants (session-local — no Global\ prefix, so each is created in the per-session namespace), defined in src/Library/Sucrose.Memory/Manage/Readonly/Mutex.cs.
| Process | Mutex name |
|---|---|
| Live engine (shared by all 7 engines) | {Sucrose-Wallpaper-Engine-Live} |
| Portal | {Sucrose-Wallpaper-Engine-Portal} |
| Update | {Sucrose-Wallpaper-Engine-Update} |
| Property | {Sucrose-Wallpaper-Engine-Property} |
| Launcher | {Sucrose-Wallpaper-Engine-Launcher} |
| Reportdog | {Sucrose-Wallpaper-Engine-Reportdog} |
| Backgroundog | {Sucrose-Wallpaper-Engine-Backgroundog} |
All eight live engines share the single {Sucrose-Wallpaper-Engine-Live} mutex concept — only one wallpaper render engine may be alive at a time.
Processes that gate on single-instance via Instance.Basic in their OnStartup / Main:
| Process | Gates with Instance.Basic? |
|---|---|
| Launcher | Yes |
| Portal | Yes |
| Update | Yes |
| Property | Yes |
| Backgroundog | Yes |
| Reportdog | Yes |
| Commandog | No — intentionally re-runnable short-lived broker |
| Watchdog | No — intentionally re-runnable short-lived crash handler |
| Undo | No |
| Live engines | Yes — each engine calls Instance.Basic(SMMRM.Live, SMMRA.<engine>Live) (the shared {Sucrose-Wallpaper-Engine-Live} mutex) ANDed with an extra SSEHR.Check() engine-readiness check, so only one render engine may be alive at a time |
Commandog and Watchdog must be re-runnable: many actions and many crashes can occur in quick succession, and each spawns a fresh short-lived broker/handler. Do not describe them as resident services.
If Instance.Basic returns false (an instance already exists), the process does not stay resident. Behavior differs by process:
-
Launcher — redirects: it calls
SSLCI.Command()(open the Portal via✔Interface✖<Portal>) and thenClose()s itself. -
Portal — simply
Close()s (no second window). -
Property / Backgroundog / Reportdog / Update — exit without starting a second copy (via
Close()).
The most user-visible consequence: double-launching the Launcher opens the settings window, it does not create a second tray icon. Because the second Launcher instance fails the gate and redirects through Commandog to open the Portal, the user gets the management UI rather than a duplicate tray presence. Likewise, asking to open the Portal while it is already running just focuses on the existing window instead of spawning a duplicate.
- Architecture Overview — the full process roster.
- Lifecycle — where the gate runs during cold start.
- Commandog Dispatcher — the re-runnable broker that does not gate.
- Data Locations — exe and mutex names in context.
Getting Started
- Installation
- System Requirements
- Quick Start
- Portal Interface Tour
- Updating Sucrose
- Uninstalling Sucrose
Wallpaper Types
Using Sucrose
- Managing Library
- Using Store
- Customizing Wallpaper
- Multi-Monitor
- Wallpaper Cycling
- Choosing Engines
- Performance Rules
- Theme, Tray & Startup
- Discord Rich Presence
Settings Reference
- Settings Overview
- Settings: General
- Settings: Personal
- Settings: Performance
- Settings: Wallpaper
- Settings: System
- Settings: Other
- Settings: All Keys
Creating Wallpapers
- Create Overview
- Create: Step By Step
- Create: Package Format
- Create: Customization Controls
- Create: JS Bridge
- Create: Audio API
- Create: System API
- Create: Property Listener & Filters
- Create: Web Architecture
- Create: Compatibility
- Create: Example Wallpapers
- Create: Sharing & Publishing
Engine Reference
- Engines Overview
- Engine: MpvPlayer
- Engine: VlcPlayer
- Engine: WebView
- Engine: CefSharp
- Engine: Nebula
- Engine: Vexana
- Engine: Xavier
- Engine: Aurora
- Engine Comparison
Automation & Command Line
Architecture & Internals
- Architecture Overview
- Lifecycle
- Commandog Dispatcher
- Single-Instance Mutexes
- IPC
- Backgroundog Service
- Crash Reporting
- Update Internals
- Property Service
- Undo Internals
Data, Files & Diagnostics
Building & Contributing
- Building From Source
- Repository Layout
- Shared Item Projects
- Code Conventions
- Preprocessor Symbols
- Publish Pipeline
- Bundle Installer Internals
- Extending Sucrose
- Contributing
- Translating with Localizer
- Localization Coverage
- Security Policy
- Privacy & Telemetry
Help & Support