Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Netbeans/ide/libs.antlr3.runtime/   (Apache JAVA IDE Version 28©)  Datei vom 3.10.2025 mit Größe 230 B image not shown  

Quelle  nsInputStreamPump.cpp   Sprache: unbekannt

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=4 sts=2 sw=2 et cin: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "nsIOService.h"
#include "nsInputStreamPump.h"
#include "nsIStreamTransportService.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "nsThreadUtils.h"
#include "nsCOMPtr.h"
#include "mozilla/Logging.h"
#include "mozilla/NonBlockingAsyncInputStream.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/SlicedInputStream.h"
#include "mozilla/StaticPrefs_network.h"
#include "nsIStreamListener.h"
#include "nsILoadGroup.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsStreamUtils.h"
#include <algorithm>

//
// MOZ_LOG=nsStreamPump:5
//
static mozilla::LazyLogModule gStreamPumpLog("nsStreamPump");
#undef LOG
#define LOG(args) MOZ_LOG(gStreamPumpLog, mozilla::LogLevel::Debug, args)

//-----------------------------------------------------------------------------
// nsInputStreamPump methods
//-----------------------------------------------------------------------------

nsInputStreamPump::nsInputStreamPump() : mOffMainThread(!NS_IsMainThread()) {}

nsresult nsInputStreamPump::Create(nsInputStreamPump** result,
                                   nsIInputStream* stream, uint32_t segsize,
                                   uint32_t segcount, bool closeWhenDone,
                                   nsISerialEventTarget* mainThreadTarget) {
  nsresult rv = NS_ERROR_OUT_OF_MEMORY;
  RefPtr<nsInputStreamPump> pump = new nsInputStreamPump();
  if (pump) {
    rv = pump->Init(stream, segsize, segcount, closeWhenDone, mainThreadTarget);
    if (NS_SUCCEEDED(rv)) {
      pump.forget(result);
    }
  }
  return rv;
}

struct PeekData {
  PeekData(nsInputStreamPump::PeekSegmentFun fun, void* closure)
      : mFunc(fun), mClosure(closure) {}

  nsInputStreamPump::PeekSegmentFun mFunc;
  void* mClosure;
};

static nsresult CallPeekFunc(nsIInputStream* aInStream, void* aClosure,
                             const char* aFromSegment, uint32_t aToOffset,
                             uint32_t aCount, uint32_t* aWriteCount) {
  NS_ASSERTION(aToOffset == 0"Called more than once?");
  NS_ASSERTION(aCount > 0"Called without data?");

  PeekData* data = static_cast<PeekData*>(aClosure);
  data->mFunc(data->mClosure, reinterpret_cast<const uint8_t*>(aFromSegment),
              aCount);
  return NS_BINDING_ABORTED;
}

nsresult nsInputStreamPump::PeekStream(PeekSegmentFun callback, void* closure) {
  RecursiveMutexAutoLock lock(mMutex);

  MOZ_ASSERT(mAsyncStream, "PeekStream called without stream");

  nsresult rv = CreateBufferedStreamIfNeeded();
  NS_ENSURE_SUCCESS(rv, rv);

  // See if the pipe is closed by checking the return of Available.
  uint64_t dummy64;
  rv = mAsyncStream->Available(&dummy64);
  if (NS_FAILED(rv)) return rv;
  uint32_t dummy = (uint32_t)std::min(dummy64, (uint64_t)UINT32_MAX);

  PeekData data(callback, closure);
  return mAsyncStream->ReadSegments(
      CallPeekFunc, &data, mozilla::net::nsIOService::gDefaultSegmentSize,
      &dummy);
}

