aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkLazyPtr.h
blob: 71876e7b41f248c95e09c9b719fe588b34a8ce91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * 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 F.
 *
 *  Example usage:
 *
 *  Foo* CreateFoo() { return SkNEW(Foo); }
 *  Foo* GetSingletonFoo() {
 *      SK_DECLARE_STATIC_LAZY_PTR(Foo, singleton, CreateFoo);  // Clean up with SkDELETE.
 *      return singleton.get();
 *  }
 *
 *  These macros take an optional void (*Destroy)(T*) at the end. If not given, we'll use SkDELETE.
 *  This option is most useful when T doesn't have a public destructor.
 *
 *  void CustomCleanup(Foo* ptr) { ... }
 *  Foo* GetSingletonFooWithCustomCleanup() {
 *      SK_DECLARE_STATIC_LAZY_PTR(Foo, singleton, CreateFoo, CustomCleanup);
 *      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:
 *
 *  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.
 *  In debug mode, each lazy pointer will be cleaned up at process exit so we
 *  can check that we've not leaked or freed them early.
 *
 *  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 or function scope, not as a class member.
 */

#define SK_DECLARE_STATIC_LAZY_PTR(T, name, Create, ...) \
    static Private::SkLazyPtr<T, Create, ##__VA_ARGS__> name

#define SK_DECLARE_STATIC_LAZY_PTR_ARRAY(T, name, N, Create, ...) \
    static Private::SkLazyPtrArray<T, N, Create, ##__VA_ARGS__> name



// Everything below here is private implementation details.  Don't touch, don't even look.

#include "SkDynamicAnnotations.h"
#include "SkThread.h"
#include "SkThreadPriv.h"

// See FIXME below.
class SkFontConfigInterface;
class SkTypeface;

namespace Private {

template <typename T> void sk_delete(T* ptr) { SkDELETE(ptr); }

// Set *dst to ptr if *dst is NULL.  Returns value of *dst, destroying ptr if not swapped in.
// Issues the same memory barriers as sk_atomic_cas: acquire on failure, release on success.
template <typename P, void (*Destroy)(P)>
static P try_cas(void** dst, P ptr) {
    P prev = (P)sk_atomic_cas(dst, NULL, ptr);

    if (prev) {
        // We need an acquire barrier before returning prev, which sk_atomic_cas provided.
        Destroy(ptr);
        return prev;
    } else {
        // We need a release barrier before returning ptr, which sk_atomic_cas provided.
        return ptr;
    }
}

// This has no constructor and must be zero-initalized (the macro above does this).
template <typename T, T* (*Create)(), void (*Destroy)(T*) = sk_delete<T> >
class SkLazyPtr {
public:
    T* get() {
        // If fPtr has already been filled, we need an acquire barrier when loading it.
        // If not, we need a release barrier when setting it.  try_cas will do that.
        T* ptr = (T*)sk_acquire_load(&fPtr);
        return ptr ? ptr : try_cas<T*, Destroy>(&fPtr, Create());
    }

#ifdef SK_DEVELOPER
    // FIXME: We know we leak refs on some classes.  For now, let them leak.
    void cleanup(SkFontConfigInterface*) {}
    void cleanup(SkTypeface*) {}
    template <typename U> void cleanup(U* ptr) { Destroy(ptr); }

    ~SkLazyPtr() {
        this->cleanup((T*)fPtr);
        fPtr = NULL;
    }
#endif

private:
    void* fPtr;
};

// This has no constructor and must be zero-initalized (the macro above does this).
template <typename T, int N, T* (*Create)(int), void (*Destroy)(T*) = sk_delete<T> >
class SkLazyPtrArray {
public:
    T* operator[](int i) {
        SkASSERT(i >= 0 && i < N);
        // If fPtr has already been filled, we need an acquire barrier when loading it.
        // If not, we need a release barrier when setting it.  try_cas will do that.
        T* ptr = (T*)sk_acquire_load(&fArray[i]);
        return ptr ? ptr : try_cas<T*, Destroy>(&fArray[i], Create(i));
    }

#ifdef SK_DEVELOPER
    ~SkLazyPtrArray() {
        for (int i = 0; i < N; i++) {
            Destroy((T*)fArray[i]);
            fArray[i] = NULL;
        }
    }
#endif

private:
    void* fArray[N];
};

}  // namespace Private

#endif//SkLazyPtr_DEFINED