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