1/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkTLS.h"
9
10// enable to help debug TLS storage
11//#define SK_TRACE_TLS_LIFETIME
12
13
14#ifdef SK_TRACE_TLS_LIFETIME
15    #include "SkAtomics.h"
16    static int32_t gTLSRecCount;
17#endif
18
19struct SkTLSRec {
20    SkTLSRec*           fNext;
21    void*               fData;
22    SkTLS::CreateProc   fCreateProc;
23    SkTLS::DeleteProc   fDeleteProc;
24
25#ifdef SK_TRACE_TLS_LIFETIME
26    SkTLSRec() {
27        int n = sk_atomic_inc(&gTLSRecCount);
28        SkDebugf(" SkTLSRec[%d]\n", n);
29    }
30#endif
31
32    ~SkTLSRec() {
33        if (fDeleteProc) {
34            fDeleteProc(fData);
35        }
36        // else we leak fData, or it will be managed by the caller
37
38#ifdef SK_TRACE_TLS_LIFETIME
39        int n = sk_atomic_dec(&gTLSRecCount);
40        SkDebugf("~SkTLSRec[%d]\n", n - 1);
41#endif
42    }
43};
44
45void SkTLS::Destructor(void* ptr) {
46#ifdef SK_TRACE_TLS_LIFETIME
47    SkDebugf("SkTLS::Destructor(%p)\n", ptr);
48#endif
49
50    SkTLSRec* rec = (SkTLSRec*)ptr;
51    do {
52        SkTLSRec* next = rec->fNext;
53        delete rec;
54        rec = next;
55    } while (rec);
56}
57
58void* SkTLS::Get(CreateProc createProc, DeleteProc deleteProc) {
59    if (nullptr == createProc) {
60        return nullptr;
61    }
62
63    void* ptr = SkTLS::PlatformGetSpecific(true);
64
65    if (ptr) {
66        const SkTLSRec* rec = (const SkTLSRec*)ptr;
67        do {
68            if (rec->fCreateProc == createProc) {
69                SkASSERT(rec->fDeleteProc == deleteProc);
70                return rec->fData;
71            }
72        } while ((rec = rec->fNext) != nullptr);
73        // not found, so create a new one
74    }
75
76    // add a new head of our change
77    SkTLSRec* rec = new SkTLSRec;
78    rec->fNext = (SkTLSRec*)ptr;
79
80    SkTLS::PlatformSetSpecific(rec);
81
82    rec->fData = createProc();
83    rec->fCreateProc = createProc;
84    rec->fDeleteProc = deleteProc;
85    return rec->fData;
86}
87
88void* SkTLS::Find(CreateProc createProc) {
89    if (nullptr == createProc) {
90        return nullptr;
91    }
92
93    void* ptr = SkTLS::PlatformGetSpecific(false);
94
95    if (ptr) {
96        const SkTLSRec* rec = (const SkTLSRec*)ptr;
97        do {
98            if (rec->fCreateProc == createProc) {
99                return rec->fData;
100            }
101        } while ((rec = rec->fNext) != nullptr);
102    }
103    return nullptr;
104}
105
106void SkTLS::Delete(CreateProc createProc) {
107    if (nullptr == createProc) {
108        return;
109    }
110
111    void* ptr = SkTLS::PlatformGetSpecific(false);
112
113    SkTLSRec* curr = (SkTLSRec*)ptr;
114    SkTLSRec* prev = nullptr;
115    while (curr) {
116        SkTLSRec* next = curr->fNext;
117        if (curr->fCreateProc == createProc) {
118            if (prev) {
119                prev->fNext = next;
120            } else {
121                // we have a new head of our chain
122                SkTLS::PlatformSetSpecific(next);
123            }
124            delete curr;
125            break;
126        }
127        prev = curr;
128        curr = next;
129    }
130}
131