Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/xpcom/io/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 14 kB image not shown  

Quelle  AsyncPlatformPipes.cpp

  Sprache: C
 

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "AsyncPlatformPipes.h"

#include "base/eintr_wrapper.h"
#include "base/message_loop.h"
#include "mozilla/CondVar.h"
#include "mozilla/EventTargetCapability.h"
#include "mozilla/UniquePtr.h"
#include "nsStreamUtils.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"

#ifndef XP_WIN
#  include <errno.h>
#  include <fcntl.h>
#  include <sys/stat.h>
#  include <unistd.h>
#endif

namespace mozilla {

namespace platform_pipe_detail {

class PlatformPipeLink
#ifdef XP_WIN
    : public MessageLoopForIO::IOHandler
#else
    : public MessageLoopForIO::Watcher
#endif
{
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PlatformPipeLink)

 public:
  PlatformPipeLink(UniqueFileHandle aHandle, uint32_t aBufferSize);

  // Clears mPending to nullptr, returning the previous value. Also notifies
  // mPendingCV in case there are any threads synchronously waiting for pending
  // I/O to complete.
  already_AddRefed<PlatformPipeLink> TakePending() MOZ_REQUIRES(mMutex);

  // NOTE: This performs potentially-blocking I/O to close mHandle, so should
  // not be called on the IO thread!
  void Close(nsresult aStatus, bool aInternal) MOZ_EXCLUDES(mMutex)
      MOZ_EXCLUDES(mIOThread);

  // Dispatch an internal Close(...) call to be performed in a background task.
  // Called when background I/O operations fail.
  void DispatchPipeError(nsresult aStatus) MOZ_REQUIRES(mMutex, mIOThread);

  // Dispatch notification of mCallback to another thread.
  //
  // NOTE: We intentionally do not notify directly on the current thread, even
  // if no event target is provided, as we don't want user code running on the
  // IPC I/O thread as it is very latency sensitive.
  void DispatchNotify() MOZ_REQUIRES(mMutex);

  void AdvanceIO() MOZ_EXCLUDES(mMutex) MOZ_REQUIRES(mIOThread);

  void AdvanceIOLocked() MOZ_REQUIRES(mMutex, mIOThread);

#ifdef XP_WIN
  void OnIOCompleted(MessageLoopForIO::IOContext* aContext,
                     DWORD aBytesTransferred, DWORD aError) override;
#else
  void OnFileCanReadWithoutBlocking(int fd) override;
  void OnFileCanWriteWithoutBlocking(int fd) override;
#endif

  const EventTargetCapability<nsISerialEventTarget> mIOThread;
  Mutex mMutex{"PlatformPipeReader"};

  UniqueFileHandle mHandle MOZ_GUARDED_BY(mMutex);
  const UniquePtr<char[]> mBuffer;
  const uint32_t mBufferSize;

  bool mProcessingSegment MOZ_GUARDED_BY(mMutex) = false;
  bool mClosing MOZ_GUARDED_BY(mMutex) = false;

  nsresult mStatus MOZ_GUARDED_BY(mMutex) = NS_OK;

  // `mOffset` is the start of the readable region in the buffer, and
  // `mAvailable` is its size.
  uint32_t mOffset MOZ_GUARDED_BY(mMutex) = 0;
  uint32_t mAvailable MOZ_GUARDED_BY(mMutex) = 0;

  bool mCallbackClosureOnly MOZ_GUARDED_BY(mMutex) = false;
  nsCOMPtr<nsIRunnable> mCallback MOZ_GUARDED_BY(mMutex);
  nsCOMPtr<nsIEventTarget> mCallbackTarget MOZ_GUARDED_BY(mMutex);

  // A reference keeping `this` alive while I/O is in-flight.
  // This is particularly important on Windows where mBuffer needs to be kept
  // alive while Overlapped IO is ongoing.
  RefPtr<PlatformPipeLink> mPending MOZ_GUARDED_BY(mMutex);

  // CondVar which is notified whenever mPending is cleared to `nullptr`.
  // Used by Close() to wait for pending I/O to complete.
  CondVar mPendingCV{mMutex, "PlatformPipeReader::mPendingCV"};

#ifdef XP_WIN
  MessageLoopForIO::IOContext mIOContext MOZ_GUARDED_BY(mMutex) = {};
#else
  MessageLoopForIO::FileDescriptorWatcher mWatcher MOZ_GUARDED_BY(mIOThread);
#endif

