Index: src/core/SkRWBuffer.cpp |
diff --git a/src/core/SkRWBuffer.cpp b/src/core/SkRWBuffer.cpp |
index c0a93bdf53c037ce768a0699983f1d7cf1ce30d5..5b3f5bf3475d9643703501f8f9790037f0ddfffc 100644 |
--- a/src/core/SkRWBuffer.cpp |
+++ b/src/core/SkRWBuffer.cpp |
@@ -40,6 +40,8 @@ struct SkBufferBlock { |
return amount; |
} |
+ // Do not call in the reader thread, since the writer may be updating fUsed. |
+ // (The assertion is still true, but TSAN still may complain about its raciness.) |
void validate() const { |
#ifdef SK_DEBUG |
SkASSERT(fCapacity > 0); |
@@ -94,7 +96,7 @@ struct SkBufferHead { |
} |
} |
- void validate(size_t minUsed, SkBufferBlock* tail = nullptr) const { |
+ void validate(size_t minUsed, const SkBufferBlock* tail = nullptr) const { |
#ifdef SK_DEBUG |
SkASSERT(fRefCnt > 0); |
size_t totalUsed = 0; |
@@ -114,19 +116,26 @@ struct SkBufferHead { |
} |
}; |
-SkROBuffer::SkROBuffer(const SkBufferHead* head, size_t used) : fHead(head), fUsed(used) { |
+SkROBuffer::SkROBuffer(const SkBufferHead* head, size_t used, const SkBufferBlock* tail) |
+ : fHead(head) |
+ , fUsed(used) |
+ , fTail(tail) |
+ , fTailUsed(tail ? tail->fUsed : 0) |
+{ |
if (head) { |
fHead->ref(); |
SkASSERT(used > 0); |
- head->validate(used); |
+ SkASSERT(tail); |
+ head->validate(used, tail); |
} else { |
SkASSERT(0 == used); |
+ SkASSERT(!tail); |
+ SkASSERT(0 == fTailUsed); |
} |
} |
SkROBuffer::~SkROBuffer() { |
if (fHead) { |
- fHead->validate(fUsed); |
fHead->unref(); |
} |
} |
@@ -136,7 +145,13 @@ SkROBuffer::Iter::Iter(const SkROBuffer* buffer) { |
} |
void SkROBuffer::Iter::reset(const SkROBuffer* buffer) { |
+ if (!buffer->fUsed) { |
+ SkASSERT(!buffer->fHead); |
+ buffer = nullptr; |
+ } |
+ fBuffer = buffer; |
if (buffer) { |
+ SkASSERT(buffer->fHead); |
fBlock = &buffer->fHead->fBlock; |
fRemaining = buffer->fUsed; |
} else { |
@@ -153,13 +168,29 @@ size_t SkROBuffer::Iter::size() const { |
if (!fBlock) { |
return 0; |
} |
- return SkTMin(fBlock->fUsed, fRemaining); |
+ |
+ if (fBuffer->fTail == fBlock) { |
+ return fBuffer->fTailUsed; |
+ } |
+ |
+ // There is another block that this object knows about, with data that is |
+ // accounted for in fRemaining. |
+ // Further, since there are more blocks, it is safe to read fUsed, which will |
+ // not be updated anymore. |
+ SkASSERT(fRemaining > fBlock->fUsed); |
+ return fBlock->fUsed; |
} |
bool SkROBuffer::Iter::next() { |
if (fRemaining) { |
fRemaining -= this->size(); |
- fBlock = fBlock->fNext; |
+ if (fBuffer->fTail == fBlock) { |
+ // There are more blocks, but fBuffer does not know about them. |
+ SkASSERT(fRemaining == 0); |
+ fBlock = nullptr; |
+ } else { |
+ fBlock = fBlock->fNext; |
+ } |
} |
return fRemaining != 0; |
} |
@@ -234,7 +265,9 @@ void SkRWBuffer::validate() const { |
} |
#endif |
-SkROBuffer* SkRWBuffer::newRBufferSnapshot() const { return new SkROBuffer(fHead, fTotalUsed); } |
+SkROBuffer* SkRWBuffer::newRBufferSnapshot() const { |
+ return new SkROBuffer(fHead, fTotalUsed, fTail); |
+} |
/////////////////////////////////////////////////////////////////////////////////////////////////// |