xref: /5.5.2/platform/src/cb_pthreads.cc (revision d912dd2b)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2015 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17#include "config.h"
18
19#include <cerrno>
20#include <cstdio>
21#include <cstdlib>
22#include <cstring>
23#include <dlfcn.h>
24#include <memory>
25#include <new>
26#include <platform/cb_malloc.h>
27#include <platform/strerror.h>
28#include <stdexcept>
29#include <string>
30#include <sys/time.h>
31#include <system_error>
32
33#include <phosphor/phosphor.h>
34
35/**
36 * The CouchbaseThread class is used to pass information between a thread
37 * and the newly created thread.
38 */
39class CouchbaseThread {
40public:
41    CouchbaseThread(cb_thread_main_func func_,
42                    void* argument_,
43                    const char* name_)
44        : func(func_),
45          argument(argument_) {
46        if (name_) {
47            name.assign(name_);
48            if (name.length() > 15) {
49                throw std::logic_error("name exceeds 15 characters");
50            }
51        }
52    }
53
54    void run() {
55        PHOSPHOR_INSTANCE.registerThread(name);
56        if (!name.empty()) {
57            cb_set_thread_name(name.c_str());
58        }
59        func(argument);
60        PHOSPHOR_INSTANCE.deregisterThread();
61    }
62
63private:
64    cb_thread_main_func func;
65    std::string name;
66    void* argument;
67};
68
69static void *platform_thread_wrap(void *arg)
70{
71    std::unique_ptr<CouchbaseThread> context(reinterpret_cast<CouchbaseThread*>(arg));
72    context->run();
73    return NULL;
74}
75
76int cb_create_thread(cb_thread_t *id,
77                     cb_thread_main_func func,
78                     void *arg,
79                     int detached)
80{
81    // Implemented in terms of cb_create_named_thread; without a name.
82    return cb_create_named_thread(id, func, arg, detached, NULL);
83}
84
85int cb_create_named_thread(cb_thread_t *id, cb_thread_main_func func, void *arg,
86                           int detached, const char* name)
87{
88    int ret;
89    CouchbaseThread* ctx;
90    try {
91        ctx = new CouchbaseThread(func, arg, name);
92    } catch (std::bad_alloc&) {
93        return -1;
94    } catch (std::logic_error&) {
95        return -1;
96    }
97
98    pthread_attr_t attr;
99    if (pthread_attr_init(&attr) != 0) {
100        delete ctx;
101        return -1;
102    }
103
104    if (detached &&
105        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) {
106        delete ctx;
107        return -1;
108    }
109
110    ret = pthread_create(id, &attr, platform_thread_wrap, ctx);
111    pthread_attr_destroy(&attr);
112
113    if (ret != 0) {
114        delete ctx;
115    }
116
117    return ret;
118}
119
120int cb_join_thread(cb_thread_t id)
121{
122    if (pthread_self() == id) {
123        throw std::runtime_error("cb_join_thread: can't try to join self");
124    }
125
126    return pthread_join(id, NULL);
127}
128
129cb_thread_t cb_thread_self(void)
130{
131    return pthread_self();
132}
133
134int cb_thread_equal(const cb_thread_t a, const cb_thread_t b)
135{
136    return pthread_equal(a, b);
137}
138
139int cb_set_thread_name(const char* name)
140{
141#if defined(__APPLE__)
142    // No thread argument (implicit current thread).
143    int ret = pthread_setname_np(name);
144    if (ret == 0) {
145        return 0;
146    } else if (errno == ENAMETOOLONG) {
147        return 1;
148    }
149#elif defined(HAVE_PTHREAD_SETNAME_NP)
150    errno = 0;
151    int ret = pthread_setname_np(pthread_self(), name);
152    if (ret == 0) {
153        return 0;
154    } else if (errno == ERANGE || ret == ERANGE) {
155        return 1;
156    }
157#endif
158
159    return -1;
160}
161
162int cb_get_thread_name(char* name, size_t size)
163{
164#if defined(HAVE_PTHREAD_GETNAME_NP)
165    return pthread_getname_np(pthread_self(), name, size);
166#else
167    return - 1;
168#endif
169}
170
171
172bool is_thread_name_supported(void)
173{
174#ifdef HAVE_PTHREAD_SETNAME_NP
175    return true;
176#else
177    return false;
178#endif
179}
180
181
182void cb_mutex_initialize(cb_mutex_t *mutex)
183{
184    int rv = pthread_mutex_init(mutex, NULL);
185    if (rv != 0) {
186        throw std::system_error(rv, std::system_category(),
187                                "Failed to initialize mutex");
188    }
189}
190
191void cb_mutex_destroy(cb_mutex_t *mutex)
192{
193    int rv = pthread_mutex_destroy(mutex);
194    if (rv != 0) {
195        throw std::system_error(rv, std::system_category(),
196                                "Failed to destroy mutex");
197    }
198}
199
200void cb_mutex_enter(cb_mutex_t *mutex)
201{
202    int rv = pthread_mutex_lock(mutex);
203    if (rv != 0) {
204        throw std::system_error(rv, std::system_category(),
205                                "Failed to lock mutex");
206    }
207}
208
209int cb_mutex_try_enter(cb_mutex_t *mutex) {
210    return pthread_mutex_trylock(mutex) == 0 ? 0 : -1;
211}
212
213void cb_mutex_exit(cb_mutex_t *mutex)
214{
215    int rv = pthread_mutex_unlock(mutex);
216    if (rv != 0) {
217        throw std::system_error(rv, std::system_category(),
218                                "Failed to release mutex");
219    }
220}
221
222void cb_cond_initialize(cb_cond_t *cond)
223{
224    int rv = pthread_cond_init(cond, NULL);
225    if (rv != 0) {
226        throw std::system_error(rv, std::system_category(),
227                                "Failed to initialize condition variable");
228    }
229}
230
231void cb_cond_destroy(cb_cond_t *cond)
232{
233    int rv = pthread_cond_destroy(cond);
234    if (rv != 0) {
235        throw std::system_error(rv, std::system_category(),
236                                "Failed to destroy condition variable");
237    }
238}
239
240void cb_cond_wait(cb_cond_t *cond, cb_mutex_t *mutex)
241{
242    int rv = pthread_cond_wait(cond, mutex);
243    if (rv != 0) {
244        throw std::system_error(rv, std::system_category(),
245                                "Failed to wait on condition variable");
246    }
247}
248
249void cb_cond_signal(cb_cond_t *cond)
250{
251    int rv = pthread_cond_signal(cond);
252    if (rv != 0) {
253        throw std::system_error(rv, std::system_category(),
254                                "Failed to signal condition variable");
255    }
256}
257
258void cb_cond_broadcast(cb_cond_t *cond)
259{
260    int rv = pthread_cond_broadcast(cond);
261    if (rv != 0) {
262        throw std::system_error(rv, std::system_category(),
263                                "Failed to broadcast condition variable");
264    }
265}
266
267void cb_cond_timedwait(cb_cond_t *cond, cb_mutex_t *mutex, unsigned int ms)
268{
269    struct timespec ts;
270    struct timeval tp;
271    uint64_t wakeup;
272
273    memset(&ts, 0, sizeof(ts));
274
275    /*
276     * Unfortunately pthreads don't support relative sleeps so we need
277     * to convert back to an absolute time...
278     */
279    gettimeofday(&tp, NULL);
280    wakeup = ((uint64_t)(tp.tv_sec) * 1000) + (tp.tv_usec / 1000) + ms;
281    /* Round up for sub ms */
282    if ((tp.tv_usec % 1000) > 499) {
283        ++wakeup;
284    }
285
286    ts.tv_sec = wakeup / 1000;
287    wakeup %= 1000;
288    ts.tv_nsec = wakeup * 1000000;
289
290    int rv = pthread_cond_timedwait(cond, mutex, &ts);
291    if (rv != 0 && rv != ETIMEDOUT) {
292        throw std::system_error(rv, std::system_category(),
293                                "Failed to do timed wait on condition variable");
294    }
295}
296
297#ifdef __APPLE__
298static const char *get_dll_name(const char *path, char *buffer)
299{
300    if (strstr(path, ".dylib") != nullptr) {
301        return path;
302    }
303
304    strcpy(buffer, path);
305
306    char* ptr = strstr(buffer, ".so");
307    if (ptr != NULL) {
308        sprintf(ptr, ".dylib");
309        return buffer;
310    }
311
312    strcat(buffer, ".dylib");
313    return buffer;
314}
315#else
316static const char *get_dll_name(const char *path, char *buffer)
317{
318    auto* ptr = strstr(path, ".so");
319    if (ptr != nullptr) {
320        return path;
321    }
322
323    strcpy(buffer, path);
324    strcat(buffer, ".so");
325    return buffer;
326}
327#endif
328
329cb_dlhandle_t cb_dlopen(const char *library, char **errmsg)
330{
331    cb_dlhandle_t handle;
332    char *buffer = NULL;
333    const int dlopen_flags = RTLD_NOW | RTLD_GLOBAL;
334
335    if (library == NULL) {
336        handle = dlopen(NULL, dlopen_flags);
337    } else {
338        handle = dlopen(library, dlopen_flags);
339        if (handle == NULL) {
340            buffer = reinterpret_cast<char*>(cb_malloc(strlen(library) + 20));
341            if (buffer == NULL) {
342                if (*errmsg) {
343                    *errmsg = cb_strdup("Failed to allocate memory");
344                }
345                return NULL;
346            }
347
348            handle = dlopen(get_dll_name(library, buffer),
349                            dlopen_flags);
350            cb_free(buffer);
351        }
352    }
353
354    if (handle == NULL && errmsg != NULL) {
355        *errmsg = cb_strdup(dlerror());
356    }
357
358    return handle;
359}
360
361void *cb_dlsym(cb_dlhandle_t handle, const char *symbol, char **errmsg)
362{
363    void *ret = dlsym(handle, symbol);
364    if (ret == NULL && errmsg) {
365        *errmsg = cb_strdup(dlerror());
366    }
367    return ret;
368}
369
370void cb_dlclose(cb_dlhandle_t handle)
371{
372    dlclose(handle);
373}
374
375int platform_set_binary_mode(FILE *fp)
376{
377    (void)fp;
378    return 0;
379}
380
381void cb_rw_lock_initialize(cb_rwlock_t *rw)
382{
383    int rv = pthread_rwlock_init(rw, NULL);
384    if (rv != 0) {
385        throw std::system_error(rv, std::system_category(),
386                                "Failed to initialize rw lock");
387    }
388}
389
390void cb_rw_lock_destroy(cb_rwlock_t *rw)
391{
392    int rv = pthread_rwlock_destroy(rw);
393    if (rv != 0) {
394        throw std::system_error(rv, std::system_category(),
395                                "Failed to destroy rw lock");
396    }
397}
398
399int cb_rw_reader_enter(cb_rwlock_t *rw)
400{
401    int result = pthread_rwlock_rdlock(rw);
402    if (result != 0) {
403        auto err = cb_strerror(result);
404        fprintf(stderr, "pthread_rwlock_rdlock returned %d (%s)\n",
405                        result, err.c_str());
406    }
407    return result;
408}
409
410int cb_rw_reader_exit(cb_rwlock_t *rw)
411{
412    int result = pthread_rwlock_unlock(rw);
413    if (result != 0) {
414        auto err = cb_strerror(result);
415        fprintf(stderr, "pthread_rwlock_unlock returned %d (%s)\n",
416                        result, err.c_str());
417    }
418    return result;
419}
420
421int cb_rw_writer_enter(cb_rwlock_t *rw)
422{
423    int result = pthread_rwlock_wrlock(rw);
424    if (result != 0) {
425        auto err = cb_strerror(result);
426        fprintf(stderr, "pthread_rwlock_wrlock returned %d (%s)\n",
427                        result, err.c_str());
428    }
429    return result;
430}
431
432int cb_rw_writer_exit(cb_rwlock_t *rw)
433{
434    int result = pthread_rwlock_unlock(rw);
435    if (result != 0) {
436        auto err = cb_strerror(result);
437        fprintf(stderr, "pthread_rwlock_unlock returned %d (%s)\n",
438                        result, err.c_str());
439    }
440    return result;
441}
442