Back to index

glibc  2.9
pthread_once.c
Go to the documentation of this file.
00001 /* Copyright (C) 2003, 2004, 2007 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Paul Mackerras <paulus@au.ibm.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 (pthread_once_t *once_control, void (*init_routine) (void))
00039 {
00040   for (;;)
00041     {
00042       int oldval;
00043       int newval;
00044       int tmp;
00045 
00046       /* Pseudo code:
00047         newval = __fork_generation | 1;
00048         oldval = *once_control;
00049         if ((oldval & 2) == 0)
00050           *once_control = newval;
00051         Do this atomically.
00052       */
00053       newval = __fork_generation | 1;
00054       __asm __volatile ("1: lwarx  %0,0,%3\n"
00055                      "      andi.  %1,%0,2\n"
00056                      "      bne    2f\n"
00057                      "      stwcx. %4,0,%3\n"
00058                      "      bne    1b\n"
00059                      "2:    isync"
00060                      : "=&r" (oldval), "=&r" (tmp), "=m" (*once_control)
00061                      : "r" (once_control), "r" (newval), "m" (*once_control)
00062                      : "cr0");
00063 
00064       /* Check if the initializer has already been done.  */
00065       if ((oldval & 2) != 0)
00066        return 0;
00067 
00068       /* Check if another thread already runs the initializer. */
00069       if ((oldval & 1) == 0)
00070        break;
00071 
00072       /* Check whether the initializer execution was interrupted by a fork.  */
00073       if (oldval != newval)
00074        break;
00075 
00076       /* Same generation, some other thread was faster. Wait.  */
00077       lll_futex_wait (once_control, oldval, LLL_PRIVATE);
00078     }
00079 
00080 
00081   /* This thread is the first here.  Do the initialization.
00082      Register a cleanup handler so that in case the thread gets
00083      interrupted the initialization can be restarted.  */
00084   pthread_cleanup_push (clear_once_control, once_control);
00085 
00086   init_routine ();
00087 
00088   pthread_cleanup_pop (0);
00089 
00090 
00091   /* Add one to *once_control to take the bottom 2 bits from 01 to 10.  */
00092   atomic_increment (once_control);
00093 
00094   /* Wake up all other threads.  */
00095   lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE);
00096 
00097   return 0;
00098 }
00099 weak_alias (__pthread_once, pthread_once)
00100 strong_alias (__pthread_once, __pthread_once_internal)