nsresult nsInputStreamPump::EnsureWaiting() {
  mMutex.AssertCurrentThreadIn();

  // no need to worry about multiple threads... an input stream pump lives
  // on only one thread at a time.
  MOZ_ASSERT(mAsyncStream);
  if (!mWaitingForInputStreamReady && !mProcessingCallbacks) {
    // Ensure OnStateStop is called on the main thread only when this pump is
    // created on main thread.
    if (mState == STATE_STOP && !mOffMainThread) {
      nsCOMPtr<nsISerialEventTarget> mainThread =
          mLabeledMainThreadTarget
              ? mLabeledMainThreadTarget
              : do_AddRef(mozilla::GetMainThreadSerialEventTarget());
      if (mTargetThread != mainThread) {
        mTargetThread = mainThread;
      }
    }
    MOZ_ASSERT(mTargetThread);
    nsresult rv = mAsyncStream->AsyncWait(this00, mTargetThread);
    if (NS_FAILED(rv)) {
      NS_ERROR("AsyncWait failed");
      return rv;
    }
    // Any retargeting during STATE_START or START_TRANSFER is complete
    // after the call to AsyncWait; next callback will be on mTargetThread.
    mRetargeting = false;
    mWaitingForInputStreamReady = true;
  }
  return NS_OK;
}

//-----------------------------------------------------------------------------
// nsInputStreamPump::nsISupports
//-----------------------------------------------------------------------------

// although this class can only be accessed from one thread at a time, we do
// allow its ownership to move from thread to thread, assuming the consumer
// understands the limitations of this.
NS_IMPL_ADDREF(nsInputStreamPump)
NS_IMPL_RELEASE(nsInputStreamPump)
NS_INTERFACE_MAP_BEGIN(nsInputStreamPump)
  NS_INTERFACE_MAP_ENTRY(nsIRequest)
  NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
  NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
  NS_INTERFACE_MAP_ENTRY(nsIInputStreamPump)
  NS_INTERFACE_MAP_ENTRY_CONCRETE(nsInputStreamPump)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStreamPump)
NS_INTERFACE_MAP_END

//-----------------------------------------------------------------------------
// nsInputStreamPump::nsIRequest
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsInputStreamPump::GetName(nsACString& result) {
  RecursiveMutexAutoLock lock(mMutex);

  result.Truncate();
  return NS_OK;
}

NS_IMETHODIMP
nsInputStreamPump::IsPending(bool* result) {
  RecursiveMutexAutoLock lock(mMutex);

  *result = (mState != STATE_IDLE && mState != STATE_DEAD);
  return NS_OK;
}

NS_IMETHODIMP
nsInputStreamPump::GetStatus(nsresult* status) {
  RecursiveMutexAutoLock lock(mMutex);

  *status = mStatus;
  return NS_OK;
}

NS_IMETHODIMP nsInputStreamPump::SetCanceledReason(const nsACString& aReason) {
  return SetCanceledReasonImpl(aReason);
}

NS_IMETHODIMP nsInputStreamPump::GetCanceledReason(nsACString& aReason) {
  return GetCanceledReasonImpl(aReason);
}

NS_IMETHODIMP nsInputStreamPump::CancelWithReason(nsresult aStatus,
                                                  const nsACString& aReason) {
  return CancelWithReasonImpl(aStatus, aReason);
}

NS_IMETHODIMP
nsInputStreamPump::Cancel(nsresult status) {
  RecursiveMutexAutoLock lock(mMutex);

  AssertOnThread();

  LOG(("nsInputStreamPump::Cancel [this=%p status=%" PRIx32 "]\n"this,
       static_cast<uint32_t>(status)));

  if (NS_FAILED(mStatus)) {
    LOG(("  already canceled\n"));
    return NS_OK;
  }

  NS_ASSERTION(NS_FAILED(status), "cancel with non-failure status code");
  mStatus = status;

  // close input stream
  if (mAsyncStream) {
    // If mSuspendCount != 0, EnsureWaiting will be called by Resume().
    // Note that while suspended, OnInputStreamReady will
    // not do anything, and also note that calling asyncWait
    // on a closed stream works and will dispatch an event immediately.

    nsCOMPtr<nsIEventTarget> currentTarget = NS_GetCurrentThread();
    if (mTargetThread && currentTarget != mTargetThread) {
      nsresult rv = mTargetThread->Dispatch(NS_NewRunnableFunction(
          "nsInputStreamPump::Cancel", [self = RefPtr{this}, status] {
            RecursiveMutexAutoLock lock(self->mMutex);
            if (!self->mAsyncStream) {
              return;
            }
            self->mAsyncStream->CloseWithStatus(status);
            if (self->mSuspendCount == 0) {
              self->EnsureWaiting();
            }
          }));
      NS_ENSURE_SUCCESS(rv, rv);
    } else {
      mAsyncStream->CloseWithStatus(status);
      if (mSuspendCount == 0) {
        EnsureWaiting();
      }
    }
  }
  return NS_OK;
}

