diff options
-rw-r--r-- | include/core/SkColorTable.h | 8 | ||||
-rw-r--r-- | include/core/SkLazyPtr.h | 188 | ||||
-rw-r--r-- | include/core/SkTypeface.h | 9 | ||||
-rw-r--r-- | include/private/SkOncePtr.h | 23 | ||||
-rw-r--r-- | src/core/SkBigPicture.cpp | 3 | ||||
-rw-r--r-- | src/core/SkBigPicture.h | 4 | ||||
-rw-r--r-- | src/core/SkColorTable.cpp | 18 | ||||
-rw-r--r-- | src/core/SkData.cpp | 11 | ||||
-rw-r--r-- | src/core/SkFontMgr.cpp | 16 | ||||
-rw-r--r-- | src/core/SkGlyphCache.cpp | 9 | ||||
-rw-r--r-- | src/core/SkImageFilter.cpp | 13 | ||||
-rw-r--r-- | src/core/SkMessageBus.h | 15 | ||||
-rw-r--r-- | src/core/SkMiniRecorder.cpp | 6 | ||||
-rw-r--r-- | src/core/SkPathRef.cpp | 20 | ||||
-rw-r--r-- | src/core/SkTypeface.cpp | 53 | ||||
-rw-r--r-- | src/core/SkXfermode.cpp | 25 | ||||
-rw-r--r-- | src/fonts/SkRemotableFontMgr.cpp | 12 | ||||
-rw-r--r-- | src/lazy/SkDiscardableMemoryPool.cpp | 19 | ||||
-rw-r--r-- | src/opts/opts_check_x86.cpp | 10 | ||||
-rw-r--r-- | src/pdf/SkPDFGraphicState.cpp | 26 | ||||
-rw-r--r-- | src/pdf/SkPDFShader.cpp | 13 | ||||
-rw-r--r-- | src/utils/SkEventTracer.cpp | 6 | ||||
-rw-r--r-- | tests/LazyPtrTest.cpp | 70 | ||||
-rw-r--r-- | tests/OncePtrTest.cpp | 2 |
24 files changed, 467 insertions, 112 deletions
diff --git a/include/core/SkColorTable.h b/include/core/SkColorTable.h index ff2bf7cde2..c6ca1e961f 100644 --- a/include/core/SkColorTable.h +++ b/include/core/SkColorTable.h @@ -10,10 +10,10 @@ #ifndef SkColorTable_DEFINED #define SkColorTable_DEFINED -#include "../private/SkOncePtr.h" #include "SkColor.h" #include "SkFlattenable.h" #include "SkImageInfo.h" +#include "SkLazyPtr.h" /** \class SkColorTable @@ -55,16 +55,16 @@ public: static SkColorTable* Create(SkReadBuffer&); private: + static void Free16BitCache(uint16_t*); + enum AllocatedWithMalloc { kAllocatedWithMalloc }; // assumes ownership of colors (assumes it was allocated w/ malloc) SkColorTable(SkPMColor* colors, int count, AllocatedWithMalloc); - struct Free16BitCache { void operator()(uint16_t* cache) const { sk_free(cache); } }; - SkPMColor* fColors; - SkOncePtr<uint16_t, Free16BitCache> f16BitCache; + SkLazyPtr<uint16_t, Free16BitCache> f16BitCache; int fCount; void init(const SkPMColor* colors, int count); diff --git a/include/core/SkLazyPtr.h b/include/core/SkLazyPtr.h new file mode 100644 index 0000000000..b0cd2ff559 --- /dev/null +++ b/include/core/SkLazyPtr.h @@ -0,0 +1,188 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkLazyPtr_DEFINED +#define SkLazyPtr_DEFINED + +/** Declare a lazily-chosen static pointer (or array of pointers) of type T. + * + * Example usage: + * + * Foo* GetSingletonFoo() { + * SK_DECLARE_STATIC_LAZY_PTR(Foo, singleton); // Created with new, destroyed with delete. + * return singleton.get(); + * } + * + * These macros take an optional T* (*Create)() and void (*Destroy)(T*) at the end. + * If not given, we'll use new and delete. + * These options are most useful when T doesn't have a public constructor or destructor. + * Create comes first, so you may use a custom Create with a default Destroy, but not vice versa. + * + * Foo* CustomCreate() { return ...; } + * void CustomDestroy(Foo* ptr) { ... } + * Foo* GetSingletonFooWithCustomCleanup() { + * SK_DECLARE_STATIC_LAZY_PTR(Foo, singleton, CustomCreate, CustomDestroy); + * return singleton.get(); + * } + * + * If you have a bunch of related static pointers of the same type, you can + * declare an array of lazy pointers together, and we'll pass the index to Create(). + * + * Foo* CreateFoo(int i) { return ...; } + * Foo* GetCachedFoo(Foo::Enum enumVal) { + * SK_DECLARE_STATIC_LAZY_PTR_ARRAY(Foo, Foo::kEnumCount, cachedFoos, CreateFoo); + * return cachedFoos[enumVal]; + * } + * + * + * You can think of SK_DECLARE_STATIC_LAZY_PTR as a cheaper specialization of + * SkOnce. There is no mutex or extra storage used past the pointer itself. + * + * We may call Create more than once, but all threads will see the same pointer + * returned from get(). Any extra calls to Create will be cleaned up. + * + * These macros must be used in a global scope, not in function scope or as a class member. + */ + +#define SK_DECLARE_STATIC_LAZY_PTR(T, name, ...) \ + namespace {} static Private::SkStaticLazyPtr<T, ##__VA_ARGS__> name + +#define SK_DECLARE_STATIC_LAZY_PTR_ARRAY(T, name, N, ...) \ + namespace {} static Private::SkStaticLazyPtrArray<T, N, ##__VA_ARGS__> name + +// namespace {} forces these macros to only be legal in global scopes. Chrome has thread-safety +// problems with them in function-local statics because it uses -fno-threadsafe-statics, and even +// in builds with threadsafe statics, those threadsafe statics are just unnecessary overhead. + +// Everything below here is private implementation details. Don't touch, don't even look. + +#include "SkAtomics.h" + +// See FIXME below. +class SkFontConfigInterfaceDirect; + +namespace Private { + +// Set *dst to ptr if *dst is NULL. Returns value of *dst, destroying ptr if not swapped in. +// Issues acquire memory barrier on failure, release on success. +template <typename P, void (*Destroy)(P)> +static P try_cas(P* dst, P ptr) { + P prev = NULL; + if (sk_atomic_compare_exchange(dst, &prev, ptr, + sk_memory_order_release/*on success*/, + sk_memory_order_acquire/*on failure*/)) { + // We need a release barrier before returning ptr. The compare_exchange provides it. + SkASSERT(!prev); + return ptr; + } else { + Destroy(ptr); + // We need an acquire barrier before returning prev. The compare_exchange provided it. + SkASSERT(prev); + return prev; + } +} + +template <typename T> +T* sk_new() { + return new T; +} +template <typename T> +void sk_delete(T* ptr) { + delete ptr; +} + +// We're basing these implementations here on this article: +// http://preshing.com/20140709/the-purpose-of-memory_order_consume-in-cpp11/ +// +// Because the users of SkLazyPtr and SkLazyPtrArray will read the pointers +// _through_ our atomically set pointer, there is a data dependency between our +// atomic and the guarded data, and so we only need writer-releases / +// reader-consumes memory pairing rather than the more general write-releases / +// reader-acquires convention. +// +// This is nice, because a consume load is free on all our platforms: x86, +// ARM, MIPS. In contrast, an acquire load issues a memory barrier on non-x86. + +template <typename T> +T consume_load(T* ptr) { +#if defined(THREAD_SANITIZER) + // TSAN gets anxious if we don't tell it what we're actually doing, a consume load. + return sk_atomic_load(ptr, sk_memory_order_consume); +#else + // All current compilers blindly upgrade consume memory order to acquire memory order. + // For our purposes, though, no memory barrier is required, so we lie and use relaxed. + return sk_atomic_load(ptr, sk_memory_order_relaxed); +#endif +} + +// This has no constructor and must be zero-initalized (the macro above does this). +template <typename T, T* (*Create)() = sk_new<T>, void (*Destroy)(T*) = sk_delete<T> > +class SkStaticLazyPtr { +public: + T* get() { + // If fPtr has already been filled, we need a consume barrier when loading it. + // If not, we need a release barrier when setting it. try_cas will do that. + T* ptr = consume_load(&fPtr); + return ptr ? ptr : try_cas<T*, Destroy>(&fPtr, Create()); + } + +private: + T* fPtr; +}; + +template <typename T> +T* sk_new_arg(int i) { + return new T(i); +} + +// This has no constructor and must be zero-initalized (the macro above does this). +template <typename T, int N, T* (*Create)(int) = sk_new_arg<T>, void (*Destroy)(T*) = sk_delete<T> > +class SkStaticLazyPtrArray { +public: + T* operator[](int i) { + SkASSERT(i >= 0 && i < N); + // If fPtr has already been filled, we need an consume barrier when loading it. + // If not, we need a release barrier when setting it. try_cas will do that. + T* ptr = consume_load(&fArray[i]); + return ptr ? ptr : try_cas<T*, Destroy>(&fArray[i], Create(i)); + } + +private: + T* fArray[N]; +}; + +} // namespace Private + +// This version is suitable for use as a class member. +// It's much the same as above except: +// - it has a constructor to zero itself; +// - it has a destructor to clean up; +// - get() calls SkNew(T) to create the pointer; +// - get(functor) calls functor to create the pointer. +template <typename T, void (*Destroy)(T*) = Private::sk_delete<T> > +class SkLazyPtr : SkNoncopyable { +public: + SkLazyPtr() : fPtr(NULL) {} + ~SkLazyPtr() { if (fPtr) { Destroy((T*)fPtr); } } + + T* get() const { + T* ptr = Private::consume_load(&fPtr); + return ptr ? ptr : Private::try_cas<T*, Destroy>(&fPtr, new T); + } + + template <typename Create> + T* get(const Create& create) const { + T* ptr = Private::consume_load(&fPtr); + return ptr ? ptr : Private::try_cas<T*, Destroy>(&fPtr, create()); + } + +private: + mutable T* fPtr; +}; + + +#endif//SkLazyPtr_DEFINED diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h index 0b1ca6a4bd..c4242181fb 100644 --- a/include/core/SkTypeface.h +++ b/include/core/SkTypeface.h @@ -10,11 +10,11 @@ #ifndef SkTypeface_DEFINED #define SkTypeface_DEFINED -#include "../private/SkOncePtr.h" -#include "../private/SkWeakRefCnt.h" #include "SkFontStyle.h" +#include "SkLazyPtr.h" #include "SkRect.h" #include "SkString.h" +#include "../private/SkWeakRefCnt.h" class SkDescriptor; class SkFontData; @@ -398,7 +398,10 @@ private: static SkTypeface* CreateDefault(int style); // SkLazyPtr requires an int, not a Style. static void DeleteDefault(SkTypeface*); - SkOncePtr<SkRect> fLazyBounds; + struct BoundsComputer; +// friend struct BoundsComputer; + + SkLazyPtr<SkRect> fLazyBounds; SkFontID fUniqueID; SkFontStyle fStyle; bool fIsFixedPitch; diff --git a/include/private/SkOncePtr.h b/include/private/SkOncePtr.h index 40bea1a4c3..9af204bcb2 100644 --- a/include/private/SkOncePtr.h +++ b/include/private/SkOncePtr.h @@ -9,23 +9,22 @@ #define SkOncePtr_DEFINED #include "SkAtomics.h" -#include "SkUniquePtr.h" -template <typename T> class SkBaseOncePtr; +template <typename T> class SkStaticOnce; // Use this to create a global static pointer that's intialized exactly once when you call get(). -#define SK_DECLARE_STATIC_ONCE_PTR(type, name) namespace {} static SkBaseOncePtr<type> name +#define SK_DECLARE_STATIC_ONCE_PTR(type, name) namespace {} static SkStaticOnce<type> name // Use this for a local or member pointer that's initialized exactly once when you call get(). -template <typename T, typename Delete = skstd::default_delete<T>> +template <typename T> class SkOncePtr : SkNoncopyable { public: SkOncePtr() { sk_bzero(this, sizeof(*this)); } - ~SkOncePtr() { - if (T* ptr = (T*)*this) { - Delete()(ptr); - } - } + + // SkOncePtr does not have a destructor and does not clean up the pointer. But you may, e.g. + // delete (T*)fOncePtr; + // SkSafeUnref((T*)fOncePtr); + // etc. template <typename F> T* get(const F& f) const { @@ -37,11 +36,11 @@ public: } private: - SkBaseOncePtr<T> fOnce; + SkStaticOnce<T> fOnce; }; /* TODO(mtklein): in next CL -typedef SkBaseOncePtr<void> SkOnceFlag; +typedef SkStaticOnce<void> SkOnceFlag; #define SK_DECLARE_STATIC_ONCE(name) namespace {} static SkOnceFlag name template <typename F> @@ -53,7 +52,7 @@ inline void SkOnce(SkOnceFlag* once, const F& f) { // Implementation details below here! No peeking! template <typename T> -class SkBaseOncePtr { +class SkStaticOnce { public: template <typename F> T* get(const F& f) const { diff --git a/src/core/SkBigPicture.cpp b/src/core/SkBigPicture.cpp index 61f3a0ef1c..02011ee745 100644 --- a/src/core/SkBigPicture.cpp +++ b/src/core/SkBigPicture.cpp @@ -58,7 +58,8 @@ void SkBigPicture::partialPlayback(SkCanvas* canvas, } const SkBigPicture::Analysis& SkBigPicture::analysis() const { - return *fAnalysis.get([&]{ return new Analysis(*fRecord); }); + auto create = [&]() { return new Analysis(*fRecord); }; + return *fAnalysis.get(create); } SkRect SkBigPicture::cullRect() const { return fCullRect; } diff --git a/src/core/SkBigPicture.h b/src/core/SkBigPicture.h index 2e42213539..dfd6b800ac 100644 --- a/src/core/SkBigPicture.h +++ b/src/core/SkBigPicture.h @@ -8,7 +8,7 @@ #ifndef SkBigPicture_DEFINED #define SkBigPicture_DEFINED -#include "SkOncePtr.h" +#include "SkLazyPtr.h" #include "SkPicture.h" #include "SkTemplates.h" @@ -79,7 +79,7 @@ private: const SkRect fCullRect; const size_t fApproxBytesUsedBySubPictures; - SkOncePtr<const Analysis> fAnalysis; + SkLazyPtr<const Analysis> fAnalysis; SkAutoTUnref<const SkRecord> fRecord; SkAutoTDelete<const SnapshotArray> fDrawablePicts; SkAutoTUnref<const SkBBoxHierarchy> fBBH; diff --git a/src/core/SkColorTable.cpp b/src/core/SkColorTable.cpp index f52ac909eb..ca9a8657c4 100644 --- a/src/core/SkColorTable.cpp +++ b/src/core/SkColorTable.cpp @@ -47,14 +47,26 @@ SkColorTable::~SkColorTable() { #include "SkColorPriv.h" -const uint16_t* SkColorTable::read16BitCache() const { - return f16BitCache.get([&]{ +namespace { +struct Build16BitCache { + const SkPMColor* fColors; + int fCount; + + uint16_t* operator()() const { uint16_t* cache = (uint16_t*)sk_malloc_throw(fCount * sizeof(uint16_t)); for (int i = 0; i < fCount; i++) { cache[i] = SkPixel32ToPixel16_ToU16(fColors[i]); } return cache; - }); + } +}; +}//namespace + +void SkColorTable::Free16BitCache(uint16_t* cache) { sk_free(cache); } + +const uint16_t* SkColorTable::read16BitCache() const { + const Build16BitCache create = { fColors, fCount }; + return f16BitCache.get(create); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkData.cpp b/src/core/SkData.cpp index 017c397bbf..9e65d2a32e 100644 --- a/src/core/SkData.cpp +++ b/src/core/SkData.cpp @@ -6,8 +6,8 @@ */ #include "SkData.h" +#include "SkLazyPtr.h" #include "SkOSFile.h" -#include "SkOncePtr.h" #include "SkReadBuffer.h" #include "SkStream.h" #include "SkWriteBuffer.h" @@ -80,9 +80,14 @@ SkData* SkData::PrivateNewWithCopy(const void* srcOrNull, size_t length) { /////////////////////////////////////////////////////////////////////////////// -SK_DECLARE_STATIC_ONCE_PTR(SkData, gEmpty); +// As a template argument these must have external linkage. +SkData* sk_new_empty_data() { return new SkData(nullptr, 0, nullptr, nullptr); } +namespace { void sk_unref_data(SkData* ptr) { return SkSafeUnref(ptr); } } + +SK_DECLARE_STATIC_LAZY_PTR(SkData, empty, sk_new_empty_data, sk_unref_data); + SkData* SkData::NewEmpty() { - return SkRef(gEmpty.get([]{return new SkData(nullptr, 0, nullptr, nullptr); })); + return SkRef(empty.get()); } // assumes fPtr was allocated via sk_malloc diff --git a/src/core/SkFontMgr.cpp b/src/core/SkFontMgr.cpp index 3190825c0f..7c06690d2e 100644 --- a/src/core/SkFontMgr.cpp +++ b/src/core/SkFontMgr.cpp @@ -7,7 +7,7 @@ #include "SkFontDescriptor.h" #include "SkFontMgr.h" -#include "SkOncePtr.h" +#include "SkLazyPtr.h" #include "SkStream.h" #include "SkTypes.h" @@ -158,10 +158,14 @@ SkTypeface* SkFontMgr::legacyCreateTypeface(const char familyName[], return this->onLegacyCreateTypeface(familyName, styleBits); } -SK_DECLARE_STATIC_ONCE_PTR(SkFontMgr, singleton); +// As a template argument this must have external linkage. +SkFontMgr* sk_fontmgr_create_default() { + SkFontMgr* fm = SkFontMgr::Factory(); + return fm ? fm : new SkEmptyFontMgr; +} + +SK_DECLARE_STATIC_LAZY_PTR(SkFontMgr, singleton, sk_fontmgr_create_default); + SkFontMgr* SkFontMgr::RefDefault() { - return SkRef(singleton.get([]{ - SkFontMgr* fm = SkFontMgr::Factory(); - return fm ? fm : new SkEmptyFontMgr; - })); + return SkRef(singleton.get()); } diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp index 9bee4e5f75..309707c66f 100644 --- a/src/core/SkGlyphCache.cpp +++ b/src/core/SkGlyphCache.cpp @@ -8,7 +8,7 @@ #include "SkGlyphCache.h" #include "SkGlyphCache_Globals.h" #include "SkGraphics.h" -#include "SkOncePtr.h" +#include "SkLazyPtr.h" #include "SkPath.h" #include "SkTemplates.h" #include "SkTraceMemoryDump.h" @@ -18,6 +18,8 @@ namespace { +SkGlyphCache_Globals* create_globals() { return new SkGlyphCache_Globals; } + const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache"; // Used to pass context to the sk_trace_dump_visitor. @@ -28,10 +30,11 @@ struct SkGlyphCacheDumpContext { } // namespace +SK_DECLARE_STATIC_LAZY_PTR(SkGlyphCache_Globals, globals, create_globals); + // Returns the shared globals -SK_DECLARE_STATIC_ONCE_PTR(SkGlyphCache_Globals, globals); static SkGlyphCache_Globals& get_globals() { - return *globals.get([]{ return new SkGlyphCache_Globals; }); + return *globals.get(); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp index ffb1f836d8..32562946ed 100644 --- a/src/core/SkImageFilter.cpp +++ b/src/core/SkImageFilter.cpp @@ -11,9 +11,9 @@ #include "SkBitmapDevice.h" #include "SkChecksum.h" #include "SkDevice.h" +#include "SkLazyPtr.h" #include "SkMatrixImageFilter.h" #include "SkMutex.h" -#include "SkOncePtr.h" #include "SkReadBuffer.h" #include "SkRect.h" #include "SkTDynamicHash.h" @@ -560,19 +560,24 @@ private: mutable SkMutex fMutex; }; +SkImageFilter::Cache* CreateCache() { + return SkImageFilter::Cache::Create(kDefaultCacheSize); +} + } // namespace SkImageFilter::Cache* SkImageFilter::Cache::Create(size_t maxBytes) { return new CacheImpl(maxBytes); } -SK_DECLARE_STATIC_ONCE_PTR(SkImageFilter::Cache, cache); +SK_DECLARE_STATIC_LAZY_PTR(SkImageFilter::Cache, cache, CreateCache); + SkImageFilter::Cache* SkImageFilter::Cache::Get() { - return cache.get([]{ return SkImageFilter::Cache::Create(kDefaultCacheSize); }); + return cache.get(); } void SkImageFilter::PurgeCache() { - Cache::Get()->purge(); + cache.get()->purge(); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkMessageBus.h b/src/core/SkMessageBus.h index a2802f06d4..6f032a1fe2 100644 --- a/src/core/SkMessageBus.h +++ b/src/core/SkMessageBus.h @@ -8,8 +8,8 @@ #ifndef SkMessageBus_DEFINED #define SkMessageBus_DEFINED +#include "SkLazyPtr.h" #include "SkMutex.h" -#include "SkOncePtr.h" #include "SkTArray.h" #include "SkTDArray.h" #include "SkTypes.h" @@ -40,17 +40,20 @@ private: SkMessageBus(); static SkMessageBus* Get(); + // Allow SkLazyPtr to call SkMessageBus::SkMessageBus(). + template <typename T> friend T* Private::sk_new(); + SkTDArray<Inbox*> fInboxes; SkMutex fInboxesMutex; }; // This must go in a single .cpp file, not some .h, or we risk creating more than one global // SkMessageBus per type when using shared libraries. NOTE: at most one per file will compile. -#define DECLARE_SKMESSAGEBUS_MESSAGE(Message) \ - SK_DECLARE_STATIC_ONCE_PTR(SkMessageBus<Message>, bus); \ - template <> \ - SkMessageBus<Message>* SkMessageBus<Message>::Get() { \ - return bus.get([]{ return new SkMessageBus<Message>(); }); \ +#define DECLARE_SKMESSAGEBUS_MESSAGE(Message) \ + SK_DECLARE_STATIC_LAZY_PTR(SkMessageBus<Message>, bus); \ + template <> \ + SkMessageBus<Message>* SkMessageBus<Message>::Get() { \ + return bus.get(); \ } // ----------------------- Implementation of SkMessageBus::Inbox ----------------------- diff --git a/src/core/SkMiniRecorder.cpp b/src/core/SkMiniRecorder.cpp index 0a4859f379..5161c64722 100644 --- a/src/core/SkMiniRecorder.cpp +++ b/src/core/SkMiniRecorder.cpp @@ -7,8 +7,8 @@ #include "SkCanvas.h" #include "SkTLazy.h" +#include "SkLazyPtr.h" #include "SkMiniRecorder.h" -#include "SkOncePtr.h" #include "SkPicture.h" #include "SkPictureCommon.h" #include "SkRecordDraw.h" @@ -27,7 +27,7 @@ public: int numSlowPaths() const override { return 0; } bool willPlayBackBitmaps() const override { return false; } }; -SK_DECLARE_STATIC_ONCE_PTR(SkEmptyPicture, gEmptyPicture); +SK_DECLARE_STATIC_LAZY_PTR(SkEmptyPicture, gEmptyPicture); template <typename T> class SkMiniPicture final : public SkPicture { @@ -108,7 +108,7 @@ SkPicture* SkMiniRecorder::detachAsPicture(const SkRect& cull) { return new SkMiniPicture<Type>(cull, reinterpret_cast<Type*>(fBuffer.get())) switch (fState) { - case State::kEmpty: return SkRef(gEmptyPicture.get([]{ return new SkEmptyPicture; })); + case State::kEmpty: return SkRef(gEmptyPicture.get()); CASE(DrawBitmapRectFixedSize); CASE(DrawPath); CASE(DrawRect); diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp index 119711381f..be7c66cc4b 100644 --- a/src/core/SkPathRef.cpp +++ b/src/core/SkPathRef.cpp @@ -6,7 +6,7 @@ */ #include "SkBuffer.h" -#include "SkOncePtr.h" +#include "SkLazyPtr.h" #include "SkPath.h" #include "SkPathRef.h" @@ -44,13 +44,17 @@ SkPathRef::~SkPathRef() { SkDEBUGCODE(fEditorsAttached = 0x7777777;) } -SK_DECLARE_STATIC_ONCE_PTR(SkPathRef, empty); +// As a template argument, this must have external linkage. +SkPathRef* sk_create_empty_pathref() { + SkPathRef* empty = new SkPathRef; + empty->computeBounds(); // Avoids races later to be the first to do this. + return empty; +} + +SK_DECLARE_STATIC_LAZY_PTR(SkPathRef, empty, sk_create_empty_pathref); + SkPathRef* SkPathRef::CreateEmpty() { - return SkRef(empty.get([]{ - SkPathRef* pr = new SkPathRef; - pr->computeBounds(); // Avoids races later to be the first to do this. - return pr; - })); + return SkRef(empty.get()); } void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, @@ -439,7 +443,7 @@ uint32_t SkPathRef::genID() const { } void SkPathRef::addGenIDChangeListener(GenIDChangeListener* listener) { - if (nullptr == listener || this == (SkPathRef*)empty) { + if (nullptr == listener || this == empty.get()) { delete listener; return; } diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp index e6a9b4d66c..3ed2565ee2 100644 --- a/src/core/SkTypeface.cpp +++ b/src/core/SkTypeface.cpp @@ -9,9 +9,9 @@ #include "SkEndian.h" #include "SkFontDescriptor.h" #include "SkFontMgr.h" +#include "SkLazyPtr.h" #include "SkMutex.h" #include "SkOTTable_OS_2.h" -#include "SkOncePtr.h" #include "SkStream.h" #include "SkTypeface.h" @@ -73,22 +73,33 @@ protected: } }; +namespace { + SK_DECLARE_STATIC_MUTEX(gCreateDefaultMutex); -SK_DECLARE_STATIC_ONCE_PTR(SkTypeface, defaults[4]); + +// As a template arguments, these must have external linkage. +SkTypeface* sk_create_default_typeface(int style) { + // It is not safe to call FontConfigTypeface::LegacyCreateTypeface concurrently. + // To be safe, we serialize here with a mutex so only one call to + // CreateTypeface is happening at any given time. + // TODO(bungeman, mtklein): This is sad. Make our fontconfig code safe? + SkAutoMutexAcquire lock(&gCreateDefaultMutex); + + SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault()); + SkTypeface* t = fm->legacyCreateTypeface(nullptr, style); + return t ? t : SkEmptyTypeface::Create(); +} + +void sk_unref_typeface(SkTypeface* ptr) { SkSafeUnref(ptr); } + +} // namespace + +SK_DECLARE_STATIC_LAZY_PTR_ARRAY(SkTypeface, defaults, 4, + sk_create_default_typeface, sk_unref_typeface); SkTypeface* SkTypeface::GetDefaultTypeface(Style style) { SkASSERT((int)style < 4); - return defaults[style].get([=]{ - // It is not safe to call FontConfigTypeface::LegacyCreateTypeface concurrently. - // To be safe, we serialize here with a mutex so only one call to - // CreateTypeface is happening at any given time. - // TODO(bungeman, mtklein): This is sad. Make our fontconfig code safe? - SkAutoMutexAcquire lock(&gCreateDefaultMutex); - - SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault()); - SkTypeface* t = fm->legacyCreateTypeface(nullptr, style); - return t ? t : SkEmptyTypeface::Create(); - }); + return defaults[style]; } SkTypeface* SkTypeface::RefDefault(Style style) { @@ -314,14 +325,22 @@ bool SkTypeface::onGetKerningPairAdjustments(const uint16_t glyphs[], int count, #include "SkDescriptor.h" #include "SkPaint.h" -SkRect SkTypeface::getBounds() const { - return *fLazyBounds.get([&] { +struct SkTypeface::BoundsComputer { + const SkTypeface& fTypeface; + + BoundsComputer(const SkTypeface& tf) : fTypeface(tf) {} + + SkRect* operator()() const { SkRect* rect = new SkRect; - if (!this->onComputeBounds(rect)) { + if (!fTypeface.onComputeBounds(rect)) { rect->setEmpty(); } return rect; - }); + } +}; + +SkRect SkTypeface::getBounds() const { + return *fLazyBounds.get(BoundsComputer(*this)); } bool SkTypeface::onComputeBounds(SkRect* bounds) const { diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp index 8548fe8aac..18ab9b6a0a 100644 --- a/src/core/SkXfermode.cpp +++ b/src/core/SkXfermode.cpp @@ -9,8 +9,8 @@ #include "SkXfermode.h" #include "SkXfermode_proccoeff.h" #include "SkColorPriv.h" +#include "SkLazyPtr.h" #include "SkMathPriv.h" -#include "SkOncePtr.h" #include "SkOpts.h" #include "SkReadBuffer.h" #include "SkString.h" @@ -996,7 +996,20 @@ void SkProcCoeffXfermode::toString(SkString* str) const { #endif -SK_DECLARE_STATIC_ONCE_PTR(SkXfermode, cached[SkXfermode::kLastMode + 1]); +// Technically, can't be static and passed as a template parameter. So we use anonymous namespace. +namespace { +SkXfermode* create_mode(int iMode) { + SkXfermode::Mode mode = (SkXfermode::Mode)iMode; + + ProcCoeff rec = gProcCoeffs[mode]; + if (auto xfermode = SkOpts::create_xfermode(rec, mode)) { + return xfermode; + } + return new SkProcCoeffXfermode(rec, mode); +} +} // namespace + +SK_DECLARE_STATIC_LAZY_PTR_ARRAY(SkXfermode, cached, SkXfermode::kLastMode + 1, create_mode); SkXfermode* SkXfermode::Create(Mode mode) { SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount); @@ -1012,13 +1025,7 @@ SkXfermode* SkXfermode::Create(Mode mode) { return nullptr; } - return SkSafeRef(cached[mode].get([=]{ - ProcCoeff rec = gProcCoeffs[mode]; - if (auto xfermode = SkOpts::create_xfermode(rec, mode)) { - return xfermode; - } - return (SkXfermode*) new SkProcCoeffXfermode(rec, mode); - })); + return SkSafeRef(cached[mode]); } SkXfermodeProc SkXfermode::GetProc(Mode mode) { diff --git a/src/fonts/SkRemotableFontMgr.cpp b/src/fonts/SkRemotableFontMgr.cpp index 41e3bc3235..5299fad5fd 100644 --- a/src/fonts/SkRemotableFontMgr.cpp +++ b/src/fonts/SkRemotableFontMgr.cpp @@ -5,9 +5,10 @@ * found in the LICENSE file. */ -#include "SkOncePtr.h" #include "SkRemotableFontMgr.h" +#include "SkLazyPtr.h" + SkRemotableFontIdentitySet::SkRemotableFontIdentitySet(int count, SkFontIdentity** data) : fCount(count), fData(count) { @@ -15,7 +16,12 @@ SkRemotableFontIdentitySet::SkRemotableFontIdentitySet(int count, SkFontIdentity *data = fData; } -SK_DECLARE_STATIC_ONCE_PTR(SkRemotableFontIdentitySet, empty); +// As a template argument, this must have external linkage. +SkRemotableFontIdentitySet* sk_remotable_font_identity_set_new() { + return new SkRemotableFontIdentitySet; +} + +SK_DECLARE_STATIC_LAZY_PTR(SkRemotableFontIdentitySet, empty, sk_remotable_font_identity_set_new); SkRemotableFontIdentitySet* SkRemotableFontIdentitySet::NewEmpty() { - return SkRef(empty.get([]{ return new SkRemotableFontIdentitySet; })); + return SkRef(empty.get()); } diff --git a/src/lazy/SkDiscardableMemoryPool.cpp b/src/lazy/SkDiscardableMemoryPool.cpp index 78c48f50c1..17b8c23d95 100644 --- a/src/lazy/SkDiscardableMemoryPool.cpp +++ b/src/lazy/SkDiscardableMemoryPool.cpp @@ -8,8 +8,8 @@ #include "SkDiscardableMemory.h" #include "SkDiscardableMemoryPool.h" #include "SkImageGenerator.h" +#include "SkLazyPtr.h" #include "SkMutex.h" -#include "SkOncePtr.h" #include "SkTInternalLList.h" // Note: @@ -246,18 +246,23 @@ void DiscardableMemoryPool::dumpPool() { this->dumpDownTo(0); } +//////////////////////////////////////////////////////////////////////////////// +SK_DECLARE_STATIC_MUTEX(gMutex); +SkDiscardableMemoryPool* create_global_pool() { + return SkDiscardableMemoryPool::Create(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, + &gMutex); +} + } // namespace SkDiscardableMemoryPool* SkDiscardableMemoryPool::Create(size_t size, SkBaseMutex* mutex) { return new DiscardableMemoryPool(size, mutex); } -SK_DECLARE_STATIC_MUTEX(gMutex); -SK_DECLARE_STATIC_ONCE_PTR(SkDiscardableMemoryPool, global); +SK_DECLARE_STATIC_LAZY_PTR(SkDiscardableMemoryPool, global, create_global_pool); SkDiscardableMemoryPool* SkGetGlobalDiscardableMemoryPool() { - return global.get([] { - return SkDiscardableMemoryPool::Create(SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE, - &gMutex); - }); + return global.get(); } + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/opts/opts_check_x86.cpp b/src/opts/opts_check_x86.cpp index 3c817e1f0a..d3450c1dfc 100644 --- a/src/opts/opts_check_x86.cpp +++ b/src/opts/opts_check_x86.cpp @@ -13,7 +13,7 @@ #include "SkBlitRow.h" #include "SkBlitRow_opts_SSE2.h" #include "SkBlitRow_opts_SSE4.h" -#include "SkOncePtr.h" +#include "SkLazyPtr.h" #include "SkRTConf.h" #if defined(_MSC_VER) && defined(_WIN64) @@ -71,7 +71,8 @@ static inline void getcpuid(int info_type, int info[4]) { /* Fetch the SIMD level directly from the CPU, at run-time. * Only checks the levels needed by the optimizations in this file. */ -static int* get_SIMD_level() { +namespace { // get_SIMD_level() technically must have external linkage, so no static. +int* get_SIMD_level() { int cpu_info[4] = { 0, 0, 0, 0 }; getcpuid(1, cpu_info); @@ -90,8 +91,9 @@ static int* get_SIMD_level() { } return level; } +} // namespace -SK_DECLARE_STATIC_ONCE_PTR(int, gSIMDLevel); +SK_DECLARE_STATIC_LAZY_PTR(int, gSIMDLevel, get_SIMD_level); /* Verify that the requested SIMD level is supported in the build. * If not, check if the platform supports it. @@ -112,7 +114,7 @@ static inline bool supports_simd(int minLevel) { */ return false; #else - return minLevel <= *gSIMDLevel.get(get_SIMD_level); + return minLevel <= *gSIMDLevel.get(); #endif } } diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp index 0ad1853f3e..162ddc8447 100644 --- a/src/pdf/SkPDFGraphicState.cpp +++ b/src/pdf/SkPDFGraphicState.cpp @@ -6,7 +6,7 @@ */ #include "SkData.h" -#include "SkOncePtr.h" +#include "SkLazyPtr.h" #include "SkPDFCanon.h" #include "SkPDFFormXObject.h" #include "SkPDFGraphicState.h" @@ -126,7 +126,8 @@ SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint( return pdfGraphicState; } -static SkPDFObject* create_invert_function() { +namespace { +SkPDFObject* create_invert_function() { // Acrobat crashes if we use a type 0 function, kpdf crashes if we use // a type 2 function, so we use a type 4 function. SkAutoTUnref<SkPDFArray> domainAndRange(new SkPDFArray); @@ -146,7 +147,13 @@ static SkPDFObject* create_invert_function() { return invertFunction; } -SK_DECLARE_STATIC_ONCE_PTR(SkPDFObject, invertFunction); +template <typename T> void unref(T* ptr) { ptr->unref(); } +} // namespace + +SK_DECLARE_STATIC_LAZY_PTR(SkPDFObject, + invertFunction, + create_invert_function, + unref<SkPDFObject>); // static SkPDFDict* SkPDFGraphicState::GetSMaskGraphicState(SkPDFFormXObject* sMask, @@ -162,7 +169,7 @@ SkPDFDict* SkPDFGraphicState::GetSMaskGraphicState(SkPDFFormXObject* sMask, } sMaskDict->insertObjRef("G", SkRef(sMask)); if (invert) { - sMaskDict->insertObjRef("TR", SkRef(invertFunction.get(create_invert_function))); + sMaskDict->insertObjRef("TR", SkRef(invertFunction.get())); } SkPDFDict* result = new SkPDFDict("ExtGState"); @@ -170,16 +177,21 @@ SkPDFDict* SkPDFGraphicState::GetSMaskGraphicState(SkPDFFormXObject* sMask, return result; } -static SkPDFDict* create_no_smask_graphic_state() { +namespace { +SkPDFDict* create_no_smask_graphic_state() { SkPDFDict* noSMaskGS = new SkPDFDict("ExtGState"); noSMaskGS->insertName("SMask", "None"); return noSMaskGS; } -SK_DECLARE_STATIC_ONCE_PTR(SkPDFDict, noSMaskGraphicState); +} // namespace +SK_DECLARE_STATIC_LAZY_PTR(SkPDFDict, + noSMaskGraphicState, + create_no_smask_graphic_state, + unref<SkPDFDict>); // static SkPDFDict* SkPDFGraphicState::GetNoSMaskGraphicState() { - return SkRef(noSMaskGraphicState.get(create_no_smask_graphic_state)); + return SkRef(noSMaskGraphicState.get()); } void SkPDFGraphicState::emitObject( diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp index fe6e47c1db..645091dad2 100644 --- a/src/pdf/SkPDFShader.cpp +++ b/src/pdf/SkPDFShader.cpp @@ -10,7 +10,6 @@ #include "SkPDFShader.h" #include "SkData.h" -#include "SkOncePtr.h" #include "SkPDFCanon.h" #include "SkPDFDevice.h" #include "SkPDFFormXObject.h" @@ -678,7 +677,8 @@ static bool split_perspective(const SkMatrix in, SkMatrix* affine, return true; } -static SkPDFObject* create_range_object() { +namespace { +SkPDFObject* create_range_object() { SkPDFArray* range = new SkPDFArray; range->reserve(6); range->appendInt(0); @@ -689,7 +689,12 @@ static SkPDFObject* create_range_object() { range->appendInt(1); return range; } -SK_DECLARE_STATIC_ONCE_PTR(SkPDFObject, rangeObject); + +template <typename T> void unref(T* ptr) { ptr->unref();} +} // namespace + +SK_DECLARE_STATIC_LAZY_PTR(SkPDFObject, rangeObject, + create_range_object, unref<SkPDFObject>); static SkPDFStream* make_ps_function(const SkString& psCode, SkPDFArray* domain) { @@ -698,7 +703,7 @@ static SkPDFStream* make_ps_function(const SkString& psCode, SkPDFStream* result = new SkPDFStream(funcData.get()); result->insertInt("FunctionType", 4); result->insertObject("Domain", SkRef(domain)); - result->insertObject("Range", SkRef(rangeObject.get(create_range_object))); + result->insertObject("Range", SkRef(rangeObject.get())); return result; } diff --git a/src/utils/SkEventTracer.cpp b/src/utils/SkEventTracer.cpp index 32a0207c23..1c916ed2f6 100644 --- a/src/utils/SkEventTracer.cpp +++ b/src/utils/SkEventTracer.cpp @@ -7,7 +7,7 @@ #include "SkAtomics.h" #include "SkEventTracer.h" -#include "SkOncePtr.h" +#include "SkLazyPtr.h" #include <stdlib.h> @@ -41,7 +41,7 @@ class SkDefaultEventTracer : public SkEventTracer { // We prefer gUserTracer if it's been set, otherwise we fall back on gDefaultTracer. static SkEventTracer* gUserTracer = nullptr; -SK_DECLARE_STATIC_ONCE_PTR(SkDefaultEventTracer, gDefaultTracer); +SK_DECLARE_STATIC_LAZY_PTR(SkDefaultEventTracer, gDefaultTracer); void SkEventTracer::SetInstance(SkEventTracer* tracer) { SkASSERT(nullptr == sk_atomic_load(&gUserTracer, sk_memory_order_acquire)); @@ -54,5 +54,5 @@ SkEventTracer* SkEventTracer::GetInstance() { if (SkEventTracer* tracer = sk_atomic_load(&gUserTracer, sk_memory_order_acquire)) { return tracer; } - return gDefaultTracer.get([]{ return new SkDefaultEventTracer; }); + return gDefaultTracer.get(); } diff --git a/tests/LazyPtrTest.cpp b/tests/LazyPtrTest.cpp new file mode 100644 index 0000000000..c6ffd72e25 --- /dev/null +++ b/tests/LazyPtrTest.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "SkLazyPtr.h" +#include "SkRunnable.h" +#include "SkTaskGroup.h" + +namespace { + +struct CreateIntFromFloat { + CreateIntFromFloat(float val) : fVal(val) {} + int* operator()() const { return new int((int)fVal); } + float fVal; +}; + +// As a template argument this must have external linkage. +void custom_destroy(int* ptr) { *ptr = 99; } + +} // namespace + +DEF_TEST(LazyPtr, r) { + // Basic usage: calls new int. + SkLazyPtr<int> lazy; + int* ptr = lazy.get(); + REPORTER_ASSERT(r, ptr); + REPORTER_ASSERT(r, lazy.get() == ptr); + + // Advanced usage: calls a functor. + SkLazyPtr<int> lazyFunctor; + int* six = lazyFunctor.get(CreateIntFromFloat(6.4f)); + REPORTER_ASSERT(r, six); + REPORTER_ASSERT(r, 6 == *six); + + // Just makes sure this is safe. + SkLazyPtr<double> neverRead; + + // SkLazyPtr supports custom destroy methods. + { + SkLazyPtr<int, custom_destroy> customDestroy; + ptr = customDestroy.get(); + // custom_destroy called here. + } + REPORTER_ASSERT(r, ptr); + REPORTER_ASSERT(r, 99 == *ptr); + // Since custom_destroy didn't actually delete ptr, we do now. + delete ptr; +} + +DEF_TEST(LazyPtr_Threaded, r) { + static const int kRacers = 321; + + // Race to intialize the pointer by calling .get(). + SkLazyPtr<int> lazy; + int* seen[kRacers]; + + sk_parallel_for(kRacers, [&](int i) { + seen[i] = lazy.get(); + }); + + // lazy.get() should return the same pointer to all threads. + for (int i = 1; i < kRacers; i++) { + REPORTER_ASSERT(r, seen[i] != nullptr); + REPORTER_ASSERT(r, seen[i] == seen[0]); + } +} diff --git a/tests/OncePtrTest.cpp b/tests/OncePtrTest.cpp index b1e4e5d1ae..1b348dfd2d 100644 --- a/tests/OncePtrTest.cpp +++ b/tests/OncePtrTest.cpp @@ -28,6 +28,8 @@ DEF_TEST(OncePtr, r) { REPORTER_ASSERT(r, *n == 5); }); REPORTER_ASSERT(r, calls.load() == 1); + + delete (int*)once; } /* TODO(mtklein): next CL |