Back to index

glibc  2.9
pthread_once.c
Go to the documentation of this file.
00001 /* Copyright (C) 2003, 2007 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Martin Schwidefsky <schwidefsky@de.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 (once_control, init_routine)
00039      pthread_once_t *once_control;
00040      void (*init_routine) (void);
00041 {
00042   while (1)
00043     {
00044       int oldval;
00045       int newval;
00046 
00047       /* Pseudo code:
00048           oldval = *once_control;
00049           if ((oldval & 2) == 0)
00050            {
00051              newval = (oldval & 3) | __fork_generation | 1;
00052              *once_control = newval;
00053            }
00054         Do this atomically.  */
00055       __asm __volatile ("   l       %1,%0\n"
00056                      "0: lhi        %2,2\n"
00057                      "   tml        %1,2\n"
00058                      "   jnz        1f\n"
00059                      "   nr  %2,%1\n"
00060                      "   ahi        %2,1\n"
00061                      "   o   %2,%3\n"
00062                      "   cs  %1,%2,%0\n"
00063                      "   jl  0b\n"
00064                      "1:"
00065                      : "=Q" (*once_control), "=&d" (oldval), "=&d" (newval)
00066                      : "m" (__fork_generation), "m" (*once_control)
00067                      : "cc" );
00068       /* Check if the initialized has already been done.  */
00069       if ((oldval & 2) != 0)
00070          break;
00071       /* Check if another thread already runs the initializer. */
00072       if ((oldval & 1) != 0)
00073        {
00074          /* Check whether the initializer execution was interrupted
00075             by a fork.       */
00076          if (((oldval ^ newval) & -4) == 0)
00077            {
00078              /* Same generation, some other thread was faster. Wait.  */
00079              lll_futex_wait (once_control, newval, LLL_PRIVATE);
00080              continue;
00081            }
00082        }
00083 
00084       /* This thread is the first here.  Do the initialization.
00085         Register a cleanup handler so that in case the thread gets
00086         interrupted the initialization can be restarted.  */
00087       pthread_cleanup_push (clear_once_control, once_control);
00088 
00089       init_routine ();
00090 
00091       pthread_cleanup_pop (0);
00092 
00093 
00094       /* Add one to *once_control.  */
00095       __asm __volatile ("   l       %1,%0\n"
00096                      "0: lr  %2,%1\n"
00097                      "   ahi        %2,1\n"
00098                      "   cs  %1,%2,%0\n"
00099                      "   jl  0b\n"
00100                      : "=Q" (*once_control), "=&d" (oldval), "=&d" (newval)
00101                      : "m" (*once_control) : "cc" );
00102 
00103       /* Wake up all other threads.  */
00104       lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE);
00105       break;
00106     }
00107 
00108   return 0;
00109 }
00110 weak_alias (__pthread_once, pthread_once)
00111 strong_alias (__pthread_once, __pthread_once_internal)