NS_IMETHODIMP
nsInputStreamPump::Suspend() {
  RecursiveMutexAutoLock lock(mMutex);

  LOG(("nsInputStreamPump::Suspend [this=%p]\n"this));
  NS_ENSURE_TRUE(mState != STATE_IDLE && mState != STATE_DEAD,
                 NS_ERROR_UNEXPECTED);
  ++mSuspendCount;
  return NS_OK;
}

NS_IMETHODIMP
nsInputStreamPump::Resume() {
  RecursiveMutexAutoLock lock(mMutex);

  LOG(("nsInputStreamPump::Resume [this=%p]\n"this));
  NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
  NS_ENSURE_TRUE(mState != STATE_IDLE && mState != STATE_DEAD,
                 NS_ERROR_UNEXPECTED);

  // There is a brief in-between state when we null out mAsyncStream in
  // OnStateStop() before calling OnStopRequest, and only afterwards set
  // STATE_DEAD, which we need to handle gracefully.
  if (--mSuspendCount == 0 && mAsyncStream) {
    EnsureWaiting();
  }
  return NS_OK;
}

NS_IMETHODIMP
nsInputStreamPump::GetLoadFlags(nsLoadFlags* aLoadFlags) {
  RecursiveMutexAutoLock lock(mMutex);

  *aLoadFlags = mLoadFlags;
  return NS_OK;
}

NS_IMETHODIMP
nsInputStreamPump::SetLoadFlags(nsLoadFlags aLoadFlags) {
  RecursiveMutexAutoLock lock(mMutex);

  mLoadFlags = aLoadFlags;
  return NS_OK;
}

NS_IMETHODIMP
nsInputStreamPump::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
  return GetTRRModeImpl(aTRRMode);
}

NS_IMETHODIMP
nsInputStreamPump::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
  return SetTRRModeImpl(aTRRMode);
}

NS_IMETHODIMP
nsInputStreamPump::GetLoadGroup(nsILoadGroup** aLoadGroup) {
  RecursiveMutexAutoLock lock(mMutex);

  *aLoadGroup = do_AddRef(mLoadGroup).take();
  return NS_OK;
}

NS_IMETHODIMP
nsInputStreamPump::SetLoadGroup(nsILoadGroup* aLoadGroup) {
  RecursiveMutexAutoLock lock(mMutex);

  mLoadGroup = aLoadGroup;
  return NS_OK;
}

