xref: /3.0.3-GA/platform/src/cb_pthreads.c (revision 0367eff7)
1#include "config.h"
2#include <assert.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <sys/time.h>
7#include <dlfcn.h>
8#include <errno.h>
9
10struct thread_execute {
11    cb_thread_main_func func;
12    void *argument;
13};
14
15static void *platform_thread_wrap(void *arg)
16{
17    struct thread_execute *ctx = arg;
18    assert(arg);
19    ctx->func(ctx->argument);
20    free(ctx);
21    return NULL;
22}
23
24int cb_create_thread(cb_thread_t *id,
25                     cb_thread_main_func func,
26                     void *arg,
27                     int detached)
28{
29    int ret;
30    struct thread_execute *ctx = malloc(sizeof(struct thread_execute));
31    if (ctx == NULL) {
32        return -1;
33    }
34
35    ctx->func = func;
36    ctx->argument = arg;
37
38    if (detached) {
39        pthread_attr_t attr;
40
41        if (pthread_attr_init(&attr) != 0 ||
42                pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) {
43            return -1;
44        }
45
46        ret = pthread_create(id, &attr, platform_thread_wrap, ctx);
47    } else {
48        ret = pthread_create(id, NULL, platform_thread_wrap, ctx);
49    }
50
51    if (ret != 0) {
52        free(ctx);
53    }
54
55    return ret;
56}
57
58int cb_join_thread(cb_thread_t id)
59{
60    return pthread_join(id, NULL);
61}
62
63cb_thread_t cb_thread_self(void)
64{
65    return pthread_self();
66}
67
68int cb_thread_equal(const cb_thread_t a, const cb_thread_t b)
69{
70    return pthread_equal(a, b);
71}
72
73void cb_mutex_initialize(cb_mutex_t *mutex)
74{
75    pthread_mutex_init(mutex, NULL);
76}
77
78void cb_mutex_destroy(cb_mutex_t *mutex)
79{
80    pthread_mutex_destroy(mutex);
81}
82
83void cb_mutex_enter(cb_mutex_t *mutex)
84{
85    int rv = pthread_mutex_lock(mutex);
86    if (rv != 0) {
87        fprintf(stderr, "FATAL: Failed to lock mutex: %d %s",
88                rv, strerror(rv));
89        abort();
90    }
91}
92
93int cb_mutex_try_enter(cb_mutex_t *mutex) {
94    return pthread_mutex_trylock(mutex) == 0 ? 0 : -1;
95}
96
97void cb_mutex_exit(cb_mutex_t *mutex)
98{
99    int rv = pthread_mutex_unlock(mutex);
100    if (rv != 0) {
101        fprintf(stderr, "FATAL: Failed to release mutex: %d %s",
102                rv, strerror(rv));
103        abort();
104    }
105}
106
107void cb_cond_initialize(cb_cond_t *cond)
108{
109    pthread_cond_init(cond, NULL);
110}
111
112void cb_cond_destroy(cb_cond_t *cond)
113{
114    pthread_cond_destroy(cond);
115}
116
117void cb_cond_wait(cb_cond_t *cond, cb_mutex_t *mutex)
118{
119    pthread_cond_wait(cond, mutex);
120}
121
122void cb_cond_signal(cb_cond_t *cond)
123{
124    pthread_cond_signal(cond);
125}
126
127void cb_cond_broadcast(cb_cond_t *cond)
128{
129    pthread_cond_broadcast(cond);
130}
131
132void cb_cond_timedwait(cb_cond_t *cond, cb_mutex_t *mutex, unsigned int ms)
133{
134    struct timespec ts;
135    struct timeval tp;
136    int ret;
137    uint64_t wakeup;
138
139    memset(&ts, 0, sizeof(ts));
140
141    /*
142     * Unfortunately pthreads don't support relative sleeps so we need
143     * to convert back to an absolute time...
144     */
145    gettimeofday(&tp, NULL);
146    wakeup = ((uint64_t)(tp.tv_sec) * 1000) + (tp.tv_usec / 1000) + ms;
147    /* Round up for sub ms */
148    if ((tp.tv_usec % 1000) > 499) {
149        ++wakeup;
150    }
151
152    ts.tv_sec = wakeup / 1000;
153    wakeup %= 1000;
154    ts.tv_nsec = wakeup * 1000000;
155
156    ret = pthread_cond_timedwait(cond, mutex, &ts);
157    switch (ret) {
158    case EINVAL:
159    case EPERM:
160        fprintf(stderr, "FATAL: pthread_cond_timewait: %s\n",
161                strerror(ret));
162        abort();
163    default:
164        ;
165    }
166}
167
168#ifdef __APPLE__
169static const char *get_dll_name(const char *path, char *buffer)
170{
171    char *ptr = strstr(path, ".dylib");
172    if (ptr != NULL) {
173        return path;
174    }
175
176    strcpy(buffer, path);
177
178    ptr = strstr(buffer, ".so");
179    if (ptr != NULL) {
180        sprintf(ptr, ".dylib");
181        return buffer;
182    }
183
184    strcat(buffer, ".dylib");
185    return buffer;
186}
187#else
188static const char *get_dll_name(const char *path, char *buffer)
189{
190    char *ptr = strstr(path, ".so");
191    if (ptr != NULL) {
192        return path;
193    }
194
195    strcpy(buffer, path);
196    strcat(buffer, ".so");
197    return buffer;
198}
199#endif
200
201cb_dlhandle_t cb_dlopen(const char *library, char **errmsg)
202{
203    cb_dlhandle_t handle;
204    char *buffer = NULL;
205
206    if (library == NULL) {
207        handle = dlopen(NULL, RTLD_NOW | RTLD_LOCAL);
208    } else {
209        handle = dlopen(library, RTLD_NOW | RTLD_LOCAL);
210        if (handle == NULL) {
211            buffer = malloc(strlen(library) + 20);
212            if (buffer == NULL) {
213                if (*errmsg) {
214                    *errmsg = strdup("Failed to allocate memory");
215                }
216                return NULL;
217            }
218
219            handle = dlopen(get_dll_name(library, buffer),
220                            RTLD_NOW | RTLD_LOCAL);
221            free(buffer);
222        }
223    }
224
225    if (handle == NULL && errmsg != NULL) {
226        *errmsg = strdup(dlerror());
227    }
228
229    return handle;
230}
231
232void *cb_dlsym(cb_dlhandle_t handle, const char *symbol, char **errmsg)
233{
234    void *ret = dlsym(handle, symbol);
235    if (ret == NULL && errmsg) {
236        *errmsg = strdup(dlerror());
237    }
238    return ret;
239}
240
241void cb_dlclose(cb_dlhandle_t handle)
242{
243    dlclose(handle);
244}
245