Home | History | Annotate | Download | only in gthread
      1 /* GLIB - Library of useful routines for C programming
      2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
      3  *
      4  * gthread.c: posix thread system implementation
      5  * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Lesser General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Lesser General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Lesser General Public
     18  * License along with this library; if not, write to the
     19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
     20  * Boston, MA 02111-1307, USA.
     21  */
     22 
     23 /*
     24  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
     25  * file for a list of people on the GLib Team.  See the ChangeLog
     26  * files for a list of changes.  These files are distributed with
     27  * GLib at ftp://ftp.gtk.org/pub/gtk/.
     28  */
     29 
     30 /*
     31  * MT safe
     32  */
     33 
     34 #include "config.h"
     35 
     36 #include <pthread.h>
     37 #include <errno.h>
     38 #include <stdlib.h>
     39 #ifdef HAVE_SYS_TIME_H
     40 # include <sys/time.h>
     41 #endif
     42 #ifdef HAVE_UNISTD_H
     43 # include <unistd.h>
     44 #endif
     45 
     46 #ifdef HAVE_SCHED_H
     47 #include <sched.h>
     48 #endif
     49 
     50 #define posix_check_err(err, name) G_STMT_START{			\
     51   int error = (err); 							\
     52   if (error)	 		 		 			\
     53     g_error ("file %s: line %d (%s): error '%s' during '%s'",		\
     54            __FILE__, __LINE__, G_STRFUNC,				\
     55            g_strerror (error), name);					\
     56   }G_STMT_END
     57 
     58 #define posix_check_cmd(cmd) posix_check_err (posix_error (cmd), #cmd)
     59 
     60 #ifdef G_ENABLE_DEBUG
     61 static gboolean posix_check_cmd_prio_warned = FALSE;
     62 # define posix_check_cmd_prio(cmd) G_STMT_START{			\
     63     int err = posix_error (cmd);					\
     64     if (err == EPERM)		 		 			\
     65       { 	 			 				\
     66         if (!posix_check_cmd_prio_warned) 		 		\
     67           { 	 				 			\
     68             posix_check_cmd_prio_warned = TRUE;		 		\
     69             g_warning ("Priorities can only be changed " 		\
     70                         "(resp. increased) by root."); 			\
     71           }			 					\
     72       }			 						\
     73     else  		 						\
     74       posix_check_err (err, #cmd);					\
     75      }G_STMT_END
     76 #else /* G_ENABLE_DEBUG */
     77 # define posix_check_cmd_prio(cmd) G_STMT_START{			\
     78     int err = posix_error (cmd);					\
     79     if (err != EPERM)		 		 			\
     80       posix_check_err (err, #cmd);					\
     81      }G_STMT_END
     82 #endif /* G_ENABLE_DEBUG */
     83 
     84 #if defined(G_THREADS_IMPL_POSIX)
     85 # define posix_error(what) (what)
     86 # define mutexattr_default NULL
     87 # define condattr_default NULL
     88 #elif defined(G_THREADS_IMPL_DCE)
     89 # define posix_error(what) ((what) == -1 ? errno : 0)
     90 # define pthread_key_create(a, b) pthread_keycreate (a, b)
     91 # define pthread_attr_init(a) pthread_attr_create (a)
     92 # define pthread_attr_destroy(a) pthread_attr_delete (a)
     93 # define pthread_create(a, b, c, d) pthread_create (a, *b, c, d)
     94 # define mutexattr_default (pthread_mutexattr_default)
     95 # define condattr_default (pthread_condattr_default)
     96 #else /* neither G_THREADS_IMPL_POSIX nor G_THREADS_IMPL_DCE are defined */
     97 # error This should not happen. Contact the GLib team.
     98 #endif
     99 
    100 #if defined (POSIX_MIN_PRIORITY) && defined (POSIX_MAX_PRIORITY)
    101 # define HAVE_PRIORITIES 1
    102 static gint priority_normal_value;
    103 # ifdef __FreeBSD__
    104    /* FreeBSD threads use different priority values from the POSIX_
    105     * defines so we just set them here. The corresponding macros
    106     * PTHREAD_MIN_PRIORITY and PTHREAD_MAX_PRIORITY are implied to be
    107     * exported by the docs, but they aren't.
    108     */
    109 #  define PRIORITY_LOW_VALUE      0
    110 #  define PRIORITY_URGENT_VALUE   31
    111 # else /* !__FreeBSD__ */
    112 #  define PRIORITY_LOW_VALUE      POSIX_MIN_PRIORITY
    113 #  define PRIORITY_URGENT_VALUE   POSIX_MAX_PRIORITY
    114 # endif /* !__FreeBSD__ */
    115 # define PRIORITY_NORMAL_VALUE    priority_normal_value
    116 #endif /* POSIX_MIN_PRIORITY && POSIX_MAX_PRIORITY */
    117 
    118 static gulong g_thread_min_stack_size = 0;
    119 
    120 #define G_MUTEX_SIZE (sizeof (pthread_mutex_t))
    121 
    122 #if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_MONOTONIC_CLOCK)
    123 #define USE_CLOCK_GETTIME 1
    124 static gint posix_clock = 0;
    125 #endif
    126 
    127 #if defined(_SC_THREAD_STACK_MIN) || defined (HAVE_PRIORITIES) || defined (USE_CLOCK_GETTIME)
    128 #define HAVE_G_THREAD_IMPL_INIT
    129 static void
    130 g_thread_impl_init(void)
    131 {
    132 #ifdef _SC_THREAD_STACK_MIN
    133   g_thread_min_stack_size = MAX (sysconf (_SC_THREAD_STACK_MIN), 0);
    134 #endif /* _SC_THREAD_STACK_MIN */
    135 #ifdef HAVE_PRIORITIES
    136 # ifdef G_THREADS_IMPL_POSIX
    137   {
    138     struct sched_param sched;
    139     int policy;
    140     posix_check_cmd (pthread_getschedparam (pthread_self(), &policy, &sched));
    141     priority_normal_value = sched.sched_priority;
    142   }
    143 # else /* G_THREADS_IMPL_DCE */
    144   posix_check_cmd (priority_normal_value =
    145 		   pthread_getprio (*(pthread_t*)thread,
    146 				    g_thread_priority_map [priority]));
    147 # endif
    148 #endif /* HAVE_PRIORITIES */
    149 
    150 #ifdef USE_CLOCK_GETTIME
    151  if (sysconf (_SC_MONOTONIC_CLOCK) >= 0)
    152    posix_clock = CLOCK_MONOTONIC;
    153  else
    154    posix_clock = CLOCK_REALTIME;
    155 #endif
    156 }
    157 #endif /* _SC_THREAD_STACK_MIN || HAVE_PRIORITIES */
    158 
    159 static GMutex *
    160 g_mutex_new_posix_impl (void)
    161 {
    162   GMutex *result = (GMutex *) g_new (pthread_mutex_t, 1);
    163   posix_check_cmd (pthread_mutex_init ((pthread_mutex_t *) result,
    164 				       mutexattr_default));
    165   return result;
    166 }
    167 
    168 static void
    169 g_mutex_free_posix_impl (GMutex * mutex)
    170 {
    171   posix_check_cmd (pthread_mutex_destroy ((pthread_mutex_t *) mutex));
    172   g_free (mutex);
    173 }
    174 
    175 /* NOTE: the functions g_mutex_lock and g_mutex_unlock may not use
    176    functions from gmem.c and gmessages.c; */
    177 
    178 /* pthread_mutex_lock, pthread_mutex_unlock can be taken directly, as
    179    signature and semantic are right, but without error check then!!!!,
    180    we might want to change this therefore. */
    181 
    182 static gboolean
    183 g_mutex_trylock_posix_impl (GMutex * mutex)
    184 {
    185   int result;
    186 
    187   result = pthread_mutex_trylock ((pthread_mutex_t *) mutex);
    188 
    189 #ifdef G_THREADS_IMPL_POSIX
    190   if (result == EBUSY)
    191     return FALSE;
    192 #else /* G_THREADS_IMPL_DCE */
    193   if (result == 0)
    194     return FALSE;
    195 #endif
    196 
    197   posix_check_err (posix_error (result), "pthread_mutex_trylock");
    198   return TRUE;
    199 }
    200 
    201 static GCond *
    202 g_cond_new_posix_impl (void)
    203 {
    204   GCond *result = (GCond *) g_new (pthread_cond_t, 1);
    205   posix_check_cmd (pthread_cond_init ((pthread_cond_t *) result,
    206 				      condattr_default));
    207   return result;
    208 }
    209 
    210 /* pthread_cond_signal, pthread_cond_broadcast and pthread_cond_wait
    211    can be taken directly, as signature and semantic are right, but
    212    without error check then!!!!, we might want to change this
    213    therefore. */
    214 
    215 #define G_NSEC_PER_SEC 1000000000
    216 
    217 static gboolean
    218 g_cond_timed_wait_posix_impl (GCond * cond,
    219 			      GMutex * entered_mutex,
    220 			      GTimeVal * abs_time)
    221 {
    222   int result;
    223   struct timespec end_time;
    224   gboolean timed_out;
    225 
    226   g_return_val_if_fail (cond != NULL, FALSE);
    227   g_return_val_if_fail (entered_mutex != NULL, FALSE);
    228 
    229   if (!abs_time)
    230     {
    231       result = pthread_cond_wait ((pthread_cond_t *)cond,
    232                                   (pthread_mutex_t *) entered_mutex);
    233       timed_out = FALSE;
    234     }
    235   else
    236     {
    237       end_time.tv_sec = abs_time->tv_sec;
    238       end_time.tv_nsec = abs_time->tv_usec * (G_NSEC_PER_SEC / G_USEC_PER_SEC);
    239 
    240       g_return_val_if_fail (end_time.tv_nsec < G_NSEC_PER_SEC, TRUE);
    241 
    242       result = pthread_cond_timedwait ((pthread_cond_t *) cond,
    243                                        (pthread_mutex_t *) entered_mutex,
    244                                        &end_time);
    245 #ifdef G_THREADS_IMPL_POSIX
    246       timed_out = (result == ETIMEDOUT);
    247 #else /* G_THREADS_IMPL_DCE */
    248       timed_out = (result == -1) && (errno == EAGAIN);
    249 #endif
    250     }
    251 
    252   if (!timed_out)
    253     posix_check_err (posix_error (result), "pthread_cond_timedwait");
    254 
    255   return !timed_out;
    256 }
    257 
    258 static void
    259 g_cond_free_posix_impl (GCond * cond)
    260 {
    261   posix_check_cmd (pthread_cond_destroy ((pthread_cond_t *) cond));
    262   g_free (cond);
    263 }
    264 
    265 static GPrivate *
    266 g_private_new_posix_impl (GDestroyNotify destructor)
    267 {
    268   GPrivate *result = (GPrivate *) g_new (pthread_key_t, 1);
    269   posix_check_cmd (pthread_key_create ((pthread_key_t *) result, destructor));
    270   return result;
    271 }
    272 
    273 /* NOTE: the functions g_private_get and g_private_set may not use
    274    functions from gmem.c and gmessages.c */
    275 
    276 static void
    277 g_private_set_posix_impl (GPrivate * private_key, gpointer value)
    278 {
    279   if (!private_key)
    280     return;
    281   pthread_setspecific (*(pthread_key_t *) private_key, value);
    282 }
    283 
    284 static gpointer
    285 g_private_get_posix_impl (GPrivate * private_key)
    286 {
    287   if (!private_key)
    288     return NULL;
    289 #ifdef G_THREADS_IMPL_POSIX
    290   return pthread_getspecific (*(pthread_key_t *) private_key);
    291 #else /* G_THREADS_IMPL_DCE */
    292   {
    293     void* data;
    294     posix_check_cmd (pthread_getspecific (*(pthread_key_t *) private_key,
    295 					  &data));
    296     return data;
    297   }
    298 #endif
    299 }
    300 
    301 static void
    302 g_thread_create_posix_impl (GThreadFunc thread_func,
    303 			    gpointer arg,
    304 			    gulong stack_size,
    305 			    gboolean joinable,
    306 			    gboolean bound,
    307 			    GThreadPriority priority,
    308 			    gpointer thread,
    309 			    GError **error)
    310 {
    311   pthread_attr_t attr;
    312   gint ret;
    313 
    314   g_return_if_fail (thread_func);
    315   g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW);
    316   g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT);
    317 
    318   posix_check_cmd (pthread_attr_init (&attr));
    319 
    320 #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
    321   if (stack_size)
    322     {
    323       stack_size = MAX (g_thread_min_stack_size, stack_size);
    324       /* No error check here, because some systems can't do it and
    325        * we simply don't want threads to fail because of that. */
    326       pthread_attr_setstacksize (&attr, stack_size);
    327     }
    328 #endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */
    329 
    330 #ifdef PTHREAD_SCOPE_SYSTEM
    331   if (bound)
    332     /* No error check here, because some systems can't do it and we
    333      * simply don't want threads to fail because of that. */
    334     pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
    335 #endif /* PTHREAD_SCOPE_SYSTEM */
    336 
    337 #ifdef G_THREADS_IMPL_POSIX
    338   posix_check_cmd (pthread_attr_setdetachstate (&attr,
    339           joinable ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED));
    340 #endif /* G_THREADS_IMPL_POSIX */
    341 
    342 #ifdef HAVE_PRIORITIES
    343 # ifdef G_THREADS_IMPL_POSIX
    344   {
    345     struct sched_param sched;
    346     posix_check_cmd (pthread_attr_getschedparam (&attr, &sched));
    347     sched.sched_priority = g_thread_priority_map [priority];
    348     posix_check_cmd_prio (pthread_attr_setschedparam (&attr, &sched));
    349   }
    350 # else /* G_THREADS_IMPL_DCE */
    351   posix_check_cmd_prio
    352     (pthread_attr_setprio (&attr, g_thread_priority_map [priority]));
    353 # endif /* G_THREADS_IMPL_DCE */
    354 #endif /* HAVE_PRIORITIES */
    355   ret = posix_error (pthread_create (thread, &attr,
    356 				     (void* (*)(void*))thread_func, arg));
    357 
    358   posix_check_cmd (pthread_attr_destroy (&attr));
    359 
    360   if (ret == EAGAIN)
    361     {
    362       g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN,
    363 		   "Error creating thread: %s", g_strerror (ret));
    364       return;
    365     }
    366 
    367   posix_check_err (ret, "pthread_create");
    368 
    369 #ifdef G_THREADS_IMPL_DCE
    370   if (!joinable)
    371     posix_check_cmd (pthread_detach (thread));
    372 #endif /* G_THREADS_IMPL_DCE */
    373 }
    374 
    375 static void
    376 g_thread_yield_posix_impl (void)
    377 {
    378   POSIX_YIELD_FUNC;
    379 }
    380 
    381 static void
    382 g_thread_join_posix_impl (gpointer thread)
    383 {
    384   gpointer ignore;
    385   posix_check_cmd (pthread_join (*(pthread_t*)thread, &ignore));
    386 }
    387 
    388 static void
    389 g_thread_exit_posix_impl (void)
    390 {
    391   pthread_exit (NULL);
    392 }
    393 
    394 static void
    395 g_thread_set_priority_posix_impl (gpointer thread, GThreadPriority priority)
    396 {
    397   g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW);
    398   g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT);
    399 #ifdef HAVE_PRIORITIES
    400 # ifdef G_THREADS_IMPL_POSIX
    401   {
    402     struct sched_param sched;
    403     int policy;
    404     posix_check_cmd (pthread_getschedparam (*(pthread_t*)thread, &policy,
    405 					    &sched));
    406     sched.sched_priority = g_thread_priority_map [priority];
    407     posix_check_cmd_prio (pthread_setschedparam (*(pthread_t*)thread, policy,
    408 						 &sched));
    409   }
    410 # else /* G_THREADS_IMPL_DCE */
    411   posix_check_cmd_prio (pthread_setprio (*(pthread_t*)thread,
    412 					 g_thread_priority_map [priority]));
    413 # endif
    414 #endif /* HAVE_PRIORITIES */
    415 }
    416 
    417 static void
    418 g_thread_self_posix_impl (gpointer thread)
    419 {
    420   *(pthread_t*)thread = pthread_self();
    421 }
    422 
    423 static gboolean
    424 g_thread_equal_posix_impl (gpointer thread1, gpointer thread2)
    425 {
    426   return (pthread_equal (*(pthread_t*)thread1, *(pthread_t*)thread2) != 0);
    427 }
    428 
    429 #ifdef USE_CLOCK_GETTIME
    430 static guint64
    431 gettime (void)
    432 {
    433   struct timespec tv;
    434 
    435   clock_gettime (posix_clock, &tv);
    436 
    437   return (guint64) tv.tv_sec * G_NSEC_PER_SEC + tv.tv_nsec;
    438 }
    439 static guint64 (*g_thread_gettime_impl)(void) = gettime;
    440 #else
    441 static guint64 (*g_thread_gettime_impl)(void) = 0;
    442 #endif
    443 
    444 static GThreadFunctions g_thread_functions_for_glib_use_default =
    445 {
    446   g_mutex_new_posix_impl,
    447   (void (*)(GMutex *)) pthread_mutex_lock,
    448   g_mutex_trylock_posix_impl,
    449   (void (*)(GMutex *)) pthread_mutex_unlock,
    450   g_mutex_free_posix_impl,
    451   g_cond_new_posix_impl,
    452   (void (*)(GCond *)) pthread_cond_signal,
    453   (void (*)(GCond *)) pthread_cond_broadcast,
    454   (void (*)(GCond *, GMutex *)) pthread_cond_wait,
    455   g_cond_timed_wait_posix_impl,
    456   g_cond_free_posix_impl,
    457   g_private_new_posix_impl,
    458   g_private_get_posix_impl,
    459   g_private_set_posix_impl,
    460   g_thread_create_posix_impl,
    461   g_thread_yield_posix_impl,
    462   g_thread_join_posix_impl,
    463   g_thread_exit_posix_impl,
    464   g_thread_set_priority_posix_impl,
    465   g_thread_self_posix_impl,
    466   g_thread_equal_posix_impl
    467 };
    468