//-----------------------------------------------------------------------------
// nsInputStreamPump::nsIInputStreamPump implementation
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsInputStreamPump::Init(nsIInputStream* stream, uint32_t segsize,
                        uint32_t segcount, bool closeWhenDone,
                        nsISerialEventTarget* mainThreadTarget) {
  // probably we can't be multithread-accessed yet
  RecursiveMutexAutoLock lock(mMutex);
  NS_ENSURE_TRUE(mState == STATE_IDLE, NS_ERROR_IN_PROGRESS);

  mStream = stream;
  mSegSize = segsize;
  mSegCount = segcount;
  mCloseWhenDone = closeWhenDone;
  mLabeledMainThreadTarget = mainThreadTarget;
  if (mOffMainThread && mLabeledMainThreadTarget) {
    MOZ_ASSERT(
        false,
        "Init stream pump off main thread with a main thread event target.");
    return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

NS_IMETHODIMP
nsInputStreamPump::AsyncRead(nsIStreamListener* listener) {
  RecursiveMutexAutoLock lock(mMutex);

  // This ensures only one thread can interact with a pump at a time
  NS_ENSURE_TRUE(mState == STATE_IDLE, NS_ERROR_IN_PROGRESS);
  NS_ENSURE_ARG_POINTER(listener);
  MOZ_ASSERT(NS_IsMainThread() || mOffMainThread,
             "nsInputStreamPump should be read from the "
             "main thread only.");

  nsresult rv = NS_MakeAsyncNonBlockingInputStream(
      mStream.forget(), getter_AddRefs(mAsyncStream), mCloseWhenDone, mSegSize,
      mSegCount);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  MOZ_ASSERT(mAsyncStream);

  // mStreamOffset now holds the number of bytes currently read.
  mStreamOffset = 0;

  // grab event queue (we must do this here by contract, since all notifications
  // must go to the thread which called AsyncRead)
  if (NS_IsMainThread() && mLabeledMainThreadTarget) {
    mTargetThread = mLabeledMainThreadTarget;
  } else {
    mTargetThread = mozilla::GetCurrentSerialEventTarget();
  }
  NS_ENSURE_STATE(mTargetThread);

  rv = EnsureWaiting();
  if (NS_FAILED(rv)) return rv;

  if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr);

  mState = STATE_START;
  mListener = listener;
  return NS_OK;
}

//-----------------------------------------------------------------------------
// nsInputStreamPump::nsIInputStreamCallback implementation
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsInputStreamPump::OnInputStreamReady(nsIAsyncInputStream* stream) {
  LOG(("nsInputStreamPump::OnInputStreamReady [this=%p]\n"this));

  AUTO_PROFILER_LABEL("nsInputStreamPump::OnInputStreamReady", NETWORK);

  // this function has been called from a PLEvent, so we can safely call
  // any listener or progress sink methods directly from here.

  for (;;) {
    // There should only be one iteration of this loop happening at a time.
    // To prevent AsyncWait() (called during callbacks or on other threads)
    // from creating a parallel OnInputStreamReady(), we use:
    // -- a mutex; and
    // -- a boolean mProcessingCallbacks to detect parallel loops
    //    when exiting the mutex for callbacks.
    RecursiveMutexAutoLock lock(mMutex);

    // Prevent parallel execution during callbacks, while out of mutex.
    if (mProcessingCallbacks) {
      MOZ_ASSERT(!mProcessingCallbacks);
      break;
    }
    mProcessingCallbacks = true;
    if (mSuspendCount || mState == STATE_IDLE || mState == STATE_DEAD) {
      mWaitingForInputStreamReady = false;
      mProcessingCallbacks = false;
      break;
    }

    uint32_t nextState;
    switch (mState) {
      case STATE_START:
        nextState = OnStateStart();
        break;
      case STATE_TRANSFER:
        nextState = OnStateTransfer();
        break;
      case STATE_STOP:
        mRetargeting = false;
        nextState = OnStateStop();
        break;
      default:
        nextState = 0;
        MOZ_ASSERT_UNREACHABLE("Unknown enum value.");
        return NS_ERROR_UNEXPECTED;
    }

    bool stillTransferring =
        (mState == STATE_TRANSFER && nextState == STATE_TRANSFER);
    if (stillTransferring) {
      NS_ASSERTION(NS_SUCCEEDED(mStatus),
                   "Should not have failed status for ongoing transfer");
    } else {
      NS_ASSERTION(mState != nextState,
                   "Only OnStateTransfer can be called more than once.");
    }
    if (mRetargeting) {
      NS_ASSERTION(mState != STATE_STOP,
                   "Retargeting should not happen during OnStateStop.");
    }

    // Set mRetargeting so EnsureWaiting will be called. It ensures that
    // OnStateStop is called on the main thread.
    if (nextState == STATE_STOP && !NS_IsMainThread() && !mOffMainThread) {
      mRetargeting = true;
    }

    // Unset mProcessingCallbacks here (while we have lock) so our own call to
    // EnsureWaiting isn't blocked by it.
    mProcessingCallbacks = false;

    // We must break the loop if suspended during one of the previous
    // operation.
    if (mSuspendCount) {
      mState = nextState;
      mWaitingForInputStreamReady = false;
      break;
    }

    // Wait asynchronously if there is still data to transfer, or we're
    // switching event delivery to another thread.
    if (stillTransferring || mRetargeting) {
      mState = nextState;
      mWaitingForInputStreamReady = false;
      nsresult rv = EnsureWaiting();
      if (NS_SUCCEEDED(rv)) break;

      // Failure to start asynchronous wait: stop transfer.
      // Do not set mStatus if it was previously set to report a failure.
      if (NS_SUCCEEDED(mStatus)) {
        mStatus = rv;
      }
      nextState = STATE_STOP;
    }

    mState = nextState;
  }
  return NS_OK;
}

uint32_t nsInputStreamPump::OnStateStart() MOZ_REQUIRES(mMutex) {
  mMutex.AssertCurrentThreadIn();

  AUTO_PROFILER_LABEL("nsInputStreamPump::OnStateStart", NETWORK);

  LOG(("  OnStateStart [this=%p]\n"this));

  nsresult rv;

  // need to check the reason why the stream is ready.  this is required
  // so our listener can check our status from OnStartRequest.
  // XXX async streams should have a GetStatus method!
  if (NS_SUCCEEDED(mStatus)) {
    uint64_t avail;
    rv = mAsyncStream->Available(&avail);
    if (NS_FAILED(rv) && rv != NS_BASE_STREAM_CLOSED) mStatus = rv;
  }

  {
    nsCOMPtr<nsIStreamListener> listener = mListener;
    // We're on the writing thread
    AssertOnThread();

    // Note: Must exit mutex for call to OnStartRequest to avoid
    // deadlocks when calls to RetargetDeliveryTo for multiple
    // nsInputStreamPumps are needed (e.g. nsHttpChannel).
    RecursiveMutexAutoUnlock unlock(mMutex);
    rv = listener->OnStartRequest(this);
  }

  // an error returned from OnStartRequest should cause us to abort; however,
  // we must not stomp on mStatus if already canceled.
  if (NS_FAILED(rv) && NS_SUCCEEDED(mStatus)) mStatus = rv;

  return NS_SUCCEEDED(mStatus) ? STATE_TRANSFER : STATE_STOP;
}

uint32_t nsInputStreamPump::OnStateTransfer() MOZ_REQUIRES(mMutex) {
  mMutex.AssertCurrentThreadIn();

  AUTO_PROFILER_LABEL("nsInputStreamPump::OnStateTransfer", NETWORK);

  LOG(("  OnStateTransfer [this=%p]\n"this));

  // if canceled, go directly to STATE_STOP...
  if (NS_FAILED(mStatus)) return STATE_STOP;

  nsresult rv = CreateBufferedStreamIfNeeded();
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return STATE_STOP;
  }

  uint64_t avail;
  rv = mAsyncStream->Available(&avail);
  LOG(("  Available returned [stream=%p rv=%" PRIx32 " avail=%" PRIu64 "]\n",
       mAsyncStream.get(), static_cast<uint32_t>(rv), avail));

  if (rv == NS_BASE_STREAM_CLOSED) {
    rv = NS_OK;
    avail = 0;
  } else if (NS_SUCCEEDED(rv) && avail) {
    // we used to limit avail to 16K - we were afraid some ODA handlers
    // might assume they wouldn't get more than 16K at once
    // we're removing that limit since it speeds up local file access.
    // Now there's an implicit 64K limit of 4 16K segments
    // NOTE: ok, so the story is as follows.  OnDataAvailable impls
    //       are by contract supposed to consume exactly |avail| bytes.
    //       however, many do not... mailnews... stream converters...
    //       cough, cough.  the input stream pump is fairly tolerant
    //       in this regard; however, if an ODA does not consume any
    //       data from the stream, then we could potentially end up in
    //       an infinite loop.  we do our best here to try to catch
    //       such an error.  (see bug 189672)

    // in most cases this QI will succeed (mAsyncStream is almost always
    // a nsPipeInputStream, which implements nsITellableStream::Tell).
    int64_t offsetBefore;
    nsCOMPtr<nsITellableStream> tellable = do_QueryInterface(mAsyncStream);
    if (tellable && NS_FAILED(tellable->Tell(&offsetBefore))) {
      MOZ_ASSERT_UNREACHABLE("Tell failed on readable stream");
      offsetBefore = 0;
    }

    uint32_t odaAvail = avail > UINT32_MAX ? UINT32_MAX : uint32_t(avail);

    LOG(("  calling OnDataAvailable [offset=%" PRIu64 " count=%" PRIu64
         "(%u)]\n",
         mStreamOffset, avail, odaAvail));

    {
      // We may be called on non-MainThread even if mOffMainThread is
      // false, due to RetargetDeliveryTo(), so don't use AssertOnThread()
      if (mTargetThread) {
        MOZ_ASSERT(mTargetThread->IsOnCurrentThread());
      } else {
        MOZ_ASSERT(NS_IsMainThread());
      }

      nsCOMPtr<nsIStreamListener> listener = mListener;
      // Note: Must exit mutex for call to OnStartRequest to avoid
      // deadlocks when calls to RetargetDeliveryTo for multiple
      // nsInputStreamPumps are needed (e.g. nsHttpChannel).
      RecursiveMutexAutoUnlock unlock(mMutex);
      // We're on the writing thread for mListener and mAsyncStream.
      // mStreamOffset is only touched in OnStateTransfer, and AsyncRead
      // shouldn't be called during OnDataAvailable()

      MOZ_PUSH_IGNORE_THREAD_SAFETY
      rv = listener->OnDataAvailable(this, mAsyncStream, mStreamOffset,
                                     odaAvail);
      MOZ_POP_THREAD_SAFETY
    }

    // don't enter this code if ODA failed or called Cancel
    if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(mStatus)) {
      // test to see if this ODA failed to consume data
      if (tellable) {
        // NOTE: if Tell fails, which can happen if the stream is
        // now closed, then we assume that everything was read.
        int64_t offsetAfter;
        if (NS_FAILED(tellable->Tell(&offsetAfter))) {
          offsetAfter = offsetBefore + odaAvail;
        }
        if (offsetAfter > offsetBefore) {
          mStreamOffset += (offsetAfter - offsetBefore);
        } else if (mSuspendCount == 0) {
          //
          // possible infinite loop if we continue pumping data!
          //
          // NOTE: although not allowed by nsIStreamListener, we
          // will allow the ODA impl to Suspend the pump.  IMAP
          // does this :-(
          //
          NS_ERROR("OnDataAvailable implementation consumed no data");
          mStatus = NS_ERROR_UNEXPECTED;
        }
      } else {
        mStreamOffset += odaAvail;  // assume ODA behaved well
      }
    }
  }

  // an error returned from Available or OnDataAvailable should cause us to
  // abort; however, we must not stop on mStatus if already canceled.

  if (NS_SUCCEEDED(mStatus)) {
    if (NS_FAILED(rv)) {
      mStatus = rv;
    } else if (avail) {
      // if stream is now closed, advance to STATE_STOP right away.
      // Available may return 0 bytes available at the moment; that
      // would not mean that we are done.
      // XXX async streams should have a GetStatus method!
      rv = mAsyncStream->Available(&avail);
      if (NS_SUCCEEDED(rv)) return STATE_TRANSFER;
      if (rv != NS_BASE_STREAM_CLOSED) mStatus = rv;
    }
  }
  return STATE_STOP;
}

