skia2/include/core/SkInstCnt.h
mtklein 6f07665768 Simplify SkInstCnt
This code requires fewer macros to use it (just one), has less code in macro
definitions, and has simpler synchronization code (just atomic ints, no SkOnce,
no SkMutex, etc.)

A minor downside, we lose indentation and reverse-ordering in the final report:
  Leaked SkRefCntBase: 7
     Leaked SkFontMgr: 1
     Leaked SkWeakRefCnt: 1
         Leaked SkTypeface: 1
     Leaked SkFlattenable: 3
         Leaked SkXfermode: 3
     Leaked SkPathRef: 1
     Leaked SkPixelRef: 1
         Leaked SkMallocPixelRef: 1
becomes
  Leaked SkXfermode: 3
  Leaked SkMallocPixelRef: 1
  Leaked SkPixelRef: 1
  Leaked SkPathRef: 1
  Leaked SkFlattenable: 3
  Leaked SkTypeface: 1
  Leaked SkWeakRefCnt: 1
  Leaked SkFontMgr: 1
  Leaked SkRefCntBase: 7

This is motivated by wanting to land https://codereview.chromium.org/806473006/,
which makes sure all static use of SkOnce are in global scope.  The current
implementation of SkInstCnt uses them in function scope, which isn't safe.
BUG=skia:

No public API changes.
TBR=reed@google.com

Review URL: https://codereview.chromium.org/841263004
2015-01-13 08:22:44 -08:00

81 lines
2.9 KiB
C++

/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkInstCnt_DEFINED
#define SkInstCnt_DEFINED
/* To count all instances of T, including all subclasses of T,
* add SK_DECLARE_INST_COUNT(T) to T's class definition.
* If you want to print out counts of leaked instances, set gPrintInstCount to true in main().
*
* E.g.
* struct Base { SK_DECLARE_INST_COUNT(Base) };
* struct A : public Base {};
* struct SubBase : public Base { SK_DECLARE_INST_COUNT(SubBase); }
* struct B : public SubBase {};
*
* If gPrintInstCount is true, at the program exit you will see something like:
* Base: <N> leaked instances
* SubBase: <M> leaked instances
* where N >= M. Leaked instances of A count against Base; leaked instances of B count against
* both SubBase and Base.
*
* If SK_ENABLE_INST_COUNT is not defined or defined to 0, or we're in a shared library build,
* this entire system is compiled away to a noop.
*/
#include "SkTypes.h"
#if SK_ENABLE_INST_COUNT && !defined(SKIA_DLL) // See skia:2058 for why we noop on shared builds.
#include "SkThread.h"
#include <stdlib.h>
#define SK_DECLARE_INST_COUNT(T) \
static const char* InstCountClassName() { return #T; } \
SkInstCount<T, T::InstCountClassName> fInstCnt; \
static int32_t GetInstanceCount() { return SkInstCount<T, InstCountClassName>::Count(); }
extern bool gPrintInstCount;
template <typename T, const char*(Name)()>
class SkInstCount {
public:
SkInstCount() { Inc(); }
SkInstCount(const SkInstCount&) { Inc(); }
~SkInstCount() { sk_atomic_dec(&gCount); }
SkInstCount& operator==(const SkInstCount&) { return *this; } // == can't change the count.
static void Inc() {
// If it's the first time we go from 0 to 1, register to print leaks at process exit.
if (0 == sk_atomic_inc(&gCount) && sk_atomic_cas(&gRegistered, 0, 1)) {
atexit(PrintAtExit);
}
}
static void PrintAtExit() {
int32_t leaks = Count();
if (gPrintInstCount && leaks > 0) {
SkDebugf("Leaked %s: %d\n", Name(), leaks);
}
}
// FIXME: Used publicly by unit tests. Seems like a bad idea in a DM world.
static int32_t Count() { return sk_acquire_load(&gCount); }
private:
static int32_t gCount, gRegistered;
};
// As template values, these will be deduplicated. (No one-definition rule problems.)
template <typename T, const char*(Name)()> int32_t SkInstCount<T, Name>::gCount = 0;
template <typename T, const char*(Name)()> int32_t SkInstCount<T, Name>::gRegistered = 0;
#else
#define SK_DECLARE_INST_COUNT(T)
#endif
#endif // SkInstCnt_DEFINED