 private:
  ~PlatformPipeLink() = default;
};

PlatformPipeLink::PlatformPipeLink(UniqueFileHandle aHandle,
                                   uint32_t aBufferSize)
    : mIOThread(XRE_GetAsyncIOEventTarget()),
      mHandle(std::move(aHandle)),
      mBuffer(MakeUnique<char[]>(aBufferSize)),
      mBufferSize(aBufferSize) {
  MOZ_ASSERT(aBufferSize > 1"invalid buffer size");
  MOZ_ASSERT(mHandle, "invalid handle");

#if defined(DEBUG) && !defined(XP_WIN)
  struct stat st{};
  MOZ_ASSERT(fstat(mHandle.get(), &st) == 0 && !S_ISREG(st.st_mode),
             "PlatformPipeLink does not support regular files");
  MOZ_ASSERT(fcntl(mHandle.get(), F_GETFL) & O_NONBLOCK,
             "PlatformPipeLink requires non-blocking file descriptors");
#endif
}

already_AddRefed<PlatformPipeLink> PlatformPipeLink::TakePending() {
  RefPtr<PlatformPipeLink> pending = mPending.forget();
  if (pending) {
    mPendingCV.NotifyAll();
  }
  return pending.forget();
}

void PlatformPipeLink::Close(nsresult aStatus, bool aInternal) {
  MOZ_RELEASE_ASSERT(!mIOThread.IsOnCurrentThread(),
                     "Close may deadlock if called on the IO thread");

  MutexAutoLock lock(mMutex);
  MOZ_RELEASE_ASSERT(aInternal || !mProcessingSegment,
                     "Cannot close pipe during ReadSegments callback");
  if (NS_FAILED(mStatus)) {
    return;
  }

  // Ensure we're marked as closing before we cancel pending IO, this prevents
  // new pending IO from being started.
  mClosing = true;

  if (mPending) {
#ifdef XP_WIN
    // On Windows, if we still have pending IO, request that it is cancelled.
    // We can do that from any thread using CancelIoEx. The notification will be
    // delivered to the IO thread.
    CancelIoEx(mHandle.get(), &mIOContext.overlapped);
#else
    // On POSIX, request cancellation by dispatching a runnable to stop watching
    // our file descriptor, and clear mPending.
    MOZ_ALWAYS_SUCCEEDS(mIOThread.Dispatch(NS_NewRunnableFunction(
        "PlatformPipeLink::CancelIO", [self = RefPtr{this}] {
          self->mIOThread.AssertOnCurrentThread();
          RefPtr<PlatformPipeLink> pending;
          MutexAutoLock lock(self->mMutex);
          if (self->mPending) {
            self->mWatcher.StopWatchingFileDescriptor();
            pending = self->TakePending();
          }
        })));
#endif

    // Wait on the condvar until all pending operations are completed.
    // NOTE: This unlocks mMutex while we wait.
    while (mPending) {
      mPendingCV.Wait();
    }
  }
  MOZ_DIAGNOSTIC_ASSERT(!mPending, "How do we still have pending I/O?");

  // Someone else may have closed the link while we were unlocked.
  if (NS_FAILED(mStatus)) {
    return;
  }

  mStatus = NS_SUCCEEDED(aStatus) ? NS_BASE_STREAM_CLOSED : aStatus;
  DispatchNotify();

  // NOTE: Make sure we've closed the handle synchronously here, as callers rely
  // on the kernel file object being destroyed.
  mHandle = nullptr;
}

void PlatformPipeLink::DispatchPipeError(nsresult aStatus) {
  MOZ_ASSERT(!mPending,
             "Shouldn't be pending when closing due to a pipe error");

  // Ensure other threads don't start new I/O after this point.
  // Don't actually change mStatus until we're in the Close method.
  mClosing = true;

  // Perform the Close() operation on a background task which is allowed to
  // block. This operation may be blocking, as it closes the pipe, which could
  // do expensive I/O.
  NS_DispatchBackgroundTask(
      NewRunnableMethod<nsresult, bool>("PlatformPipeLink::Close", this,
                                        &PlatformPipeLink::Close, aStatus,
                                        /* aInternal */ true),
      NS_DISPATCH_EVENT_MAY_BLOCK);
}

void PlatformPipeLink::DispatchNotify() {
  nsCOMPtr<nsIRunnable> callback = mCallback.forget();
  nsCOMPtr<nsIEventTarget> target = mCallbackTarget.forget();
  if (!callback) {
    return;
  }
  if (target) {
    target->Dispatch(callback.forget());
  } else {
    NS_DispatchBackgroundTask(callback.forget());
  }
}

void PlatformPipeLink::AdvanceIO() {
  MutexAutoLock lock(mMutex);
  AdvanceIOLocked();
}

void PlatformPipeLink::AdvanceIOLocked() {
  // If we've closed, or are in the process of closing, our PlatformPipeLink,
  // don't start any new I/O operations.
  if (mClosing || !mHandle || NS_FAILED(mStatus)) {
    return;
  }

  // We still have outstanding I/O, or our buffer already has data waiting to
  // be consumed. Either way, don't start new I/O.
  if (mPending || mAvailable) {
    return;
  }

#ifdef XP_WIN
  // On Windows, we need to register the IO handler the first time we're on the
  // I/O thread.
  if (!mIOContext.handler) {
    MessageLoopForIO::current()->RegisterIOHandler(mHandle.get(), this);
    mIOContext.handler = this;
  }
  BOOL ok = ReadFile(mHandle.get(), mBuffer.get(), mBufferSize, nullptr,
                     &mIOContext.overlapped);
  if (!ok) {
    DWORD error = GetLastError();
    if (error == ERROR_IO_PENDING) {
      mPending = this;
      return;
    }
    if (error == ERROR_BROKEN_PIPE || error == ERROR_HANDLE_EOF) {
      DispatchPipeError(NS_BASE_STREAM_CLOSED);
    } else {
      DispatchPipeError(NS_ERROR_FAILURE);
    }
    return;
  }

  mPending = this;
#else
  ssize_t rv = HANDLE_EINTR(read(mHandle.get(), mBuffer.get(), mBufferSize));
  if (rv > 0) {
    mOffset = 0;
    mAvailable = static_cast<uint32_t>(rv);
    if (!mCallbackClosureOnly) {
      DispatchNotify();
    }
    return;
  }

  if (rv == 0) {
    DispatchPipeError(NS_BASE_STREAM_CLOSED);
    return;
  }

  if (errno == EAGAIN
#  if EWOULDBLOCK != EAGAIN
      || errno == EWOULDBLOCK
#  endif
  ) {
    if (MessageLoopForIO::current()->WatchFileDescriptor(
            mHandle.get(), false, MessageLoopForIO::WATCH_READ, &mWatcher,
            this)) {
      mPending = this;
      return;
    }
  }

  DispatchPipeError(NS_ERROR_FAILURE);
#endif
}

#ifdef XP_WIN
void PlatformPipeLink::OnIOCompleted(MessageLoopForIO::IOContext* aContext,
                                     DWORD aBytesTransferred, DWORD aError) {
  mIOThread.AssertOnCurrentThread();
  RefPtr<PlatformPipeLink> pending;
  MutexAutoLock lock(mMutex);
  if (aContext != &mIOContext) {
    return;
  }

  pending = TakePending();
  if (!pending) {
    return;
  }

  if (mClosing || NS_FAILED(mStatus)) {
    return;
  }

  if (aError != ERROR_SUCCESS) {
    if (aError == ERROR_BROKEN_PIPE || aError == ERROR_HANDLE_EOF ||
        aError == ERROR_OPERATION_ABORTED) {
      DispatchPipeError(NS_BASE_STREAM_CLOSED);
    } else {
      DispatchPipeError(NS_ERROR_FAILURE);
    }
    return;
  }

  if (aBytesTransferred == 0) {
    DispatchPipeError(NS_BASE_STREAM_CLOSED);
    return;
  }

  mOffset = 0;
  mAvailable = aBytesTransferred;
  if (!mCallbackClosureOnly) {
    DispatchNotify();
  }
}
#else
void PlatformPipeLink::OnFileCanReadWithoutBlocking(int fd) {
  mIOThread.AssertOnCurrentThread();
  RefPtr<PlatformPipeLink> pending;
  MutexAutoLock lock(mMutex);
  pending = TakePending();
  AdvanceIOLocked();
}

void PlatformPipeLink::OnFileCanWriteWithoutBlocking(int fd) {
  MOZ_ASSERT_UNREACHABLE();
}
#endif

}  // namespace platform_pipe_detail

