Strategy for waiting for two near-synchronous events
I have a library that I'm writing which processes keystroke events using the Win32 API. Every time a keystroke happens, two separate events occur almost simultaneously (this and this). In my library I have two seperate threads, one for each kind of event (hook and raw).
Thread 1 (hook) waits on Thread 2 (raw) to get its data before it proceeds. It currently uses a barrier-like mechanism to achieve this. So both events must come in before either one is actually processed. So far so good.
But there's a problem (of course). In an ideal world, I'd be guaranteed to get both events all the time. Unfortunately for reasons I don't understand, sometimes Windows decides not to deliver ONE of the two events to me (often times another app temporarily is screwing with the input). And so if I type "Hello world", Thread 1 may be processing "H" while Thread 2 may have never gotten the "H" event, and skips to "e". So the events get out of sync and all hell breaks loose.
Basically what I'd like is this: I want to pair the events in a way that makes sense. If Thread 1 gets an "H" event, and Thread 2 gets an "e", it should either (1) try to wait for the correct "H" event, or (2) time out (yes, Thread 1 can gracefully fail if it has to). Since I know that the events should both be coming in within a certain time window, I suppose that makes this a real-time programming task.
I know positively nothing about real-time programming. Are ther开发者_JAVA百科e already solutions / data structures for this? If so, what are they? If not, what would the general approach be for this type of problem (keeping two temporally close events in sync)?
Thanks.
I'm not an expert in win32, but I can't help but feel that if you're losing events, nothing's going to be completely reliable in any case. As I understand it, you're trying to control where keyboard events go (ie, to which window they go) based on the physical keyboard of origin - perhaps it would be better to, instead of filtering events, simply inject them in the first place.
That is, register a low-level keyboard hook that simply drops all injected events. Now use your raw keyboard event handler to inject emulated WM_KEYUP/WM_KEYDOWN messages to whatever window should receive them in the end.
Failing that, however, we'll need to (somehow) synchronize the WM_INPUT and WM_KEY* event streams. I'll assume you're already worked out how to share data between your injected hook DLL, and the WM_INPUT listener in your 'master' process. We'll start out by defining a cooked-keystream mutex. An application processing a WM_KEY* hook will immediately take this mutex before doing further processing, in order to avoid racing with other processes which may also be processing keys.
Your WM_INPUT-handling process can begin writing to a shared-memory ring buffer (synchronized by a named mutex) the keycodes received over WM_INPUT in order. Each keycode should have a timestamp; WM_KEY* hooks will ignore old keycodes. The WM_KEY* handlers will first check whether their event matches the oldest non-expired event in the buffer; if it is, a notification shall be sent to the master process, and the hook will do whatever processing is desired. No problem.
If there is no data in the ring buffer, a named semaphore can be used to sleep. The semaphore's count should be an optimistic count of the number of entries remaining in the ring buffer; acquiring a count gives a process the right and obligation to deque one item from the buffer if present, but if the buffer is empty, the process must drop the ring mutex and go back to waiting on the semaphore. The hook procedure can then put a (short!) timeout on its wait on the mutex; if the timeout expires, the hook shall consider there to have been a desync, and proceed with normal processing. It may be a good idea to then have the hook disable itself for a short interval, to avoid slowing down fast typing.
If the hook reads from the ring buffer a keycode that does not match the keystroke it was sent, then there has been a desync somewhere. The handling of this may depend on what you're doing; if you're implementing a hotkey system, for example, one approach would be to simply drop all raw events, and turn the hook into a pure pass-through, until all keyboards are silent for a short interval.
Keep in mind that, since you're dealing with unsynchronized threads, each seeing a different slice of multiple, unsynchronized event streams, desyncs will always be a problem. Event injection may still be a good way to avoid this, as it gives you a single synchronization point that can provide an ordering on events.
精彩评论