mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	Bug 1941210 - Strongly grab FetchStreamReader while waiting for writing r=smaug
Nothing strongly grabs ReadableStream nor FetchStreamReader while waiting for nsIAsyncOutputStream to respond. mAsyncWaitReader should now strongly grab the reader until the output stream responds. Differential Revision: https://phabricator.services.mozilla.com/D234031
This commit is contained in:
		
							parent
							
								
									f51f04f6e1
								
							
						
					
					
						commit
						b9af86bfb4
					
				
					 3 changed files with 35 additions and 1 deletions
				
			
		| 
						 | 
					@ -72,9 +72,17 @@ nsresult OutputStreamHolder::AsyncWait(uint32_t aFlags,
 | 
				
			||||||
                                       uint32_t aRequestedCount,
 | 
					                                       uint32_t aRequestedCount,
 | 
				
			||||||
                                       nsIEventTarget* aEventTarget) {
 | 
					                                       nsIEventTarget* aEventTarget) {
 | 
				
			||||||
  mAsyncWaitWorkerRef = mWorkerRef;
 | 
					  mAsyncWaitWorkerRef = mWorkerRef;
 | 
				
			||||||
 | 
					  // Grab the strong reference for the reader but only when we are waiting for
 | 
				
			||||||
 | 
					  // the output stream, because it means we still have things to write.
 | 
				
			||||||
 | 
					  // (WAIT_CLOSURE_ONLY happens when waiting for ReadableStream to respond, at
 | 
				
			||||||
 | 
					  // which point the pull callback should get an indirect strong reference via
 | 
				
			||||||
 | 
					  // the controller argument.)
 | 
				
			||||||
 | 
					  mAsyncWaitReader =
 | 
				
			||||||
 | 
					      aFlags == nsIAsyncOutputStream::WAIT_CLOSURE_ONLY ? nullptr : mReader;
 | 
				
			||||||
  nsresult rv = mOutput->AsyncWait(this, aFlags, aRequestedCount, aEventTarget);
 | 
					  nsresult rv = mOutput->AsyncWait(this, aFlags, aRequestedCount, aEventTarget);
 | 
				
			||||||
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
					  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
				
			||||||
    mAsyncWaitWorkerRef = nullptr;
 | 
					    mAsyncWaitWorkerRef = nullptr;
 | 
				
			||||||
 | 
					    mAsyncWaitReader = nullptr;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return rv;
 | 
					  return rv;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -84,10 +92,16 @@ NS_IMETHODIMP OutputStreamHolder::OnOutputStreamReady(
 | 
				
			||||||
  // We may get called back after ::Shutdown()
 | 
					  // We may get called back after ::Shutdown()
 | 
				
			||||||
  if (!mReader) {
 | 
					  if (!mReader) {
 | 
				
			||||||
    mAsyncWaitWorkerRef = nullptr;
 | 
					    mAsyncWaitWorkerRef = nullptr;
 | 
				
			||||||
 | 
					    MOZ_ASSERT(!mAsyncWaitReader);
 | 
				
			||||||
    return NS_OK;
 | 
					    return NS_OK;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (!mReader->OnOutputStreamReady()) {
 | 
					
 | 
				
			||||||
 | 
					  // mAsyncWaitReader may be reset during OnOutputStreamReady, make sure to let
 | 
				
			||||||
 | 
					  // it live during the call
 | 
				
			||||||
 | 
					  RefPtr<FetchStreamReader> reader = mReader.get();
 | 
				
			||||||
 | 
					  if (!reader->OnOutputStreamReady()) {
 | 
				
			||||||
    mAsyncWaitWorkerRef = nullptr;
 | 
					    mAsyncWaitWorkerRef = nullptr;
 | 
				
			||||||
 | 
					    mAsyncWaitReader = nullptr;
 | 
				
			||||||
    return NS_OK;
 | 
					    return NS_OK;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return NS_OK;
 | 
					  return NS_OK;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,6 +51,7 @@ class OutputStreamHolder final : public nsIOutputStreamCallback {
 | 
				
			||||||
 private:
 | 
					 private:
 | 
				
			||||||
  ~OutputStreamHolder();
 | 
					  ~OutputStreamHolder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RefPtr<FetchStreamReader> mAsyncWaitReader;
 | 
				
			||||||
  // WeakPtr to avoid cycles
 | 
					  // WeakPtr to avoid cycles
 | 
				
			||||||
  WeakPtr<FetchStreamReader> mReader;
 | 
					  WeakPtr<FetchStreamReader> mReader;
 | 
				
			||||||
  // To ensure the worker sticks around
 | 
					  // To ensure the worker sticks around
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										19
									
								
								testing/web-platform/tests/fetch/api/basic/gc.any.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								testing/web-platform/tests/fetch/api/basic/gc.any.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					// META: global=window,worker
 | 
				
			||||||
 | 
					// META: script=/common/gc.js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					promise_test(async () => {
 | 
				
			||||||
 | 
					  let i = 0;
 | 
				
			||||||
 | 
					  const repeat = 5;
 | 
				
			||||||
 | 
					  const buffer = await new Response(new ReadableStream({
 | 
				
			||||||
 | 
					    pull(c) {
 | 
				
			||||||
 | 
					      if (i >= repeat) {
 | 
				
			||||||
 | 
					        c.close();
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      ++i;
 | 
				
			||||||
 | 
					      c.enqueue(new Uint8Array([0]))
 | 
				
			||||||
 | 
					      garbageCollect();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })).arrayBuffer();
 | 
				
			||||||
 | 
					  assert_equals(buffer.byteLength, repeat, `The buffer should be ${repeat}-byte long`);
 | 
				
			||||||
 | 
					}, "GC/CC should not abruptly close the stream while being consumed by Response");
 | 
				
			||||||
		Loading…
	
		Reference in a new issue