netwerk/base/src/Tickler.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:4f8ce6d18279
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "Tickler.h"
7
8 #ifdef MOZ_USE_WIFI_TICKLER
9 #include "nsComponentManagerUtils.h"
10 #include "nsIPrefBranch.h"
11 #include "nsIPrefService.h"
12 #include "nsServiceManagerUtils.h"
13 #include "nsThreadUtils.h"
14 #include "prnetdb.h"
15
16 #include "AndroidBridge.h"
17
18 namespace mozilla {
19 namespace net {
20
21 NS_IMPL_ISUPPORTS(Tickler, nsISupportsWeakReference, Tickler)
22
23 Tickler::Tickler()
24 : mLock("Tickler::mLock")
25 , mActive(false)
26 , mCanceled(false)
27 , mEnabled(false)
28 , mDelay(16)
29 , mDuration(TimeDuration::FromMilliseconds(400))
30 , mFD(nullptr)
31 {
32 MOZ_ASSERT(NS_IsMainThread());
33 }
34
35 class TicklerThreadDestructor : public nsRunnable
36 {
37 public:
38 explicit TicklerThreadDestructor(nsIThread *aThread)
39 : mThread(aThread) { }
40
41 NS_IMETHOD Run() MOZ_OVERRIDE
42 {
43 MOZ_ASSERT(NS_IsMainThread());
44 if (mThread)
45 mThread->Shutdown();
46 return NS_OK;
47 }
48
49 private:
50 ~TicklerThreadDestructor() { }
51 nsCOMPtr<nsIThread> mThread;
52 };
53
54 Tickler::~Tickler()
55 {
56 // non main thread uses of the tickler should hold weak
57 // references to it if they must hold a reference at all
58 MOZ_ASSERT(NS_IsMainThread());
59
60 // Shutting down a thread can spin the event loop - which is a surprising
61 // thing to do from a dtor. Running it on its own event is safer.
62 nsRefPtr<nsIRunnable> event = new TicklerThreadDestructor(mThread);
63 if (NS_FAILED(NS_DispatchToCurrentThread(event))) {
64 mThread->Shutdown();
65 }
66 mThread = nullptr;
67
68 if (mTimer)
69 mTimer->Cancel();
70 if (mFD)
71 PR_Close(mFD);
72 }
73
74 nsresult
75 Tickler::Init()
76 {
77 MOZ_ASSERT(NS_IsMainThread());
78 MOZ_ASSERT(!mTimer);
79 MOZ_ASSERT(!mActive);
80 MOZ_ASSERT(!mThread);
81 MOZ_ASSERT(!mFD);
82
83 if (AndroidBridge::HasEnv()) {
84 mozilla::widget::android::GeckoAppShell::EnableNetworkNotifications();
85 }
86
87 mFD = PR_OpenUDPSocket(PR_AF_INET);
88 if (!mFD)
89 return NS_ERROR_FAILURE;
90
91 // make sure new socket has a ttl of 1
92 // failure is not fatal.
93 PRSocketOptionData opt;
94 opt.option = PR_SockOpt_IpTimeToLive;
95 opt.value.ip_ttl = 1;
96 PR_SetSocketOption(mFD, &opt);
97
98 nsresult rv = NS_NewNamedThread("wifi tickler",
99 getter_AddRefs(mThread));
100 if (NS_FAILED(rv))
101 return rv;
102
103 nsCOMPtr<nsITimer> tmpTimer(do_CreateInstance(NS_TIMER_CONTRACTID, &rv));
104 if (NS_FAILED(rv))
105 return rv;
106
107 rv = tmpTimer->SetTarget(mThread);
108 if (NS_FAILED(rv))
109 return rv;
110
111 mTimer.swap(tmpTimer);
112
113 mAddr.inet.family = PR_AF_INET;
114 mAddr.inet.port = PR_htons (4886);
115 mAddr.inet.ip = 0;
116
117 return NS_OK;
118 }
119
120 void Tickler::Tickle()
121 {
122 MutexAutoLock lock(mLock);
123 MOZ_ASSERT(mThread);
124 mLastTickle = TimeStamp::Now();
125 if (!mActive)
126 MaybeStartTickler();
127 }
128
129 void Tickler::PostCheckTickler()
130 {
131 mLock.AssertCurrentThreadOwns();
132 mThread->Dispatch(NS_NewRunnableMethod(this, &Tickler::CheckTickler),
133 NS_DISPATCH_NORMAL);
134 return;
135 }
136
137 void Tickler::MaybeStartTicklerUnlocked()
138 {
139 MutexAutoLock lock(mLock);
140 MaybeStartTickler();
141 }
142
143 void Tickler::MaybeStartTickler()
144 {
145 mLock.AssertCurrentThreadOwns();
146 if (!NS_IsMainThread()) {
147 NS_DispatchToMainThread(
148 NS_NewRunnableMethod(this, &Tickler::MaybeStartTicklerUnlocked),
149 NS_DISPATCH_NORMAL);
150 return;
151 }
152
153 if (!mPrefs)
154 mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
155 if (mPrefs) {
156 int32_t val;
157 bool boolVal;
158
159 if (NS_SUCCEEDED(mPrefs->GetBoolPref("network.tickle-wifi.enabled", &boolVal)))
160 mEnabled = boolVal;
161
162 if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.duration", &val))) {
163 if (val < 1)
164 val = 1;
165 if (val > 100000)
166 val = 100000;
167 mDuration = TimeDuration::FromMilliseconds(val);
168 }
169
170 if (NS_SUCCEEDED(mPrefs->GetIntPref("network.tickle-wifi.delay", &val))) {
171 if (val < 1)
172 val = 1;
173 if (val > 1000)
174 val = 1000;
175 mDelay = static_cast<uint32_t>(val);
176 }
177 }
178
179 PostCheckTickler();
180 }
181
182 void Tickler::CheckTickler()
183 {
184 MutexAutoLock lock(mLock);
185 MOZ_ASSERT(mThread == NS_GetCurrentThread());
186
187 bool shouldRun = (!mCanceled) &&
188 ((TimeStamp::Now() - mLastTickle) <= mDuration);
189
190 if ((shouldRun && mActive) || (!shouldRun && !mActive))
191 return; // no change in state
192
193 if (mActive)
194 StopTickler();
195 else
196 StartTickler();
197 }
198
199 void Tickler::Cancel()
200 {
201 MutexAutoLock lock(mLock);
202 MOZ_ASSERT(NS_IsMainThread());
203 mCanceled = true;
204 if (mThread)
205 PostCheckTickler();
206 }
207
208 void Tickler::StopTickler()
209 {
210 mLock.AssertCurrentThreadOwns();
211 MOZ_ASSERT(mThread == NS_GetCurrentThread());
212 MOZ_ASSERT(mTimer);
213 MOZ_ASSERT(mActive);
214
215 mTimer->Cancel();
216 mActive = false;
217 }
218
219 class TicklerTimer MOZ_FINAL : public nsITimerCallback
220 {
221 NS_DECL_THREADSAFE_ISUPPORTS
222 NS_DECL_NSITIMERCALLBACK
223
224 TicklerTimer(Tickler *aTickler)
225 {
226 mTickler = do_GetWeakReference(aTickler);
227 }
228
229 ~TicklerTimer() {};
230
231 private:
232 nsWeakPtr mTickler;
233 };
234
235 void Tickler::StartTickler()
236 {
237 mLock.AssertCurrentThreadOwns();
238 MOZ_ASSERT(mThread == NS_GetCurrentThread());
239 MOZ_ASSERT(!mActive);
240 MOZ_ASSERT(mTimer);
241
242 if (NS_SUCCEEDED(mTimer->InitWithCallback(new TicklerTimer(this),
243 mEnabled ? mDelay : 1000,
244 nsITimer::TYPE_REPEATING_SLACK)))
245 mActive = true;
246 }
247
248 // argument should be in network byte order
249 void Tickler::SetIPV4Address(uint32_t address)
250 {
251 mAddr.inet.ip = address;
252 }
253
254 // argument should be in network byte order
255 void Tickler::SetIPV4Port(uint16_t port)
256 {
257 mAddr.inet.port = port;
258 }
259
260 NS_IMPL_ISUPPORTS(TicklerTimer, nsITimerCallback)
261
262 NS_IMETHODIMP TicklerTimer::Notify(nsITimer *timer)
263 {
264 nsRefPtr<Tickler> tickler = do_QueryReferent(mTickler);
265 if (!tickler)
266 return NS_ERROR_FAILURE;
267 MutexAutoLock lock(tickler->mLock);
268
269 if (!tickler->mFD) {
270 tickler->StopTickler();
271 return NS_ERROR_FAILURE;
272 }
273
274 if (tickler->mCanceled ||
275 ((TimeStamp::Now() - tickler->mLastTickle) > tickler->mDuration)) {
276 tickler->StopTickler();
277 return NS_OK;
278 }
279
280 if (!tickler->mEnabled)
281 return NS_OK;
282
283 PR_SendTo(tickler->mFD, "", 0, 0, &tickler->mAddr, 0);
284 return NS_OK;
285 }
286
287 } // namespace mozilla::net
288 } // namespace mozilla
289
290 #else // not defined MOZ_USE_WIFI_TICKLER
291
292 namespace mozilla {
293 namespace net {
294 NS_IMPL_ISUPPORTS0(Tickler)
295 } // namespace mozilla::net
296 } // namespace mozilla
297
298 #endif // defined MOZ_USE_WIFI_TICKLER
299

mercurial