/* -*- 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/. */
// this class is used to delay notifications until the end of a particular // scope. it helps avoid the complexity of issuing callbacks while inside // a critical section. class nsPipeEvents { public:
nsPipeEvents() = default;
~nsPipeEvents();
// This class is used to maintain input stream state. Its broken out from the // nsPipeInputStream class because generally the nsPipe should be modifying // this state and not the input stream itself. struct nsPipeReadState {
nsPipeReadState()
: mReadCursor(nullptr),
mReadLimit(nullptr),
mSegment(0),
mAvailable(0),
mActiveRead(false),
mNeedDrain(false) {}
// All members of this type are guarded by the pipe monitor, however it cannot // be named from this type, so the less-reliable MOZ_GUARDED_VAR is used // instead. In the future it would be nice to avoid this, especially as // MOZ_GUARDED_VAR is deprecated. char* mReadCursor MOZ_GUARDED_VAR; char* mReadLimit MOZ_GUARDED_VAR;
int32_t mSegment MOZ_GUARDED_VAR;
uint32_t mAvailable MOZ_GUARDED_VAR;
// This flag is managed using the AutoReadSegment RAII stack class. bool mActiveRead MOZ_GUARDED_VAR;
// Set to indicate that the input stream has closed and should be drained, // but that drain has been delayed due to an active read. When the read // completes, this flag indicate the drain should then be performed. bool mNeedDrain MOZ_GUARDED_VAR;
};
// an input end of a pipe (maintained as a list of refs within the pipe) class nsPipeInputStream final : public nsIAsyncInputStream, public nsITellableStream, public nsISearchableInputStream, public nsICloneableInputStream, public nsIClassInfo, public nsIBufferedInputStream, public nsIInputStreamPriority { public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSIASYNCINPUTSTREAM
NS_DECL_NSITELLABLESTREAM
NS_DECL_NSISEARCHABLEINPUTSTREAM
NS_DECL_NSICLONEABLEINPUTSTREAM
NS_DECL_NSICLASSINFO
NS_DECL_NSIBUFFEREDINPUTSTREAM
NS_DECL_NSIINPUTSTREAMPRIORITY
// synchronously wait for the pipe to become readable.
nsresult Wait();
// These two don't acquire the monitor themselves. Instead they // expect their caller to have done so and to pass the monitor as // evidence.
MonitorAction OnInputReadable(uint32_t aBytesWritten, nsPipeEvents&, const ReentrantMonitorAutoEnter& ev)
MOZ_REQUIRES(Monitor());
MonitorAction OnInputException(nsresult, nsPipeEvents&, const ReentrantMonitorAutoEnter& ev)
MOZ_REQUIRES(Monitor());
// A version of Status() that doesn't acquire the monitor.
nsresult Status(const ReentrantMonitorAutoEnter& ev) const
MOZ_REQUIRES(Monitor());
// The status of this input stream, ignoring the status of the underlying // monitor. If this status is errored, the input stream has either already // been removed from the pipe, or will be removed from the pipe shortly.
nsresult InputStatus(const ReentrantMonitorAutoEnter&) const
MOZ_REQUIRES(Monitor()) { return mInputStatus;
}
ReentrantMonitor& Monitor() const;
private: virtual ~nsPipeInputStream();
RefPtr<nsPipe> mPipe;
int64_t mLogicalOffset; // Individual input streams can be closed without effecting the rest of the // pipe. So track individual input stream status separately. |mInputStatus| // is protected by |mPipe->mReentrantMonitor|.
nsresult mInputStatus MOZ_GUARDED_BY(Monitor()); bool mBlocking;
// these variables can only be accessed while inside the pipe's monitor bool mBlocked MOZ_GUARDED_BY(Monitor());
CallbackHolder mCallback MOZ_GUARDED_BY(Monitor());
// requires pipe's monitor to access members; usually treat as an opaque token // to pass to nsPipe
nsPipeReadState mReadState;
Atomic<uint32_t, Relaxed> mPriority;
};
// the output end of a pipe (allocated as a member of the pipe). class nsPipeOutputStream : public nsIAsyncOutputStream, public nsIClassInfo { public: // since this class will be allocated as a member of the pipe, we do not // need our own ref count. instead, we share the lifetime (the ref count) // of the entire pipe. this macro is just convenience since it does not // declare a mRefCount variable; however, don't let the name fool you... // we are not inheriting from nsPipe ;-)
NS_DECL_ISUPPORTS_INHERITED
// separate refcnt so that we know when to close the producer
ThreadSafeAutoRefCnt mWriterRefCnt;
int64_t mLogicalOffset; bool mBlocking;
// these variables can only be accessed while inside the pipe's monitor bool mBlocked MOZ_GUARDED_BY(Monitor()); bool mWritable MOZ_GUARDED_BY(Monitor());
CallbackHolder mCallback MOZ_GUARDED_BY(Monitor());
};
// // Methods below may only be called while inside the pipe's monitor. Some // of these methods require passing a ReentrantMonitorAutoEnter to prove the // monitor is held. //
// methods below should only be called by AutoReadSegment
nsresult GetReadSegment(nsPipeReadState& aReadState, constchar*& aSegment,
uint32_t& aLength); void ReleaseReadSegment(nsPipeReadState& aReadState, nsPipeEvents& aEvents); void AdvanceReadCursor(nsPipeReadState& aReadState, uint32_t aCount);
// We can't inherit from both nsIInputStream and nsIOutputStream // because they collide on their Close method. Consequently we nest their // implementations to avoid the extra object allocation.
nsPipeOutputStream mOutput;
// Since the input stream can be cloned, we may have more than one. Use // a weak reference as the streams will clear their entry here in their // destructor. Using a strong reference would create a reference cycle. // Only usable while mReentrantMonitor is locked.
nsTArray<nsPipeInputStream*> mInputList MOZ_GUARDED_BY(mReentrantMonitor);
// The maximum number of segments to allow to be buffered in advance // of the fastest reader. This is collection of segments is called // the "advance buffer".
uint32_t mMaxAdvanceBufferSegmentCount MOZ_GUARDED_BY(mReentrantMonitor);
// Declarations of Monitor() methods on the streams. // // These must be placed early to provide MOZ_RETURN_CAPABILITY annotations for // the thread-safety analysis. This couldn't be done at the declaration due to // nsPipe not yet being defined.
// RAII class representing an active read segment. When it goes out of scope // it automatically updates the read cursor and releases the read segment. class MOZ_STACK_CLASS AutoReadSegment final { public:
AutoReadSegment(nsPipe* aPipe, nsPipeReadState& aReadState,
uint32_t aMaxLength)
: mPipe(aPipe),
mReadState(aReadState),
mStatus(NS_ERROR_FAILURE),
mSegment(nullptr),
mLength(0),
mOffset(0) {
MOZ_DIAGNOSTIC_ASSERT(mPipe);
MOZ_DIAGNOSTIC_ASSERT(!mReadState.mActiveRead);
mStatus = mPipe->GetReadSegment(mReadState, mSegment, mLength); if (NS_SUCCEEDED(mStatus)) {
MOZ_DIAGNOSTIC_ASSERT(mReadState.mActiveRead);
MOZ_DIAGNOSTIC_ASSERT(mSegment);
mLength = std::min(mLength, aMaxLength);
MOZ_DIAGNOSTIC_ASSERT(mLength);
}
}
private: // guaranteed to remain alive due to limited stack lifetime of AutoReadSegment
nsPipe* mPipe;
nsPipeReadState& mReadState;
nsresult mStatus; constchar* mSegment;
uint32_t mLength;
uint32_t mOffset;
};
// // NOTES on buffer architecture: // // +-----------------+ - - mBuffer.GetSegment(0) // | | // + - - - - - - - - + - - nsPipeReadState.mReadCursor // |/////////////////| // |/////////////////| // |/////////////////| // |/////////////////| // +-----------------+ - - nsPipeReadState.mReadLimit // | // +-----------------+ // |/////////////////| // |/////////////////| // |/////////////////| // |/////////////////| // |/////////////////| // |/////////////////| // +-----------------+ // | // +-----------------+ - - mBuffer.GetSegment(mWriteSegment) // |/////////////////| // |/////////////////| // |/////////////////| // + - - - - - - - - + - - mWriteCursor // | | // | | // +-----------------+ - - mWriteLimit // // (shaded region contains data) // // NOTE: Each input stream produced by the nsPipe contains its own, separate // nsPipeReadState. This means there are multiple mReadCursor and // mReadLimit values in play. The pipe cannot discard old data until // all mReadCursors have moved beyond that point in the stream. // // Likewise, each input stream reader will have it's own amount of // buffered data. The pipe size threshold, however, is only applied // to the input stream that is being read fastest. We call this // the "advance buffer" in that its in advance of all readers. We // allow slower input streams to buffer more data so that we don't // stall processing of the faster input stream. // // NOTE: on some systems (notably OS/2), the heap allocator uses an arena for // small allocations (e.g., 64 byte allocations). this means that buffers may // be allocated back-to-back. in the diagram above, for example, mReadLimit // would actually be pointing at the beginning of the next segment. when // making changes to this file, please keep this fact in mind. //
nsPipe::nsPipe(uint32_t aSegmentSize, uint32_t aSegmentCount)
: mOutput(this),
mReentrantMonitor("nsPipe.mReentrantMonitor"), // protect against overflow
mMaxAdvanceBufferSegmentCount(
std::min(aSegmentCount, UINT32_MAX / aSegmentSize)),
mWriteSegment(-1),
mWriteCursor(nullptr),
mWriteLimit(nullptr),
mStatus(NS_OK) { // The internal buffer is always "infinite" so that we can allow // the size to expand when cloned streams are read at different // rates. We enforce a limit on how much data can be buffered // ahead of the fastest reader in GetWriteSegment().
MOZ_ALWAYS_SUCCEEDS(mBuffer.Init(aSegmentSize));
}
// The input stream locks the pipe while getting the buffer to read from, // but then unlocks while actual data copying is taking place. In // order to avoid deleting the buffer out from under this lockless read // set a flag to indicate a read is active. This flag is only modified // while the lock is held.
MOZ_DIAGNOSTIC_ASSERT(!aReadState.mActiveRead);
aReadState.mActiveRead = true;
// When a read completes and releases the mActiveRead flag, we may have // blocked a drain from completing. This occurs when the input stream is // closed during the read. In these cases, we need to complete the drain as // soon as the active read completes. if (aReadState.mNeedDrain) {
aReadState.mNeedDrain = false;
DrainInputStream(aReadState, aEvents);
}
}
// Check to see if we're at the end of the available read data. If we // are, and this segment is not still being written, then we can possibly // free up the segment. if (aReadState.mReadCursor == aReadState.mReadLimit &&
!ReadSegmentBeingWritten(aReadState)) { // Advance the segment position. If we have read any segments from the // advance buffer then we can potentially notify blocked writers.
mOutput.Monitor().AssertCurrentThreadIn(); if (AdvanceReadSegment(aReadState, mon) == SegmentAdvanceBufferRead &&
mOutput.OnOutputWritable(events) == NotifyMonitor) {
mon.NotifyAll();
}
}
ReleaseReadSegment(aReadState, events);
}
}
SegmentChangeResult nsPipe::AdvanceReadSegment(
nsPipeReadState& aReadState, const ReentrantMonitorAutoEnter& ev) { // Calculate how many segments are buffered for this stream to start.
uint32_t startBufferSegments = GetBufferSegmentCount(aReadState, ev);
int32_t currentSegment = aReadState.mSegment;
// Move to the next segment to read
aReadState.mSegment += 1;
// If this was the last reference to the first segment, then remove it. if (currentSegment == 0 && CountSegmentReferences(currentSegment) == 0) { // shift write and read segment index (-1 indicates an empty buffer).
mWriteSegment -= 1;
// Directly modify the current read state. If the associated input // stream is closed simultaneous with reading, then it may not be // in the mInputList any more.
aReadState.mSegment -= 1;
for (uint32_t i = 0; i < mInputList.Length(); ++i) { // Skip the current read state structure since we modify it manually // before entering this loop. if (&mInputList[i]->ReadState() == &aReadState) { continue;
}
mInputList[i]->ReadState().mSegment -= 1;
}
// done with this segment
mBuffer.DeleteFirstSegment();
LOG(("III deleting first segment\n"));
}
if (mWriteSegment < aReadState.mSegment) { // read cursor has hit the end of written data, so reset it
MOZ_DIAGNOSTIC_ASSERT(mWriteSegment == (aReadState.mSegment - 1));
aReadState.mReadCursor = nullptr;
aReadState.mReadLimit = nullptr; // also, the buffer is completely empty, so reset the write cursor if (mWriteSegment == -1) {
mWriteCursor = nullptr;
mWriteLimit = nullptr;
}
} else { // advance read cursor and limit to next buffer segment
aReadState.mReadCursor = mBuffer.GetSegment(aReadState.mSegment); if (mWriteSegment == aReadState.mSegment) {
aReadState.mReadLimit = mWriteCursor;
} else {
aReadState.mReadLimit = aReadState.mReadCursor + mBuffer.GetSegmentSize();
}
}
// Calculate how many segments are buffered for the stream after // reading.
uint32_t endBufferSegments = GetBufferSegmentCount(aReadState, ev);
// If the stream has read a segment out of the set of advanced buffer // segments, then the writer may advance. if (startBufferSegments >= mMaxAdvanceBufferSegmentCount &&
endBufferSegments < mMaxAdvanceBufferSegmentCount) { return SegmentAdvanceBufferRead;
}
// Otherwise there are no significant changes to the segment structure. return SegmentNotChanged;
}
// If a segment is actively being read in ReadSegments() for this input // stream, then we cannot drain the stream. This can happen because // ReadSegments() does not hold the lock while copying from the buffer. // If we detect this condition, simply note that we need a drain once // the read completes and return immediately. if (aReadState.mActiveRead) {
MOZ_DIAGNOSTIC_ASSERT(!aReadState.mNeedDrain);
aReadState.mNeedDrain = true; return;
}
while (mWriteSegment >= aReadState.mSegment) { // If the last segment to free is still being written to, we're done // draining. We can't free any more. if (ReadSegmentBeingWritten(aReadState)) { break;
}
// Don't bother checking if this results in an advance buffer segment // read. Since we are draining the entire stream we will read an // advance buffer segment no matter what.
AdvanceReadSegment(aReadState, mon);
}
// Force the stream into an empty state. Make sure mAvailable, mCursor, and // mReadLimit are consistent with one another.
aReadState.mAvailable = 0;
aReadState.mReadCursor = nullptr;
aReadState.mReadLimit = nullptr;
// Remove the input stream from the pipe's list of streams. This will // prevent the pipe from holding the stream alive or trying to update // its read state any further.
DebugOnly<uint32_t> numRemoved = 0;
mInputList.RemoveElementsBy([&](nsPipeInputStream* aEntry) { bool result = &aReadState == &aEntry->ReadState();
numRemoved += result ? 1 : 0; return result;
});
MOZ_ASSERT(numRemoved == 1);
// If we have read any segments from the advance buffer then we can // potentially notify blocked writers.
mOutput.Monitor().AssertCurrentThreadIn(); if (!IsAdvanceBufferFull(mon) &&
mOutput.OnOutputWritable(aEvents) == NotifyMonitor) {
mon.NotifyAll();
}
}
// write cursor and limit may both be null indicating an empty buffer. if (mWriteCursor == mWriteLimit) { // The pipe is full if we have hit our limit on advance data buffering. // This means the fastest reader is still reading slower than data is // being written into the pipe. if (IsAdvanceBufferFull(mon)) { return NS_BASE_STREAM_WOULD_BLOCK;
}
// The nsSegmentedBuffer is configured to be "infinite", so this // should never return nullptr here. char* seg = mBuffer.AppendNewSegment(); if (!seg) { return NS_ERROR_OUT_OF_MEMORY;
}
// make sure read cursor is initialized
SetAllNullReadCursors();
// check to see if we can roll-back our read and write cursors to the // beginning of the current/first segment. this is purely an optimization. if (mWriteSegment == 0 && AllReadCursorsMatchWriteCursor()) { char* head = mBuffer.GetSegment(0);
LOG(("OOO rolling back write cursor %" PRId64 " bytes\n", static_cast<int64_t>(mWriteCursor - head)));
RollBackAllReadCursors(head);
mWriteCursor = head;
}
// update read limit if reading in the same segment
UpdateAllReadCursors(newWriteCursor);
mWriteCursor = newWriteCursor;
ValidateAllReadCursors();
// update the writable flag on the output stream if (mWriteCursor == mWriteLimit) {
mOutput.Monitor().AssertCurrentThreadIn();
mOutput.SetWritable(!IsAdvanceBufferFull(mon));
}
// notify input stream that pipe now contains additional data bool needNotify = false; for (uint32_t i = 0; i < mInputList.Length(); ++i) {
mInputList[i]->Monitor().AssertCurrentThreadIn(); if (mInputList[i]->OnInputReadable(aBytesWritten, events, mon) ==
NotifyMonitor) {
needNotify = true;
}
}
// Its possible to re-enter this method when we call OnPipeException() or // OnInputExection() below. If there is a caller stuck in our synchronous // Wait() method, then they will get woken up with a failure code which // re-enters this method. Therefore, gracefully handle unknown streams // here.
// If we only have one stream open and it is the given stream, then shut // down the entire pipe. if (mInputList.Length() == 1) { if (mInputList[0] == aStream) {
OnPipeException(aReason);
} return;
}
// Otherwise just close the particular stream that hit an exception. for (uint32_t i = 0; i < mInputList.Length(); ++i) { if (mInputList[i] != aStream) { continue;
}
// if we've already hit an exception, then ignore this one. if (NS_FAILED(mStatus)) { return;
}
mStatus = aReason;
bool needNotify = false;
// OnInputException() can drain the stream and remove it from // mInputList. So iterate over a temp list instead.
nsTArray<nsPipeInputStream*> list = mInputList.Clone(); for (uint32_t i = 0; i < list.Length(); ++i) { // an output-only exception applies to the input end if the pipe has // zero bytes available.
list[i]->Monitor().AssertCurrentThreadIn(); if (aOutputOnly && list[i]->Available()) { continue;
}
void nsPipe::UpdateAllReadCursors(char* aWriteCursor) {
mReentrantMonitor.AssertCurrentThreadIn(); for (uint32_t i = 0; i < mInputList.Length(); ++i) {
nsPipeReadState& readState = mInputList[i]->ReadState(); if (mWriteSegment == readState.mSegment &&
readState.mReadLimit == mWriteCursor) {
readState.mReadLimit = aWriteCursor;
}
}
}
void nsPipe::ValidateAllReadCursors() {
mReentrantMonitor.AssertCurrentThreadIn(); // The only way mReadCursor == mWriteCursor is if: // // - mReadCursor is at the start of a segment (which, based on how // nsSegmentedBuffer works, means that this segment is the "first" // segment) // - mWriteCursor points at the location past the end of the current // write segment (so the current write filled the current write // segment, so we've incremented mWriteCursor to point past the end // of it) // - the segment to which data has just been written is located // exactly one segment's worth of bytes before the first segment // where mReadCursor is located // // Consequently, the byte immediately after the end of the current // write segment is the first byte of the first segment, so // mReadCursor == mWriteCursor. (Another way to think about this is // to consider the buffer architecture diagram above, but consider it // with an arena allocator which allocates from the *end* of the // arena to the *beginning* of the arena.) #ifdef DEBUG for (uint32_t i = 0; i < mInputList.Length(); ++i) { const nsPipeReadState& state = mInputList[i]->ReadState();
MOZ_ASSERT(state.mReadCursor != mWriteCursor ||
(mBuffer.GetSegment(state.mSegment) == state.mReadCursor &&
mWriteCursor == mWriteLimit));
} #endif
}
uint32_t nsPipe::GetBufferSegmentCount( const nsPipeReadState& aReadState, const ReentrantMonitorAutoEnter& ev) const { // The write segment can be smaller than the current reader position // in some cases. For example, when the first write segment has not // been allocated yet mWriteSegment is negative. In these cases // the stream is effectively using zero segments. if (mWriteSegment < aReadState.mSegment) { return 0;
}
// Otherwise at least one segment is being used. We add one here // since a single segment is being used when the write and read // segment indices are the same. return 1 + mWriteSegment - aReadState.mSegment;
}
bool nsPipe::IsAdvanceBufferFull(const ReentrantMonitorAutoEnter& ev) const { // If we have fewer total segments than the limit we can immediately // determine we are not full. Note, we must add one to mWriteSegment // to convert from a index to a count.
MOZ_DIAGNOSTIC_ASSERT(mWriteSegment >= -1);
MOZ_DIAGNOSTIC_ASSERT(mWriteSegment < INT32_MAX);
uint32_t totalWriteSegments = mWriteSegment + 1; if (totalWriteSegments < mMaxAdvanceBufferSegmentCount) { returnfalse;
}
// Otherwise we must inspect all of our reader streams. We need // to determine the buffer depth of the fastest reader.
uint32_t minBufferSegments = UINT32_MAX; for (uint32_t i = 0; i < mInputList.Length(); ++i) { // Only count buffer segments from input streams that are open.
mInputList[i]->Monitor().AssertCurrentThreadIn(); if (NS_FAILED(mInputList[i]->Status(ev))) { continue;
} const nsPipeReadState& state = mInputList[i]->ReadState();
uint32_t bufferSegments = GetBufferSegmentCount(state, ev);
minBufferSegments = std::min(minBufferSegments, bufferSegments); // We only care if any reader has fewer segments buffered than // our threshold. We can stop once we hit that threshold. if (minBufferSegments < mMaxAdvanceBufferSegmentCount) { returnfalse;
}
}
// Note, its possible for minBufferSegments to exceed our // mMaxAdvanceBufferSegmentCount here. This happens when a cloned // reader gets far behind, but then the fastest reader stream is // closed. This leaves us with a single stream that is buffered // beyond our max. Naturally we continue to indicate the pipe // is full at this point.
NS_IMETHODIMP
nsPipeInputStream::Init(nsIInputStream*, uint32_t) {
MOZ_CRASH( "nsPipeInputStream should never be initialized with " "nsIBufferedInputStream::Init!\n");
}
NS_IMETHODIMP
nsPipeInputStream::GetData(nsIInputStream** aResult) { // as this was not created with init() we are not // wrapping anything return NS_ERROR_NOT_IMPLEMENTED;
}
*aReadCount = 0; while (aCount) {
AutoReadSegment segment(mPipe, mReadState, aCount);
rv = segment.Status(); if (NS_FAILED(rv)) { // ignore this error if we've already read something. if (*aReadCount > 0) {
rv = NS_OK; break;
} if (rv == NS_BASE_STREAM_WOULD_BLOCK) { // pipe is empty if (!mBlocking) { break;
} // wait for some data to be written to the pipe
rv = Wait(); if (NS_SUCCEEDED(rv)) { continue;
}
} // ignore this error, just return. if (rv == NS_BASE_STREAM_CLOSED) {
rv = NS_OK; break;
}
mPipe->OnInputStreamException(this, rv); break;
}
uint32_t writeCount; while (segment.Length()) {
writeCount = 0;
if (NS_FAILED(rv) || writeCount == 0) {
aCount = 0; // any errors returned from the writer end here: do not // propagate to the caller of ReadSegments.
rv = NS_OK; break;
}
if (NS_FAILED(Status(mon)) ||
(mReadState.mAvailable && !(aFlags & WAIT_CLOSURE_ONLY))) { // stream is already closed or readable; post event.
pipeEvents.NotifyReady(std::move(callback));
} else { // queue up callback object to be notified when data becomes available
mCallback = std::move(callback);
}
} return NS_OK;
}
while (true) {
uint32_t i, len1 = limit1 - cursor1;
// check if the string is in the buffer segment for (i = 0; i < len1 - strLen + 1; i++) { if (strings_equal(aIgnoreCase, &cursor1[i], aForString, strLen)) {
*aFound = true;
*aOffsetSearchedTo = offset + i;
LOG((" result [aFound=%u offset=%u]\n", *aFound, *aOffsetSearchedTo)); return NS_OK;
}
}
// get the next segment char* cursor2; char* limit2;
uint32_t len2;
if (mReadState.mAvailable) { // Still something to read and this input stream state is OK. return NS_OK;
}
// Nothing to read, just fall through to the pipe's state that // may reflect state of its output stream side (already closed). return mPipe->mStatus;
}
*aWriteCount = 0; while (aCount) {
rv = mPipe->GetWriteSegment(segment, segmentLen); if (NS_FAILED(rv)) { if (rv == NS_BASE_STREAM_WOULD_BLOCK) { // pipe is full if (!mBlocking) { // ignore this error if we've already written something if (*aWriteCount > 0) {
rv = NS_OK;
} break;
} // wait for the pipe to have an empty segment.
rv = Wait(); if (NS_SUCCEEDED(rv)) { continue;
}
}
mPipe->OnPipeException(rv); break;
}
// write no more than aCount if (segmentLen > aCount) {
segmentLen = aCount;
}
if (NS_FAILED(rv) || readCount == 0) {
aCount = 0; // any errors returned from the aReader end here: do not // propagate to the caller of WriteSegments.
rv = NS_OK; break;
}
if (NS_FAILED(mPipe->mStatus) ||
(mWritable && !(aFlags & WAIT_CLOSURE_ONLY))) { // stream is already closed or writable; post event.
pipeEvents.NotifyReady(std::move(callback));
} else { // queue up callback object to be notified when data becomes available
mCallback = std::move(callback);
}
} return NS_OK;
}
// Handle aMaxSize of UINT32_MAX as a special case
uint32_t segmentCount; if (aMaxSize == UINT32_MAX) {
segmentCount = UINT32_MAX;
} else {
segmentCount = aMaxSize / aSegmentSize;
}
// Thin nsIPipe implementation for consumers of the component manager interface // for creating pipes. Acts as a thin wrapper around NS_NewPipe2 for JS callers. class nsPipeHolder final : public nsIPipe { public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIPIPE
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 ist noch experimentell.