18a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com/* 2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2006 The Android Open Source Project 38a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com * 4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be 5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file. 68a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com */ 78a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 88a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#ifndef SkRefCnt_DEFINED 98a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#define SkRefCnt_DEFINED 108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 115f939ab658a228dce34a3b14a545638407150b92mtklein#include "../private/SkTLogic.h" 12f3c15b7cfc4eed2528f7db87ea6c1444b55ee856bungeman#include "SkTypes.h" 132c4bd0798e929acb9663668985eabe74d7378c46bungeman#include <atomic> 14c901c11549acd19e7fc225276f78374ac1600496bungeman#include <functional> 155f939ab658a228dce34a3b14a545638407150b92mtklein#include <memory> 16801b44c45348144a483793e7b24f4d4092a4f74dbungeman#include <type_traits> 17217c0b3f137d824413e806759bc25378abbe8c18halcanary#include <utility> 188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 196d2533ebd2ad5168d08f5a83e681881c0d533949commit-bot@chromium.org/** \class SkRefCntBase 206d2533ebd2ad5168d08f5a83e681881c0d533949commit-bot@chromium.org 2110ba006631a0c350aa2bcba188a60404869607c8bungeman@google.com SkRefCntBase is the base class for objects that may be shared by multiple 22a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com objects. When an existing owner wants to share a reference, it calls ref(). 23a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com When an owner wants to release its reference, it calls unref(). When the 24a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com shared object's reference count goes to zero as the result of an unref() 25a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com call, its (virtual) destructor is called. It is an error for the 26a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com destructor to be called explicitly (or via the object going out of scope on 27a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com the stack or calling delete) if getRefCnt() > 1. 288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com*/ 29e3beb6bd7de7fa211681abbb0be58e80b19885e0commit-bot@chromium.orgclass SK_API SkRefCntBase : SkNoncopyable { 308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.compublic: 318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com /** Default construct, initializing the reference count to 1. 328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com */ 3310ba006631a0c350aa2bcba188a60404869607c8bungeman@google.com SkRefCntBase() : fRefCnt(1) {} 348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 35a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com /** Destruct, asserting that the reference count is 1. 368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com */ 3710ba006631a0c350aa2bcba188a60404869607c8bungeman@google.com virtual ~SkRefCntBase() { 384c888aae25c1f6a420caceec7c42736ba6ddad3dreed@google.com#ifdef SK_DEBUG 392c4bd0798e929acb9663668985eabe74d7378c46bungeman SkASSERTF(getRefCnt() == 1, "fRefCnt was %d", getRefCnt()); 402c4bd0798e929acb9663668985eabe74d7378c46bungeman // illegal value, to catch us if we reuse after delete 412c4bd0798e929acb9663668985eabe74d7378c46bungeman fRefCnt.store(0, std::memory_order_relaxed); 424c888aae25c1f6a420caceec7c42736ba6ddad3dreed@google.com#endif 434c888aae25c1f6a420caceec7c42736ba6ddad3dreed@google.com } 448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 4521180e26519a23cc69f4673f0b518395fddac57bmtklein#ifdef SK_DEBUG 46f64c6842c15e1ba126639be7578e4642cb396987bungeman@google.com /** Return the reference count. Use only for debugging. */ 472c4bd0798e929acb9663668985eabe74d7378c46bungeman int32_t getRefCnt() const { 482c4bd0798e929acb9663668985eabe74d7378c46bungeman return fRefCnt.load(std::memory_order_relaxed); 492c4bd0798e929acb9663668985eabe74d7378c46bungeman } 502c4bd0798e929acb9663668985eabe74d7378c46bungeman 512c4bd0798e929acb9663668985eabe74d7378c46bungeman void validate() const { 522c4bd0798e929acb9663668985eabe74d7378c46bungeman SkASSERT(getRefCnt() > 0); 532c4bd0798e929acb9663668985eabe74d7378c46bungeman } 5421180e26519a23cc69f4673f0b518395fddac57bmtklein#endif 558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 56ea6e14a3825d6f805527ddfbce4fd6b2bf73a7dfcommit-bot@chromium.org /** May return true if the caller is the only owner. 57f64c6842c15e1ba126639be7578e4642cb396987bungeman@google.com * Ensures that all previous owner's actions are complete. 58f64c6842c15e1ba126639be7578e4642cb396987bungeman@google.com */ 59f64c6842c15e1ba126639be7578e4642cb396987bungeman@google.com bool unique() const { 602c4bd0798e929acb9663668985eabe74d7378c46bungeman if (1 == fRefCnt.load(std::memory_order_acquire)) { 617b274c78fbeefa3818af68099545f2839c854847mtklein // The acquire barrier is only really needed if we return true. It 627b274c78fbeefa3818af68099545f2839c854847mtklein // prevents code conditioned on the result of unique() from running 637b274c78fbeefa3818af68099545f2839c854847mtklein // until previous owners are all totally done calling unref(). 647b274c78fbeefa3818af68099545f2839c854847mtklein return true; 65f64c6842c15e1ba126639be7578e4642cb396987bungeman@google.com } 667b274c78fbeefa3818af68099545f2839c854847mtklein return false; 67f64c6842c15e1ba126639be7578e4642cb396987bungeman@google.com } 68f64c6842c15e1ba126639be7578e4642cb396987bungeman@google.com 698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com /** Increment the reference count. Must be balanced by a call to unref(). 708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com */ 718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com void ref() const { 722c4bd0798e929acb9663668985eabe74d7378c46bungeman SkASSERT(getRefCnt() > 0); 732c4bd0798e929acb9663668985eabe74d7378c46bungeman // No barrier required. 742c4bd0798e929acb9663668985eabe74d7378c46bungeman (void)fRefCnt.fetch_add(+1, std::memory_order_relaxed); 758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com /** Decrement the reference count. If the reference count is 1 before the 78a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com decrement, then delete the object. Note that if this is the case, then 79a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com the object needs to have been allocated via new, and not on the stack. 808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com */ 818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com void unref() const { 822c4bd0798e929acb9663668985eabe74d7378c46bungeman SkASSERT(getRefCnt() > 0); 837b274c78fbeefa3818af68099545f2839c854847mtklein // A release here acts in place of all releases we "should" have been doing in ref(). 842c4bd0798e929acb9663668985eabe74d7378c46bungeman if (1 == fRefCnt.fetch_add(-1, std::memory_order_acq_rel)) { 857b274c78fbeefa3818af68099545f2839c854847mtklein // Like unique(), the acquire is only needed on success, to make sure 867b274c78fbeefa3818af68099545f2839c854847mtklein // code in internal_dispose() doesn't happen before the decrement. 877b274c78fbeefa3818af68099545f2839c854847mtklein this->internal_dispose(); 888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 91f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.comprotected: 92f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.com /** 93f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.com * Allow subclasses to call this if they've overridden internal_dispose 949c5052f16b249d2b7674ea86bd24ed0038ccc61fmtklein * so they can reset fRefCnt before the destructor is called or if they 959c5052f16b249d2b7674ea86bd24ed0038ccc61fmtklein * choose not to call the destructor (e.g. using a free list). 96f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.com */ 97f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.com void internal_dispose_restore_refcnt_to_1() const { 982c4bd0798e929acb9663668985eabe74d7378c46bungeman SkASSERT(0 == getRefCnt()); 992c4bd0798e929acb9663668985eabe74d7378c46bungeman fRefCnt.store(1, std::memory_order_relaxed); 100f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.com } 101f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.com 102f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.comprivate: 103f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.com /** 104f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.com * Called when the ref count goes to 0. 105f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.com */ 106f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.com virtual void internal_dispose() const { 107f79430350d9f06a72b307af879d7f3bdec7ff706reed@google.com this->internal_dispose_restore_refcnt_to_1(); 108385fe4d4b62d7d1dd76116dd570df3290a2f487bhalcanary delete this; 109a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com } 11015c0fea699b25343fe6f49668a5632866e1a0306robertphillips@google.com 111f64c6842c15e1ba126639be7578e4642cb396987bungeman@google.com // The following friends are those which override internal_dispose() 112f64c6842c15e1ba126639be7578e4642cb396987bungeman@google.com // and conditionally call SkRefCnt::internal_dispose(). 113a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com friend class SkWeakRefCnt; 114a02bc1519cf49afa31fb38bed097dd5014880d04bungeman@google.com 1152c4bd0798e929acb9663668985eabe74d7378c46bungeman mutable std::atomic<int32_t> fRefCnt; 1164d73ac22a1b99402fc8cff78a4eb4b27aa8fe019robertphillips@google.com 11710ba006631a0c350aa2bcba188a60404869607c8bungeman@google.com typedef SkNoncopyable INHERITED; 1188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}; 1198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 12010ba006631a0c350aa2bcba188a60404869607c8bungeman@google.com#ifdef SK_REF_CNT_MIXIN_INCLUDE 12110ba006631a0c350aa2bcba188a60404869607c8bungeman@google.com// It is the responsibility of the following include to define the type SkRefCnt. 12210ba006631a0c350aa2bcba188a60404869607c8bungeman@google.com// This SkRefCnt should normally derive from SkRefCntBase. 12310ba006631a0c350aa2bcba188a60404869607c8bungeman@google.com#include SK_REF_CNT_MIXIN_INCLUDE 12410ba006631a0c350aa2bcba188a60404869607c8bungeman@google.com#else 125f9634b95eccd58acf5b20d98c5d2ee5af353d3d2benjaminwagnerclass SK_API SkRefCnt : public SkRefCntBase { 126f9634b95eccd58acf5b20d98c5d2ee5af353d3d2benjaminwagner // "#include SK_REF_CNT_MIXIN_INCLUDE" doesn't work with this build system. 1276613cc5186c200e053c5df9bd8f051ffba6e3564Mike Klein #if defined(SK_BUILD_FOR_GOOGLE3) 128f9634b95eccd58acf5b20d98c5d2ee5af353d3d2benjaminwagner public: 129f9634b95eccd58acf5b20d98c5d2ee5af353d3d2benjaminwagner void deref() const { this->unref(); } 130f9634b95eccd58acf5b20d98c5d2ee5af353d3d2benjaminwagner #endif 131f9634b95eccd58acf5b20d98c5d2ee5af353d3d2benjaminwagner}; 13210ba006631a0c350aa2bcba188a60404869607c8bungeman@google.com#endif 13310ba006631a0c350aa2bcba188a60404869607c8bungeman@google.com 1347f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com/////////////////////////////////////////////////////////////////////////////// 1357f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com 1367f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com/** Helper macro to safely assign one SkRefCnt[TS]* to another, checking for 1377f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com null in on each side of the assignment, and ensuring that ref() is called 1387f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com before unref(), in case the two pointers point to the same object. 1397f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com */ 1406dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger 1413849b64cbbdb7db6d5d4af44479b2d7c5bf5e6c7Derek Sollenberger#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) 1426dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger// This version heuristically detects data races, since those otherwise result 1436dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger// in redundant reference count decrements, which are exceedingly 1446dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger// difficult to debug. 1456dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger 1466dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger#define SkRefCnt_SafeAssign(dst, src) \ 1476dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger do { \ 1486dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger typedef typename std::remove_reference<decltype(dst)>::type \ 1496dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger SkRefCntPtrT; \ 1506dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger SkRefCntPtrT old_dst = *const_cast<SkRefCntPtrT volatile *>(&dst); \ 1516dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger if (src) src->ref(); \ 1526dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger if (old_dst) old_dst->unref(); \ 1536dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger if (old_dst != *const_cast<SkRefCntPtrT volatile *>(&dst)) { \ 1546dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger SkDebugf("Detected racing Skia calls at %s:%d\n", \ 1556dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger __FILE__, __LINE__); \ 1566dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger } \ 1576dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger dst = src; \ 1586dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger } while (0) 1596dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger 1603849b64cbbdb7db6d5d4af44479b2d7c5bf5e6c7Derek Sollenberger#else /* !SK_BUILD_FOR_ANDROID_FRAMEWORK */ 1616dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger 1627f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com#define SkRefCnt_SafeAssign(dst, src) \ 1637f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com do { \ 1647f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com if (src) src->ref(); \ 1657f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com if (dst) dst->unref(); \ 1667f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com dst = src; \ 1677f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com } while (0) 1687f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com 1696dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger#endif 1706dd88144d5ae947090499793d68322f661b6192aDerek Sollenberger 1717f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com 17277a53de20d723ca21cc824fd97e68aaa60e022eabungeman/** Call obj->ref() and return obj. The obj must not be nullptr. 1737f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com */ 1741fd201b9dfc739b7a615fdc1eb64dbe136e7cf76bungeman@google.comtemplate <typename T> static inline T* SkRef(T* obj) { 1751fd201b9dfc739b7a615fdc1eb64dbe136e7cf76bungeman@google.com SkASSERT(obj); 1761fd201b9dfc739b7a615fdc1eb64dbe136e7cf76bungeman@google.com obj->ref(); 1771fd201b9dfc739b7a615fdc1eb64dbe136e7cf76bungeman@google.com return obj; 1781fd201b9dfc739b7a615fdc1eb64dbe136e7cf76bungeman@google.com} 1791fd201b9dfc739b7a615fdc1eb64dbe136e7cf76bungeman@google.com 1801fd201b9dfc739b7a615fdc1eb64dbe136e7cf76bungeman@google.com/** Check if the argument is non-null, and if so, call obj->ref() and return obj. 1811fd201b9dfc739b7a615fdc1eb64dbe136e7cf76bungeman@google.com */ 1821fd201b9dfc739b7a615fdc1eb64dbe136e7cf76bungeman@google.comtemplate <typename T> static inline T* SkSafeRef(T* obj) { 1837f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com if (obj) { 1847f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com obj->ref(); 1857f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com } 1861fd201b9dfc739b7a615fdc1eb64dbe136e7cf76bungeman@google.com return obj; 1877f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com} 1887f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com 1897f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com/** Check if the argument is non-null, and if so, call obj->unref() 1907f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com */ 1917f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.comtemplate <typename T> static inline void SkSafeUnref(T* obj) { 1927f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com if (obj) { 1937f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com obj->unref(); 1947f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com } 1957f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com} 1967f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com 197a4de8c257ea0be8ff7081f645249b6afe5c48e7ecommit-bot@chromium.orgtemplate<typename T> static inline void SkSafeSetNull(T*& obj) { 19849f085dddff10473b6ebf832a974288300224e60bsalomon if (obj) { 199a4de8c257ea0be8ff7081f645249b6afe5c48e7ecommit-bot@chromium.org obj->unref(); 20077a53de20d723ca21cc824fd97e68aaa60e022eabungeman obj = nullptr; 201a4de8c257ea0be8ff7081f645249b6afe5c48e7ecommit-bot@chromium.org } 202a4de8c257ea0be8ff7081f645249b6afe5c48e7ecommit-bot@chromium.org} 203a4de8c257ea0be8ff7081f645249b6afe5c48e7ecommit-bot@chromium.org 2047f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com/////////////////////////////////////////////////////////////////////////////// 2057f6d6d4571c0682c81f8508ac4862b2dfea20aecreed@google.com 20608d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtklein// This is a variant of SkRefCnt that's Not Virtual, so weighs 4 bytes instead of 8 or 16. 20708d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtklein// There's only benefit to using this if the deriving class does not otherwise need a vtable. 20808d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtkleintemplate <typename Derived> 20908d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtkleinclass SkNVRefCnt : SkNoncopyable { 21008d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtkleinpublic: 21108d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtklein SkNVRefCnt() : fRefCnt(1) {} 2122c4bd0798e929acb9663668985eabe74d7378c46bungeman ~SkNVRefCnt() { SkASSERTF(1 == getRefCnt(), "NVRefCnt was %d", getRefCnt()); } 21308d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtklein 21408d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtklein // Implementation is pretty much the same as SkRefCntBase. All required barriers are the same: 21508d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtklein // - unique() needs acquire when it returns true, and no barrier if it returns false; 21608d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtklein // - ref() doesn't need any barrier; 21708d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtklein // - unref() needs a release barrier, and an acquire if it's going to call delete. 21808d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtklein 2192c4bd0798e929acb9663668985eabe74d7378c46bungeman bool unique() const { return 1 == fRefCnt.load(std::memory_order_acquire); } 2202c4bd0798e929acb9663668985eabe74d7378c46bungeman void ref() const { (void)fRefCnt.fetch_add(+1, std::memory_order_relaxed); } 22190d0ff013bbd8e5295d1517d41cb408e9d9f4d93reed void unref() const { 2222c4bd0798e929acb9663668985eabe74d7378c46bungeman if (1 == fRefCnt.fetch_add(-1, std::memory_order_acq_rel)) { 2232c4bd0798e929acb9663668985eabe74d7378c46bungeman // restore the 1 for our destructor's assert 2242c4bd0798e929acb9663668985eabe74d7378c46bungeman SkDEBUGCODE(fRefCnt.store(1, std::memory_order_relaxed)); 22597466ab03e025b6edd94c4f0f369cd0b0d8c3319mtklein delete (const Derived*)this; 22690d0ff013bbd8e5295d1517d41cb408e9d9f4d93reed } 22790d0ff013bbd8e5295d1517d41cb408e9d9f4d93reed } 2287b274c78fbeefa3818af68099545f2839c854847mtklein void deref() const { this->unref(); } 229844aa334a43a95cd83a27d565db1d3370d8dc10cFlorin Malita 23008d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtkleinprivate: 2312c4bd0798e929acb9663668985eabe74d7378c46bungeman mutable std::atomic<int32_t> fRefCnt; 2322c4bd0798e929acb9663668985eabe74d7378c46bungeman int32_t getRefCnt() const { 2332c4bd0798e929acb9663668985eabe74d7378c46bungeman return fRefCnt.load(std::memory_order_relaxed); 2342c4bd0798e929acb9663668985eabe74d7378c46bungeman } 23508d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtklein}; 23608d1fccf6eeec0a9fd5421e59e4d05daccf6e339mtklein 237bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed/////////////////////////////////////////////////////////////////////////////////////////////////// 238bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 239bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed/** 240bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * Shared pointer class to wrap classes that support a ref()/unref() interface. 241bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * 242bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * This can be used for classes inheriting from SkRefCnt, but it also works for other 243bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * classes that match the interface, but have different internal choices: e.g. the hosted class 244bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * may have its ref/unref be thread-safe, but that is not assumed/imposed by sk_sp. 245bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed */ 246bb7b043b2db64197d2f6a1edaf3562a50c77afb1reedtemplate <typename T> class sk_sp { 247beab9e7ba06ea8bd01fcbfd44f2ca2ed69c8a1d9bungeman /** Supports safe bool idiom. Obsolete with explicit operator bool. */ 248beab9e7ba06ea8bd01fcbfd44f2ca2ed69c8a1d9bungeman using unspecified_bool_type = T* sk_sp::*; 249bb7b043b2db64197d2f6a1edaf3562a50c77afb1reedpublic: 250c901c11549acd19e7fc225276f78374ac1600496bungeman using element_type = T; 251c901c11549acd19e7fc225276f78374ac1600496bungeman 25206ca8ec87cf6fab57cadd043a5ac18c4154a4129bungeman constexpr sk_sp() : fPtr(nullptr) {} 25306ca8ec87cf6fab57cadd043a5ac18c4154a4129bungeman constexpr sk_sp(std::nullptr_t) : fPtr(nullptr) {} 254bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 255bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed /** 256bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * Shares the underlying object by calling ref(), so that both the argument and the newly 257bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * created sk_sp both have a reference to it. 258bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed */ 259bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed sk_sp(const sk_sp<T>& that) : fPtr(SkSafeRef(that.get())) {} 260801b44c45348144a483793e7b24f4d4092a4f74dbungeman template <typename U, typename = skstd::enable_if_t<std::is_convertible<U*, T*>::value>> 261cb6cb3841adec77d178c6d6ed006102077fd3c48halcanary sk_sp(const sk_sp<U>& that) : fPtr(SkSafeRef(that.get())) {} 262bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 263bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed /** 264bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * Move the underlying object from the argument to the newly created sk_sp. Afterwards only 265bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * the new sk_sp will have a reference to the object, and the argument will point to null. 266bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * No call to ref() or unref() will be made. 267bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed */ 268bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed sk_sp(sk_sp<T>&& that) : fPtr(that.release()) {} 269801b44c45348144a483793e7b24f4d4092a4f74dbungeman template <typename U, typename = skstd::enable_if_t<std::is_convertible<U*, T*>::value>> 270cb6cb3841adec77d178c6d6ed006102077fd3c48halcanary sk_sp(sk_sp<U>&& that) : fPtr(that.release()) {} 271bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 272bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed /** 273bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * Adopt the bare pointer into the newly created sk_sp. 274bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * No call to ref() or unref() will be made. 275bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed */ 276bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed explicit sk_sp(T* obj) : fPtr(obj) {} 277bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 278bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed /** 279bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * Calls unref() on the underlying object pointer. 280bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed */ 281bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed ~sk_sp() { 282bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed SkSafeUnref(fPtr); 283c901c11549acd19e7fc225276f78374ac1600496bungeman SkDEBUGCODE(fPtr = nullptr); 284bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed } 285bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 286cb6cb3841adec77d178c6d6ed006102077fd3c48halcanary sk_sp<T>& operator=(std::nullptr_t) { this->reset(); return *this; } 287bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 288bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed /** 289bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * Shares the underlying object referenced by the argument by calling ref() on it. If this 290bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * sk_sp previously had a reference to an object (i.e. not null) it will call unref() on that 291bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * object. 292bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed */ 293bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed sk_sp<T>& operator=(const sk_sp<T>& that) { 294bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed this->reset(SkSafeRef(that.get())); 295bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed return *this; 296bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed } 297801b44c45348144a483793e7b24f4d4092a4f74dbungeman template <typename U, typename = skstd::enable_if_t<std::is_convertible<U*, T*>::value>> 298cb6cb3841adec77d178c6d6ed006102077fd3c48halcanary sk_sp<T>& operator=(const sk_sp<U>& that) { 299cb6cb3841adec77d178c6d6ed006102077fd3c48halcanary this->reset(SkSafeRef(that.get())); 300cb6cb3841adec77d178c6d6ed006102077fd3c48halcanary return *this; 301cb6cb3841adec77d178c6d6ed006102077fd3c48halcanary } 302bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 303bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed /** 304bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * Move the underlying object from the argument to the sk_sp. If the sk_sp previously held 305bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * a reference to another object, unref() will be called on that object. No call to ref() 306bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * will be made. 307bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed */ 308bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed sk_sp<T>& operator=(sk_sp<T>&& that) { 309bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed this->reset(that.release()); 310bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed return *this; 311bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed } 312801b44c45348144a483793e7b24f4d4092a4f74dbungeman template <typename U, typename = skstd::enable_if_t<std::is_convertible<U*, T*>::value>> 313cb6cb3841adec77d178c6d6ed006102077fd3c48halcanary sk_sp<T>& operator=(sk_sp<U>&& that) { 314cb6cb3841adec77d178c6d6ed006102077fd3c48halcanary this->reset(that.release()); 315cb6cb3841adec77d178c6d6ed006102077fd3c48halcanary return *this; 316cb6cb3841adec77d178c6d6ed006102077fd3c48halcanary } 317bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 318beab9e7ba06ea8bd01fcbfd44f2ca2ed69c8a1d9bungeman T& operator*() const { 319beab9e7ba06ea8bd01fcbfd44f2ca2ed69c8a1d9bungeman SkASSERT(this->get() != nullptr); 320beab9e7ba06ea8bd01fcbfd44f2ca2ed69c8a1d9bungeman return *this->get(); 321beab9e7ba06ea8bd01fcbfd44f2ca2ed69c8a1d9bungeman } 322beab9e7ba06ea8bd01fcbfd44f2ca2ed69c8a1d9bungeman 32389bd99bed85083c736390f3e340fd30be350d461halcanary // MSVC 2013 does not work correctly with explicit operator bool. 32489bd99bed85083c736390f3e340fd30be350d461halcanary // https://chromium-cpp.appspot.com/#core-blacklist 325beab9e7ba06ea8bd01fcbfd44f2ca2ed69c8a1d9bungeman // When explicit operator bool can be used, remove operator! and operator unspecified_bool_type. 32689bd99bed85083c736390f3e340fd30be350d461halcanary //explicit operator bool() const { return this->get() != nullptr; } 327beab9e7ba06ea8bd01fcbfd44f2ca2ed69c8a1d9bungeman operator unspecified_bool_type() const { return this->get() ? &sk_sp::fPtr : nullptr; } 32889bd99bed85083c736390f3e340fd30be350d461halcanary bool operator!() const { return this->get() == nullptr; } 329bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 330bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed T* get() const { return fPtr; } 331bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed T* operator->() const { return fPtr; } 332bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 333bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed /** 334bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * Adopt the new bare pointer, and call unref() on any previously held object (if not null). 335bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * No call to ref() will be made. 336bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed */ 337bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed void reset(T* ptr = nullptr) { 338c901c11549acd19e7fc225276f78374ac1600496bungeman // Calling fPtr->unref() may call this->~() or this->reset(T*). 339c901c11549acd19e7fc225276f78374ac1600496bungeman // http://wg21.cmeerw.net/lwg/issue998 340c901c11549acd19e7fc225276f78374ac1600496bungeman // http://wg21.cmeerw.net/lwg/issue2262 341c901c11549acd19e7fc225276f78374ac1600496bungeman T* oldPtr = fPtr; 342941da9d66171bd8efd2f6c5e25ff90c8c69885c1reed fPtr = ptr; 343c901c11549acd19e7fc225276f78374ac1600496bungeman SkSafeUnref(oldPtr); 344bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed } 345bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 346bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed /** 347bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * Return the bare pointer, and set the internal object pointer to nullptr. 348bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * The caller must assume ownership of the object, and manage its reference count directly. 349bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed * No call to unref() will be made. 350bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed */ 351bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed T* SK_WARN_UNUSED_RESULT release() { 352bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed T* ptr = fPtr; 353bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed fPtr = nullptr; 354bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed return ptr; 355bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed } 356bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 357c901c11549acd19e7fc225276f78374ac1600496bungeman void swap(sk_sp<T>& that) /*noexcept*/ { 358c901c11549acd19e7fc225276f78374ac1600496bungeman using std::swap; 359c901c11549acd19e7fc225276f78374ac1600496bungeman swap(fPtr, that.fPtr); 360c901c11549acd19e7fc225276f78374ac1600496bungeman } 361c901c11549acd19e7fc225276f78374ac1600496bungeman 362bb7b043b2db64197d2f6a1edaf3562a50c77afb1reedprivate: 363bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed T* fPtr; 364bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed}; 365bb7b043b2db64197d2f6a1edaf3562a50c77afb1reed 366c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline void swap(sk_sp<T>& a, sk_sp<T>& b) /*noexcept*/ { 367c901c11549acd19e7fc225276f78374ac1600496bungeman a.swap(b); 368c901c11549acd19e7fc225276f78374ac1600496bungeman} 369c901c11549acd19e7fc225276f78374ac1600496bungeman 370c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T, typename U> inline bool operator==(const sk_sp<T>& a, const sk_sp<U>& b) { 371c901c11549acd19e7fc225276f78374ac1600496bungeman return a.get() == b.get(); 372c901c11549acd19e7fc225276f78374ac1600496bungeman} 373c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline bool operator==(const sk_sp<T>& a, std::nullptr_t) /*noexcept*/ { 374c901c11549acd19e7fc225276f78374ac1600496bungeman return !a; 375c901c11549acd19e7fc225276f78374ac1600496bungeman} 376c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline bool operator==(std::nullptr_t, const sk_sp<T>& b) /*noexcept*/ { 377c901c11549acd19e7fc225276f78374ac1600496bungeman return !b; 378c901c11549acd19e7fc225276f78374ac1600496bungeman} 379c901c11549acd19e7fc225276f78374ac1600496bungeman 380c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T, typename U> inline bool operator!=(const sk_sp<T>& a, const sk_sp<U>& b) { 381c901c11549acd19e7fc225276f78374ac1600496bungeman return a.get() != b.get(); 382c901c11549acd19e7fc225276f78374ac1600496bungeman} 383c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline bool operator!=(const sk_sp<T>& a, std::nullptr_t) /*noexcept*/ { 384c901c11549acd19e7fc225276f78374ac1600496bungeman return static_cast<bool>(a); 385c901c11549acd19e7fc225276f78374ac1600496bungeman} 386c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline bool operator!=(std::nullptr_t, const sk_sp<T>& b) /*noexcept*/ { 387c901c11549acd19e7fc225276f78374ac1600496bungeman return static_cast<bool>(b); 388c901c11549acd19e7fc225276f78374ac1600496bungeman} 389c901c11549acd19e7fc225276f78374ac1600496bungeman 390c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T, typename U> inline bool operator<(const sk_sp<T>& a, const sk_sp<U>& b) { 391c901c11549acd19e7fc225276f78374ac1600496bungeman // Provide defined total order on sk_sp. 392c901c11549acd19e7fc225276f78374ac1600496bungeman // http://wg21.cmeerw.net/lwg/issue1297 393c901c11549acd19e7fc225276f78374ac1600496bungeman // http://wg21.cmeerw.net/lwg/issue1401 . 394c901c11549acd19e7fc225276f78374ac1600496bungeman return std::less<skstd::common_type_t<T*, U*>>()(a.get(), b.get()); 395c901c11549acd19e7fc225276f78374ac1600496bungeman} 396c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline bool operator<(const sk_sp<T>& a, std::nullptr_t) { 397c901c11549acd19e7fc225276f78374ac1600496bungeman return std::less<T*>()(a.get(), nullptr); 398c901c11549acd19e7fc225276f78374ac1600496bungeman} 399c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline bool operator<(std::nullptr_t, const sk_sp<T>& b) { 400c901c11549acd19e7fc225276f78374ac1600496bungeman return std::less<T*>()(nullptr, b.get()); 401c901c11549acd19e7fc225276f78374ac1600496bungeman} 402c901c11549acd19e7fc225276f78374ac1600496bungeman 403c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T, typename U> inline bool operator<=(const sk_sp<T>& a, const sk_sp<U>& b) { 404c901c11549acd19e7fc225276f78374ac1600496bungeman return !(b < a); 405c901c11549acd19e7fc225276f78374ac1600496bungeman} 406c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline bool operator<=(const sk_sp<T>& a, std::nullptr_t) { 407c901c11549acd19e7fc225276f78374ac1600496bungeman return !(nullptr < a); 408c901c11549acd19e7fc225276f78374ac1600496bungeman} 409c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline bool operator<=(std::nullptr_t, const sk_sp<T>& b) { 410c901c11549acd19e7fc225276f78374ac1600496bungeman return !(b < nullptr); 411c901c11549acd19e7fc225276f78374ac1600496bungeman} 412c901c11549acd19e7fc225276f78374ac1600496bungeman 413c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T, typename U> inline bool operator>(const sk_sp<T>& a, const sk_sp<U>& b) { 414c901c11549acd19e7fc225276f78374ac1600496bungeman return b < a; 415c901c11549acd19e7fc225276f78374ac1600496bungeman} 416c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline bool operator>(const sk_sp<T>& a, std::nullptr_t) { 417c901c11549acd19e7fc225276f78374ac1600496bungeman return nullptr < a; 418c901c11549acd19e7fc225276f78374ac1600496bungeman} 419c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline bool operator>(std::nullptr_t, const sk_sp<T>& b) { 420c901c11549acd19e7fc225276f78374ac1600496bungeman return b < nullptr; 421c901c11549acd19e7fc225276f78374ac1600496bungeman} 422c901c11549acd19e7fc225276f78374ac1600496bungeman 423c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T, typename U> inline bool operator>=(const sk_sp<T>& a, const sk_sp<U>& b) { 424c901c11549acd19e7fc225276f78374ac1600496bungeman return !(a < b); 425c901c11549acd19e7fc225276f78374ac1600496bungeman} 426c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline bool operator>=(const sk_sp<T>& a, std::nullptr_t) { 427c901c11549acd19e7fc225276f78374ac1600496bungeman return !(a < nullptr); 428c901c11549acd19e7fc225276f78374ac1600496bungeman} 429c901c11549acd19e7fc225276f78374ac1600496bungemantemplate <typename T> inline bool operator>=(std::nullptr_t, const sk_sp<T>& b) { 430c901c11549acd19e7fc225276f78374ac1600496bungeman return !(nullptr < b); 431c901c11549acd19e7fc225276f78374ac1600496bungeman} 432c901c11549acd19e7fc225276f78374ac1600496bungeman 433217c0b3f137d824413e806759bc25378abbe8c18halcanarytemplate <typename T, typename... Args> 434217c0b3f137d824413e806759bc25378abbe8c18halcanarysk_sp<T> sk_make_sp(Args&&... args) { 435217c0b3f137d824413e806759bc25378abbe8c18halcanary return sk_sp<T>(new T(std::forward<Args>(args)...)); 436217c0b3f137d824413e806759bc25378abbe8c18halcanary} 437217c0b3f137d824413e806759bc25378abbe8c18halcanary 438647cc8474828202c98d540f799742e3074a2aacereed/* 439647cc8474828202c98d540f799742e3074a2aacereed * Returns a sk_sp wrapping the provided ptr AND calls ref on it (if not null). 440647cc8474828202c98d540f799742e3074a2aacereed * 441647cc8474828202c98d540f799742e3074a2aacereed * This is different than the semantics of the constructor for sk_sp, which just wraps the ptr, 442647cc8474828202c98d540f799742e3074a2aacereed * effectively "adopting" it. 443647cc8474828202c98d540f799742e3074a2aacereed */ 444647cc8474828202c98d540f799742e3074a2aacereedtemplate <typename T> sk_sp<T> sk_ref_sp(T* obj) { 445647cc8474828202c98d540f799742e3074a2aacereed return sk_sp<T>(SkSafeRef(obj)); 446647cc8474828202c98d540f799742e3074a2aacereed} 447647cc8474828202c98d540f799742e3074a2aacereed 44819d7bd65045e88724ad59a8d7066a9092754c7e4Mike Reedtemplate <typename T> sk_sp<T> sk_ref_sp(const T* obj) { 44919d7bd65045e88724ad59a8d7066a9092754c7e4Mike Reed return sk_sp<T>(const_cast<T*>(SkSafeRef(obj))); 45019d7bd65045e88724ad59a8d7066a9092754c7e4Mike Reed} 45119d7bd65045e88724ad59a8d7066a9092754c7e4Mike Reed 452647cc8474828202c98d540f799742e3074a2aacereed#endif 453