aboutsummaryrefslogtreecommitdiffstats
path: root/common/threads.h
blob: a5f6ce452257ac4c173a7275039cd6690957b4cb (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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#ifndef AL_THREADS_H
#define AL_THREADS_H

#include <time.h>

#if defined(__GNUC__) && defined(__i386__)
/* force_align_arg_pointer is required for proper function arguments aligning
 * when SSE code is used. Some systems (Windows, QNX) do not guarantee our
 * thread functions will be properly aligned on the stack, even though GCC may
 * generate code with the assumption that it is. */
#define FORCE_ALIGN __attribute__((force_align_arg_pointer))
#else
#define FORCE_ALIGN
#endif

#ifdef __cplusplus
extern "C" {
#endif

enum {
    althrd_success = 0,
    althrd_error,
    althrd_nomem,
    althrd_timedout,
    althrd_busy
};

enum {
    almtx_plain = 0,
    almtx_recursive = 1,
};


#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

typedef CRITICAL_SECTION almtx_t;
typedef HANDLE alsem_t;


inline void althrd_yield(void)
{
    SwitchToThread();
}


inline int almtx_lock(almtx_t *mtx)
{
    if(!mtx) return althrd_error;
    EnterCriticalSection(mtx);
    return althrd_success;
}

inline int almtx_unlock(almtx_t *mtx)
{
    if(!mtx) return althrd_error;
    LeaveCriticalSection(mtx);
    return althrd_success;
}

#else

#include <stdint.h>
#include <errno.h>
#include <pthread.h>
#ifdef __APPLE__
#include <dispatch/dispatch.h>
#else /* !__APPLE__ */
#include <semaphore.h>
#endif /* __APPLE__ */

typedef pthread_t althrd_t;
typedef pthread_mutex_t almtx_t;
#ifdef __APPLE__
typedef dispatch_semaphore_t alsem_t;
#else /* !__APPLE__ */
typedef sem_t alsem_t;
#endif /* __APPLE__ */

typedef int (*althrd_start_t)(void*);


inline void althrd_yield(void)
{
    sched_yield();
}


inline int almtx_lock(almtx_t *mtx)
{
    if(pthread_mutex_lock(mtx) != 0)
        return althrd_error;
    return althrd_success;
}

inline int almtx_unlock(almtx_t *mtx)
{
    if(pthread_mutex_unlock(mtx) != 0)
        return althrd_error;
    return althrd_success;
}

int althrd_create(althrd_t *thr, althrd_start_t func, void *arg);
int althrd_detach(althrd_t thr);
int althrd_join(althrd_t thr, int *res);

#endif

void althrd_setname(const char *name);

int almtx_init(almtx_t *mtx, int type);
void almtx_destroy(almtx_t *mtx);

int alsem_init(alsem_t *sem, unsigned int initial);
void alsem_destroy(alsem_t *sem);
int alsem_post(alsem_t *sem);
int alsem_wait(alsem_t *sem);
int alsem_trywait(alsem_t *sem);

#ifdef __cplusplus
} // extern "C"

#include <mutex>

/* Add specializations for std::lock_guard and std::unique_lock which take an
 * almtx_t and call the appropriate almtx_* functions.
 */
namespace std {

template<>
class lock_guard<almtx_t> {
    almtx_t &mMtx;

public:
    using mutex_type = almtx_t;

    explicit lock_guard(almtx_t &mtx) : mMtx(mtx) { almtx_lock(&mMtx); }
    lock_guard(almtx_t &mtx, std::adopt_lock_t) noexcept : mMtx(mtx) { }
    ~lock_guard() { almtx_unlock(&mMtx); }

    lock_guard(const lock_guard&) = delete;
    lock_guard& operator=(const lock_guard&) = delete;
};

template<>
class unique_lock<almtx_t> {
    almtx_t *mMtx{nullptr};
    bool mLocked{false};

public:
    using mutex_type = almtx_t;

    unique_lock() noexcept = default;
    explicit unique_lock(almtx_t &mtx) : mMtx(&mtx) { almtx_lock(mMtx); mLocked = true; }
    unique_lock(unique_lock&& rhs) noexcept : mMtx(rhs.mMtx), mLocked(rhs.mLocked)
    { rhs.mMtx = nullptr; rhs.mLocked = false; }
    ~unique_lock() { if(mLocked) almtx_unlock(mMtx); }

    unique_lock& operator=(const unique_lock&) = delete;
    unique_lock& operator=(unique_lock&& rhs)
    {
        if(mLocked)
            almtx_unlock(mMtx);
        mMtx = rhs.mMtx; rhs.mMtx = nullptr;
        mLocked = rhs.mLocked; rhs.mLocked = false;
        return *this;
    }

    void lock()
    {
        almtx_lock(mMtx);
        mLocked = true;
    }
    void unlock()
    {
        mLocked = false;
        almtx_unlock(mMtx);
    }
};

} // namespace std
#endif

#endif /* AL_THREADS_H */