//-----------------------------------------------------------------------------
// PlatformPipeReader
//-----------------------------------------------------------------------------

NS_IMPL_ISUPPORTS(PlatformPipeReader, nsIInputStream, nsIAsyncInputStream)

PlatformPipeReader::PlatformPipeReader(UniqueFileHandle aHandle,
                                       uint32_t aBufferSize)
    : mLink(new platform_pipe_detail::PlatformPipeLink(std::move(aHandle),
                                                       aBufferSize)) {}

PlatformPipeReader::~PlatformPipeReader() { Close(); }

NS_IMETHODIMP PlatformPipeReader::Close() {
  return CloseWithStatus(NS_BASE_STREAM_CLOSED);
}

NS_IMETHODIMP PlatformPipeReader::Available(uint64_t* aAvailable) {
  MutexAutoLock lock(mLink->mMutex);
  if (NS_FAILED(mLink->mStatus)) {
    return mLink->mStatus;
  }
  *aAvailable = mLink->mAvailable;
  return NS_OK;
}

NS_IMETHODIMP PlatformPipeReader::StreamStatus() {
  MutexAutoLock lock(mLink->mMutex);
  return mLink->mStatus;
}

NS_IMETHODIMP PlatformPipeReader::Read(char* aBuf, uint32_t aCount,
                                       uint32_t* aReadCount) {
  return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aReadCount);
}

