summaryrefslogtreecommitdiff
path: root/lib/librthread
diff options
context:
space:
mode:
Diffstat (limited to 'lib/librthread')
-rw-r--r--lib/librthread/Makefile8
-rw-r--r--lib/librthread/arch/amd64/_atomic_lock.c26
-rw-r--r--lib/librthread/arch/amd64/rfork_thread.S99
-rw-r--r--lib/librthread/arch/i386/_atomic_lock.c25
-rw-r--r--lib/librthread/arch/i386/rfork_thread.S126
-rw-r--r--lib/librthread/rthread.c265
-rw-r--r--lib/librthread/rthread.h111
-rw-r--r--lib/librthread/rthread_attr.c109
-rw-r--r--lib/librthread/rthread_sched.c136
-rw-r--r--lib/librthread/rthread_sig.c90
-rw-r--r--lib/librthread/rthread_sync.c477
-rw-r--r--lib/librthread/rthread_tls.c117
12 files changed, 1589 insertions, 0 deletions
diff --git a/lib/librthread/Makefile b/lib/librthread/Makefile
new file mode 100644
index 00000000000..245106ce583
--- /dev/null
+++ b/lib/librthread/Makefile
@@ -0,0 +1,8 @@
+LIB=rthread
+CFLAGS+=-Wall -g -Werror
+
+.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}
+SRCS= rthread.c rthread_attr.c rthread_sched.c rthread_sync.c rthread_tls.c rthread_sig.c
+OBJS+= _atomic_lock.o rfork_thread.o # .S on alpha
+
+.include <bsd.lib.mk>
diff --git a/lib/librthread/arch/amd64/_atomic_lock.c b/lib/librthread/arch/amd64/_atomic_lock.c
new file mode 100644
index 00000000000..14569edc562
--- /dev/null
+++ b/lib/librthread/arch/amd64/_atomic_lock.c
@@ -0,0 +1,26 @@
+/* $OpenBSD: _atomic_lock.c,v 1.1 2005/12/03 18:16:19 tedu Exp $ */
+
+/* David Leonard, <d@csee.uq.edu.au>. Public domain. */
+
+/*
+ * Atomic lock for amd64 -- taken from i386 code.
+ */
+
+#include "spinlock.h"
+
+int
+_atomic_lock(volatile _spinlock_lock_t *lock)
+{
+ _spinlock_lock_t old;
+
+ /*
+ * Use the eXCHanGe instruction to swap the lock value with
+ * a local variable containing the locked state.
+ */
+ old = _SPINLOCK_LOCKED;
+ __asm__("xchg %0,%1"
+ : "=r" (old), "=m" (*lock)
+ : "0" (old), "1" (*lock));
+
+ return (old != _SPINLOCK_UNLOCKED);
+}
diff --git a/lib/librthread/arch/amd64/rfork_thread.S b/lib/librthread/arch/amd64/rfork_thread.S
new file mode 100644
index 00000000000..faec9b233c7
--- /dev/null
+++ b/lib/librthread/arch/amd64/rfork_thread.S
@@ -0,0 +1,99 @@
+/* $OpenBSD: rfork_thread.S,v 1.1 2005/12/03 18:16:19 tedu Exp $ */
+/*-
+ * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org>
+ * Copyright (c) 2003 Alan L. Cox <alc@cs.rice.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#if 0
+__FBSDID("$FreeBSD: /repoman/r/ncvs/src/lib/libc/amd64/gen/rfork_thread.S,v 1.1 2003/10/13 20:32:33 alc Exp $");
+#endif
+
+/*
+ * With thanks to John Dyson for the original version of this.
+ */
+
+#include "../../../libc/arch/amd64/SYS.h"
+
+/*
+ * %edi %rsi %rdx %rcx
+ * rfork_thread(flags, stack_addr, start_fnc, start_arg);
+ *
+ * flags: Flags to rfork system call. See rfork(2).
+ * stack_addr: Top of stack for thread.
+ * start_fnc: Address of thread function to call in child.
+ * start_arg: Argument to pass to the thread function in child.
+ */
+
+ENTRY(rfork_thread)
+ pushq %rbx
+ pushq %r12
+ movq %rdx, %rbx
+ movq %rcx, %r12
+
+ /*
+ * Prepare and execute the thread creation syscall
+ */
+ movq $SYS_rfork, %rax
+ int $0x80
+ jb 2f
+
+ /*
+ * Check to see if we are in the parent or child
+ */
+ cmpl $0, %edx
+ jnz 1f
+ popq %r12
+ popq %rbx
+ ret
+
+ /*
+ * If we are in the child (new thread), then
+ * set-up the call to the internal subroutine. If it
+ * returns, then call __exit.
+ */
+1:
+ movq %rsi, %rsp
+ movq %r12, %rdi
+ call *%rbx
+ movl %eax, %edi
+
+ /*
+ * Exit system call
+ */
+#ifdef SYS_exit
+ movq $SYS_exit, %rax
+#else
+ movq $SYS_sys_exit, %rax
+#endif
+ int $0x80
+
+ /*
+ * Branch here if the thread creation fails:
+ */
+2:
+ popq %r12
+ popq %rbx
+ jmp CERROR
diff --git a/lib/librthread/arch/i386/_atomic_lock.c b/lib/librthread/arch/i386/_atomic_lock.c
new file mode 100644
index 00000000000..7b9b46a73c7
--- /dev/null
+++ b/lib/librthread/arch/i386/_atomic_lock.c
@@ -0,0 +1,25 @@
+/* $OpenBSD: _atomic_lock.c,v 1.1 2005/12/03 18:16:19 tedu Exp $ */
+/* David Leonard, <d@csee.uq.edu.au>. Public domain. */
+
+/*
+ * Atomic lock for i386
+ */
+
+#include <machine/spinlock.h>
+
+int
+_atomic_lock(register volatile _spinlock_lock_t *lock)
+{
+ register _spinlock_lock_t old;
+
+ /*
+ * Use the eXCHanGe instruction to swap the lock value with
+ * a local variable containing the locked state.
+ */
+ old = _SPINLOCK_LOCKED;
+ __asm__("xchg %0,%1"
+ : "=r" (old), "=m" (*lock)
+ : "0" (old), "1" (*lock));
+
+ return (old != _SPINLOCK_UNLOCKED);
+}
diff --git a/lib/librthread/arch/i386/rfork_thread.S b/lib/librthread/arch/i386/rfork_thread.S
new file mode 100644
index 00000000000..39e35ac67c2
--- /dev/null
+++ b/lib/librthread/arch/i386/rfork_thread.S
@@ -0,0 +1,126 @@
+/* $OpenBSD: rfork_thread.S,v 1.1 2005/12/03 18:16:19 tedu Exp $ */
+/*-
+ * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+#if 0
+__FBSDID("$FreeBSD: /repoman/r/ncvs/src/lib/libc/i386/gen/rfork_thread.S,v 1.5 2003/05/07 17:23:25 jhb Exp $");
+#endif
+
+/*
+ * With thanks to John Dyson for the original version of this.
+ */
+
+#include "../../../libc/arch/i386/SYS.h"
+
+/*
+ * 8 12 16 20
+ * rfork_thread(flags, stack_addr, start_fnc, start_arg);
+ *
+ * flags: Flags to rfork system call. See rfork(2).
+ * stack_addr: Top of stack for thread.
+ * start_fnc: Address of thread function to call in child.
+ * start_arg: Argument to pass to the thread function in child.
+ */
+
+ENTRY(rfork_thread)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+
+ /*
+ * Push thread info onto the new thread's stack
+ */
+ movl 12(%ebp), %esi # get stack addr
+
+ subl $4, %esi
+ movl 20(%ebp), %eax # get start argument
+ movl %eax, (%esi)
+
+ subl $4, %esi
+ movl 16(%ebp), %eax # get start thread address
+ movl %eax, (%esi)
+
+ /*
+ * Prepare and execute the thread creation syscall
+ */
+ pushl 8(%ebp)
+ pushl $0
+ movl $SYS_rfork, %eax
+ int $0x80
+#if 0
+ jb 2f
+#endif
+
+ /*
+ * Check to see if we are in the parent or child
+ */
+ cmpl $0, %edx
+ jnz 1f
+ addl $8, %esp
+ popl %esi
+ movl %ebp, %esp
+ popl %ebp
+ ret
+ .p2align 2
+
+ /*
+ * If we are in the child (new thread), then
+ * set-up the call to the internal subroutine. If it
+ * returns, then call __exit.
+ */
+1:
+ movl %esi,%esp
+ popl %eax
+ call *%eax
+ addl $4, %esp
+
+ /*
+ * Exit system call
+ */
+ pushl %eax
+ pushl $0
+#ifdef SYS_exit
+ movl $SYS_exit, %eax
+#else
+ movl $SYS_sys_exit, %eax
+#endif
+ int $0x80
+
+ /*
+ * Branch here if the thread creation fails:
+ */
+2:
+ addl $8, %esp
+ popl %esi
+ movl %ebp, %esp
+ popl %ebp
+#if 0
+ /* FreeBSD code. how to translate? */
+ PIC_PROLOGUE
+ jmp PIC_PLT(HIDENAME(cerror))
+#endif
+ jmp CERROR
diff --git a/lib/librthread/rthread.c b/lib/librthread/rthread.c
new file mode 100644
index 00000000000..7e3cc057c7b
--- /dev/null
+++ b/lib/librthread/rthread.c
@@ -0,0 +1,265 @@
+/* $OpenBSD: rthread.c,v 1.1 2005/12/03 18:16:19 tedu Exp $ */
+/*
+ * Copyright (c) 2004 Ted Unangst <tedu@openbsd.org>
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * The heart of rthreads. Basic functions like creating and joining
+ * threads.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <machine/spinlock.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include "rthread.h"
+
+static int threads_ready;
+static pthread_t thread_list;
+static _spinlock_lock_t thread_lock;
+
+int yield();
+int getthrid();
+void threxit(int);
+int rfork_thread(int, void *, void (*)(void *), void *);
+
+/*
+ * internal support functions
+ */
+void
+_spinlock(_spinlock_lock_t *lock)
+{
+
+ while (_atomic_lock(lock))
+ pthread_yield();
+}
+
+void
+_spinunlock(_spinlock_lock_t *lock)
+{
+
+ *lock = _SPINLOCK_UNLOCKED;
+}
+
+static pthread_t
+thread_findself(void)
+{
+ pthread_t me;
+ pid_t tid = getthrid();
+
+ for (me = thread_list; me; me = me->next)
+ if (me->tid == tid)
+ break;
+
+ return (me);
+}
+
+
+static void
+thread_start(void *v)
+{
+ pthread_t thread = v;
+ void *retval;
+
+ /* ensure parent returns from rfork, sets up tid */
+ _spinlock(&thread_lock);
+ _spinunlock(&thread_lock);
+ retval = thread->fn(thread->arg);
+ pthread_exit(retval);
+}
+
+static void
+thread_init(void)
+{
+ pthread_t thread;
+ extern int __isthreaded;
+
+ __isthreaded = 1;
+
+ thread = malloc(sizeof(*thread));
+ memset(thread, 0, sizeof(*thread));
+ thread->tid = getthrid();
+ snprintf(thread->name, sizeof(thread->name), "Main process");
+ thread_list = thread;
+ threads_ready = 1;
+}
+
+static struct stack *
+alloc_stack(size_t len)
+{
+ struct stack *stack;
+
+ stack = malloc(sizeof(*stack));
+ stack->base = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
+ stack->sp = (void *)(((size_t)stack->base + len - 16) & ~15);
+ return (stack);
+}
+
+
+/*
+ * real pthread functions
+ */
+pthread_t
+pthread_self(void)
+{
+ pthread_t thread;
+
+ if (!threads_ready)
+ thread_init();
+
+ _spinlock(&thread_lock);
+ thread = thread_findself();
+ _spinunlock(&thread_lock);
+
+ return (thread);
+}
+
+void
+pthread_exit(void *retval)
+{
+ pthread_t thread = pthread_self();
+
+ thread->retval = retval;
+ thread->flags |= THREAD_DONE;
+ _sem_wakeup(&thread->donesem);
+#if 0
+ if (thread->flags & THREAD_DETACHED)
+ free(thread);
+#endif
+ threxit(0);
+ for(;;);
+}
+
+int
+pthread_join(pthread_t thread, void **retval)
+{
+
+ _sem_wait(&thread->donesem, 0, 0);
+ printf("joined %d %p\n", thread->tid, thread->retval);
+ if (retval)
+ *retval = thread->retval;
+
+ return (0);
+}
+
+int
+pthread_detach(pthread_t thread)
+{
+ _spinlock(&thread_lock);
+#if 0
+ if (thread->flags & THREAD_DONE)
+ free(thread);
+ else
+#endif
+ thread->flags |= THREAD_DETACHED;
+ _spinunlock(&thread_lock);
+ return (0);
+}
+
+int
+pthread_create(pthread_t *threadp, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg)
+{
+ pthread_t thread;
+ pid_t tid;
+
+ if (!threads_ready)
+ thread_init();
+
+ thread = malloc(sizeof(*thread));
+ memset(thread, 0, sizeof(*thread));
+ thread->fn = start_routine;
+ thread->arg = arg;
+
+ _spinlock(&thread_lock);
+ thread->next = thread_list;
+ thread_list = thread;
+
+ thread->stack = alloc_stack(64 * 1024);
+
+ tid = rfork_thread(RFPROC | RFTHREAD | RFMEM | RFNOWAIT,
+ thread->stack->sp, thread_start, thread);
+ /* new thread will appear thread_start */
+ thread->tid = tid;
+ printf("new thread %d\n", tid);
+ *threadp = thread;
+ _spinunlock(&thread_lock);
+
+ return (0);
+}
+
+int
+pthread_equal(pthread_t t1, pthread_t t2)
+{
+ return (t1 == t2);
+}
+
+/*
+ * _np functions
+ */
+void
+pthread_set_name_np(pthread_t thread, char *name)
+{
+ strlcpy(thread->name, name, sizeof(thread->name));
+}
+
+/*
+ * compat debug stuff
+ */
+void
+_thread_dump_info(void)
+{
+ pthread_t thread;
+
+ _spinlock(&thread_lock);
+ for (thread = thread_list; thread; thread = thread->next)
+ printf("thread %d flags %d name %s\n",
+ thread->tid, thread->flags, thread->name);
+ _spinunlock(&thread_lock);
+}
+
+
+/*
+ * the malloc lock
+ */
+static _spinlock_lock_t malloc_lock;
+
+void
+_thread_malloc_lock()
+{
+ _spinlock(&malloc_lock);
+}
+
+void
+_thread_malloc_unlock()
+{
+ _spinunlock(&malloc_lock);
+}
+
+void
+_thread_malloc_init()
+{
+}
diff --git a/lib/librthread/rthread.h b/lib/librthread/rthread.h
new file mode 100644
index 00000000000..3deec5835d5
--- /dev/null
+++ b/lib/librthread/rthread.h
@@ -0,0 +1,111 @@
+/* $OpenBSD: rthread.h,v 1.1 2005/12/03 18:16:19 tedu Exp $ */
+/*
+ * Copyright (c) 2004 Ted Unangst <tedu@openbsd.org>
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Private data structures that back up the typedefs in pthread.h.
+ * Since only the thread library cares about their size or arrangement,
+ * it should be possible to switch libraries without relinking.
+ */
+
+struct stack {
+ void *sp;
+ void *base;
+ size_t len;
+};
+
+typedef struct semaphore {
+ volatile int waitcount;
+ volatile int value;
+ _spinlock_lock_t lock;
+} *sem_t;
+
+struct pthread_mutex {
+ struct semaphore sem;
+ int type;
+ pthread_t owner;
+ int count;
+};
+
+struct pthread_mutex_attr {
+ int type;
+};
+
+struct pthread_cond {
+ struct semaphore sem;
+};
+
+struct pthread_cond_attr {
+ int shared;
+};
+
+struct pthread_rwlock {
+ _spinlock_lock_t lock;
+ int readers;
+ int writer;
+ struct semaphore sem;
+};
+
+struct pthread_rwlockattr {
+ int dummy;
+};
+
+struct pthread_attr {
+ size_t stack_size;
+ int detach_state;
+ int contention_scope;
+ int sched_policy;
+ struct sched_param sched_param;
+ int sched_inherit;
+};
+
+struct rthread_key {
+ int keyid;
+ struct rthread_key *next;
+ void (*destructor)(void *);
+};
+
+struct rthread_storage {
+ int keyid;
+ struct rthread_storage *next;
+ void *data;
+};
+
+struct pthread {
+ pid_t tid;
+ struct semaphore donesem;
+ int flags;
+ void *retval;
+ void *(*fn)(void *);
+ void *arg;
+ char name[32];
+ struct stack *stack;
+ pthread_t next;
+ int sched_policy;
+ struct sched_param sched_param;
+ struct rthread_storage *local_storage;
+ int sigpend;
+};
+#define THREAD_DONE 0x001
+#define THREAD_DETACHED 0x002
+
+void _spinlock(_spinlock_lock_t *);
+void _spinunlock(_spinlock_lock_t *);
+int _sem_wait(sem_t, int, int);
+int _sem_wakeup(sem_t);
+int _sem_wakeall(sem_t);
+
+int _atomic_lock(register volatile _spinlock_lock_t *);
diff --git a/lib/librthread/rthread_attr.c b/lib/librthread/rthread_attr.c
new file mode 100644
index 00000000000..532a53d0f17
--- /dev/null
+++ b/lib/librthread/rthread_attr.c
@@ -0,0 +1,109 @@
+/* $OpenBSD: rthread_attr.c,v 1.1 2005/12/03 18:16:19 tedu Exp $ */
+/*
+ * Copyright (c) 2004 Ted Unangst <tedu@openbsd.org>
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * generic attribute support
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <machine/spinlock.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include "rthread.h"
+
+int
+pthread_attr_init(pthread_attr_t *attrp)
+{
+ pthread_attr_t attr;
+
+ attr = malloc(sizeof(*attr));
+ if (!attr)
+ return (errno);
+ memset(attr, 0, sizeof(*attr));
+ *attrp = attr;
+
+ return (0);
+}
+
+int
+pthread_attr_destroy(pthread_attr_t *attrp)
+{
+ free(*attrp);
+ *attrp = NULL;
+
+ return (0);
+}
+
+int
+pthread_attr_getdetachstate(const pthread_attr_t *attrp, int *detachstate)
+{
+ *detachstate = (*attrp)->detach_state;
+
+ return (0);
+}
+
+int
+pthread_attr_setdetachstate(pthread_attr_t *attrp, int detachstate)
+{
+ (*attrp)->detach_state = detachstate;
+
+ return (0);
+}
+
+int
+pthread_attr_getstacksize(const pthread_attr_t *attrp, size_t *stacksize)
+{
+ *stacksize = (*attrp)->stack_size;
+
+ return (0);
+
+}
+
+int
+pthread_attr_setstacksize(pthread_attr_t *attrp, size_t stacksize)
+{
+ (*attrp)->stack_size = stacksize;
+
+ return (0);
+}
+
+int
+pthread_attr_getscope(const pthread_attr_t *attrp, int *contentionscope)
+{
+ *contentionscope = (*attrp)->contention_scope;
+
+ return (0);
+}
+
+int
+pthread_attr_setscope(pthread_attr_t *attrp, int contentionscope)
+{
+ (*attrp)->contention_scope = contentionscope;
+
+ return (0);
+}
diff --git a/lib/librthread/rthread_sched.c b/lib/librthread/rthread_sched.c
new file mode 100644
index 00000000000..3ca3113c483
--- /dev/null
+++ b/lib/librthread/rthread_sched.c
@@ -0,0 +1,136 @@
+/* $OpenBSD: rthread_sched.c,v 1.1 2005/12/03 18:16:19 tedu Exp $ */
+/*
+ * Copyright (c) 2004 Ted Unangst <tedu@openbsd.org>
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * scheduling routines
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <machine/spinlock.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include "rthread.h"
+
+int yield(void);
+
+int
+pthread_getschedparam(pthread_t thread, int *policy,
+ struct sched_param *param)
+{
+ *policy = thread->sched_policy;
+ if (param)
+ *param = thread->sched_param;
+
+ return (0);
+}
+
+int
+pthread_setschedparam(pthread_t thread, int policy,
+ const struct sched_param *param)
+{
+ thread->sched_policy = policy;
+ if (param)
+ thread->sched_param = *param;
+
+ return (0);
+}
+
+int
+pthread_attr_getschedparam(const pthread_attr_t *attrp, struct sched_param *param)
+{
+ *param = (*attrp)->sched_param;
+
+ return (0);
+}
+
+int
+pthread_attr_setschedparam(pthread_attr_t *attrp, const struct sched_param *param)
+{
+ (*attrp)->sched_param = *param;
+
+ return (0);
+}
+
+int
+pthread_attr_getschedpolicy(const pthread_attr_t *attrp, int *policy)
+{
+ *policy = (*attrp)->sched_policy;
+
+ return (0);
+}
+
+int
+pthread_attr_setschedpolicy(pthread_attr_t *attrp, int policy)
+{
+ (*attrp)->sched_policy = policy;
+
+ return (0);
+}
+
+int
+pthread_attr_getinheritsched(const pthread_attr_t *attrp, int *inherit)
+{
+ *inherit = (*attrp)->sched_inherit;
+
+ return (0);
+}
+
+int
+pthread_attr_setinheritsched(pthread_attr_t *attrp, int inherit)
+{
+ (*attrp)->sched_inherit = inherit;
+
+ return (0);
+}
+
+int
+pthread_getprio(pthread_t thread)
+{
+ return (thread->sched_param.sched_priority);
+}
+
+int
+pthread_setprio(pthread_t thread, int priority)
+{
+ thread->sched_param.sched_priority = priority;
+
+ return (0);
+}
+
+void
+pthread_yield(void)
+{
+ yield();
+}
+int
+sched_yield(void)
+{
+ yield();
+
+ return (0);
+}
diff --git a/lib/librthread/rthread_sig.c b/lib/librthread/rthread_sig.c
new file mode 100644
index 00000000000..5fcb9002e86
--- /dev/null
+++ b/lib/librthread/rthread_sig.c
@@ -0,0 +1,90 @@
+/* $OpenBSD: rthread_sig.c,v 1.1 2005/12/03 18:16:19 tedu Exp $ */
+/*
+ * Copyright (c) 2004 Ted Unangst <tedu@openbsd.org>
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * signals
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <machine/spinlock.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include "rthread.h"
+
+int thrwakeup(long);
+int thrsleep(long, int, void *);
+int thrsigdivert(const sigset_t *);
+
+int
+pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
+{
+ return (sigprocmask(how, set, oset));
+}
+
+/*
+ * implementation of sigwait:
+ * 1. we install a handler for each masked signal.
+ * 2. we inform the kernel we are interested in this signal set.
+ * 3. sleep. the handler will wake us up.
+ *
+ * this is atomic because the kernel will only divert one signal
+ * to a thread until it asks for more.
+ */
+static void
+sigwait_handler(int sig)
+{
+ pthread_t self = pthread_self();
+ self->sigpend = sig;
+ thrwakeup((long)&self->sigpend);
+}
+
+typedef void (*sigfn)(int);
+
+int
+sigwait(const sigset_t *set, int *sig)
+{
+ int i;
+ sigset_t mask = *set;
+ pthread_t self = pthread_self();
+ sigfn oldhandlers[NSIG];
+
+ for (i = 0; i < NSIG; i++) {
+ if (mask & (1 << i))
+ oldhandlers[i] = signal(i, sigwait_handler);
+ }
+
+ thrsigdivert(set);
+ thrsleep((long)&self->sigpend, 0, NULL);
+
+ for (i = 0; i < NSIG; i++) {
+ if (mask & (1 << i))
+ signal(i, oldhandlers[i]);
+ }
+ *sig = self->sigpend;
+ return (0);
+}
diff --git a/lib/librthread/rthread_sync.c b/lib/librthread/rthread_sync.c
new file mode 100644
index 00000000000..a2d7faf01cd
--- /dev/null
+++ b/lib/librthread/rthread_sync.c
@@ -0,0 +1,477 @@
+/* $OpenBSD: rthread_sync.c,v 1.1 2005/12/03 18:16:19 tedu Exp $ */
+/*
+ * Copyright (c) 2004 Ted Unangst <tedu@openbsd.org>
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Mutexes, conditions, rwlocks, and semaphores - synchronization functions.
+ */
+
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <machine/spinlock.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include "rthread.h"
+
+int thrsleep(long, int, void *);
+int thrwakeup(long);
+
+/*
+ * Internal implementation of semaphores
+ */
+int
+_sem_wait(sem_t sem, int tryonly, int timo)
+{
+ int sleep;
+
+ _spinlock(&sem->lock);
+again:
+ if (sem->value == 0) {
+ if (tryonly) {
+ _spinunlock(&sem->lock);
+ return (0);
+ }
+ sem->waitcount++;
+ sleep = 1;
+ } else {
+ sem->value--;
+ sleep = 0;
+ }
+
+ if (sleep) {
+ if (thrsleep((long)sem, timo, &sem->lock) == EWOULDBLOCK)
+ return (0);
+ _spinlock(&sem->lock);
+ sem->waitcount--;
+ goto again;
+ }
+ _spinunlock(&sem->lock);
+ return (1);
+}
+
+int
+_sem_wakeup(sem_t sem)
+{
+ int rv = 0;
+
+ _spinlock(&sem->lock);
+ sem->value++;
+ if (sem->waitcount) {
+ thrwakeup((long)sem);
+ rv = 1;
+ }
+ _spinunlock(&sem->lock);
+ return (rv);
+}
+
+int
+_sem_wakeall(sem_t sem)
+{
+ int rv;
+
+ _spinlock(&sem->lock);
+ rv = sem->waitcount;
+ sem->value += rv;
+ thrwakeup((long)sem);
+ _spinunlock(&sem->lock);
+
+ return (rv);
+}
+
+/*
+ * mutexen
+ */
+int
+pthread_mutex_init(pthread_mutex_t *mutexp, const pthread_mutexattr_t *attr)
+{
+ pthread_mutex_t mutex;
+
+ mutex = malloc(sizeof(*mutex));
+ if (!mutex)
+ return (errno);
+ memset((void *)mutex, 0, sizeof(*mutex));
+ mutex->sem.value = 1; /* unlocked */
+ mutex->type = attr ? (*attr)->type : PTHREAD_MUTEX_ERRORCHECK;
+ *mutexp = mutex;
+
+ return (0);
+}
+
+int
+pthread_mutex_destroy(pthread_mutex_t *mutexp)
+{
+
+ /* check for waiters */
+ free((void *)*mutexp);
+ *mutexp = NULL;
+ return (0);
+}
+
+int
+rthread_mutex_lock(pthread_mutex_t *mutexp, int trywait)
+{
+ pthread_mutex_t mutex = *mutexp;
+ pthread_t thread = pthread_self();
+
+ if (!mutex) {
+ pthread_mutex_init(mutexp, NULL);
+ mutex = *mutexp;
+ }
+ if (mutex->owner == thread) {
+ if (mutex->type == PTHREAD_MUTEX_RECURSIVE) {
+ mutex->count++;
+ return (0);
+ }
+ if (mutex->type == PTHREAD_MUTEX_ERRORCHECK)
+ return (EDEADLK);
+ }
+ if (!_sem_wait((void *)&mutex->sem, trywait, 0))
+ return (EBUSY);
+ mutex->owner = thread;
+ mutex->count = 1;
+
+ return (0);
+}
+
+int
+pthread_mutex_lock(pthread_mutex_t *p)
+{
+ return (rthread_mutex_lock(p, 0));
+}
+
+int
+pthread_mutex_trylock(pthread_mutex_t *p)
+{
+ return (rthread_mutex_lock(p, 1));
+}
+
+int
+pthread_mutex_unlock(pthread_mutex_t *mutexp)
+{
+ pthread_t thread = pthread_self();
+ pthread_mutex_t mutex = *mutexp;
+
+ if (mutex->owner != thread)
+ return (EPERM);
+
+ if (--mutex->count == 0) {
+ mutex->owner = NULL;
+ _sem_wakeup((void *)&mutex->sem);
+ }
+
+ return (0);
+}
+
+/*
+ * mutexen attributes
+ */
+int
+pthread_mutexattr_init(pthread_mutexattr_t *attrp)
+{
+ pthread_mutexattr_t attr;
+
+ attr = malloc(sizeof(*attr));
+ if (!attr)
+ return (errno);
+ memset(attr, 0, sizeof(*attr));
+ attr->type = PTHREAD_MUTEX_ERRORCHECK;
+ *attrp = attr;
+
+ return (0);
+}
+
+int
+pthread_mutexattr_destroy(pthread_mutexattr_t *attrp)
+{
+ free(*attrp);
+ *attrp = NULL;
+
+ return (0);
+}
+
+int
+pthread_mutexattr_settype(pthread_mutexattr_t *attrp, int type)
+{
+ (*attrp)->type = type;
+
+ return (0);
+}
+
+/*
+ * condition variables
+ */
+int
+pthread_cond_init(pthread_cond_t *condp, const pthread_condattr_t *attrp)
+{
+ pthread_cond_t cond;
+
+ cond = malloc(sizeof(*cond));
+ if (!cond)
+ return (errno);
+ memset(cond, 0, sizeof(*cond));
+
+ *condp = cond;
+
+ return (0);
+}
+
+int
+pthread_cond_destroy(pthread_cond_t *condp)
+{
+
+ free(*condp);
+ *condp = NULL;
+
+ return (0);
+}
+
+int
+pthread_cond_timedwait(pthread_cond_t *condp, pthread_mutex_t *mutexp,
+ const struct timespec *abstime)
+{
+ int error;
+
+ if (!*condp)
+ if ((error = pthread_cond_init(condp, NULL)))
+ return (error);
+
+ pthread_mutex_unlock(mutexp);
+ _sem_wait(&(*condp)->sem, 0, (abstime ?
+ abstime->tv_sec * 100 + abstime->tv_nsec / 10000000 : 0));
+ error = pthread_mutex_lock(mutexp);
+
+ return (error);
+}
+
+int
+pthread_cond_wait(pthread_cond_t *condp, pthread_mutex_t *mutexp)
+{
+ return (pthread_cond_timedwait(condp, mutexp, NULL));
+}
+
+int
+pthread_cond_signal(pthread_cond_t *condp)
+{
+ int error;
+
+ if (!*condp)
+ if ((error = pthread_cond_init(condp, NULL)))
+ return (error);
+
+ _sem_wakeup(&(*condp)->sem);
+
+ return (0);
+}
+
+int
+pthread_cond_broadcast(pthread_cond_t *condp)
+{
+ if (!*condp)
+ pthread_cond_init(condp, NULL);
+
+ _sem_wakeall(&(*condp)->sem);
+
+ return (0);
+}
+
+/*
+ * condition variable attributes
+ */
+int
+pthread_condattr_init(pthread_condattr_t *attrp)
+{
+ pthread_condattr_t attr;
+
+ attr = malloc(sizeof(*attr));
+ if (!attr)
+ return (errno);
+ memset(attr, 0, sizeof(*attr));
+ *attrp = attr;
+
+ return (0);
+}
+
+int
+pthread_condattr_destroy(pthread_condattr_t *attrp)
+{
+ free(*attrp);
+ *attrp = NULL;
+
+ return (0);
+}
+
+/*
+ * rwlocks
+ */
+int
+pthread_rwlock_init(pthread_rwlock_t *lockp, const pthread_rwlockattr_t *attrp)
+{
+ pthread_rwlock_t lock;
+
+ lock = malloc(sizeof(*lock));
+ if (!lock)
+ return (errno);
+ memset(lock, 0, sizeof(*lock));
+ *lockp = lock;
+
+ return (0);
+}
+
+int
+pthread_rwlock_destroy(pthread_rwlock_t *lockp)
+{
+ free(*lockp);
+ *lockp = NULL;
+
+ return (0);
+}
+
+int
+pthread_rwlock_rdlock(pthread_rwlock_t *lockp)
+{
+ pthread_rwlock_t lock;
+
+ lock = *lockp;
+again:
+ _spinlock(&lock->lock);
+ if (lock->writer) {
+ _spinunlock(&lock->lock);
+ _sem_wait(&lock->sem, 0, 0);
+ goto again;
+ }
+ lock->readers++;
+ _spinunlock(&lock->lock);
+
+ return (0);
+}
+
+int
+pthread_rwlock_tryrdlock(pthread_rwlock_t *lockp)
+{
+ pthread_rwlock_t lock;
+
+ lock = *lockp;
+
+ _spinlock(&lock->lock);
+ if (lock->writer) {
+ _spinunlock(&lock->lock);
+ return (EBUSY);
+ }
+ lock->readers++;
+ _spinunlock(&lock->lock);
+
+ return (0);
+}
+
+int
+pthread_rwlock_wrlock(pthread_rwlock_t *lockp)
+{
+ pthread_rwlock_t lock;
+
+ lock = *lockp;
+
+again:
+ _spinlock(&lock->lock);
+ lock->writer++;
+ if (lock->readers) {
+ _spinunlock(&lock->lock);
+ _sem_wait(&lock->sem, 0, 0);
+ goto again;
+ }
+ lock->readers = -pthread_self()->tid;
+ _spinunlock(&lock->lock);
+
+ return (0);
+}
+
+int
+pthread_rwlock_trywrlock(pthread_rwlock_t *lockp)
+{
+ pthread_rwlock_t lock;
+
+ lock = *lockp;
+
+ _spinlock(&lock->lock);
+ if (lock->readers || lock->writer) {
+ _spinunlock(&lock->lock);
+ return (EBUSY);
+ }
+ lock->writer = 1;
+ lock->readers = -pthread_self()->tid;
+ _spinunlock(&lock->lock);
+
+ return (0);
+}
+
+int
+pthread_rwlock_unlock(pthread_rwlock_t *lockp)
+{
+ pthread_rwlock_t lock;
+
+ lock = *lockp;
+
+ _spinlock(&lock->lock);
+ if (lock->readers == -pthread_self()->tid) {
+ lock->readers = 0;
+ lock->writer--;
+ } else if (lock->readers > 0) {
+ lock->readers--;
+ } else {
+ _spinunlock(&lock->lock);
+ return (EPERM);
+ }
+ _spinunlock(&lock->lock);
+ _sem_wakeall(&lock->sem);
+
+ return (0);
+}
+
+/*
+ * rwlock attributes
+ */
+int
+pthread_rwlockattr_init(pthread_rwlockattr_t *attrp)
+{
+ pthread_rwlockattr_t attr;
+
+ attr = malloc(sizeof(*attr));
+ if (!attr)
+ return (errno);
+ memset(attr, 0, sizeof(*attr));
+ *attrp = attr;
+
+ return (0);
+}
+
+int
+pthread_rwlockattr_destory(pthread_rwlockattr_t *attrp)
+{
+ free(*attrp);
+ *attrp = NULL;
+
+ return (0);
+}
diff --git a/lib/librthread/rthread_tls.c b/lib/librthread/rthread_tls.c
new file mode 100644
index 00000000000..a1e844fe497
--- /dev/null
+++ b/lib/librthread/rthread_tls.c
@@ -0,0 +1,117 @@
+/* $OpenBSD: rthread_tls.c,v 1.1 2005/12/03 18:16:19 tedu Exp $ */
+/*
+ * Copyright (c) 2004 Ted Unangst <tedu@openbsd.org>
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * thread specific storage
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <machine/spinlock.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include "rthread.h"
+
+struct rthread_key *rthread_key_list;
+
+int
+pthread_key_create(pthread_key_t *key, void (*destructor)(void*))
+{
+ static int last_key;
+ struct rthread_key *rkey;
+
+ last_key++;
+
+ rkey = malloc(sizeof(*key));
+ if (!rkey)
+ return (errno);
+ rkey->keyid = last_key;
+ rkey->destructor = destructor;
+ rkey->next = rthread_key_list;
+ rthread_key_list = rkey;
+
+ *key = last_key;
+
+ return (0);
+}
+
+int
+pthread_key_delete(pthread_key_t key)
+{
+
+ return (0);
+}
+
+static struct rthread_storage *
+rthread_findstorage(pthread_key_t key)
+{
+ struct rthread_storage *rs;
+ pthread_t self;
+
+ self = pthread_self();
+
+ for (rs = self->local_storage; rs; rs = rs->next) {
+ if (rs->keyid == key)
+ break;
+ }
+ if (!rs) {
+ rs = malloc(sizeof(*rs));
+ if (!rs)
+ return (NULL);
+ rs->keyid = key;
+ rs->data = NULL;
+ rs->next = self->local_storage;
+ self->local_storage = rs;
+ }
+
+ return (rs);
+}
+
+void *
+pthread_getspecific(pthread_key_t key)
+{
+ struct rthread_storage *rs;
+
+ rs = rthread_findstorage(key);
+ if (!rs)
+ return (NULL);
+
+ return (rs->data);
+}
+
+int
+pthread_setspecific(pthread_key_t key, const void *data)
+{
+ struct rthread_storage *rs;
+
+ rs = rthread_findstorage(key);
+ if (!rs)
+ return (ENOMEM);
+ rs->data = (void *)data;
+
+ return (0);
+}