libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

mhd_threads.c (13834B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2016-2024 Evgeny Grin (Karlson2k)
      5 
      6   GNU libmicrohttpd is free software; you can redistribute it and/or
      7   modify it under the terms of the GNU Lesser General Public
      8   License as published by the Free Software Foundation; either
      9   version 2.1 of the License, or (at your option) any later version.
     10 
     11   GNU libmicrohttpd is distributed in the hope that it will be useful,
     12   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14   Lesser General Public License for more details.
     15 
     16   Alternatively, you can redistribute GNU libmicrohttpd and/or
     17   modify it under the terms of the GNU General Public License as
     18   published by the Free Software Foundation; either version 2 of
     19   the License, or (at your option) any later version, together
     20   with the eCos exception, as follows:
     21 
     22     As a special exception, if other files instantiate templates or
     23     use macros or inline functions from this file, or you compile this
     24     file and link it with other works to produce a work based on this
     25     file, this file does not by itself cause the resulting work to be
     26     covered by the GNU General Public License. However the source code
     27     for this file must still be made available in accordance with
     28     section (3) of the GNU General Public License v2.
     29 
     30     This exception does not invalidate any other reasons why a work
     31     based on this file might be covered by the GNU General Public
     32     License.
     33 
     34   You should have received copies of the GNU Lesser General Public
     35   License and the GNU General Public License along with this library;
     36   if not, see <https://www.gnu.org/licenses/>.
     37 */
     38 
     39 /**
     40  * @file microhttpd/mhd_threads.c
     41  * @brief  Implementation for thread functions
     42  * @author Karlson2k (Evgeny Grin)
     43  */
     44 
     45 #include "mhd_sys_options.h"
     46 
     47 #include "mhd_threads.h"
     48 #include "sys_null_macro.h"
     49 #ifdef mhd_THREADS_KIND_W32
     50 #  include <process.h>
     51 #endif
     52 #if defined(mhd_USE_THREAD_NAME)
     53 #  if ! defined(MHD_USE_THREAD_ATTR_SETNAME)
     54 #    include "sys_malloc.h"
     55 #  endif
     56 #  ifdef HAVE_PTHREAD_NP_H
     57 #    include <pthread_np.h>
     58 #  endif /* HAVE_PTHREAD_NP_H */
     59 #endif /* mhd_USE_THREAD_NAME */
     60 #include "sys_errno.h"
     61 #include "mhd_assert.h"
     62 #ifdef mhd_HAVE_MHD_THREAD_BLOCK_SIGPIPE
     63 #  ifdef HAVE_SIGNAL_H
     64 #    include <signal.h>
     65 #  endif
     66 #endif
     67 
     68 #ifndef mhd_USE_THREAD_NAME
     69 
     70 #  define mhd_set_thread_name(t, n) (void)
     71 #  define mhd_set_cur_thread_name(n) (void)
     72 
     73 #else  /* mhd_USE_THREAD_NAME */
     74 
     75 #  if defined(mhd_THREADS_KIND_POSIX)
     76 #    if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \
     77   defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI)
     78 #      define MHD_USE_THREAD_ATTR_SETNAME 1
     79 #    endif /* HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD || \
     80           HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI */
     81 
     82 #    if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \
     83   defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) \
     84   || defined(HAVE_PTHREAD_SETNAME_NP_NETBSD)
     85 
     86 /**
     87  * Set thread name
     88  *
     89  * @param thread_id ID of thread
     90  * @param thread_name name to set
     91  * @return true on success, false otherwise
     92  */
     93 static bool
     94 mhd_set_thread_name (const mhd_thread_ID_native thread_id,
     95                      const char *thread_name)
     96 {
     97   if (NULL == thread_name)
     98     return false;
     99 
    100 #      if defined(HAVE_PTHREAD_SETNAME_NP_GNU)
    101   return 0 == pthread_setname_np (thread_id, thread_name);
    102 #      elif defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD)
    103   /* FreeBSD and OpenBSD use different function name and void return type */
    104   pthread_set_name_np (thread_id, thread_name);
    105   return true;
    106 #      elif defined(HAVE_PTHREAD_SETNAME_NP_NETBSD)
    107   /* NetBSD uses 3 arguments: second argument is string in printf-like format,
    108    *                          third argument is a single argument for printf();
    109    * OSF1 use 3 arguments too, but last one always must be zero (NULL).
    110    * MHD doesn't use '%' in thread names, so both form are used in same way.
    111    */
    112   return 0 == pthread_setname_np (thread_id, thread_name, 0);
    113 #      endif /* HAVE_PTHREAD_SETNAME_NP_NETBSD */
    114 }
    115 
    116 
    117 #      ifndef __QNXNTO__
    118 /**
    119  * Set current thread name
    120  * @param n name to set
    121  * @return non-zero on success, zero otherwise
    122  */
    123 #        define mhd_set_cur_thread_name(n) \
    124         mhd_set_thread_name (pthread_self (),(n))
    125 #      else  /* __QNXNTO__ */
    126 /* Special case for QNX Neutrino - using zero for thread ID sets name faster. */
    127 #        define mhd_set_cur_thread_name(n) mhd_set_thread_name (0,(n))
    128 #      endif /* __QNXNTO__ */
    129 #    elif defined(HAVE_PTHREAD_SETNAME_NP_DARWIN)
    130 
    131 /**
    132  * Set current thread name
    133  * @param n name to set
    134  * @return non-zero on success, zero otherwise
    135  */
    136 #      define mhd_set_cur_thread_name(n) (! (pthread_setname_np ((n))))
    137 #    endif /* HAVE_PTHREAD_SETNAME_NP_DARWIN */
    138 
    139 #  elif defined(mhd_THREADS_KIND_W32)
    140 #    ifndef _MSC_FULL_VER
    141 #error Thread name available only for VC-compiler
    142 #    else  /* _MSC_FULL_VER */
    143 /**
    144  * Set thread name
    145  *
    146  * @param thread_id ID of thread, -1 for current thread
    147  * @param thread_name name to set
    148  * @return true on success, false otherwise
    149  */
    150 static bool
    151 mhd_set_thread_name (const mhd_thread_ID_native thread_id,
    152                      const char *thread_name)
    153 {
    154   static const DWORD VC_SETNAME_EXC = 0x406D1388;
    155 #pragma pack(push,8)
    156   struct thread_info_struct
    157   {
    158     DWORD type;   /* Must be 0x1000. */
    159     LPCSTR name;  /* Pointer to name (in user address space). */
    160     DWORD ID;     /* Thread ID (-1 = caller thread). */
    161     DWORD flags;  /* Reserved for future use, must be zero. */
    162   } thread_info;
    163 #pragma pack(pop)
    164 
    165   if (NULL == thread_name)
    166     return false;
    167 
    168   thread_info.type  = 0x1000;
    169   thread_info.name  = thread_name;
    170   thread_info.ID    = thread_id;
    171   thread_info.flags = 0;
    172 
    173   __try
    174   { /* This exception is intercepted by debugger */
    175     RaiseException (VC_SETNAME_EXC,
    176                     0,
    177                     sizeof (thread_info) / sizeof(ULONG_PTR),
    178                     (ULONG_PTR *) &thread_info);
    179   }
    180   __except (EXCEPTION_EXECUTE_HANDLER)
    181   {}
    182 
    183   return true;
    184 }
    185 
    186 
    187 /**
    188  * Set current thread name
    189  * @param n name to set
    190  * @return true on success, false otherwise
    191  */
    192 #      define mhd_set_cur_thread_name(n) \
    193         mhd_set_thread_name ((mhd_thread_ID_native) (-1),(n))
    194 #    endif /* _MSC_FULL_VER */
    195 #  endif /* mhd_THREADS_KIND_W32 */
    196 
    197 #endif /* mhd_USE_THREAD_NAME */
    198 
    199 
    200 /**
    201  * Create a thread and set the attributes according to our options.
    202  *
    203  * If thread is created, thread handle must be freed by mhd_join_thread().
    204  *
    205  * @param handle_id     handle to initialise
    206  * @param stack_size    size of stack for new thread, 0 for default
    207  * @param start_routine main function of thread
    208  * @param arg argument  for start_routine
    209  * @return non-zero on success; zero otherwise (with errno set)
    210  */
    211 bool
    212 mhd_create_thread (mhd_thread_handle_ID *handle_id,
    213                    size_t stack_size,
    214                    mhd_THREAD_START_ROUTINE start_routine,
    215                    void *arg)
    216 {
    217 #if defined(mhd_THREADS_KIND_POSIX)
    218   int res;
    219 #  if defined(mhd_thread_handle_ID_get_native_handle_ptr)
    220   pthread_t *const new_tid_ptr =
    221     mhd_thread_handle_ID_get_native_handle_ptr (handle_id);
    222 #  else  /* ! mhd_thread_handle_ID_get_native_handle_ptr */
    223   pthread_t new_tid;
    224   pthread_t *const new_tid_ptr = &new_tid;
    225 #  endif /* ! mhd_thread_handle_ID_get_native_handle_ptr */
    226 
    227   mhd_assert (! mhd_thread_handle_ID_is_valid_handle (*handle_id));
    228 
    229   if (0 != stack_size)
    230   {
    231     pthread_attr_t attr;
    232     res = pthread_attr_init (&attr);
    233     if (0 == res)
    234     {
    235       res = pthread_attr_setstacksize (&attr,
    236                                        stack_size);
    237       if (0 == res)
    238         res = pthread_create (new_tid_ptr,
    239                               &attr,
    240                               start_routine,
    241                               arg);
    242       pthread_attr_destroy (&attr);
    243     }
    244   }
    245   else
    246     res = pthread_create (new_tid_ptr,
    247                           NULL,
    248                           start_routine,
    249                           arg);
    250 
    251   if (0 != res)
    252   {
    253     errno = res;
    254     mhd_thread_handle_ID_set_invalid (handle_id);
    255   }
    256 #  if ! defined(mhd_thread_handle_ID_get_native_handle_ptr)
    257   else
    258     mhd_thread_handle_ID_set_native_handle (handle_id, new_tid);
    259 #  endif /* ! mhd_thread_handle_ID_set_current_thread_ID */
    260 
    261   return 0 == res;
    262 #elif defined(mhd_THREADS_KIND_W32)
    263   uintptr_t thr_handle;
    264   unsigned int stack_size_w32;
    265 
    266   mhd_assert (! mhd_thread_handle_ID_is_valid_handle (*handle_id));
    267 
    268   stack_size_w32 = (unsigned int) stack_size;
    269 #  if SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT
    270   if (stack_size != stack_size_w32)
    271   {
    272     errno = EINVAL;
    273     return false;
    274   }
    275 #  endif /* SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT */
    276   thr_handle = _beginthreadex (NULL,
    277                                stack_size_w32,
    278                                start_routine,
    279                                arg,
    280                                0,
    281                                NULL);
    282   if ((mhd_thread_handle_native) 0 == (mhd_thread_handle_native) thr_handle)
    283     return false;
    284 
    285   mhd_thread_handle_ID_set_native_handle (handle_id, \
    286                                           (mhd_thread_handle_native) \
    287                                           thr_handle);
    288 
    289   return true;
    290 #endif /* mhd_THREADS_KIND_W32 */
    291 }
    292 
    293 
    294 #ifdef mhd_USE_THREAD_NAME
    295 
    296 #  ifndef MHD_USE_THREAD_ATTR_SETNAME
    297 struct mhd_named_helper_param
    298 {
    299   /**
    300    * Real thread start routine
    301    */
    302   mhd_THREAD_START_ROUTINE start_routine;
    303 
    304   /**
    305    * Argument for thread start routine
    306    */
    307   void *arg;
    308 
    309   /**
    310    * Name for thread
    311    */
    312   const char *name;
    313 };
    314 
    315 
    316 static mhd_THRD_RTRN_TYPE mhd_THRD_CALL_SPEC
    317 named_thread_starter (void *data)
    318 {
    319   struct mhd_named_helper_param *const param =
    320     (struct mhd_named_helper_param *) data;
    321   void *arg;
    322   mhd_THREAD_START_ROUTINE thr_func;
    323 
    324   if (NULL == data)
    325     return (mhd_THRD_RTRN_TYPE) 0;
    326 
    327   mhd_set_cur_thread_name (param->name);
    328 
    329   arg = param->arg;
    330   thr_func = param->start_routine;
    331   free (data);
    332 
    333   return thr_func (arg);
    334 }
    335 
    336 
    337 #  endif /* ! MHD_USE_THREAD_ATTR_SETNAME */
    338 
    339 
    340 /**
    341  * Create a named thread and set the attributes according to our options.
    342  *
    343  * @param handle_id     handle to initialise
    344  * @param thread_name   name for new thread
    345  * @param stack_size    size of stack for new thread, 0 for default
    346  * @param start_routine main function of thread
    347  * @param arg argument  for start_routine
    348  * @return non-zero on success; zero otherwise (with errno set)
    349  */
    350 bool
    351 mhd_create_named_thread (mhd_thread_handle_ID *handle_id,
    352                          const char *thread_name,
    353                          size_t stack_size,
    354                          mhd_THREAD_START_ROUTINE start_routine,
    355                          void *arg)
    356 {
    357 #  if defined(MHD_USE_THREAD_ATTR_SETNAME)
    358   int res;
    359   pthread_attr_t attr;
    360 #    if defined(mhd_thread_handle_ID_get_native_handle_ptr)
    361   pthread_t *const new_tid_ptr =
    362     mhd_thread_handle_ID_get_native_handle_ptr (handle_id);
    363 #    else  /* ! mhd_thread_handle_ID_get_native_handle_ptr */
    364   pthread_t new_tid;
    365   pthread_t *const new_tid_ptr = &new_tid;
    366 #    endif /* ! mhd_thread_handle_ID_get_native_handle_ptr */
    367 
    368   res = pthread_attr_init (&attr);
    369   if (0 == res)
    370   {
    371 #    if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD)
    372     /* NetBSD uses 3 arguments: second argument is string in printf-like format,
    373      *                          third argument is single argument for printf;
    374      * OSF1 uses 3 arguments too, but last one always must be zero (NULL).
    375      * MHD doesn't use '%' in thread names, so both forms are used in same way.
    376      */
    377     res = pthread_attr_setname_np (&attr,
    378                                    thread_name,
    379                                    0);
    380 #    elif defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI)
    381     res = pthread_attr_setname_np (&attr,
    382                                    thread_name);
    383 #    else
    384 #error No pthread_attr_setname_np() function.
    385 #    endif
    386     if ((res == 0) && (0 != stack_size) )
    387       res = pthread_attr_setstacksize (&attr,
    388                                        stack_size);
    389     if (0 == res)
    390       res = pthread_create (new_tid_ptr,
    391                             &attr,
    392                             start_routine,
    393                             arg);
    394     pthread_attr_destroy (&attr);
    395   }
    396   if (0 != res)
    397   {
    398     errno = res;
    399     mhd_thread_handle_ID_set_invalid (handle_id);
    400   }
    401 #    if ! defined(mhd_thread_handle_ID_get_native_handle_ptr)
    402   else
    403     mhd_thread_handle_ID_set_native_handle (handle_id, new_tid);
    404 #    endif /* ! mhd_thread_handle_ID_set_current_thread_ID */
    405 
    406   return 0 == res;
    407 #  else  /* ! MHD_USE_THREAD_ATTR_SETNAME */
    408   struct mhd_named_helper_param *param;
    409 
    410   if (NULL == thread_name)
    411   {
    412     errno = EINVAL;
    413     return false;
    414   }
    415 
    416   param = (struct mhd_named_helper_param *)
    417           malloc (sizeof (struct mhd_named_helper_param));
    418   if (NULL == param)
    419     return false;
    420 
    421   param->start_routine = start_routine;
    422   param->arg = arg;
    423   param->name = thread_name;
    424 
    425   /* Set the thread name in the thread itself to avoid problems with
    426    * threads which terminated before the name is set in other thread.
    427    */
    428   if (! mhd_create_thread (handle_id,
    429                            stack_size,
    430                            &named_thread_starter,
    431                            (void *) param))
    432   {
    433     int err_num;
    434 
    435     err_num = errno;
    436     free (param);
    437     errno = err_num;
    438     return false;
    439   }
    440 
    441   return true;
    442 #  endif /* ! MHD_USE_THREAD_ATTR_SETNAME */
    443 }
    444 
    445 
    446 #endif /* mhd_USE_THREAD_NAME */
    447 
    448 #ifdef mhd_HAVE_MHD_THREAD_BLOCK_SIGPIPE
    449 MHD_INTERNAL bool
    450 mhd_thread_block_sigpipe (void)
    451 {
    452   sigset_t s_mask;
    453 
    454   return ((0 == sigemptyset (&s_mask)) &&
    455           (0 == sigaddset (&s_mask, SIGPIPE)) &&
    456           (0 == pthread_sigmask (SIG_BLOCK, &s_mask, NULL)));
    457 }
    458 
    459 
    460 #endif /* mhd_HAVE_MHD_THREAD_BLOCK_SIGPIPE */