nsresult nsInputStreamPump::CallOnStateStop() {
  RecursiveMutexAutoLock lock(mMutex);

  MOZ_ASSERT(NS_IsMainThread(),
             "CallOnStateStop should only be called on the main thread.");

  mState = OnStateStop();
  return NS_OK;
}

uint32_t nsInputStreamPump::OnStateStop() MOZ_REQUIRES(mMutex) {
  mMutex.AssertCurrentThreadIn();

  if (!NS_IsMainThread() && !mOffMainThread) {
    // This method can be called on a different thread if nsInputStreamPump
    // is used off the main-thread.
    if (NS_SUCCEEDED(mStatus) && mListener &&
        mozilla::StaticPrefs::network_send_OnDataFinished_nsInputStreamPump()) {
      nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
          do_QueryInterface(mListener);
      if (retargetableListener) {
        retargetableListener->OnDataFinished(mStatus);
      }
    }
    nsresult rv = mLabeledMainThreadTarget->Dispatch(
        mozilla::NewRunnableMethod("nsInputStreamPump::CallOnStateStop"this,
                                   &nsInputStreamPump::CallOnStateStop));
    NS_ENSURE_SUCCESS(rv, STATE_DEAD);
    return STATE_DEAD;
  }

  AUTO_PROFILER_LABEL("nsInputStreamPump::OnStateStop", NETWORK);

  LOG(("  OnStateStop [this=%p status=%" PRIx32 "]\n"this,
       static_cast<uint32_t>(mStatus)));

  // if an error occurred, we must be sure to pass the error onto the async
  // stream.  in some cases, this is redundant, but since close is idempotent,
  // this is OK.  otherwise, be sure to honor the "close-when-done" option.

  if (!mAsyncStream || !mListener) {
    MOZ_ASSERT(mAsyncStream, "null mAsyncStream: OnStateStop called twice?");
    MOZ_ASSERT(mListener, "null mListener: OnStateStop called twice?");
    return STATE_DEAD;
  }

  if (NS_FAILED(mStatus)) {
    mAsyncStream->CloseWithStatus(mStatus);
  } else if (mCloseWhenDone) {
    mAsyncStream->Close();
  }

  mAsyncStream = nullptr;
  mIsPending = false;
  {
    // We're on the writing thread.
    // We believe that mStatus can't be changed on us here.
    AssertOnThread();

    nsCOMPtr<nsIStreamListener> listener = mListener;
    nsresult status = mStatus;
    // Note: Must exit mutex for call to OnStartRequest to avoid
    // deadlocks when calls to RetargetDeliveryTo for multiple
    // nsInputStreamPumps are needed (e.g. nsHttpChannel).
    RecursiveMutexAutoUnlock unlock(mMutex);

    listener->OnStopRequest(this, status);
  }
  mTargetThread = nullptr;
  mListener = nullptr;

  if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);

  return STATE_DEAD;
}

