1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/Tickler.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,299 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "Tickler.h" 1.10 + 1.11 +#ifdef MOZ_USE_WIFI_TICKLER 1.12 +#include "nsComponentManagerUtils.h" 1.13 +#include "nsIPrefBranch.h" 1.14 +#include "nsIPrefService.h" 1.15 +#include "nsServiceManagerUtils.h" 1.16 +#include "nsThreadUtils.h" 1.17 +#include "prnetdb.h" 1.18 + 1.19 +#include "AndroidBridge.h" 1.20 + 1.21 +namespace mozilla { 1.22 +namespace net { 1.23 + 1.24 +NS_IMPL_ISUPPORTS(Tickler, nsISupportsWeakReference, Tickler) 1.25 + 1.26 +Tickler::Tickler() 1.27 + : mLock("Tickler::mLock") 1.28 + , mActive(false) 1.29 + , mCanceled(false) 1.30 + , mEnabled(false) 1.31 + , mDelay(16) 1.32 + , mDuration(TimeDuration::FromMilliseconds(400)) 1.33 + , mFD(nullptr) 1.34 +{ 1.35 + MOZ_ASSERT(NS_IsMainThread()); 1.36 +} 1.37 + 1.38 +class TicklerThreadDestructor : public nsRunnable 1.39 +{ 1.40 +public: 1.41 + explicit TicklerThreadDestructor(nsIThread *aThread) 1.42 + : mThread(aThread) { } 1.43 + 1.44 + NS_IMETHOD Run() MOZ_OVERRIDE 1.45 + { 1.46 + MOZ_ASSERT(NS_IsMainThread()); 1.47 + if (mThread) 1.48 + mThread->Shutdown(); 1.49 + return NS_OK; 1.50 + } 1.51 + 1.52 +private: 1.53 + ~TicklerThreadDestructor() { } 1.54 + nsCOMPtr<nsIThread> mThread; 1.55 +}; 1.56 + 1.57 +Tickler::~Tickler() 1.58 +{ 1.59 + // non main thread uses of the tickler should hold weak 1.60 + // references to it if they must hold a reference at all 1.61 + MOZ_ASSERT(NS_IsMainThread()); 1.62 + 1.63 + // Shutting down a thread can spin the event loop - which is a surprising 1.64 + // thing to do from a dtor. Running it on its own event is safer. 1.65 + nsRefPtr<nsIRunnable> event = new TicklerThreadDestructor(mThread); 1.66 + if (NS_FAILED(NS_DispatchToCurrentThread(event))) { 1.67 + mThread->Shutdown(); 1.68 + } 1.69 + mThread = nullptr; 1.70 + 1.71 + if (mTimer) 1.72 + mTimer->Cancel(); 1.73 + if (mFD) 1.74 + PR_Close(mFD); 1.75 +} 1.76 + 1.77 +nsresult 1.78 +Tickler::Init() 1.79 +{ 1.80 + MOZ_ASSERT(NS_IsMainThread()); 1.81 + MOZ_ASSERT(!mTimer); 1.82 + MOZ_ASSERT(!mActive); 1.83 + MOZ_ASSERT(!mThread); 1.84 + MOZ_ASSERT(!mFD); 1.85 + 1.86 + if (AndroidBridge::HasEnv()) { 1.87 + mozilla::widget::android::GeckoAppShell::EnableNetworkNotifications(); 1.88 + } 1.89 + 1.90 + mFD = PR_OpenUDPSocket(PR_AF_INET); 1.91 + if (!mFD) 1.92 + return NS_ERROR_FAILURE; 1.93 + 1.94 + // make sure new socket has a ttl of 1 1.95 + // failure is not fatal. 1.96 + PRSocketOptionData opt; 1.97 + opt.option = PR_SockOpt_IpTimeToLive; 1.98 + opt.value.ip_ttl = 1; 1.99 + PR_SetSocketOption(mFD, &opt); 1.100 + 1.101 + nsresult rv = NS_NewNamedThread("wifi tickler", 1.102 + getter_AddRefs(mThread)); 1.103 + if (NS_FAILED(rv)) 1.104 + return rv; 1.105 + 1.106 + nsCOMPtr<nsITimer> tmpTimer(do_CreateInstance(NS_TIMER_CONTRACTID, &rv)); 1.107 + if (NS_FAILED(rv)) 1.108 + return rv; 1.109 + 1.110 + rv = tmpTimer->SetTarget(mThread); 1.111 + if (NS_FAILED(rv)) 1.112 + return rv; 1.113 + 1.114 + mTimer.swap(tmpTimer); 1.115 + 1.116 + mAddr.inet.family = PR_AF_INET; 1.117 + mAddr.inet.port = PR_htons (4886); 1.118 + mAddr.inet.ip = 0; 1.119 + 1.120 + return NS_OK; 1.121 +} 1.122 + 1.123 +void Tickler::Tickle() 1.124 +{ 1.125 + MutexAutoLock lock(mLock); 1.126 + MOZ_ASSERT(mThread); 1.127 + mLastTickle = TimeStamp::Now(); 1.128 + if (!mActive) 1.129 + MaybeStartTickler(); 1.130 +} 1.131 + 1.132 +void Tickler::PostCheckTickler() 1.133 +{ 1.134 + mLock.AssertCurrentThreadOwns(); 1.135 + mThread->Dispatch(NS_NewRunnableMethod(this, &Tickler::CheckTickler), 1.136 + NS_DISPATCH_NORMAL); 1.137 + return; 1.138 +} 1.139 + 1.140 +void Tickler::MaybeStartTicklerUnlocked() 1.141 +{ 1.142 + MutexAutoLock lock(mLock); 1.143 + MaybeStartTickler(); 1.144 +} 1.145 + 1.146 +void Tickler::MaybeStartTickler() 1.147 +{ 1.148 + mLock.AssertCurrentThreadOwns(); 1.149 + if (!NS_IsMainThread()) { 1.150 + NS_DispatchToMainThread( 1.151 + NS_NewRunnableMethod(this, &Tickler::MaybeStartTicklerUnlocked), 1.152 + NS_DISPATCH_NORMAL); 1.153 + return; 1.154 + } 1.155 + 1.156 + if (!mPrefs) 1.157 + mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID); 1.158 + if (mPrefs) { 1.159 + int32_t val; 1.160 + bool boolVal; 1.161 + 1.162 + if (NS_SUCCEEDED(mPrefs->GetBoolPref("network.tickle-wifi.enabled", &boolVal))) 1.163 + mEnabled = boolVal; 1.164 + 1.165 + if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.duration", &val))) { 1.166 + if (val < 1) 1.167 + val = 1; 1.168 + if (val > 100000) 1.169 + val = 100000; 1.170 + mDuration = TimeDuration::FromMilliseconds(val); 1.171 + } 1.172 + 1.173 + if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.delay", &val))) { 1.174 + if (val < 1) 1.175 + val = 1; 1.176 + if (val > 1000) 1.177 + val = 1000; 1.178 + mDelay = static_cast<uint32_t>(val); 1.179 + } 1.180 + } 1.181 + 1.182 + PostCheckTickler(); 1.183 +} 1.184 + 1.185 +void Tickler::CheckTickler() 1.186 +{ 1.187 + MutexAutoLock lock(mLock); 1.188 + MOZ_ASSERT(mThread == NS_GetCurrentThread()); 1.189 + 1.190 + bool shouldRun = (!mCanceled) && 1.191 + ((TimeStamp::Now() - mLastTickle) <= mDuration); 1.192 + 1.193 + if ((shouldRun && mActive) || (!shouldRun && !mActive)) 1.194 + return; // no change in state 1.195 + 1.196 + if (mActive) 1.197 + StopTickler(); 1.198 + else 1.199 + StartTickler(); 1.200 +} 1.201 + 1.202 +void Tickler::Cancel() 1.203 +{ 1.204 + MutexAutoLock lock(mLock); 1.205 + MOZ_ASSERT(NS_IsMainThread()); 1.206 + mCanceled = true; 1.207 + if (mThread) 1.208 + PostCheckTickler(); 1.209 +} 1.210 + 1.211 +void Tickler::StopTickler() 1.212 +{ 1.213 + mLock.AssertCurrentThreadOwns(); 1.214 + MOZ_ASSERT(mThread == NS_GetCurrentThread()); 1.215 + MOZ_ASSERT(mTimer); 1.216 + MOZ_ASSERT(mActive); 1.217 + 1.218 + mTimer->Cancel(); 1.219 + mActive = false; 1.220 +} 1.221 + 1.222 +class TicklerTimer MOZ_FINAL : public nsITimerCallback 1.223 +{ 1.224 + NS_DECL_THREADSAFE_ISUPPORTS 1.225 + NS_DECL_NSITIMERCALLBACK 1.226 + 1.227 + TicklerTimer(Tickler *aTickler) 1.228 + { 1.229 + mTickler = do_GetWeakReference(aTickler); 1.230 + } 1.231 + 1.232 + ~TicklerTimer() {}; 1.233 + 1.234 +private: 1.235 + nsWeakPtr mTickler; 1.236 +}; 1.237 + 1.238 +void Tickler::StartTickler() 1.239 +{ 1.240 + mLock.AssertCurrentThreadOwns(); 1.241 + MOZ_ASSERT(mThread == NS_GetCurrentThread()); 1.242 + MOZ_ASSERT(!mActive); 1.243 + MOZ_ASSERT(mTimer); 1.244 + 1.245 + if (NS_SUCCEEDED(mTimer->InitWithCallback(new TicklerTimer(this), 1.246 + mEnabled ? mDelay : 1000, 1.247 + nsITimer::TYPE_REPEATING_SLACK))) 1.248 + mActive = true; 1.249 +} 1.250 + 1.251 +// argument should be in network byte order 1.252 +void Tickler::SetIPV4Address(uint32_t address) 1.253 +{ 1.254 + mAddr.inet.ip = address; 1.255 +} 1.256 + 1.257 +// argument should be in network byte order 1.258 +void Tickler::SetIPV4Port(uint16_t port) 1.259 +{ 1.260 + mAddr.inet.port = port; 1.261 +} 1.262 + 1.263 +NS_IMPL_ISUPPORTS(TicklerTimer, nsITimerCallback) 1.264 + 1.265 +NS_IMETHODIMP TicklerTimer::Notify(nsITimer *timer) 1.266 +{ 1.267 + nsRefPtr<Tickler> tickler = do_QueryReferent(mTickler); 1.268 + if (!tickler) 1.269 + return NS_ERROR_FAILURE; 1.270 + MutexAutoLock lock(tickler->mLock); 1.271 + 1.272 + if (!tickler->mFD) { 1.273 + tickler->StopTickler(); 1.274 + return NS_ERROR_FAILURE; 1.275 + } 1.276 + 1.277 + if (tickler->mCanceled || 1.278 + ((TimeStamp::Now() - tickler->mLastTickle) > tickler->mDuration)) { 1.279 + tickler->StopTickler(); 1.280 + return NS_OK; 1.281 + } 1.282 + 1.283 + if (!tickler->mEnabled) 1.284 + return NS_OK; 1.285 + 1.286 + PR_SendTo(tickler->mFD, "", 0, 0, &tickler->mAddr, 0); 1.287 + return NS_OK; 1.288 +} 1.289 + 1.290 +} // namespace mozilla::net 1.291 +} // namespace mozilla 1.292 + 1.293 +#else // not defined MOZ_USE_WIFI_TICKLER 1.294 + 1.295 +namespace mozilla { 1.296 +namespace net { 1.297 +NS_IMPL_ISUPPORTS0(Tickler) 1.298 +} // namespace mozilla::net 1.299 +} // namespace mozilla 1.300 + 1.301 +#endif // defined MOZ_USE_WIFI_TICKLER 1.302 +