Add SkRacy
SkRacy<T> is a zero-overhead wrapper for a T, except it also silences race warnings when TSAN is running. Here we apply in several classes. In SkMatrix and SkPathRef, we use it to opportunistically cache some idempotent work. In SkPixelRef, we wrap the genIDs. We think the worst that can happen here is we'll increment the global next-genID a few times instead of once when we go to get another ID. BUG=skia: R=reed@google.com, mtklein@google.com Author: mtklein@chromium.org Review URL: https://codereview.chromium.org/371363004
This commit is contained in:
parent
f9552230dc
commit
d5e3e6ae1b
@ -19,6 +19,8 @@ extern "C" {
|
||||
// TSAN provides these hooks.
|
||||
void AnnotateIgnoreReadsBegin(const char* file, int line);
|
||||
void AnnotateIgnoreReadsEnd(const char* file, int line);
|
||||
void AnnotateBenignRaceSized(const char* file, int line,
|
||||
const void* addr, long size, const char* desc);
|
||||
} // extern "C"
|
||||
|
||||
// SK_ANNOTATE_UNPROTECTED_READ can wrap any variable read to tell TSAN to ignore that it appears to
|
||||
@ -37,10 +39,46 @@ inline T SK_ANNOTATE_UNPROTECTED_READ(const volatile T& x) {
|
||||
return read;
|
||||
}
|
||||
|
||||
// Ignore racy reads and racy writes to this pointer, indefinitely.
|
||||
// If at all possible, use the more precise SK_ANNOTATE_UNPROTECTED_READ.
|
||||
template <typename T>
|
||||
void SK_ANNOTATE_BENIGN_RACE(T* ptr) {
|
||||
AnnotateBenignRaceSized(__FILE__, __LINE__, ptr, sizeof(*ptr), "SK_ANNOTATE_BENIGN_RACE");
|
||||
}
|
||||
|
||||
#else // !DYNAMIC_ANNOTATIONS_ENABLED
|
||||
|
||||
#define SK_ANNOTATE_UNPROTECTED_READ(x) (x)
|
||||
#define SK_ANNOTATE_BENIGN_RACE(ptr)
|
||||
|
||||
#endif
|
||||
|
||||
// Can be used to wrap values that are intentionally racy, usually small mutable cached values, e.g.
|
||||
// - SkMatrix type mask
|
||||
// - SkPathRef bounds
|
||||
// - SkPixelRef genIDs
|
||||
template <typename T>
|
||||
class SkTRacy {
|
||||
public:
|
||||
SkTRacy() { SK_ANNOTATE_BENIGN_RACE(&fVal); }
|
||||
|
||||
operator const T&() const {
|
||||
return fVal;
|
||||
}
|
||||
|
||||
SkTRacy& operator=(const T& val) {
|
||||
fVal = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const T* get() const { return &fVal; }
|
||||
T* get() { return &fVal; }
|
||||
|
||||
const T* operator->() const { return &fVal; }
|
||||
T* operator->() { return &fVal; }
|
||||
|
||||
private:
|
||||
T fVal;
|
||||
};
|
||||
|
||||
#endif//SkDynamicAnnotations_DEFINED
|
||||
|
@ -10,6 +10,7 @@
|
||||
#ifndef SkMatrix_DEFINED
|
||||
#define SkMatrix_DEFINED
|
||||
|
||||
#include "SkDynamicAnnotations.h"
|
||||
#include "SkRect.h"
|
||||
|
||||
class SkString;
|
||||
@ -643,7 +644,7 @@ private:
|
||||
};
|
||||
|
||||
SkScalar fMat[9];
|
||||
mutable uint32_t fTypeMask;
|
||||
mutable SkTRacy<uint32_t> fTypeMask;
|
||||
|
||||
uint8_t computeTypeMask() const;
|
||||
uint8_t computePerspectiveTypeMask() const;
|
||||
@ -664,7 +665,7 @@ private:
|
||||
void clearTypeMask(int mask) {
|
||||
// only allow a valid mask
|
||||
SkASSERT((mask & kAllMasks) == mask);
|
||||
fTypeMask &= ~mask;
|
||||
fTypeMask = fTypeMask & ~mask;
|
||||
}
|
||||
|
||||
TypeMask getPerspectiveTypeMaskOnly() const {
|
||||
|
@ -9,6 +9,7 @@
|
||||
#ifndef SkPathRef_DEFINED
|
||||
#define SkPathRef_DEFINED
|
||||
|
||||
#include "SkDynamicAnnotations.h"
|
||||
#include "SkMatrix.h"
|
||||
#include "SkPoint.h"
|
||||
#include "SkRect.h"
|
||||
@ -292,7 +293,7 @@ private:
|
||||
SkDEBUGCODE(this->validate();)
|
||||
SkASSERT(fBoundsIsDirty);
|
||||
|
||||
fIsFinite = ComputePtBounds(&fBounds, *this);
|
||||
fIsFinite = ComputePtBounds(fBounds.get(), *this);
|
||||
fBoundsIsDirty = false;
|
||||
}
|
||||
|
||||
@ -300,7 +301,7 @@ private:
|
||||
SkASSERT(rect.fLeft <= rect.fRight && rect.fTop <= rect.fBottom);
|
||||
fBounds = rect;
|
||||
fBoundsIsDirty = false;
|
||||
fIsFinite = fBounds.isFinite();
|
||||
fIsFinite = fBounds->isFinite();
|
||||
}
|
||||
|
||||
/** Makes additional room but does not change the counts or change the genID */
|
||||
@ -432,11 +433,12 @@ private:
|
||||
kMinSize = 256,
|
||||
};
|
||||
|
||||
mutable SkRect fBounds;
|
||||
uint8_t fSegmentMask;
|
||||
mutable uint8_t fBoundsIsDirty;
|
||||
mutable SkBool8 fIsFinite; // only meaningful if bounds are valid
|
||||
mutable SkBool8 fIsOval;
|
||||
mutable SkTRacy<SkRect> fBounds;
|
||||
mutable SkTRacy<uint8_t> fBoundsIsDirty;
|
||||
mutable SkTRacy<SkBool8> fIsFinite; // only meaningful if bounds are valid
|
||||
|
||||
SkBool8 fIsOval;
|
||||
uint8_t fSegmentMask;
|
||||
|
||||
SkPoint* fPoints; // points to begining of the allocation
|
||||
uint8_t* fVerbs; // points just past the end of the allocation (verbs grow backwards)
|
||||
|
@ -9,6 +9,7 @@
|
||||
#define SkPixelRef_DEFINED
|
||||
|
||||
#include "SkBitmap.h"
|
||||
#include "SkDynamicAnnotations.h"
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkString.h"
|
||||
#include "SkFlattenable.h"
|
||||
@ -349,8 +350,8 @@ private:
|
||||
LockRec fRec;
|
||||
int fLockCount;
|
||||
|
||||
mutable uint32_t fGenerationID;
|
||||
mutable bool fUniqueGenerationID;
|
||||
mutable SkTRacy<uint32_t> fGenerationID;
|
||||
mutable SkTRacy<bool> fUniqueGenerationID;
|
||||
|
||||
SkTDArray<GenIDChangeListener*> fGenIDChangeListeners; // pointers are owned
|
||||
|
||||
|
@ -30,9 +30,7 @@ SkPathRef::Editor::Editor(SkAutoTUnref<SkPathRef>* pathRef,
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkPathRef* SkPathRef::CreateEmptyImpl() {
|
||||
SkPathRef* p = SkNEW(SkPathRef);
|
||||
p->computeBounds(); // Preemptively avoid a race to clear fBoundsIsDirty.
|
||||
return p;
|
||||
return SkNEW(SkPathRef);
|
||||
}
|
||||
|
||||
SkPathRef* SkPathRef::CreateEmpty() {
|
||||
@ -85,13 +83,13 @@ void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst,
|
||||
if (canXformBounds) {
|
||||
(*dst)->fBoundsIsDirty = false;
|
||||
if (src.fIsFinite) {
|
||||
matrix.mapRect(&(*dst)->fBounds, src.fBounds);
|
||||
if (!((*dst)->fIsFinite = (*dst)->fBounds.isFinite())) {
|
||||
(*dst)->fBounds.setEmpty();
|
||||
matrix.mapRect((*dst)->fBounds.get(), src.fBounds);
|
||||
if (!((*dst)->fIsFinite = (*dst)->fBounds->isFinite())) {
|
||||
(*dst)->fBounds->setEmpty();
|
||||
}
|
||||
} else {
|
||||
(*dst)->fIsFinite = false;
|
||||
(*dst)->fBounds.setEmpty();
|
||||
(*dst)->fBounds->setEmpty();
|
||||
}
|
||||
} else {
|
||||
(*dst)->fBoundsIsDirty = true;
|
||||
@ -441,14 +439,14 @@ void SkPathRef::validate() const {
|
||||
SkASSERT(this->currSize() ==
|
||||
fFreeSpace + sizeof(SkPoint) * fPointCnt + sizeof(uint8_t) * fVerbCnt);
|
||||
|
||||
if (!fBoundsIsDirty && !fBounds.isEmpty()) {
|
||||
if (!fBoundsIsDirty && !fBounds->isEmpty()) {
|
||||
bool isFinite = true;
|
||||
for (int i = 0; i < fPointCnt; ++i) {
|
||||
SkASSERT(!fPoints[i].isFinite() || (
|
||||
fBounds.fLeft - fPoints[i].fX < SK_ScalarNearlyZero &&
|
||||
fPoints[i].fX - fBounds.fRight < SK_ScalarNearlyZero &&
|
||||
fBounds.fTop - fPoints[i].fY < SK_ScalarNearlyZero &&
|
||||
fPoints[i].fY - fBounds.fBottom < SK_ScalarNearlyZero));
|
||||
fBounds->fLeft - fPoints[i].fX < SK_ScalarNearlyZero &&
|
||||
fPoints[i].fX - fBounds->fRight < SK_ScalarNearlyZero &&
|
||||
fBounds->fTop - fPoints[i].fY < SK_ScalarNearlyZero &&
|
||||
fPoints[i].fY - fBounds->fBottom < SK_ScalarNearlyZero));
|
||||
if (!fPoints[i].isFinite()) {
|
||||
isFinite = false;
|
||||
}
|
||||
|
@ -25,21 +25,3 @@ race:is_lcd_supported
|
||||
race:RefFCI
|
||||
race:SkString
|
||||
race:SkPDF
|
||||
|
||||
# These race benignly as used by DMQuiltTask: skia:2725.
|
||||
# Suppress while I look for a more focused way to silence this.
|
||||
race:SkPixelRef::callGenIDChangeListeners
|
||||
race:SkPixelRef::needsNewGenID
|
||||
|
||||
# SkPathRef caches its bounding box the first time it's needed.
|
||||
# This will be fixed naturally once we create (from a single thread) a
|
||||
# bounding-box hierarchy for SkRecord-based SkPictures; all bounds will come pre-cached.
|
||||
# So just shut this up for now.
|
||||
race:SkPathRef::computeBounds
|
||||
|
||||
# SkMatrix caches a type mask. If we race on this, we'll just calculate the same thing a few times.
|
||||
race:SkMatrix::getType
|
||||
race:SkMatrix::rectStaysRect
|
||||
race:SkMatrix::getPerspectiveTypeMaskOnly
|
||||
|
||||
# TODO: some sort of SkRacy<T> to handle cases like SkMatrix, SkPathRef, SkPixelRef above?
|
||||
|
Loading…
Reference in New Issue
Block a user