NS_IMETHODIMP PlatformPipeReader::ReadSegments(nsWriteSegmentFun aWriter,
                                               void* aClosure, uint32_t aCount,
                                               uint32_t* aReadCount) {
  *aReadCount = 0;

  MutexAutoLock lock(mLink->mMutex);
  if (NS_FAILED(mLink->mStatus)) {
    return mLink->mStatus == NS_BASE_STREAM_CLOSED ? NS_OK : mLink->mStatus;
  }

  if (!mLink->mAvailable) {
    return NS_BASE_STREAM_WOULD_BLOCK;
  }

  MOZ_RELEASE_ASSERT(!mLink->mProcessingSegment,
                     "Only one thread may be processing a segment at a time");

  char* start = mLink->mBuffer.get() + mLink->mOffset;
  uint32_t length = std::min(aCount, mLink->mAvailable);

  mLink->mProcessingSegment = true;
  {
    MutexAutoUnlock unlock(mLink->mMutex);
    nsresult rv = aWriter(this, aClosure, start, 0, length, aReadCount);
    if (NS_FAILED(rv)) {
      *aReadCount = 0;
    }
    MOZ_RELEASE_ASSERT(*aReadCount <= length);
  }
  mLink->mProcessingSegment = false;

  mLink->mOffset += *aReadCount;
  mLink->mAvailable -= *aReadCount;

  // If a closure-only callback is armed, the caller isn't listening for new
  // data, so only the IO thread is able to notice the peer closing. Re-kick
  // AdvanceIO once the buffer has drained so a subsequent close is observed.
  if (!mLink->mAvailable && mLink->mCallback && mLink->mCallbackClosureOnly) {
    mLink->mIOThread.Dispatch(
        NewRunnableMethod("PlatformPipeLink::AdvanceIO", mLink,
                          &platform_pipe_detail::PlatformPipeLink::AdvanceIO));
  }
  return NS_OK;
}

NS_IMETHODIMP PlatformPipeReader::IsNonBlocking(bool* aNonBlocking) {
  *aNonBlocking = true;
  return NS_OK;
}

NS_IMETHODIMP PlatformPipeReader::CloseWithStatus(nsresult aStatus) {
  mLink->Close(aStatus, /* aInternal */ false);
  return NS_OK;
}

NS_IMETHODIMP PlatformPipeReader::AsyncWait(nsIInputStreamCallback* aCallback,
                                            uint32_t aFlags,
                                            uint32_t aRequestedCount,
                                            nsIEventTarget* aTarget) {
  MutexAutoLock lock(mLink->mMutex);

  if (!aCallback) {
    mLink->mCallback = nullptr;
    mLink->mCallbackTarget = nullptr;
    return NS_OK;
  }

  mLink->mCallback = NS_NewRunnableFunction(
      "PlatformPipeReader::AsyncWait",
      [self = RefPtr{this}, callback = RefPtr{aCallback}] {
        callback->OnInputStreamReady(self);
      });
  mLink->mCallbackTarget = aTarget;
  mLink->mCallbackClosureOnly = aFlags & WAIT_CLOSURE_ONLY;

  if (NS_FAILED(mLink->mStatus) ||
      (!mLink->mCallbackClosureOnly && mLink->mAvailable)) {
    mLink->DispatchNotify();
  } else {
    mLink->mIOThread.Dispatch(
        NewRunnableMethod("PlatformPipeLink::AdvanceIO", mLink,
                          &platform_pipe_detail::PlatformPipeLink::AdvanceIO));
  }
  return NS_OK;
}

}  // namespace mozilla

Messung V0.5 in Prozent
C=94 H=95 G=94

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.