Last time, we broke up a series of operations on the persistent thread pool thread so that the thread pool did the waiting rather then clogging up the persistent thread with a synchronous wait. This does still have the downside of processing the notification on the persistent thread, which could be a problem if that processing is time-consuming.
What we can do is to use the persistent thread pool thread only for things that absolutely must be done on a persistent thread, and move everything else to a thread pool task thread.
Make the highlighted changes to the code we had from last time.
// Error checking elided for expository purposes. void WidgetMonitor::RegisterNotificationWait( void* parameter) { WidgetNotificationContext* context = reinterpret_cast<WidgetNotificationContext*>(parameter); RegisterWaitForSingleObject(&context->waitHandle, context->registryEvent, WidgetNotificationWaitCallback, context, INFINITE, WT_EXECUTEONLYONCE/* | WT_EXECUTEINPERSISTENTTHREAD */); RegNotifyChangeKeyValue(context->hkey, false, REG_NOTIFY_CHANGE_LAST_SET, context->registryEvent, TRUE); } void WidgetMonitor::WidgetNotificationWaitCallback( void* parameter, BOOLEAN /* TimerOrWaitFired */) { WidgetNotificationContext* context = reinterpret_cast<WidgetNotificationContext*>(parameter); ... process the change ... QueueUserWorkItem(RegisterNotificationWait, context, WT_EXECUTEINPERSISTENTTHREAD); } void WidgetMonitor::StartMonitoring() { auto context = new WidgetNotificationContext(); context->hkey = ...; context->registryEvent = ...; QueueUserWorkItem(RegisterNotificationWait, context, WT_EXECUTEINPERSISTENTTHREAD); } void WidgetMonitor::StopMonitoring( WidgetNotificationContext* context) { // WARNING! Massive race conditions here need to be addressed. if (context->waitHandle) { UnregisterWait(context->waitHandle); context->waitHandle = nullptr; } ... clean up other resources ... delete context; }
What we did this time was to put only the RegNotifyChangekeyValue
on the persistent thread. Everything else runs on a normal thread pool thread. That way, we minimize the amount of code running on the persistent thread.
The last fix we can make is to take advantage of a new feature in Windows 8: The REG_NOTIFY_THREAD_AGNOSTIC
flag, which turns off the old behavior of stopping the notification when the thread exits. With that change, we don’t need the WT_EXECUTEINPERSISTENTTHREAD
flag at all.
// Error checking elided for expository purposes. void WidgetMonitor::RegisterNotificationWait( WidgetNotificationContext* context) { RegisterWaitForSingleObject(&context->waitHandle, context->registryEvent, WidgetNotificationWaitCallback, context, INFINITE, WT_EXECUTEONLYONCE/* | WT_EXECUTEINPERSISTENTTHREAD */); RegNotifyChangeKeyValue(context->hkey, false, REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_THREAD_AGNOSTIC, context->registryEvent, TRUE); } void WidgetMonitor::WidgetNotificationWaitCallback( void* parameter, BOOLEAN /* TimerOrWaitFired */) { WidgetNotificationContext* context = reinterpret_cast<WidgetNotificationContext*>(parameter); ... process the change ... RegisterNotificationWait(context); } void WidgetMonitor::StartMonitoring() { auto context = new WidgetNotificationContext(); context->hkey = ...; context->registryEvent = ...; RegisterNotificationWait(context); } void WidgetMonitor::StopMonitoring( WidgetNotificationContext* context) { // WARNING! Massive race conditions here need to be addressed. if (context->waitHandle) { UnregisterWait(context->waitHandle); context->waitHandle = nullptr; } ... clean up other resources ... delete context; }
0 comments