You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
369 lines
8.1 KiB
369 lines
8.1 KiB
#include <tpwrap.h>
|
|
#include <windows.h>
|
|
|
|
class StaticTLS
|
|
{
|
|
public:
|
|
StaticTLS() : index_(TlsAlloc()){}
|
|
~StaticTLS() { if (index_!=TLS_OUT_OF_INDEXES) TlsFree(index_); index_ = TLS_OUT_OF_INDEXES;}
|
|
void * getValue(void) { return TlsGetValue(index_);}
|
|
BOOL setValue(void * ptr) { return TlsSetValue(index_, ptr);}
|
|
bool valid() { return index_!=TLS_OUT_OF_INDEXES;};
|
|
DWORD index_;
|
|
} HandlerTLS;
|
|
|
|
|
|
|
|
LIST_ENTRY
|
|
Dispatcher::handlerListHead_ = { &handlerListHead_, &handlerListHead_};
|
|
|
|
volatile
|
|
LONG Dispatcher::registeredHandlers_ = 0;
|
|
|
|
bool
|
|
Dispatcher::startShutDown_ = false;
|
|
|
|
CriticalSection
|
|
Dispatcher::handlerListLock_(NOTHROW_LOCK, 4000 & 0x80000000);
|
|
|
|
|
|
EventHandler::EventHandler(void) :
|
|
handlerID_(NULL), once_(0),
|
|
dispatcher_(0), refCount_(1),
|
|
scheduledClose_(0)
|
|
|
|
{ Flink = 0; Blink=0; };
|
|
|
|
EventHandler::~EventHandler(void)
|
|
{ }
|
|
|
|
HANDLE
|
|
EventHandler::getHandle(void)
|
|
{ return INVALID_HANDLE_VALUE;}
|
|
|
|
|
|
int
|
|
EventHandler::handleTimeout(void)
|
|
{ return -1; }
|
|
|
|
int
|
|
EventHandler::handleEvent(void)
|
|
{ return -1; }
|
|
|
|
void
|
|
EventHandler::close(void)
|
|
{ return; }
|
|
|
|
LONG
|
|
EventHandler::AddRef(void)
|
|
{ return InterlockedIncrement(&refCount_); }
|
|
|
|
LONG
|
|
EventHandler::Release(void)
|
|
{ LONG newReference = InterlockedDecrement(&refCount_);
|
|
if (newReference==0)
|
|
delete this;
|
|
return newReference;
|
|
}
|
|
|
|
DWORD
|
|
EventHandler::getTimeOut(void)
|
|
{ return INFINITE; }
|
|
|
|
|
|
|
|
VOID CALLBACK
|
|
Dispatcher::HandleWaitOrTimerCallback( PVOID lpParameter, BOOLEAN TimerOrWaitFired )
|
|
{
|
|
EventHandler * handler = reinterpret_cast<EventHandler *>(lpParameter);
|
|
|
|
if (startShutDown_)
|
|
return;
|
|
|
|
if (handler->scheduledClose_)
|
|
return;
|
|
|
|
assert(lpParameter!=0);
|
|
assert(HandlerTLS.getValue()==0);
|
|
HandlerTLS.setValue(lpParameter);
|
|
|
|
|
|
int result = 0;
|
|
if (TimerOrWaitFired == TRUE)
|
|
result = handler->handleTimeout();
|
|
else
|
|
result = handler->handleEvent();
|
|
|
|
if (result == -1)
|
|
removeHandler(*handler);
|
|
|
|
HandlerTLS.setValue(0);
|
|
};
|
|
|
|
|
|
int
|
|
Dispatcher::registerHandler(EventHandler& eh, int flags)
|
|
{
|
|
assert(startShutDown_==0);
|
|
assert(eh.handlerID_ == NULL);
|
|
|
|
eh.type_ = EventHandler::WAIT;
|
|
handlerListLock_.acquire();
|
|
BOOL retVal = RegisterWaitForSingleObject(&eh.handlerID_, eh.getHandle(), HandleWaitOrTimerCallback, &eh, eh.getTimeOut(), flags);
|
|
if (retVal)
|
|
openHandler(&eh);
|
|
handlerListLock_.release();
|
|
return retVal;
|
|
};
|
|
|
|
|
|
int
|
|
Dispatcher::registerHandlerOnce(EventHandler& eh, int flags)
|
|
{
|
|
assert(startShutDown_==0);
|
|
assert(eh.handlerID_ == NULL);
|
|
|
|
eh.type_ = EventHandler::WAIT;
|
|
handlerListLock_.acquire();
|
|
eh.once_ = 1;
|
|
BOOL retVal = RegisterWaitForSingleObject(&eh.handlerID_, eh.getHandle(), HandleWaitOrTimerCallback, &eh, eh.getTimeOut(), flags|WT_EXECUTEONLYONCE);
|
|
if (retVal)
|
|
openHandler(&eh);
|
|
handlerListLock_.release();
|
|
return retVal;
|
|
};
|
|
|
|
|
|
void Dispatcher::insertTail(EventHandler *entry)
|
|
{
|
|
|
|
LIST_ENTRY* _EX_Blink;
|
|
LIST_ENTRY* _EX_ListhandlerListHead_;
|
|
_EX_ListhandlerListHead_ = &handlerListHead_;
|
|
_EX_Blink = _EX_ListhandlerListHead_->Blink;
|
|
entry->Flink = _EX_ListhandlerListHead_;
|
|
entry->Blink = _EX_Blink;
|
|
_EX_Blink->Flink = entry;
|
|
_EX_ListhandlerListHead_->Blink = entry;
|
|
};
|
|
|
|
void Dispatcher::removeEntry(EventHandler *entry)
|
|
{
|
|
entry->Flink->Blink = entry->Blink;
|
|
entry->Blink->Flink = entry->Flink;
|
|
entry->Flink = 0;
|
|
entry->Blink = 0;
|
|
};
|
|
|
|
void Dispatcher::openHandler(EventHandler* entry)
|
|
{
|
|
entry->AddRef();
|
|
InterlockedIncrement(®isteredHandlers_);
|
|
insertTail(entry);
|
|
}
|
|
|
|
void Dispatcher::closeHandler(EventHandler* entry)
|
|
{
|
|
handlerListLock_.acquire();
|
|
removeEntry(entry);
|
|
handlerListLock_.release();
|
|
|
|
entry->handlerID_ = NULL;
|
|
entry->close();
|
|
entry->Release();
|
|
InterlockedDecrement(®isteredHandlers_);
|
|
};
|
|
|
|
int
|
|
Dispatcher::removeHandler(EventHandler& handler)
|
|
{
|
|
long scheduledDelete = InterlockedCompareExchange(&handler.scheduledClose_, 1, 0);
|
|
if (scheduledDelete==1)
|
|
return 0;
|
|
|
|
assert(handler.handlerID_);
|
|
if (HandlerTLS.getValue()==0)
|
|
{
|
|
return DeleteNotification(&handler);
|
|
}
|
|
else
|
|
{
|
|
if (PostDeleteNotification(handler)==0)
|
|
InterlockedDecrement(®isteredHandlers_);
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
int
|
|
Dispatcher::scheduleTimer(EventHandler& eh, DWORD first, DWORD repeat, DWORD mode )
|
|
{
|
|
assert(startShutDown_==0);
|
|
|
|
eh.type_ = EventHandler::TIMER;
|
|
handlerListLock_.acquire();
|
|
|
|
bool once = (repeat==0) ||( (mode & WT_EXECUTEONLYONCE) != 0);
|
|
if (once)
|
|
repeat = INFINITE-1;
|
|
eh.once_ = once;
|
|
|
|
BOOL retCode = CreateTimerQueueTimer(&eh.handlerID_, 0, TimerCallback, &eh, first, repeat, mode);
|
|
|
|
if (retCode)
|
|
openHandler(&eh);
|
|
handlerListLock_.release();
|
|
return retCode;
|
|
};
|
|
|
|
int
|
|
Dispatcher::cancelTimer(EventHandler& handler)
|
|
{
|
|
long scheduledDelete = InterlockedCompareExchange(&handler.scheduledClose_, 1, 0);
|
|
if (scheduledDelete==1)
|
|
return 0;
|
|
|
|
if (HandlerTLS.getValue()==0)
|
|
{
|
|
return DeleteTimer(&handler);
|
|
}
|
|
else
|
|
{
|
|
if(PostDeleteTimer(handler)==0)
|
|
InterlockedDecrement(®isteredHandlers_);
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
int
|
|
Dispatcher::changeTimer(EventHandler& handler, DWORD first, DWORD repeat)
|
|
{
|
|
assert(handler.handlerID_);
|
|
if (repeat==0)
|
|
{
|
|
repeat = INFINITE-1;
|
|
handler.once_ = 1;
|
|
}
|
|
return ChangeTimerQueueTimer(NULL, handler.handlerID_, first, repeat);
|
|
};
|
|
|
|
|
|
DWORD WINAPI Dispatcher::DeleteTimer( LPVOID lpParameter)
|
|
{
|
|
EventHandler * ev = reinterpret_cast<EventHandler *>(lpParameter);
|
|
if (DeleteTimerQueueTimer(NULL, ev->handlerID_, INVALID_HANDLE_VALUE))
|
|
{
|
|
closeHandler(ev);
|
|
return 1;
|
|
}
|
|
else
|
|
InterlockedDecrement(®isteredHandlers_);
|
|
return 0;
|
|
};
|
|
|
|
DWORD WINAPI Dispatcher::DeleteNotification( LPVOID lpParameter)
|
|
{
|
|
EventHandler * ev = reinterpret_cast<EventHandler *>(lpParameter);
|
|
if (UnregisterWaitEx(ev->handlerID_, INVALID_HANDLE_VALUE))
|
|
{
|
|
closeHandler(ev);
|
|
return 1;
|
|
}
|
|
else
|
|
InterlockedDecrement(®isteredHandlers_);
|
|
return 0;
|
|
};
|
|
|
|
BOOL Dispatcher::PostDeleteNotification(EventHandler& ev)
|
|
{
|
|
return QueueUserWorkItem(DeleteNotification, &ev, WT_EXECUTEDEFAULT);
|
|
}
|
|
|
|
BOOL Dispatcher::PostDeleteTimer(EventHandler& ev)
|
|
{
|
|
return QueueUserWorkItem(DeleteTimer, &ev, WT_EXECUTEDEFAULT);
|
|
};
|
|
|
|
void CALLBACK
|
|
Dispatcher::TimerCallback(void* lpParameter, BOOLEAN TimerOrWaitFired)
|
|
{
|
|
assert(registeredHandlers_!=0);
|
|
assert(HandlerTLS.getValue()==0);
|
|
assert(TimerOrWaitFired==TRUE);
|
|
assert(lpParameter!=0);
|
|
|
|
EventHandler * handler = reinterpret_cast<EventHandler*>(lpParameter);
|
|
|
|
if (startShutDown_)
|
|
return;
|
|
|
|
if (handler->scheduledClose_)
|
|
return;
|
|
|
|
if (handler->once_)
|
|
{
|
|
LONG fired = InterlockedCompareExchange(&handler->once_,2,1);
|
|
if (fired != 1)
|
|
return;
|
|
}
|
|
|
|
HandlerTLS.setValue(lpParameter);
|
|
|
|
int result = handler->handleTimeout();
|
|
if (result == -1)
|
|
cancelTimer(*handler);
|
|
HandlerTLS.setValue(0);
|
|
};
|
|
|
|
int
|
|
Dispatcher::open()
|
|
{ return handlerListLock_.valid() && HandlerTLS.valid(); };
|
|
|
|
int Dispatcher::close()
|
|
{
|
|
startShutDown_ = 1;
|
|
int outstandingHandlers = 0;
|
|
handlerListLock_.acquire();
|
|
LIST_ENTRY * p = &handlerListHead_;
|
|
LIST_ENTRY * next = p->Flink;
|
|
int entries = 0;
|
|
while((p = next) != &handlerListHead_)
|
|
{
|
|
next = next->Flink;
|
|
EventHandler * ev = static_cast<EventHandler *>(p);
|
|
long scheduledDelete = InterlockedCompareExchange(&ev->scheduledClose_, 1, 0);
|
|
if (!scheduledDelete)
|
|
{
|
|
if (ev->type_==EventHandler::WAIT)
|
|
DeleteNotification(ev);
|
|
else
|
|
DeleteTimer(ev);
|
|
}
|
|
}
|
|
handlerListLock_.release();
|
|
|
|
while(0!=registeredHandlers_)
|
|
if (!SwitchToThread())
|
|
Sleep(1);
|
|
|
|
p = &handlerListHead_;
|
|
next = p->Flink;
|
|
while((p = next) != &handlerListHead_)
|
|
{
|
|
BOOL ret;
|
|
next = next->Flink;
|
|
EventHandler * ev = static_cast<EventHandler *>(p);
|
|
|
|
if (ev->type_==EventHandler::WAIT)
|
|
ret = UnregisterWaitEx(ev->handlerID_, INVALID_HANDLE_VALUE);
|
|
else
|
|
ret = DeleteTimerQueueTimer(NULL, ev->handlerID_, INVALID_HANDLE_VALUE);
|
|
if (ret)
|
|
{
|
|
ev->close();
|
|
ev->Release();
|
|
}
|
|
}
|
|
|
|
startShutDown_ = 0;
|
|
return 0;
|
|
};
|