#include "config.h"
#ifndef HAVE_SOCKLEN_T
#define socklen_t int
#endif
#if !defined(__FreeBSD__) && !defined(__APPLE__)
#define _POSIX_SOURCE
#endif
#define _GNU_SOURCE
#define _XOPEN_SOURCE
#define _BSD_TYPES
#define __EXTENSIONS__
#define _ALL_SOURCE
#define _LARGE_FILE_API
#define _XOPEN_SOURCE_EXTENDED 1
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <sched.h>
#include <fcntl.h>
#include "mf-runtime.h"
#include "mf-impl.h"
#ifdef _MUDFLAP
#error "Do not compile this file with -fmudflap!"
#endif
#ifndef LIBMUDFLAPTH
#error "pthreadstuff is to be included only in libmudflapth"
#endif
struct pthread_info
{
short used_p;
short dead_p;
pthread_t self;
void *stack;
size_t stack_size;
void *stack_alloc;
size_t stack_size_alloc;
int *thread_errno;
enum __mf_state_enum state;
};
struct pthread_start_info
{
void * (*user_fn)(void *);
void *user_arg;
struct pthread_info *thread_info;
};
#define LIBMUDFLAPTH_THREADS_MAX 1024
static struct pthread_info __mf_pthread_info[LIBMUDFLAPTH_THREADS_MAX];
static unsigned __mf_pthread_info_idx[LIBMUDFLAPTH_THREADS_MAX];
#define PTHREAD_HASH(p) ((unsigned) (p) % LIBMUDFLAPTH_THREADS_MAX)
struct pthread_info*
__mf_allocate_blank_threadinfo (unsigned* idx)
{
static unsigned probe = LIBMUDFLAPTH_THREADS_MAX-1;
unsigned probe_at_start = probe;
static pthread_mutex_t mutex =
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
#else
PTHREAD_MUTEX_INITIALIZER;
#endif
int rc;
rc = pthread_mutex_lock (& mutex);
assert (rc == 0);
do
{
probe = (probe + 1) % LIBMUDFLAPTH_THREADS_MAX;
struct pthread_info* pi = & __mf_pthread_info [probe];
if (! pi->used_p)
{
pi->used_p = 1;
if (idx != NULL) *idx = probe;
rc = pthread_mutex_unlock (& mutex);
assert (rc == 0);
return pi;
}
}
while (probe != probe_at_start);
rc = pthread_mutex_unlock (& mutex);
assert (rc == 0);
return NULL;
}
static struct pthread_info*
__mf_find_threadinfo ()
{
pthread_t it = pthread_self ();
unsigned *hash = & __mf_pthread_info_idx [PTHREAD_HASH (it)];
struct pthread_info *result = NULL;
static pthread_t last;
static int main_thread_seen_p;
{
struct pthread_info* pi = & __mf_pthread_info [*hash];
unsigned i;
if (pi->used_p && pi->self == it)
result = pi;
else for (i = 0; i < LIBMUDFLAPTH_THREADS_MAX; i++)
{
struct pthread_info* pi2 = & __mf_pthread_info [i];
if (pi2->used_p && pi2->self == it)
{
*hash = i;
result = pi2;
break;
}
}
}
if (result == NULL)
{
unsigned *hash = & __mf_pthread_info_idx [PTHREAD_HASH (it)];
struct pthread_info* pi = __mf_allocate_blank_threadinfo (hash);
assert (pi != NULL);
assert (pi->used_p);
result = pi;
result->self = it;
if (! main_thread_seen_p)
{
result->state = active;
main_thread_seen_p = 1;
}
else
{
result->state = reentrant;
}
}
if (last != it)
{
last = it;
}
assert (result != NULL);
assert (result->self == it);
return result;
}
enum __mf_state_enum *
__mf_state_perthread ()
{
assert (! __mf_starting_p);
return & (__mf_find_threadinfo()->state);
}
static void
__mf_pthread_cleanup (void *arg)
{
struct pthread_info *pi = arg;
if (pi->thread_errno != NULL)
__mf_unregister (pi->thread_errno, sizeof (int), __MF_TYPE_GUESS);
pi->state = reentrant;
pi->dead_p = 1;
VERBOSE_TRACE ("thread pi %p exiting\n", pi);
}
static void *
__mf_pthread_spawner (void *arg)
{
struct pthread_info *pi = __mf_find_threadinfo ();
void *result = NULL;
assert (pi->state == reentrant);
pi->state = active;
VERBOSE_TRACE ("new user thread\n");
if (__mf_opts.heur_std_data)
{
pi->thread_errno = & errno;
__mf_register (pi->thread_errno, sizeof (int),
__MF_TYPE_GUESS, "errno area (thread)");
}
pthread_cleanup_push (& __mf_pthread_cleanup, pi);
{
struct pthread_start_info *psi = arg;
void * (*user_fn)(void *) = psi->user_fn;
void *user_arg = psi->user_arg;
psi->thread_info = pi;
result = (*user_fn)(user_arg);
}
pthread_cleanup_pop (1 );
return result;
}
#if PIC
int
__mf_0fn_pthread_create (pthread_t *thr, const pthread_attr_t *attr,
void * (*start) (void *), void *arg)
{
return -1;
}
#endif
#undef pthread_create
WRAPPER(int, pthread_create, pthread_t *thr, const pthread_attr_t *attr,
void * (*start) (void *), void *arg)
{
DECLARE(int, munmap, void *p, size_t l);
DECLARE(void *, mmap, void *p, size_t l, int prot, int flags, int fd, off_t of);
DECLARE(int, pthread_create, pthread_t *thr, const pthread_attr_t *attr,
void * (*start) (void *), void *arg);
int result;
pthread_attr_t override_attr;
void *override_stack;
size_t override_stacksize;
void *override_stack_alloc = (void *) 0;
size_t override_stacksize_alloc = 0;
unsigned i;
TRACE ("pthread_create\n");
LOCKTH ();
for (i = 0; i < LIBMUDFLAPTH_THREADS_MAX; i++)
{
struct pthread_info *pi = & __mf_pthread_info [i];
if (! pi->used_p)
continue;
if (! pi->dead_p)
continue;
if (pi->dead_p)
pi->dead_p ++;
if (pi->dead_p >= 10 )
{
if (pi->stack)
CALL_REAL (munmap, pi->stack_alloc, pi->stack_size_alloc);
VERBOSE_TRACE ("slot %u freed, stack %p\n", i, pi->stack_alloc);
memset (pi, 0, sizeof (*pi));
break;
}
}
UNLOCKTH ();
if (attr != NULL)
override_attr = *attr;
else
pthread_attr_init (& override_attr);
if (pthread_attr_getstackaddr (& override_attr, & override_stack) != 0 ||
pthread_attr_getstacksize (& override_attr, & override_stacksize) != 0)
{
override_stack = NULL;
override_stacksize = 0;
}
if (__mf_opts.thread_stack && override_stack == NULL)
{
uintptr_t alignment = 256;
static unsigned perturb = 0;
const unsigned perturb_delta = 32;
const unsigned perturb_count = 16;
perturb += perturb_delta;
if (perturb > perturb_delta*perturb_count) perturb = 0;
#ifndef PTHREAD_STACK_MIN
#define PTHREAD_STACK_MIN 65536
#endif
override_stacksize = max (PTHREAD_STACK_MIN, __mf_opts.thread_stack * 1024);
#if defined(MAP_ANONYMOUS)
#define MF_MAP_ANON MAP_ANONYMOUS
#elif defined(MAP_ANON)
#define MF_MAP_ANON MAP_ANON
#endif
#ifndef MAP_FAILED
#define MAP_FAILED ((void *) -1)
#endif
#ifdef MF_MAP_ANON
override_stack = CALL_REAL (mmap, NULL, override_stacksize,
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MF_MAP_ANON,
0, 0);
#else
{
static int zerofd = -1;
if (zerofd == -1)
zerofd = open ("/dev/zero", O_RDWR);
if (zerofd == -1)
override_stack = MAP_FAILED;
else
override_stack = CALL_REAL (mmap, NULL, override_stacksize,
PROT_READ|PROT_WRITE,
MAP_PRIVATE, zerofd, 0);
}
#endif
if (override_stack == 0 || override_stack == MAP_FAILED)
{
errno = EAGAIN;
return -1;
}
VERBOSE_TRACE ("thread stack alloc %p size %lu\n",
override_stack, (unsigned long) override_stacksize);
override_stack_alloc = override_stack;
override_stacksize_alloc = override_stacksize;
override_stack = (void *)
(((uintptr_t) override_stack + override_stacksize - alignment - perturb)
& (~(uintptr_t)(alignment-1)));
if (pthread_attr_setstackaddr (& override_attr, override_stack) != 0 ||
pthread_attr_setstacksize (& override_attr,
override_stacksize - alignment - perturb) != 0)
{
CALL_REAL (munmap, override_stack, override_stacksize);
errno = EAGAIN;
return -1;
}
}
{
struct pthread_start_info psi;
struct pthread_info *pi = NULL;
psi.user_fn = start;
psi.user_arg = arg;
psi.thread_info = NULL;
__mf_state = reentrant;
result = CALL_REAL (pthread_create, thr, & override_attr,
& __mf_pthread_spawner, (void *) & psi);
__mf_state = active;
while (1)
{
volatile struct pthread_start_info *psip = & psi;
pi = psip->thread_info;
if (pi != NULL)
break;
sched_yield ();
}
pi->stack = override_stack;
pi->stack_size = override_stacksize;
pi->stack_alloc = override_stack_alloc;
pi->stack_size_alloc = override_stacksize_alloc;
}
if (attr == NULL)
pthread_attr_destroy (& override_attr);
return result;
}
#if PIC
int
__mf_0fn_pthread_join (pthread_t thr, void **rc)
{
return -1;
}
#endif
#undef pthread_join
WRAPPER(int, pthread_join, pthread_t thr, void **rc)
{
DECLARE(int, pthread_join, pthread_t thr, void **rc);
int result;
TRACE ("pthread_join\n");
__mf_state = reentrant;
result = CALL_REAL (pthread_join, thr, rc);
__mf_state = active;
return result;
}
#if PIC
void
__mf_0fn_pthread_exit (void *rc)
{
}
#endif
#undef pthread_exit
WRAPPER(void, pthread_exit, void *rc)
{
DECLARE(void, pthread_exit, void *rc);
TRACE ("pthread_exit\n");
CALL_REAL (pthread_exit, rc);
exit (0);
}