/* $OpenBSD: rthread_libc.c,v 1.7 2008/06/13 21:18:43 otto Exp $ */ /* $snafu: libc_tag.c,v 1.4 2004/11/30 07:00:06 marc Exp $ */ /* PUBLIC DOMAIN: No Rights Reserved. Marco S Hyman <marc@snafu.org> */ #define _POSIX_THREADS #include <sys/time.h> #include <machine/spinlock.h> #include <pthread.h> #include <stdlib.h> #include <string.h> #include "thread_private.h" /* in libc/include */ #include "rthread.h" /* * A thread tag is a pointer to a structure of this type. An opaque * tag is used to decouple libc from the thread library. */ struct _thread_tag { pthread_mutex_t m; /* the tag's mutex */ pthread_key_t k; /* a key for private data */ }; /* * local mutex to protect against tag creation races. */ static pthread_mutex_t _thread_tag_mutex = PTHREAD_MUTEX_INITIALIZER; /* * Initialize a thread tag structure once. This function is called * if the tag is null. Allocation and initialization are controlled * by a mutex. If the tag is not null when the mutex is obtained * the caller lost a race -- some other thread initialized the tag. * This function will never return NULL. */ static void _thread_tag_init(void **tag) { struct _thread_tag *tt; int result; result = pthread_mutex_lock(&_thread_tag_mutex); if (result == 0) { if (*tag == NULL) { tt = malloc(sizeof *tt); if (tt != NULL) { result = pthread_mutex_init(&tt->m, NULL); result |= pthread_key_create(&tt->k, free); *tag = tt; } } result |= pthread_mutex_unlock(&_thread_tag_mutex); } if (result != 0) _rthread_debug(1, "tag init failure"); } /* * lock the mutex associated with the given tag */ void _thread_tag_lock(void **tag) { struct _thread_tag *tt; if (__isthreaded) { if (*tag == NULL) _thread_tag_init(tag); tt = *tag; if (pthread_mutex_lock(&tt->m) != 0) _rthread_debug(1, "tag mutex lock failure"); } } /* * unlock the mutex associated with the given tag */ void _thread_tag_unlock(void **tag) { struct _thread_tag *tt; if (__isthreaded) { if (*tag == NULL) _thread_tag_init(tag); tt = *tag; if (pthread_mutex_unlock(&tt->m) != 0) _rthread_debug(1, "tag mutex unlock failure"); } } /* * return the thread specific data for the given tag. If there * is no data for this thread initialize it from 'storage'. * On any error return 'err'. */ void * _thread_tag_storage(void **tag, void *storage, size_t sz, void *err) { struct _thread_tag *tt; void *ret; if (*tag == NULL) _thread_tag_init(tag); tt = *tag; ret = pthread_getspecific(tt->k); if (ret == NULL) { ret = malloc(sz); if (ret == NULL) ret = err; else { if (pthread_setspecific(tt->k, ret) == 0) memcpy(ret, storage, sz); else { free(ret); ret = err; } } } return ret; } void _thread_mutex_lock(void **mutex) { pthread_mutex_t *pmutex = (pthread_mutex_t *)mutex; if (pthread_mutex_lock(pmutex) != 0) _rthread_debug(1, "mutex lock failure"); } void _thread_mutex_unlock(void **mutex) { pthread_mutex_t *pmutex = (pthread_mutex_t *)mutex; if (pthread_mutex_unlock(pmutex) != 0) _rthread_debug(1, "mutex unlock failure"); } void _thread_mutex_destroy(void **mutex) { pthread_mutex_t *pmutex = (pthread_mutex_t *)mutex; if (pthread_mutex_destroy(pmutex) != 0) _rthread_debug(1, "mutex destroy failure"); } /* * the malloc lock */ static _spinlock_lock_t malloc_lock = _SPINLOCK_UNLOCKED; void _thread_malloc_lock(void) { _spinlock(&malloc_lock); } void _thread_malloc_unlock(void) { _spinunlock(&malloc_lock); } /* * atexit lock */ static _spinlock_lock_t atexit_lock = _SPINLOCK_UNLOCKED; void _thread_atexit_lock(void) { _spinlock(&atexit_lock); } void _thread_atexit_unlock(void) { _spinunlock(&atexit_lock); } /* * arc4random lock */ static _spinlock_lock_t arc4_lock = _SPINLOCK_UNLOCKED; void _thread_arc4_lock(void) { _spinlock(&arc4_lock); } void _thread_arc4_unlock(void) { _spinunlock(&arc4_lock); } #if 0 /* * miscellaneous libc exported symbols we want to override */ int close(int fd) { int rv; pthread_testcancel(); rv = _thread_sys_close(fd); pthread_testcancel(); return (rv); } #if 0 /* libc calls open */ int creat(const char *path, mode_t mode) { } #endif #if 0 int fcntl(int fd, int cmd, ...) { va_list ap; int rv; pthread_testcancel(); va_start(ap, cmd); rv = _thread_sys_fcntl(fd, cmd, va_arg(cmd, void *)); va_end(ap); pthread_testcancel(); return (rv); } #endif int fsync(int fd) { int rv; pthread_testcancel(); rv = _thread_sys_fsync(fd); pthread_testcancel(); return (rv); } int msync(void *addr, size_t len, int flags) { int rv; pthread_testcancel(); rv = _thread_sys_msync(addr, len, flags); pthread_testcancel(); return (rv); } int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) { int rv; pthread_testcancel(); rv = _thread_sys_nanosleep(rqtp, rmtp); pthread_testcancel(); return (rv); } #if 0 int open(const char *path, int flags, ...) { va_list ap; int rv; pthread_testcancel(); va_start(ap, cmd); rv = _thread_sys_open(fd, cmd, va_arg(cmd, mode_t)); va_end(ap); pthread_testcancel(); return (rv); } #endif #if 0 int pause(void) { } #endif ssize_t read(int fd, void *buf, size_t nbytes) { ssize_t rv; pthread_testcancel(); rv = read(fd, buf, nbytes); pthread_testcancel(); return (rv); } #if 0 int sigwaitinfo() { } #endif int sigsuspend(const sigset_t *sigmask) { int rv; pthread_testcancel(); rv = sigsuspend(sigmask); pthread_testcancel(); return (rv); } #if 0 /* libc sleep(3) calls nanosleep(2), so we'll catch it there */ unsigned int sleep(unsigned int seconds) { } #endif #if 0 int system(const char *string) { } #endif #if 0 int tcdrain(int fd) { } #endif #if 0 /* wait and waitpid will be handled by libc calling wait4 */ pid_t wait(int *status) { } pid_t waitpid(pid_t wpid, int *status, int options) { } #endif pid_t wait4(pid_t wpid, int *status, int options, struct rusage *rusage) { pid_t rv; pthread_testcancel(); rv = _thread_sys_wait4(wpid, status, options, rusage); pthread_testcancel(); return (rv); } ssize_t write(int fd, const void *buf, size_t nbytes) { ssize_t rv; pthread_testcancel(); rv = _thread_sys_write(fd, buf, nbytes); pthread_testcancel(); return (rv); } #endif