nsresult nsInputStreamPump::CreateBufferedStreamIfNeeded() {
  if (mAsyncStreamIsBuffered) {
    return NS_OK;
  }

  // ReadSegments is not available for any nsIAsyncInputStream. In order to use
  // it, we wrap a nsIBufferedInputStream around it, if needed.

  if (NS_InputStreamIsBuffered(mAsyncStream)) {
    mAsyncStreamIsBuffered = true;
    return NS_OK;
  }

  nsCOMPtr<nsIInputStream> stream;
  nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(stream),
                                          mAsyncStream.forget(), 4096);
  NS_ENSURE_SUCCESS(rv, rv);

  // A buffered inputStream must implement nsIAsyncInputStream.
  mAsyncStream = do_QueryInterface(stream);
  MOZ_DIAGNOSTIC_ASSERT(mAsyncStream);
  mAsyncStreamIsBuffered = true;

  return NS_OK;
}

//-----------------------------------------------------------------------------
// nsIThreadRetargetableRequest
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsInputStreamPump::RetargetDeliveryTo(nsISerialEventTarget* aNewTarget) {
  RecursiveMutexAutoLock lock(mMutex);

  NS_ENSURE_ARG(aNewTarget);
  NS_ENSURE_TRUE(mState == STATE_START || mState == STATE_TRANSFER,
                 NS_ERROR_UNEXPECTED);

  // If canceled, do not retarget. Return with canceled status.
  if (NS_FAILED(mStatus)) {
    return mStatus;
  }

  if (aNewTarget == mTargetThread) {
    NS_WARNING("Retargeting delivery to same thread");
    return NS_OK;
  }

  if (mOffMainThread) {
    // Don't support retargeting if this pump is already used off the main
    // thread.
    return NS_ERROR_FAILURE;
  }

  // Ensure that |mListener| and any subsequent listeners can be retargeted
  // to another thread.
  nsresult rv = NS_OK;
  nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
      do_QueryInterface(mListener, &rv);
  if (NS_SUCCEEDED(rv) && retargetableListener) {
    rv = retargetableListener->CheckListenerChain();
    if (NS_SUCCEEDED(rv)) {
      mTargetThread = aNewTarget;
      mRetargeting = true;
    }
  }
  LOG(
      ("nsInputStreamPump::RetargetDeliveryTo [this=%p aNewTarget=%p] "
       "%s listener [%p] rv[%" PRIx32 "]",
       this, aNewTarget, (mTargetThread == aNewTarget ? "success" : "failure"),
       (nsIStreamListener*)mListener, static_cast<uint32_t>(rv)));
  return rv;
}

NS_IMETHODIMP
nsInputStreamPump::GetDeliveryTarget(nsISerialEventTarget** aNewTarget) {
  RecursiveMutexAutoLock lock(mMutex);

  nsCOMPtr<nsISerialEventTarget> target = mTargetThread;
  target.forget(aNewTarget);
  return NS_OK;
}

Messung V0.5 in Prozent
C=89 H=98 G=93

[Dauer der Verarbeitung: 0.23 Sekunden, vorverarbeitet 2026-06-05]