blob: 95fc70a63625670c1dd5109e4767dd5714c58d3f [file] [log] [blame]
/*
This file is part of libmicrohttpd
Copyright (C) 2016 Karlson2k (Evgeny Grin)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file microhttpd/mhd_threads.c
* @brief Implementation for thread functions
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_threads.h"
#ifdef MHD_USE_W32_THREADS
#include "mhd_limits.h"
#include <process.h>
#endif
#ifdef MHD_USE_THREAD_NAME_
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif /* HAVE_STDLIB_H */
#ifdef HAVE_PTHREAD_NP_H
#include <pthread_np.h>
#endif /* HAVE_PTHREAD_NP_H */
#endif /* MHD_USE_THREAD_NAME_ */
#include <errno.h>
#ifndef MHD_USE_THREAD_NAME_
#define MHD_set_thread_name_(t, n) (void)
#define MHD_set_cur_thread_name_(n) (void)
#else /* MHD_USE_THREAD_NAME_ */
#if defined(MHD_USE_POSIX_THREADS)
#if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \
defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI)
# define MHD_USE_THREAD_ATTR_SETNAME 1
#endif /* HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD || \
HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI */
#if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \
defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) \
|| defined(HAVE_PTHREAD_SETNAME_NP_NETBSD)
/**
* Set thread name
*
* @param thread_id ID of thread
* @param thread_name name to set
* @return non-zero on success, zero otherwise
*/
static int
MHD_set_thread_name_ (const MHD_thread_ID_ thread_id,
const char *thread_name)
{
if (NULL == thread_name)
return 0;
#if defined(HAVE_PTHREAD_SETNAME_NP_GNU)
return ! pthread_setname_np (thread_id, thread_name);
#elif defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD)
/* FreeBSD and OpenBSD use different function name and void return type */
pthread_set_name_np (thread_id, thread_name);
return ! 0;
#elif defined(HAVE_PTHREAD_SETNAME_NP_NETBSD)
/* NetBSD use 3 arguments: second argument is string in printf-like format,
* third argument is a single argument for printf();
* OSF1 use 3 arguments too, but last one always must be zero (NULL).
* MHD doesn't use '%' in thread names, so both form are used in same way.
*/
return ! pthread_setname_np (thread_id, thread_name, 0);
#endif /* HAVE_PTHREAD_SETNAME_NP_NETBSD */
}
#ifndef __QNXNTO__
/**
* Set current thread name
* @param n name to set
* @return non-zero on success, zero otherwise
*/
#define MHD_set_cur_thread_name_(n) MHD_set_thread_name_ (pthread_self (),(n))
#else /* __QNXNTO__ */
/* Special case for QNX Neutrino - using zero for thread ID sets name faster. */
#define MHD_set_cur_thread_name_(n) MHD_set_thread_name_ (0,(n))
#endif /* __QNXNTO__ */
#elif defined(HAVE_PTHREAD_SETNAME_NP_DARWIN)
/**
* Set current thread name
* @param n name to set
* @return non-zero on success, zero otherwise
*/
#define MHD_set_cur_thread_name_(n) (! (pthread_setname_np ((n))))
#endif /* HAVE_PTHREAD_SETNAME_NP_DARWIN */
#elif defined(MHD_USE_W32_THREADS)
#ifndef _MSC_FULL_VER
/* Thread name available only for VC-compiler */
#else /* _MSC_FULL_VER */
/**
* Set thread name
*
* @param thread_id ID of thread, -1 for current thread
* @param thread_name name to set
* @return non-zero on success, zero otherwise
*/
static int
MHD_set_thread_name_ (const MHD_thread_ID_ thread_id,
const char *thread_name)
{
static const DWORD VC_SETNAME_EXC = 0x406D1388;
#pragma pack(push,8)
struct thread_info_struct
{
DWORD type; /* Must be 0x1000. */
LPCSTR name; /* Pointer to name (in user address space). */
DWORD ID; /* Thread ID (-1 = caller thread). */
DWORD flags; /* Reserved for future use, must be zero. */
} thread_info;
#pragma pack(pop)
if (NULL == thread_name)
return 0;
thread_info.type = 0x1000;
thread_info.name = thread_name;
thread_info.ID = thread_id;
thread_info.flags = 0;
__try
{ /* This exception is intercepted by debugger */
RaiseException (VC_SETNAME_EXC,
0,
sizeof (thread_info) / sizeof(ULONG_PTR),
(ULONG_PTR *) &thread_info);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{}
return ! 0;
}
/**
* Set current thread name
* @param n name to set
* @return non-zero on success, zero otherwise
*/
#define MHD_set_cur_thread_name_(n) \
MHD_set_thread_name_ ((MHD_thread_ID_) -1,(n))
#endif /* _MSC_FULL_VER */
#endif /* MHD_USE_W32_THREADS */
#endif /* MHD_USE_THREAD_NAME_ */
/**
* Create a thread and set the attributes according to our options.
*
* If thread is created, thread handle must be freed by MHD_join_thread_().
*
* @param thread handle to initialize
* @param stack_size size of stack for new thread, 0 for default
* @param start_routine main function of thread
* @param arg argument for start_routine
* @return non-zero on success; zero otherwise (with errno set)
*/
int
MHD_create_thread_ (MHD_thread_handle_ID_ *thread,
size_t stack_size,
MHD_THREAD_START_ROUTINE_ start_routine,
void *arg)
{
#if defined(MHD_USE_POSIX_THREADS)
int res;
if (0 != stack_size)
{
pthread_attr_t attr;
res = pthread_attr_init (&attr);
if (0 == res)
{
res = pthread_attr_setstacksize (&attr,
stack_size);
if (0 == res)
res = pthread_create (&(thread->handle),
&attr,
start_routine,
arg);
pthread_attr_destroy (&attr);
}
}
else
res = pthread_create (&(thread->handle),
NULL,
start_routine,
arg);
if (0 != res)
errno = res;
return ! res;
#elif defined(MHD_USE_W32_THREADS)
#if SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT
if (stack_size > UINT_MAX)
{
errno = EINVAL;
return 0;
}
#endif /* SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT */
thread->handle = (MHD_thread_handle_) (uintptr_t)
_beginthreadex (NULL,
(unsigned int) stack_size,
start_routine,
arg,
0,
NULL);
if ((MHD_thread_handle_) - 1 == thread->handle)
return 0;
return ! 0;
#endif
}
#ifdef MHD_USE_THREAD_NAME_
#ifndef MHD_USE_THREAD_ATTR_SETNAME
struct MHD_named_helper_param_
{
/**
* Real thread start routine
*/
MHD_THREAD_START_ROUTINE_ start_routine;
/**
* Argument for thread start routine
*/
void *arg;
/**
* Name for thread
*/
const char *name;
};
static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
named_thread_starter (void *data)
{
struct MHD_named_helper_param_ *const param =
(struct MHD_named_helper_param_ *) data;
void *arg;
MHD_THREAD_START_ROUTINE_ thr_func;
if (NULL == data)
return (MHD_THRD_RTRN_TYPE_) 0;
MHD_set_cur_thread_name_ (param->name);
arg = param->arg;
thr_func = param->start_routine;
free (data);
return thr_func (arg);
}
#endif /* ! MHD_USE_THREAD_ATTR_SETNAME */
/**
* Create a named thread and set the attributes according to our options.
*
* @param thread handle to initialize
* @param thread_name name for new thread
* @param stack_size size of stack for new thread, 0 for default
* @param start_routine main function of thread
* @param arg argument for start_routine
* @return non-zero on success; zero otherwise (with errno set)
*/
int
MHD_create_named_thread_ (MHD_thread_handle_ID_ *thread,
const char *thread_name,
size_t stack_size,
MHD_THREAD_START_ROUTINE_ start_routine,
void *arg)
{
#if defined(MHD_USE_THREAD_ATTR_SETNAME)
int res;
pthread_attr_t attr;
res = pthread_attr_init (&attr);
if (0 == res)
{
#if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD)
/* NetBSD use 3 arguments: second argument is string in printf-like format,
* third argument is single argument for printf;
* OSF1 use 3 arguments too, but last one always must be zero (NULL).
* MHD doesn't use '%' in thread names, so both form are used in same way.
*/
res = pthread_attr_setname_np (&attr,
thread_name,
0);
#elif defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI)
res = pthread_attr_setname_np (&attr,
thread_name);
#else
#error No pthread_attr_setname_np() function.
#endif
if ((res == 0) && (0 != stack_size) )
res = pthread_attr_setstacksize (&attr,
stack_size);
if (0 == res)
res = pthread_create (&(thread->handle),
&attr,
start_routine,
arg);
pthread_attr_destroy (&attr);
}
if (0 != res)
errno = res;
return ! res;
#else /* ! MHD_USE_THREAD_ATTR_SETNAME */
struct MHD_named_helper_param_ *param;
if (NULL == thread_name)
{
errno = EINVAL;
return 0;
}
param = malloc (sizeof (struct MHD_named_helper_param_));
if (NULL == param)
return 0;
param->start_routine = start_routine;
param->arg = arg;
param->name = thread_name;
/* Set thread name in thread itself to avoid problems with
* threads which terminated before name is set in other thread.
*/
if (! MHD_create_thread_ (thread,
stack_size,
&named_thread_starter,
(void *) param))
{
free (param);
return 0;
}
return ! 0;
#endif /* ! MHD_USE_THREAD_ATTR_SETNAME */
}
#endif /* MHD_USE_THREAD_NAME_ */