Bug 1201037 - coalesce network-events on Windows, r=mcmanus

This commit is contained in:
Daniel Stenberg 2015-10-08 03:52:00 +02:00
parent b3e7671265
commit 45cd0b2833
2 changed files with 88 additions and 19 deletions

View File

@ -47,6 +47,10 @@ static decltype(CancelMibChangeNotify2)* sCancelMibChangeNotify2;
#define NETWORK_NOTIFY_CHANGED_PREF "network.notify.changed"
// period during which to absorb subsequent network change events, in
// milliseconds
static const unsigned int kNetworkChangeCoalescingPeriod = 1000;
static void InitIphlpapi(void)
{
if (!sIphlpapi) {
@ -98,9 +102,11 @@ nsNotifyAddrListener::nsNotifyAddrListener()
: mLinkUp(true) // assume true by default
, mStatusKnown(false)
, mCheckAttempted(false)
, mShutdownEvent(nullptr)
, mCheckEvent(nullptr)
, mShutdown(false)
, mIPInterfaceChecksum(0)
, mAllowChangedEvent(true)
, mCoalescingActive(false)
{
InitIphlpapi();
}
@ -149,12 +155,30 @@ static void WINAPI OnInterfaceChange(PVOID callerContext,
notify->CheckLinkStatus();
}
DWORD
nsNotifyAddrListener::nextCoalesceWaitTime()
{
// check if coalescing period should continue
double period = (TimeStamp::Now() - mChangeTime).ToMilliseconds();
if (period >= kNetworkChangeCoalescingPeriod) {
SendEvent(NS_NETWORK_LINK_DATA_CHANGED);
mCoalescingActive = false;
return INFINITE; // return default
} else {
// wait no longer than to the end of the period
return static_cast<DWORD>
(kNetworkChangeCoalescingPeriod - period);
}
}
NS_IMETHODIMP
nsNotifyAddrListener::Run()
{
PR_SetCurrentThreadName("Link Monitor");
mChangedTime = TimeStamp::Now();
mStartTime = TimeStamp::Now();
DWORD waitTime = INFINITE;
if (!sNotifyIpInterfaceChange || !sCancelMibChangeNotify2) {
// For Windows versions which are older than Vista which lack
@ -162,7 +186,7 @@ nsNotifyAddrListener::Run()
HANDLE ev = CreateEvent(nullptr, FALSE, FALSE, nullptr);
NS_ENSURE_TRUE(ev, NS_ERROR_OUT_OF_MEMORY);
HANDLE handles[2] = { ev, mShutdownEvent };
HANDLE handles[2] = { ev, mCheckEvent };
OVERLAPPED overlapped = { 0 };
bool shuttingDown = false;
@ -172,9 +196,11 @@ nsNotifyAddrListener::Run()
DWORD ret = NotifyAddrChange(&h, &overlapped);
if (ret == ERROR_IO_PENDING) {
ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
ret = WaitForMultipleObjects(2, handles, FALSE, waitTime);
if (ret == WAIT_OBJECT_0) {
CheckLinkStatus();
} else if (!mShutdown) {
waitTime = nextCoalesceWaitTime();
} else {
shuttingDown = true;
}
@ -194,9 +220,15 @@ nsNotifyAddrListener::Run()
false, // no initial notification
&interfacechange);
if (ret == NO_ERROR) {
ret = WaitForSingleObject(mShutdownEvent, INFINITE);
}
do {
ret = WaitForSingleObject(mCheckEvent, waitTime);
if (!mShutdown) {
waitTime = nextCoalesceWaitTime();
}
else {
break;
}
} while (ret != WAIT_FAILED);
sCancelMibChangeNotify2(interfacechange);
}
return NS_OK;
@ -231,8 +263,8 @@ nsNotifyAddrListener::Init(void)
Preferences::AddBoolVarCache(&mAllowChangedEvent,
NETWORK_NOTIFY_CHANGED_PREF, true);
mShutdownEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
NS_ENSURE_TRUE(mShutdownEvent, NS_ERROR_OUT_OF_MEMORY);
mCheckEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
NS_ENSURE_TRUE(mCheckEvent, NS_ERROR_OUT_OF_MEMORY);
rv = NS_NewThread(getter_AddRefs(mThread), this);
NS_ENSURE_SUCCESS(rv, rv);
@ -249,10 +281,11 @@ nsNotifyAddrListener::Shutdown(void)
if (observerService)
observerService->RemoveObserver(this, "xpcom-shutdown-threads");
if (!mShutdownEvent)
if (!mCheckEvent)
return NS_OK;
SetEvent(mShutdownEvent);
mShutdown = true;
SetEvent(mCheckEvent);
nsresult rv = mThread->Shutdown();
@ -261,12 +294,33 @@ nsNotifyAddrListener::Shutdown(void)
// via its mRunnable
mThread = nullptr;
CloseHandle(mShutdownEvent);
mShutdownEvent = nullptr;
CloseHandle(mCheckEvent);
mCheckEvent = nullptr;
return rv;
}
/*
* A network event has been registered. Delay the actual sending of the event
* for a while and absorb subsequent events in the mean time in an effort to
* squash potentially many triggers into a single event.
* Only ever called from the same thread.
*/
nsresult
nsNotifyAddrListener::NetworkChanged()
{
if (mCoalescingActive) {
LOG(("NetworkChanged: absorbed an event (coalescing active)\n"));
} else {
// A fresh trigger!
mChangeTime = TimeStamp::Now();
mCoalescingActive = true;
SetEvent(mCheckEvent);
LOG(("NetworkChanged: coalescing period started\n"));
}
return NS_OK;
}
/* Sends the given event. Assumes aEventID never goes out of scope (static
* strings are ideal).
*/
@ -517,12 +571,12 @@ nsNotifyAddrListener::CheckLinkStatus(void)
}
if (mLinkUp && (prevCsum != mIPInterfaceChecksum)) {
TimeDuration since = TimeStamp::Now() - mChangedTime;
TimeDuration since = TimeStamp::Now() - mStartTime;
// Network is online. Topology has changed. Always send CHANGED
// before UP - if allowed to and having cooled down.
if (mAllowChangedEvent && (since.ToMilliseconds() > 2000)) {
SendEvent(NS_NETWORK_LINK_DATA_CHANGED);
NetworkChanged();
}
}
if (prevLinkUp != mLinkUp) {

View File

@ -60,18 +60,33 @@ protected:
nsCOMPtr<nsIThread> mThread;
HANDLE mShutdownEvent;
private:
// Returns the new timeout period for coalescing (or INFINITE)
DWORD nextCoalesceWaitTime();
// Called for every detected network change
nsresult NetworkChanged();
HANDLE mCheckEvent;
// set true when mCheckEvent means shutdown
bool mShutdown;
// This is a checksum of various meta data for all network interfaces
// considered UP at last check.
ULONG mIPInterfaceChecksum;
// time of the last sent changed event
mozilla::TimeStamp mChangedTime;
// start time of the checking
mozilla::TimeStamp mStartTime;
// Network changed events are enabled
bool mAllowChangedEvent;
// Flag set while coalescing change events
bool mCoalescingActive;
// Time stamp for first event during coalescing
mozilla::TimeStamp mChangeTime;
};
#endif /* NSNOTIFYADDRLISTENER_H_ */