1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "SkThread.h"
9
10#include <pthread.h>
11#include <errno.h>
12
13#ifndef SK_BUILD_FOR_ANDROID
14
15/**
16 We prefer the GCC intrinsic implementation of the atomic operations over the
17 SkMutex-based implementation. The SkMutex version suffers from static
18 destructor ordering problems.
19 Note clang also defines the GCC version macros and implements the intrinsics.
20 TODO: Verify that gcc-style __sync_* intrinsics work on ARM
21 According to this the intrinsics are supported on ARM in LLVM 2.7+
22 http://llvm.org/releases/2.7/docs/ReleaseNotes.html
23*/
24#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || __GNUC__ > 4
25    #if (defined(__x86_64) || defined(__i386__))
26        #define GCC_INTRINSIC
27    #endif
28#endif
29
30#if defined(GCC_INTRINSIC)
31
32int32_t sk_atomic_inc(int32_t* addr)
33{
34    return __sync_fetch_and_add(addr, 1);
35}
36
37int32_t sk_atomic_add(int32_t* addr, int32_t inc)
38{
39    return __sync_fetch_and_add(addr, inc);
40}
41
42int32_t sk_atomic_dec(int32_t* addr)
43{
44    return __sync_fetch_and_add(addr, -1);
45}
46void sk_membar_aquire__after_atomic_dec() { }
47
48int32_t sk_atomic_conditional_inc(int32_t* addr)
49{
50    int32_t value = *addr;
51
52    while (true) {
53        if (value == 0) {
54            return 0;
55        }
56
57        int32_t before = __sync_val_compare_and_swap(addr, value, value + 1);
58
59        if (before == value) {
60            return value;
61        } else {
62            value = before;
63        }
64    }
65}
66void sk_membar_aquire__after_atomic_conditional_inc() { }
67
68#else
69
70SkMutex gAtomicMutex;
71
72int32_t sk_atomic_inc(int32_t* addr)
73{
74    SkAutoMutexAcquire ac(gAtomicMutex);
75
76    int32_t value = *addr;
77    *addr = value + 1;
78    return value;
79}
80
81int32_t sk_atomic_add(int32_t* addr, int32_t inc)
82{
83    SkAutoMutexAcquire ac(gAtomicMutex);
84
85    int32_t value = *addr;
86    *addr = value + inc;
87    return value;
88}
89
90int32_t sk_atomic_dec(int32_t* addr)
91{
92    SkAutoMutexAcquire ac(gAtomicMutex);
93
94    int32_t value = *addr;
95    *addr = value - 1;
96    return value;
97}
98void sk_membar_aquire__after_atomic_dec() { }
99
100int32_t sk_atomic_conditional_inc(int32_t* addr)
101{
102    SkAutoMutexAcquire ac(gAtomicMutex);
103
104    int32_t value = *addr;
105    if (value != 0) ++*addr;
106    return value;
107}
108void sk_membar_aquire__after_atomic_conditional_inc() { }
109
110#endif
111
112#endif // SK_BUILD_FOR_ANDROID
113
114//////////////////////////////////////////////////////////////////////////////
115
116static void print_pthread_error(int status) {
117    switch (status) {
118    case 0: // success
119        break;
120    case EINVAL:
121        SkDebugf("pthread error [%d] EINVAL\n", status);
122        break;
123    case EBUSY:
124        SkDebugf("pthread error [%d] EBUSY\n", status);
125        break;
126    default:
127        SkDebugf("pthread error [%d] unknown\n", status);
128        break;
129    }
130}
131
132#ifdef SK_USE_POSIX_THREADS
133
134SkMutex::SkMutex() {
135    int status;
136
137    status = pthread_mutex_init(&fMutex, NULL);
138    if (status != 0) {
139        print_pthread_error(status);
140        SkASSERT(0 == status);
141    }
142}
143
144SkMutex::~SkMutex() {
145    int status = pthread_mutex_destroy(&fMutex);
146
147    // only report errors on non-global mutexes
148    if (status != 0) {
149        print_pthread_error(status);
150        SkASSERT(0 == status);
151    }
152}
153
154#else // !SK_USE_POSIX_THREADS
155
156SkMutex::SkMutex() {
157    if (sizeof(pthread_mutex_t) > sizeof(fStorage)) {
158        SkDEBUGF(("pthread mutex size = %d\n", sizeof(pthread_mutex_t)));
159        SkDEBUGFAIL("mutex storage is too small");
160    }
161
162    int status;
163    pthread_mutexattr_t attr;
164
165    status = pthread_mutexattr_init(&attr);
166    print_pthread_error(status);
167    SkASSERT(0 == status);
168
169    status = pthread_mutex_init((pthread_mutex_t*)fStorage, &attr);
170    print_pthread_error(status);
171    SkASSERT(0 == status);
172}
173
174SkMutex::~SkMutex() {
175    int status = pthread_mutex_destroy((pthread_mutex_t*)fStorage);
176#if 0
177    // only report errors on non-global mutexes
178    if (!fIsGlobal) {
179        print_pthread_error(status);
180        SkASSERT(0 == status);
181    }
182#endif
183}
184
185void SkMutex::acquire() {
186    int status = pthread_mutex_lock((pthread_mutex_t*)fStorage);
187    print_pthread_error(status);
188    SkASSERT(0 == status);
189}
190
191void SkMutex::release() {
192    int status = pthread_mutex_unlock((pthread_mutex_t*)fStorage);
193    print_pthread_error(status);
194    SkASSERT(0 == status);
195}
196
197#endif // !SK_USE_POSIX_THREADS
198