Back to index

glibc  2.9
pthread_once.c
Go to the documentation of this file.
00001 /* Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include "pthreadP.h"
00021 #include <lowlevellock.h>
00022 
00023 
00024 unsigned long int __fork_generation attribute_hidden;
00025 
00026 
00027 static void
00028 clear_once_control (void *arg)
00029 {
00030   pthread_once_t *once_control = (pthread_once_t *) arg;
00031 
00032   *once_control = 0;
00033   lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE);
00034 }
00035 
00036 
00037 int
00038 __pthread_once (once_control, init_routine)
00039      pthread_once_t *once_control;
00040      void (*init_routine) (void);
00041 {
00042   while (1)
00043     {
00044       int oldval, val, newval;
00045 
00046       val = *once_control;
00047       do
00048        {
00049          /* Check if the initialized has already been done.  */
00050          if ((val & 2) != 0)
00051            return 0;
00052 
00053          oldval = val;
00054          newval = (oldval & 3) | __fork_generation | 1;
00055          val = atomic_compare_and_exchange_val_acq (once_control, newval,
00056                                                oldval);
00057        }
00058       while (__builtin_expect (val != oldval, 0));
00059 
00060       /* Check if another thread already runs the initializer. */
00061       if ((oldval & 1) != 0)
00062        {
00063          /* Check whether the initializer execution was interrupted
00064             by a fork.       */
00065          if (((oldval ^ newval) & -4) == 0)
00066            {
00067              /* Same generation, some other thread was faster. Wait.  */
00068              lll_futex_wait (once_control, newval, LLL_PRIVATE);
00069              continue;
00070            }
00071        }
00072 
00073       /* This thread is the first here.  Do the initialization.
00074         Register a cleanup handler so that in case the thread gets
00075         interrupted the initialization can be restarted.  */
00076       pthread_cleanup_push (clear_once_control, once_control);
00077 
00078       init_routine ();
00079 
00080       pthread_cleanup_pop (0);
00081 
00082 
00083       /* Add one to *once_control.  */
00084       atomic_increment (once_control);
00085 
00086       /* Wake up all other threads.  */
00087       lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE);
00088       break;
00089     }
00090 
00091   return 0;
00092 }
00093 weak_alias (__pthread_once, pthread_once)
00094 strong_alias (__pthread_once, __pthread_once_internal)