Single Instance Program with a Twist

I'm looking for a bit of Win32 help here.

I have a small program that runs in the background, listens for key presses, and automates stuff I don't feel like doing (mostly changing settings and showing notifications). It's mostly just an excuse to practice C++ again and learn more about the Win32 API.

What I want to do is enforce a single instance policy for the program. The twist is I want the surviving process to be the most recently opened process, not the existing process. This is complicated by the fact that hotkeys can only be registered for once in windows, making this a 'shared resource'.

Now, I've implemented this in a naive way with an event. It works like this:

    Process 1
  • Starts, creates the event.
  • Event didn't already exist, this is the first process.
  • RegisterHotkey
  • In the message loop wait for input or the event.

  • Process 2
  • Starts, creates the event.
  • Event already exists, this is not the first process.
  • Signal the event.
  • Wait on the event.

  • Process 1
  • Wakes up due to the event.
  • UnregisterHotkey
  • Signal the event.
  • Exit

  • Process 2
  • Wakes up due to the event.
  • RegisterHotkey
  • In the message loop wait for input or the event.

This mostly works, but it can fail in a couple places. One, since signalling an already signaled event has no effect, information is lost when two threads signal the event before it gets used. I don't think I managed to get this to happen in practice, but in theory it would lead to one of the threads getting stuck on a wait function. Two, the order in which threads are woken by the event is not strictly FIFO. It's mostly FIFO, hence the pattern mostly working. This manifests in a few ways, the important one being when a new thread signals the event it may wake a thread that isn't the one that actually owns the hotkeys and that usually leads to no one owning the hotkeys and everyone shutting down, including the newest instance. I'm able to cause the second issue fairly easily by highlighting the exe, holding enter for a few seconds, and waiting for Windows to decide to start waking threads 'out of order'.

I'm looking for suggestions on how to handle this robustly.

I've come up with a couple ideas that I'm fairly certain would work, but they're always complicated, involving 3 forms of synchronization (e.g. mutex for the hotkeys, manual reset event to signal threads to shutdown, and shared memory to know who the newest process is). I'm assuming there's simpler, more clever ways to handle this I've yet to come up with.

Here are my requirements:
  • The newest process always wins.
  • Solution uses 3 or fewer forms of synchronization.
  • OPTIONAL: An older process is notified once for every new process.
(remember, this is an exercise for fun and learning so I don't care that my requirements don't map to some 'real world use case').

The code is here if anyone wants to poke around: https://github.com/akbyrd/Pigeon

The optional requirement is because I show a notification when a process is superseded and it's fun to watch this happen: http://i.imgur.com/ApVYTLd.gif

Edited by Adam Byrd on
Instead of events you could use windows message. SendMessage(HWND_BROADCAST, ...) will end your specific message to all other windows and there's no risk to lose the event.

To solve out-of-order problem, you could get process handle and check when it was started (GetProcessTimes). If it is newer than your start time that means you are not latest process and you need to exit.
Hmm, I thought about SendMessage briefly, but I had forgotten about HWND_BROADCAST. I could broadcast and include the startup time as one of the parameters. I'm a bit hesitant because I'd rather not send a message to every window in existence if I can avoid it. I'll have to look into that. Do you know of a way to limit the broadcast to a specific window class or something?

I shied away from getting process handles for the same reason: I don't want to enumerate every running process.
I don't think you can limit message broadcasting to subset of windows.
But yeah, instead of enumerating processes you either send startup time or your process id and let the receiver figure out time.
I never followed up but I settled for using a mutex around the hotkeys and SendMessage(HWND_BROADCAST, ...) with the startup time and process ID as parameters. I'm not a fan of sending a message to every window in the system but this works perfectly in terms functionality. I left a note to maybe switch to enumerating processes manually later. It's still doing 'unnecessary' work, but at least I don't bother other windows.

Thanks for pointing me in the right direction mmozeiko.