1/*
2 * Copyright 2013 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#ifndef SkMutex_pthread_DEFINED
9#define SkMutex_pthread_DEFINED
10
11/** Posix pthread_mutex based mutex. */
12
13#include <errno.h>
14#include <pthread.h>
15
16// A SkBaseMutex is a POD structure that can be directly initialized
17// at declaration time with SK_DECLARE_STATIC/GLOBAL_MUTEX. This avoids the
18// generation of a static initializer in the final machine code (and
19// a corresponding static finalizer).
20struct SkBaseMutex {
21    void acquire() {
22        SkASSERT(fOwner != pthread_self());  // SkMutex is not re-entrant
23        pthread_mutex_lock(&fMutex);
24        SkDEBUGCODE(fOwner = pthread_self();)
25    }
26    void release() {
27        this->assertHeld();
28        SkDEBUGCODE(fOwner = 0;)
29        pthread_mutex_unlock(&fMutex);
30    }
31    void assertHeld() {
32        SkASSERT(pthread_self() == fOwner);
33    }
34
35    pthread_mutex_t fMutex;
36    SkDEBUGCODE(pthread_t fOwner;)
37};
38
39// A normal mutex that requires to be initialized through normal C++ construction,
40// i.e. when it's a member of another class, or allocated on the heap.
41class SkMutex : public SkBaseMutex {
42public:
43    SkMutex() {
44        SkDEBUGCODE(int status = )pthread_mutex_init(&fMutex, NULL);
45        SkDEBUGCODE(
46            if (status != 0) {
47                print_pthread_error(status);
48                SkASSERT(0 == status);
49            }
50        )
51    }
52
53    ~SkMutex() {
54        SkDEBUGCODE(int status = )pthread_mutex_destroy(&fMutex);
55        SkDEBUGCODE(
56            if (status != 0) {
57                print_pthread_error(status);
58                SkASSERT(0 == status);
59            }
60        )
61    }
62
63private:
64    SkMutex(const SkMutex&);
65    SkMutex& operator=(const SkMutex&);
66
67    static void print_pthread_error(int status) {
68        switch (status) {
69        case 0: // success
70            break;
71        case EINVAL:
72            SkDebugf("pthread error [%d] EINVAL\n", status);
73            break;
74        case EBUSY:
75            SkDebugf("pthread error [%d] EBUSY\n", status);
76            break;
77        default:
78            SkDebugf("pthread error [%d] unknown\n", status);
79            break;
80        }
81    }
82};
83
84#define SK_BASE_MUTEX_INIT { PTHREAD_MUTEX_INITIALIZER, SkDEBUGCODE(0) }
85
86// Using POD-style initialization prevents the generation of a static initializer.
87#define SK_DECLARE_STATIC_MUTEX(name) static SkBaseMutex name = SK_BASE_MUTEX_INIT
88
89// Special case used when the static mutex must be available globally.
90#define SK_DECLARE_GLOBAL_MUTEX(name) SkBaseMutex name = SK_BASE_MUTEX_INIT
91
92#endif
93