mirror of
				https://github.com/facebookincubator/mvfst.git
				synced 2025-11-03 12:33:15 +03:00 
			
		
		
		
	Allow unset read callback during connection close
Summary: setReadCallback didn't function properly during shutdown 1) it was completely ignored when state_ == CLOSING 2) cancelAllAppCallbacks made a copy of readCallbacks_ This is problematic for application constructs that use groups of streams -- eg: HTTP WebTransport. When one stream (the WebTransport session) is reset during shutdown, it needs to clean up any dependent streams as well, including preventing them from getting error callbacks. The fix is to use only the streamId's from readCallbacksCopy for iteration, but look up the actual callback value from readCallbacks_. Note: there's an as yet unhandled corner case which is that a readError callback installs a new stream read callback, but it seems far fetched enough I'm not including a fix here. Reviewed By: sharmafb Differential Revision: D48159644 fbshipit-source-id: c9d522a6b200538193969d60d242a505831e4cd0
This commit is contained in:
		
				
					committed by
					
						
						Facebook GitHub Bot
					
				
			
			
				
	
			
			
			
						parent
						
							185d853a9c
						
					
				
				
					commit
					900e22e18d
				
			@@ -753,7 +753,7 @@ QuicTransportBaseLite::setReadCallback(
 | 
			
		||||
  if (isSendingStream(conn_->nodeType, id)) {
 | 
			
		||||
    return folly::makeUnexpected(LocalErrorCode::INVALID_OPERATION);
 | 
			
		||||
  }
 | 
			
		||||
  if (closeState_ != CloseState::OPEN) {
 | 
			
		||||
  if (cb != nullptr && closeState_ != CloseState::OPEN) {
 | 
			
		||||
    return folly::makeUnexpected(LocalErrorCode::CONNECTION_CLOSED);
 | 
			
		||||
  }
 | 
			
		||||
  if (!conn_->streamManager->streamExists(id)) {
 | 
			
		||||
@@ -2224,20 +2224,28 @@ void QuicTransportBaseLite::cancelAllAppCallbacks(
 | 
			
		||||
  // structure of read callbacks.
 | 
			
		||||
  // TODO: this approach will make the app unable to setReadCallback to
 | 
			
		||||
  // nullptr during the loop. Need to fix that.
 | 
			
		||||
  // TODO: setReadCallback to nullptr closes the stream, so the app
 | 
			
		||||
  // may just do that...
 | 
			
		||||
  auto readCallbacksCopy = readCallbacks_;
 | 
			
		||||
  for (auto& cb : readCallbacksCopy) {
 | 
			
		||||
    readCallbacks_.erase(cb.first);
 | 
			
		||||
    if (cb.second.readCb) {
 | 
			
		||||
      auto stream = CHECK_NOTNULL(conn_->streamManager->getStream(cb.first));
 | 
			
		||||
    auto streamId = cb.first;
 | 
			
		||||
    auto it = readCallbacks_.find(streamId);
 | 
			
		||||
    if (it == readCallbacks_.end()) {
 | 
			
		||||
      // An earlier call to readError removed the stream from readCallbacks
 | 
			
		||||
      // May not be possible?
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (it->second.readCb) {
 | 
			
		||||
      auto stream = CHECK_NOTNULL(conn_->streamManager->getStream(streamId));
 | 
			
		||||
      if (!stream->groupId) {
 | 
			
		||||
        cb.second.readCb->readError(cb.first, err);
 | 
			
		||||
        it->second.readCb->readError(streamId, err);
 | 
			
		||||
      } else {
 | 
			
		||||
        cb.second.readCb->readErrorWithGroup(cb.first, *stream->groupId, err);
 | 
			
		||||
        it->second.readCb->readErrorWithGroup(streamId, *stream->groupId, err);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    readCallbacks_.erase(it);
 | 
			
		||||
  }
 | 
			
		||||
  // TODO: what if a call to readError installs a new read callback?
 | 
			
		||||
  LOG_IF(ERROR, !readCallbacks_.empty())
 | 
			
		||||
      << readCallbacks_.size() << " read callbacks remaining to be cleared";
 | 
			
		||||
 | 
			
		||||
  VLOG(4) << "Clearing datagram callback";
 | 
			
		||||
  datagramCallback_ = nullptr;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user