Skip to content

Commit 84be70e

Browse files
author
Randell Jesup
committed
Bug 1749786 - websocketchannel cleanup r=necko-reviewers,kershaw
Differential Revision: https://phabricator.services.mozilla.com/D135727
1 parent 1c9aed5 commit 84be70e

File tree

3 files changed

+97
-27
lines changed

3 files changed

+97
-27
lines changed

netwerk/protocol/websocket/BaseWebSocketChannel.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ class BaseWebSocketChannel : public nsIWebSocketChannel,
103103
nsCOMPtr<nsILoadInfo> mLoadInfo;
104104
nsCOMPtr<nsITransportProvider> mServerTransportProvider;
105105

106-
// Used to ensure atomicity of mTargetThread
106+
// Used to ensure atomicity of mTargetThread.
107+
// Set before AsyncOpen via RetargetDeliveryTo or in AsyncOpen, never changed after AsyncOpen
107108
DataMutex<nsCOMPtr<nsIEventTarget>> mTargetThread{
108109
"BaseWebSocketChannel::EventTargetMutex"};
109110

netwerk/protocol/websocket/WebSocketChannel.cpp

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ class FailDelayManager {
240240
if (remainingDelay) {
241241
// reconnecting within delay interval: delay by remaining time
242242
nsresult rv;
243+
MutexAutoLock lock(ws->mMutex);
243244
rv = NS_NewTimerWithCallback(getter_AddRefs(ws->mReconnectDelayTimer),
244245
ws, remainingDelay,
245246
nsITimer::TYPE_ONE_SHOT);
@@ -427,9 +428,14 @@ class nsWSAdmissionManager {
427428
// Only way a connecting channel may get here w/o failing is if it
428429
// was closed with GOING_AWAY (1001) because of navigation, tab
429430
// close, etc.
430-
MOZ_ASSERT(
431+
#ifdef DEBUG
432+
{
433+
MutexAutoLock lock(aChannel->mMutex);
434+
MOZ_ASSERT(
431435
NS_FAILED(aReason) || aChannel->mScriptCloseCode == CLOSE_GOING_AWAY,
432436
"websocket closed while connecting w/o failing?");
437+
}
438+
#endif
433439
Unused << aReason;
434440

435441
sManager->RemoveFromQueue(aChannel);
@@ -1331,8 +1337,10 @@ void WebSocketChannel::BeginOpen(bool aCalledFromAdmissionManager) {
13311337
}
13321338
}
13331339

1340+
// MainThread
13341341
void WebSocketChannel::BeginOpenInternal() {
13351342
LOG(("WebSocketChannel::BeginOpenInternal() %p\n", this));
1343+
MOZ_ASSERT(NS_IsMainThread(), "not main thread");
13361344

13371345
nsresult rv;
13381346

@@ -2017,6 +2025,7 @@ void WebSocketChannel::PrimeNewOutgoingMessage() {
20172025
// If the channel user provided a code and reason during Close()
20182026
// and there isn't an internal error, use that.
20192027
if (NS_SUCCEEDED(mStopOnClose)) {
2028+
MutexAutoLock lock(mMutex);
20202029
if (mScriptCloseCode) {
20212030
NetworkEndian::writeUint16(payload, mScriptCloseCode);
20222031
mOutHeader[1] += 2;
@@ -2236,7 +2245,17 @@ class RemoveObserverRunnable : public Runnable {
22362245
} // namespace
22372246

22382247
void WebSocketChannel::CleanupConnection() {
2248+
// normally this should be called on socket thread, but it may be called
2249+
// on MainThread
2250+
22392251
LOG(("WebSocketChannel::CleanupConnection() %p", this));
2252+
// This needs to run on the IOThread so we don't need to lock a bunch of these
2253+
if (!mIOThread->IsOnCurrentThread()) {
2254+
mIOThread->Dispatch(
2255+
NewRunnableMethod("net::WebSocketChannel::CleanupConnection", this,
2256+
&WebSocketChannel::CleanupConnection),
2257+
NS_DISPATCH_NORMAL);
2258+
}
22402259

22412260
if (mLingeringCloseTimer) {
22422261
mLingeringCloseTimer->Cancel();
@@ -2271,8 +2290,7 @@ void WebSocketChannel::CleanupConnection() {
22712290
mConnectionLogService->RemoveHost(mHost, mSerial);
22722291
}
22732292

2274-
// This method can run in any thread, but the observer has to be removed on
2275-
// the main-thread.
2293+
// The observer has to be removed on the main-thread.
22762294
NS_DispatchToMainThread(new RemoveObserverRunnable(this));
22772295

22782296
DecrementSessionCount();
@@ -2298,7 +2316,9 @@ void WebSocketChannel::DoStopSession(nsresult reason) {
22982316
static_cast<uint32_t>(reason)));
22992317

23002318
// normally this should be called on socket thread, but it is ok to call it
2301-
// from OnStartRequest before the socket thread machine has gotten underway
2319+
// from OnStartRequest before the socket thread machine has gotten underway.
2320+
// If mDataStarted is false, this is called on MainThread for Close().
2321+
// Otherwise it should be called on the IO thread
23022322

23032323
MOZ_ASSERT(mStopped);
23042324
MOZ_ASSERT(mIOThread->IsOnCurrentThread() || mTCPClosed || !mDataStarted);
@@ -2317,14 +2337,19 @@ void WebSocketChannel::DoStopSession(nsresult reason) {
23172337
mCloseTimer = nullptr;
23182338
}
23192339

2340+
// mOpenTimer must be null if mDataStarted is true and we're not on MainThread
23202341
if (mOpenTimer) {
2342+
MOZ_ASSERT(NS_IsMainThread(), "not main thread");
23212343
mOpenTimer->Cancel();
23222344
mOpenTimer = nullptr;
23232345
}
23242346

2325-
if (mReconnectDelayTimer) {
2326-
mReconnectDelayTimer->Cancel();
2327-
mReconnectDelayTimer = nullptr;
2347+
{
2348+
MutexAutoLock lock(mMutex);
2349+
if (mReconnectDelayTimer) {
2350+
mReconnectDelayTimer->Cancel();
2351+
NS_ReleaseOnMainThread("WebSocketChannel::mMutex", mReconnectDelayTimer.forget());
2352+
}
23282353
}
23292354

23302355
if (mPingTimer) {
@@ -2406,6 +2431,8 @@ void WebSocketChannel::DoStopSession(nsresult reason) {
24062431
}
24072432
}
24082433

2434+
// Called from MainThread, and called from IOThread in
2435+
// PrimeNewOutgoingMessage
24092436
void WebSocketChannel::AbortSession(nsresult reason) {
24102437
LOG(("WebSocketChannel::AbortSession() %p [reason %" PRIx32
24112438
"] stopped = %d\n",
@@ -2415,6 +2442,7 @@ void WebSocketChannel::AbortSession(nsresult reason) {
24152442

24162443
// normally this should be called on socket thread, but it is ok to call it
24172444
// from the main thread before StartWebsocketData() has completed
2445+
MOZ_ASSERT(mIOThread->IsOnCurrentThread() || !mDataStarted);
24182446

24192447
// When we are failing we need to close the TCP connection immediately
24202448
// as per 7.1.1
@@ -3234,12 +3262,16 @@ WebSocketChannel::Notify(nsITimer* timer) {
32343262
}
32353263

32363264
AbortSession(NS_ERROR_NET_TIMEOUT);
3265+
// mReconnectDelayTimer is only modified on MainThread
32373266
} else if (timer == mReconnectDelayTimer) {
3238-
MOZ_ASSERT(mConnecting == CONNECTING_DELAYED,
3239-
"woke up from delay w/o being delayed?");
3267+
MOZ_RELEASE_ASSERT(mConnecting == CONNECTING_DELAYED,
3268+
"woke up from delay w/o being delayed?");
32403269
MOZ_ASSERT(NS_IsMainThread(), "not main thread");
32413270

3242-
mReconnectDelayTimer = nullptr;
3271+
{
3272+
MutexAutoLock lock(mMutex);
3273+
mReconnectDelayTimer = nullptr;
3274+
}
32433275
LOG(("WebSocketChannel: connecting [this=%p] after reconnect delay", this));
32443276
BeginOpen(false);
32453277
} else if (timer == mPingTimer) {
@@ -3344,7 +3376,7 @@ WebSocketChannel::AsyncOpenNative(nsIURI* aURI, const nsACString& aOrigin,
33443376

33453377
nsresult rv;
33463378

3347-
// Ensure target thread is set.
3379+
// Ensure target thread is set if RetargetDeliveryTo isn't called
33483380
{
33493381
auto lock = mTargetThread.Lock();
33503382
if (!lock.ref()) {
@@ -4034,7 +4066,7 @@ WebSocketChannel::OnInputStreamReady(nsIAsyncInputStream* aStream) {
40344066
nsresult rv;
40354067

40364068
do {
4037-
rv = mSocketIn->Read((char*)buffer, 2048, &count);
4069+
rv = mSocketIn->Read((char*)buffer, sizeof(buffer), &count);
40384070
LOG(("WebSocketChannel::OnInputStreamReady: read %u rv %" PRIx32 "\n",
40394071
count, static_cast<uint32_t>(rv)));
40404072

netwerk/protocol/websocket/WebSocketChannel.h

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -211,77 +211,104 @@ class WebSocketChannel : public BaseWebSocketChannel,
211211
}
212212

213213
nsCOMPtr<nsIEventTarget> mIOThread;
214+
// Set in AsyncOpenNative and AsyncOnChannelRedirect, modified in
215+
// DoStopSession on IO thread (.forget()). Probably ok...
214216
nsCOMPtr<nsIHttpChannelInternal> mChannel;
215217
nsCOMPtr<nsIHttpChannel> mHttpChannel;
218+
216219
nsCOMPtr<nsICancelable> mCancelable;
220+
// Mainthread only
217221
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
222+
// Set on Mainthread during AsyncOpen, used on IO thread and Mainthread
218223
nsCOMPtr<nsIRandomGenerator> mRandomGenerator;
219224

220-
nsCString mHashedSecret;
225+
nsCString mHashedSecret; // MainThread only
221226

222227
// Used as key for connection managment: Initially set to hostname from URI,
223228
// then to IP address (unless we're leaving DNS resolution to a proxy server)
229+
// MainThread only
224230
nsCString mAddress;
225231
int32_t mPort; // WS server port
226232
// Secondary key for the connection queue. Used by nsWSAdmissionManager.
227-
nsCString mOriginSuffix;
233+
nsCString mOriginSuffix; // MainThread only
228234

229235
// Used for off main thread access to the URI string.
236+
// Set on MainThread in AsyncOpenNative, used on TargetThread and IO thread
230237
nsCString mHost;
231238
nsString mEffectiveURL;
232239

240+
// Set on MainThread before multithread use, used on IO thread, cleared on IOThread
233241
nsCOMPtr<nsISocketTransport> mTransport;
234242
nsCOMPtr<nsIAsyncInputStream> mSocketIn;
235243
nsCOMPtr<nsIAsyncOutputStream> mSocketOut;
236244
RefPtr<WebSocketConnectionBase> mConnection;
237245

246+
// Only used on IO Thread (accessed when known-to-be-null in DoStopSession
247+
// on MainThread before mDataStarted)
238248
nsCOMPtr<nsITimer> mCloseTimer;
249+
// set in AsyncOpenInternal on MainThread, used on IO thread.
250+
// No multithread use before it's set, no changes after that.
239251
uint32_t mCloseTimeout; /* milliseconds */
240252

241-
nsCOMPtr<nsITimer> mOpenTimer;
242-
uint32_t mOpenTimeout; /* milliseconds */
243-
wsConnectingState mConnecting; /* 0 if not connecting */
253+
nsCOMPtr<nsITimer> mOpenTimer; /* Mainthread only */
254+
uint32_t mOpenTimeout; /* milliseconds, MainThread only */
255+
wsConnectingState mConnecting; /* 0 if not connecting, MainThread only */
256+
// Set on MainThread, deleted on either MainThread mainthread, used on
257+
// MainThread or IO Thread in DoStopSession
244258
nsCOMPtr<nsITimer> mReconnectDelayTimer;
245259

260+
// Only touched on IOThread (DoStopSession reads it on MainThread if
261+
// we haven't connected yet (mDataStarted==false), and it's always null
262+
// until mDataStarted=true)
246263
nsCOMPtr<nsITimer> mPingTimer;
247264

265+
// Created in DoStopSession on IO thread (mDataStarted=true), accessed
266+
// only from IO Thread
248267
nsCOMPtr<nsITimer> mLingeringCloseTimer;
249268
const static int32_t kLingeringCloseTimeout = 1000;
250269
const static int32_t kLingeringCloseThreshold = 50;
251270

252-
RefPtr<WebSocketEventService> mService;
271+
RefPtr<WebSocketEventService> mService; // effectively const
253272

254-
int32_t mMaxConcurrentConnections;
273+
int32_t
274+
mMaxConcurrentConnections; // only used in AsyncOpenNative on MainThread
255275

276+
// Set on MainThread in AsyncOpenNative; then used on IO thread
256277
uint64_t mInnerWindowID;
257278

258279
// following members are accessed only on the main thread
259280
uint32_t mGotUpgradeOK : 1;
260281
uint32_t mRecvdHttpUpgradeTransport : 1;
261282
uint32_t mAutoFollowRedirects : 1;
262283
uint32_t mAllowPMCE : 1;
263-
uint32_t : 0;
284+
uint32_t : 0; // ensure these aren't mixed with the next set
264285

265-
// following members are accessed only on the socket thread
286+
// following members are accessed only on the IO thread
266287
uint32_t mPingOutstanding : 1;
267288
uint32_t mReleaseOnTransmit : 1;
268289
uint32_t : 0;
269290

270291
Atomic<bool> mDataStarted;
292+
// All changes to mRequestedClose happen under mutex, but since it's atomic,
293+
// it can be read anywhere without a lock
271294
Atomic<bool> mRequestedClose;
295+
// mServer/ClientClosed are only modified on IOThread
272296
Atomic<bool> mClientClosed;
273297
Atomic<bool> mServerClosed;
298+
// All changes to mStopped happen under mutex, but since it's atomic, it
299+
// can be read anywhere without a lock
274300
Atomic<bool> mStopped;
275301
Atomic<bool> mCalledOnStop;
276302
Atomic<bool> mTCPClosed;
277303
Atomic<bool> mOpenedHttpChannel;
278304
Atomic<bool> mIncrementedSessionCount;
279305
Atomic<bool> mDecrementedSessionCount;
280306

281-
int32_t mMaxMessageSize;
307+
int32_t mMaxMessageSize; // set on MainThread in AsyncOpenNative, read on IO thread
308+
// Set on IOThread, or on MainThread before mDataStarted. Used on IO Thread (after mDataStarted)
282309
nsresult mStopOnClose;
283-
uint16_t mServerCloseCode;
284-
nsCString mServerCloseReason;
310+
uint16_t mServerCloseCode; // only used on IO thread
311+
nsCString mServerCloseReason; // only used on IO thread
285312
uint16_t mScriptCloseCode;
286313
nsCString mScriptCloseReason;
287314

@@ -292,6 +319,7 @@ class WebSocketChannel : public BaseWebSocketChannel,
292319
// increase the buffer temporarily, then drop back down to this size.
293320
const static uint32_t kIncomingBufferStableSize = 128 * 1024;
294321

322+
// Set at creation, used/modified only on IO thread
295323
uint8_t* mFramePtr;
296324
uint8_t* mBuffer;
297325
uint8_t mFragmentOpcode;
@@ -302,6 +330,7 @@ class WebSocketChannel : public BaseWebSocketChannel,
302330
// These are for the send buffers
303331
const static int32_t kCopyBreak = 1000;
304332

333+
// Only used on IO thread
305334
OutboundMessage* mCurrentOut;
306335
uint32_t mCurrentOutSent;
307336
nsDeque<OutboundMessage> mOutgoingMessages;
@@ -310,12 +339,20 @@ class WebSocketChannel : public BaseWebSocketChannel,
310339
uint32_t mHdrOutToSend;
311340
uint8_t* mHdrOut;
312341
uint8_t mOutHeader[kCopyBreak + 16]{0};
342+
343+
// Set on MainThread in OnStartRequest (before mDataStarted), used on IO
344+
// Thread (after mDataStarted), cleared in DoStopSession on IOThread or
345+
// on MainThread (if mDataStarted == false)
313346
UniquePtr<PMCECompression> mPMCECompressor;
347+
348+
// Used by EnsureHdrOut, which isn't called anywhere
314349
uint32_t mDynamicOutputSize;
315350
uint8_t* mDynamicOutput;
316-
bool mPrivateBrowsing;
351+
// Set on creation and AsyncOpen, read on both threads
352+
Atomic<bool> mPrivateBrowsing;
317353

318-
nsCOMPtr<nsIDashboardEventNotifier> mConnectionLogService;
354+
nsCOMPtr<nsIDashboardEventNotifier>
355+
mConnectionLogService; // effectively const
319356

320357
mozilla::Mutex mMutex;
321358
};

0 commit comments

Comments
 (0)