From patchwork Mon Apr 7 12:37:31 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Torvald Riegel X-Patchwork-Id: 417 Return-Path: X-Original-To: siddhesh@wilcox.dreamhost.com Delivered-To: siddhesh@wilcox.dreamhost.com Received: from homiemail-mx20.g.dreamhost.com (mx2.sub5.homie.mail.dreamhost.com [208.113.200.128]) by wilcox.dreamhost.com (Postfix) with ESMTP id 335D8360060 for ; Mon, 7 Apr 2014 05:38:06 -0700 (PDT) Received: by homiemail-mx20.g.dreamhost.com (Postfix, from userid 14307373) id DBA8A40E7E287; Mon, 7 Apr 2014 05:38:05 -0700 (PDT) X-Original-To: glibc@patchwork.siddhesh.in Delivered-To: x14307373@homiemail-mx20.g.dreamhost.com Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by homiemail-mx20.g.dreamhost.com (Postfix) with ESMTPS id B886840E7E226 for ; Mon, 7 Apr 2014 05:38:05 -0700 (PDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:subject:from:to:cc:in-reply-to:references :content-type:date:message-id:mime-version; q=dns; s=default; b= dKJX1Y2PYsyUMWGU8xNKk2Fs7PcrI4HFSjLd4y4VPLpuT++lKPdAeneVHnwjMf8x O2cvlXvOKYE1OvbtcPmsHwyMbyZiabuPK0hdUMCaJQsBgWNOp0G4tnN6/9Pw5VQM pA4DcPFHw0OnH9sJ9UweuZ0sMknjPiShepa2p/CNhY8= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:subject:from:to:cc:in-reply-to:references :content-type:date:message-id:mime-version; s=default; bh=DvfIeS zeq8+rzOHvcxGWJI2Hk0k=; b=nx6fl/PeGcsl+sGg7pV6P9afN0HlJbWgnt+HMd xaYM+p0P5o7mzUEODn9n65kFi71kf2YR6k8zfW4ZN6c1xTbrEVpbg+X8FP5+g+9R qpjoXPOaogrtDyIdBvtyEYLI4l3KF4+nGcMJYX36Li85vqTn3VYIzApxg7qyMNvp IgXQ0= Received: (qmail 16552 invoked by alias); 7 Apr 2014 12:38:03 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 16543 invoked by uid 89); 7 Apr 2014 12:38:02 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.4 required=5.0 tests=AWL, BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Subject: Re: [PATCH] Unify pthread_once (bug 15215) From: Torvald Riegel To: "Carlos O'Donell" Cc: David Miller , Marcus Shawcroft , "Joseph S. Myers" , Mike Frysinger , Andreas Schwab , Steve Ellcey , Chris Metcalf , GLIBC Devel In-Reply-To: <533605BF.9000005@redhat.com> References: <1368024237.7774.794.camel@triegel.csb> <519D97E4.4030808@redhat.com> <1381018836.8757.3598.camel@triegel.csb> <1381182784.18547.138.camel@triegel.csb> <533605BF.9000005@redhat.com> Date: Mon, 07 Apr 2014 14:37:31 +0200 Message-ID: <1396874251.10643.8736.camel@triegel.csb> Mime-Version: 1.0 X-DH-Original-To: glibc@patchwork.siddhesh.in On Fri, 2014-03-28 at 19:29 -0400, Carlos O'Donell wrote: > David, Marcus, Joseph, Mike, Andreas, Steve, Chris, > > We would like to unify all C-based pthread_once implmentations > per the plan in bug 15215 for glibc 2.20. > > Your machines are on the list of C-based pthread_once implementations. > > See this for the intial discussions on the unified pthread_once: > https://sourceware.org/ml/libc-alpha/2013-05/msg00210.html > > The goal is to provide a single and correct C implementation of > pthread_once. Architectures can then build on that if they need more > optimal implementations, but I don't encourage that and I'd rather > see deep discussions on how to make one unified solution where > possible. > > I've also just reviewed Torvald's new pthread_once microbenchmark which > you can use to compare your previous C implementation with the new > standard C implementation (measures pthread_once latency). The primary > use of this test is to help provide objective proof for or against the > i386 and x86_64 assembly implementations. > > We are not presently converting any of the machines with custom > implementations, but that will be a next step after testing with the > help of the maintainers for sh, i386, x86_64, powerpc, s390 and alpha. > > If we don't hear any objections we will go forward with this change > in one week and unify ia64, hppa, mips, tile, sparc, m68k, arm > and aarch64 on a single pthread_once implementation based on sparc's C > implementation. So far, I've seen an okay for tile, and a question about ARM. Will, are you okay with the change for ARM? Any other objections to the updated patch that's attached? > > + When forking the process, some threads can be interrupted during the second > > + state; they won't be present in the forked child, so we need to restart > > + initialization in the child. To distinguish an in-progress initialization > > + from an interrupted initialization (in which case we need to reclaim the > > + lock), we look at the fork generation that's part of the second state: We > > + can reclaim iff it differs from the current fork generation. > > + XXX: This algorithm has an ABA issue on the fork generation: If an > > + initialization is interrupted, we then fork 2^30 times (30b of once_control > > What's "30b?" 30 bits? Please spell it out. > > > + are used for the fork generation), and try to initialize again, we can > > + deadlock because we can't distinguish the in-progress and interrupted cases > > + anymore. */ > > Would you mind filing a bug for this in the upstream bugzilla? https://sourceware.org/bugzilla/show_bug.cgi?id=16816 > It's a distinct bug from this unification work, but a valid problem. > > Can this be fixed by detecting generation counter overflow in fork > and failing the function call? Yes, but this would prevent us from doing more than 2^30 fork calls. That may not be a problem in practice -- but if so, then we won't hit the ABA either :) > > + do > > + { > > + /* Check if the initialization has already been done. */ > > + if (__builtin_expect ((val & 2) != 0, 1)) > > Use __glibc_likely. > > e.g. if (__glibc_likely ((val & 2) != 0)) > > This is the fast path that we are testing for in the microbenchmark? Yes. > > + return 0; > > + > > + oldval = val; > > + /* We try to set the state to in-progress and having the current > > + fork generation. We don't need atomic accesses for the fork > > + generation because it's immutable in a particular process, and > > + forked child processes start with a single thread that modified > > + the generation. */ > > + newval = __fork_generation | 1; > > OT: I wonder if Valgrind will report a benign race in accessing __fork_generation. Perhaps. I believe that eventually, lots of this and similar variables should be atomic-typed and/or accessed with relaxed-memory-order atomic loads. This would clarify that we expect concurrent accesses and that they don't constitute a data race. [BZ #15215] * nptl/sysdeps/unix/sysv/linux/sparc/pthread_once.c: Moved to ... * nptl/sysdeps/unix/sysv/linux/pthread_once.c: ... here. Add missing memory barriers. Add comments. * sysdeps/unix/sysv/linux/aarch64/nptl/pthread_once.c: Remove file. * sysdeps/unix/sysv/linux/arm/nptl/pthread_once.c: Remove file. * sysdeps/unix/sysv/linux/ia64/nptl/pthread_once.c: Remove file. * sysdeps/unix/sysv/linux/m68k/nptl/pthread_once.c: Remove file. * sysdeps/unix/sysv/linux/mips/nptl/pthread_once.c: Remove file. * sysdeps/unix/sysv/linux/tile/nptl/pthread_once.c: Remove file. Changelog.hppa: [BZ #15215] * sysdeps/unix/sysv/linux/hppa/nptl/pthread_once.c: Remove file. diff --git a/nptl/sysdeps/unix/sysv/linux/pthread_once.c b/nptl/sysdeps/unix/sysv/linux/pthread_once.c new file mode 100644 index 0000000..8453d2d --- /dev/null +++ b/nptl/sysdeps/unix/sysv/linux/pthread_once.c @@ -0,0 +1,131 @@ +/* Copyright (C) 2003-2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek , 2003. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include "pthreadP.h" +#include +#include + + +unsigned long int __fork_generation attribute_hidden; + + +static void +clear_once_control (void *arg) +{ + pthread_once_t *once_control = (pthread_once_t *) arg; + + /* Reset to the uninitialized state here. We don't need a stronger memory + order because we do not need to make any other of our writes visible to + other threads that see this value: This function will be called if we + get interrupted (see __pthread_once), so all we need to relay to other + threads is the state being reset again. */ + *once_control = 0; + lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); +} + + +/* This is similar to a lock implementation, but we distinguish between three + states: not yet initialized (0), initialization finished (2), and + initialization in progress (__fork_generation | 1). If in the first state, + threads will try to run the initialization by moving to the second state; + the first thread to do so via a CAS on once_control runs init_routine, + other threads block. + When forking the process, some threads can be interrupted during the second + state; they won't be present in the forked child, so we need to restart + initialization in the child. To distinguish an in-progress initialization + from an interrupted initialization (in which case we need to reclaim the + lock), we look at the fork generation that's part of the second state: We + can reclaim iff it differs from the current fork generation. + XXX: This algorithm has an ABA issue on the fork generation: If an + initialization is interrupted, we then fork 2^30 times (30 bits of + once_control are used for the fork generation), and try to initialize + again, we can deadlock because we can't distinguish the in-progress and + interrupted cases anymore. */ +int +__pthread_once (once_control, init_routine) + pthread_once_t *once_control; + void (*init_routine) (void); +{ + while (1) + { + int oldval, val, newval; + + /* We need acquire memory order for this load because if the value + signals that initialization has finished, we need to be see any + data modifications done during initialization. */ + val = *once_control; + atomic_read_barrier(); + do + { + /* Check if the initialization has already been done. */ + if (__glibc_likely ((val & 2) != 0)) + return 0; + + oldval = val; + /* We try to set the state to in-progress and having the current + fork generation. We don't need atomic accesses for the fork + generation because it's immutable in a particular process, and + forked child processes start with a single thread that modified + the generation. */ + newval = __fork_generation | 1; + /* We need acquire memory order here for the same reason as for the + load from once_control above. */ + val = atomic_compare_and_exchange_val_acq (once_control, newval, + oldval); + } + while (__glibc_unlikely (val != oldval)); + + /* Check if another thread already runs the initializer. */ + if ((oldval & 1) != 0) + { + /* Check whether the initializer execution was interrupted by a + fork. We know that for both values, bit 0 is set and bit 1 is + not. */ + if (oldval == newval) + { + /* Same generation, some other thread was faster. Wait. */ + lll_futex_wait (once_control, newval, LLL_PRIVATE); + continue; + } + } + + /* This thread is the first here. Do the initialization. + Register a cleanup handler so that in case the thread gets + interrupted the initialization can be restarted. */ + pthread_cleanup_push (clear_once_control, once_control); + + init_routine (); + + pthread_cleanup_pop (0); + + + /* Mark *once_control as having finished the initialization. We need + release memory order here because we need to synchronize with other + threads that want to use the initialized data. */ + atomic_write_barrier(); + *once_control = 2; + + /* Wake up all other threads. */ + lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); + break; + } + + return 0; +} +weak_alias (__pthread_once, pthread_once) +hidden_def (__pthread_once) diff --git a/nptl/sysdeps/unix/sysv/linux/sparc/pthread_once.c b/nptl/sysdeps/unix/sysv/linux/sparc/pthread_once.c deleted file mode 100644 index a231e55..0000000 --- a/nptl/sysdeps/unix/sysv/linux/sparc/pthread_once.c +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright (C) 2003-2014 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Jakub Jelinek , 2003. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - . */ - -#include "pthreadP.h" -#include - - -unsigned long int __fork_generation attribute_hidden; - - -static void -clear_once_control (void *arg) -{ - pthread_once_t *once_control = (pthread_once_t *) arg; - - *once_control = 0; - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); -} - - -int -__pthread_once (once_control, init_routine) - pthread_once_t *once_control; - void (*init_routine) (void); -{ - while (1) - { - int oldval, val, newval; - - val = *once_control; - do - { - /* Check if the initialized has already been done. */ - if ((val & 2) != 0) - return 0; - - oldval = val; - newval = (oldval & 3) | __fork_generation | 1; - val = atomic_compare_and_exchange_val_acq (once_control, newval, - oldval); - } - while (__builtin_expect (val != oldval, 0)); - - /* Check if another thread already runs the initializer. */ - if ((oldval & 1) != 0) - { - /* Check whether the initializer execution was interrupted - by a fork. */ - if (((oldval ^ newval) & -4) == 0) - { - /* Same generation, some other thread was faster. Wait. */ - lll_futex_wait (once_control, newval, LLL_PRIVATE); - continue; - } - } - - /* This thread is the first here. Do the initialization. - Register a cleanup handler so that in case the thread gets - interrupted the initialization can be restarted. */ - pthread_cleanup_push (clear_once_control, once_control); - - init_routine (); - - pthread_cleanup_pop (0); - - - /* Add one to *once_control. */ - atomic_increment (once_control); - - /* Wake up all other threads. */ - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); - break; - } - - return 0; -} -weak_alias (__pthread_once, pthread_once) -hidden_def (__pthread_once) diff --git a/ports/sysdeps/unix/sysv/linux/hppa/nptl/pthread_once.c b/ports/sysdeps/unix/sysv/linux/hppa/nptl/pthread_once.c deleted file mode 100644 index ee6b496..0000000 --- a/ports/sysdeps/unix/sysv/linux/hppa/nptl/pthread_once.c +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright (C) 2003-2014 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Jakub Jelinek , 2003. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library. If not, see - . */ - -#include "pthreadP.h" -#include - - -unsigned long int __fork_generation attribute_hidden; - - -static void -clear_once_control (void *arg) -{ - pthread_once_t *once_control = (pthread_once_t *) arg; - - *once_control = 0; - lll_private_futex_wake (once_control, INT_MAX); -} - - -int -__pthread_once (once_control, init_routine) - pthread_once_t *once_control; - void (*init_routine) (void); -{ - while (1) - { - int oldval, val, newval; - - val = *once_control; - do - { - /* Check if the initialized has already been done. */ - if ((val & 2) != 0) - return 0; - - oldval = val; - newval = (oldval & 3) | __fork_generation | 1; - val = atomic_compare_and_exchange_val_acq (once_control, newval, - oldval); - } - while (__builtin_expect (val != oldval, 0)); - - /* Check if another thread already runs the initializer. */ - if ((oldval & 1) != 0) - { - /* Check whether the initializer execution was interrupted - by a fork. */ - if (((oldval ^ newval) & -4) == 0) - { - /* Same generation, some other thread was faster. Wait. */ - lll_private_futex_wait (once_control, newval); - continue; - } - } - - /* This thread is the first here. Do the initialization. - Register a cleanup handler so that in case the thread gets - interrupted the initialization can be restarted. */ - pthread_cleanup_push (clear_once_control, once_control); - - init_routine (); - - pthread_cleanup_pop (0); - - - /* Add one to *once_control. */ - atomic_increment (once_control); - - /* Wake up all other threads. */ - lll_private_futex_wake (once_control, INT_MAX); - break; - } - - return 0; -} -weak_alias (__pthread_once, pthread_once) -hidden_def (__pthread_once) diff --git a/sysdeps/unix/sysv/linux/aarch64/nptl/pthread_once.c b/sysdeps/unix/sysv/linux/aarch64/nptl/pthread_once.c deleted file mode 100644 index d1b28ff..0000000 --- a/sysdeps/unix/sysv/linux/aarch64/nptl/pthread_once.c +++ /dev/null @@ -1,90 +0,0 @@ -/* Copyright (C) 2004-2014 Free Software Foundation, Inc. - - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - . */ - -#include "pthreadP.h" -#include - -unsigned long int __fork_generation attribute_hidden; - -static void -clear_once_control (void *arg) -{ - pthread_once_t *once_control = (pthread_once_t *) arg; - - *once_control = 0; - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); -} - -int -__pthread_once (pthread_once_t *once_control, void (*init_routine) (void)) -{ - for (;;) - { - int oldval; - int newval; - - /* Pseudo code: - newval = __fork_generation | 1; - oldval = *once_control; - if ((oldval & 2) == 0) - *once_control = newval; - Do this atomically. - */ - do - { - newval = __fork_generation | 1; - oldval = *once_control; - if (oldval & 2) - break; - } while (atomic_compare_and_exchange_val_acq (once_control, newval, oldval) != oldval); - - /* Check if the initializer has already been done. */ - if ((oldval & 2) != 0) - return 0; - - /* Check if another thread already runs the initializer. */ - if ((oldval & 1) == 0) - break; - - /* Check whether the initializer execution was interrupted by a fork. */ - if (oldval != newval) - break; - - /* Same generation, some other thread was faster. Wait. */ - lll_futex_wait (once_control, oldval, LLL_PRIVATE); - } - - /* This thread is the first here. Do the initialization. - Register a cleanup handler so that in case the thread gets - interrupted the initialization can be restarted. */ - pthread_cleanup_push (clear_once_control, once_control); - - init_routine (); - - pthread_cleanup_pop (0); - - /* Say that the initialisation is done. */ - *once_control = __fork_generation | 2; - - /* Wake up all other threads. */ - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); - - return 0; -} -weak_alias (__pthread_once, pthread_once) -hidden_def (__pthread_once) diff --git a/sysdeps/unix/sysv/linux/arm/nptl/pthread_once.c b/sysdeps/unix/sysv/linux/arm/nptl/pthread_once.c deleted file mode 100644 index a063149..0000000 --- a/sysdeps/unix/sysv/linux/arm/nptl/pthread_once.c +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright (C) 2004-2014 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library. If not, see - . */ - -#include "pthreadP.h" -#include - -unsigned long int __fork_generation attribute_hidden; - -static void -clear_once_control (void *arg) -{ - pthread_once_t *once_control = (pthread_once_t *) arg; - - *once_control = 0; - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); -} - -int -__pthread_once (pthread_once_t *once_control, void (*init_routine) (void)) -{ - for (;;) - { - int oldval; - int newval; - - /* Pseudo code: - newval = __fork_generation | 1; - oldval = *once_control; - if ((oldval & 2) == 0) - *once_control = newval; - Do this atomically. - */ - do - { - newval = __fork_generation | 1; - oldval = *once_control; - if (oldval & 2) - break; - } while (atomic_compare_and_exchange_val_acq (once_control, newval, oldval) != oldval); - - /* Check if the initializer has already been done. */ - if ((oldval & 2) != 0) - return 0; - - /* Check if another thread already runs the initializer. */ - if ((oldval & 1) == 0) - break; - - /* Check whether the initializer execution was interrupted by a fork. */ - if (oldval != newval) - break; - - /* Same generation, some other thread was faster. Wait. */ - lll_futex_wait (once_control, oldval, LLL_PRIVATE); - } - - /* This thread is the first here. Do the initialization. - Register a cleanup handler so that in case the thread gets - interrupted the initialization can be restarted. */ - pthread_cleanup_push (clear_once_control, once_control); - - init_routine (); - - pthread_cleanup_pop (0); - - /* Say that the initialisation is done. */ - *once_control = __fork_generation | 2; - - /* Wake up all other threads. */ - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); - - return 0; -} -weak_alias (__pthread_once, pthread_once) -hidden_def (__pthread_once) diff --git a/sysdeps/unix/sysv/linux/ia64/nptl/pthread_once.c b/sysdeps/unix/sysv/linux/ia64/nptl/pthread_once.c deleted file mode 100644 index a231e55..0000000 --- a/sysdeps/unix/sysv/linux/ia64/nptl/pthread_once.c +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright (C) 2003-2014 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Jakub Jelinek , 2003. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - . */ - -#include "pthreadP.h" -#include - - -unsigned long int __fork_generation attribute_hidden; - - -static void -clear_once_control (void *arg) -{ - pthread_once_t *once_control = (pthread_once_t *) arg; - - *once_control = 0; - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); -} - - -int -__pthread_once (once_control, init_routine) - pthread_once_t *once_control; - void (*init_routine) (void); -{ - while (1) - { - int oldval, val, newval; - - val = *once_control; - do - { - /* Check if the initialized has already been done. */ - if ((val & 2) != 0) - return 0; - - oldval = val; - newval = (oldval & 3) | __fork_generation | 1; - val = atomic_compare_and_exchange_val_acq (once_control, newval, - oldval); - } - while (__builtin_expect (val != oldval, 0)); - - /* Check if another thread already runs the initializer. */ - if ((oldval & 1) != 0) - { - /* Check whether the initializer execution was interrupted - by a fork. */ - if (((oldval ^ newval) & -4) == 0) - { - /* Same generation, some other thread was faster. Wait. */ - lll_futex_wait (once_control, newval, LLL_PRIVATE); - continue; - } - } - - /* This thread is the first here. Do the initialization. - Register a cleanup handler so that in case the thread gets - interrupted the initialization can be restarted. */ - pthread_cleanup_push (clear_once_control, once_control); - - init_routine (); - - pthread_cleanup_pop (0); - - - /* Add one to *once_control. */ - atomic_increment (once_control); - - /* Wake up all other threads. */ - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); - break; - } - - return 0; -} -weak_alias (__pthread_once, pthread_once) -hidden_def (__pthread_once) diff --git a/sysdeps/unix/sysv/linux/m68k/nptl/pthread_once.c b/sysdeps/unix/sysv/linux/m68k/nptl/pthread_once.c deleted file mode 100644 index 01542e9..0000000 --- a/sysdeps/unix/sysv/linux/m68k/nptl/pthread_once.c +++ /dev/null @@ -1,90 +0,0 @@ -/* Copyright (C) 2010-2014 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Maxim Kuvyrkov , 2010. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library. If not, see - . */ - -#include "pthreadP.h" -#include - -unsigned long int __fork_generation attribute_hidden; - -static void -clear_once_control (void *arg) -{ - pthread_once_t *once_control = (pthread_once_t *) arg; - - *once_control = 0; - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); -} - -int -__pthread_once (pthread_once_t *once_control, void (*init_routine) (void)) -{ - for (;;) - { - int oldval; - int newval; - - /* Pseudo code: - newval = __fork_generation | 1; - oldval = *once_control; - if ((oldval & 2) == 0) - *once_control = newval; - Do this atomically. - */ - do - { - newval = __fork_generation | 1; - oldval = *once_control; - if (oldval & 2) - break; - } while (atomic_compare_and_exchange_val_acq (once_control, newval, oldval) != oldval); - - /* Check if the initializer has already been done. */ - if ((oldval & 2) != 0) - return 0; - - /* Check if another thread already runs the initializer. */ - if ((oldval & 1) == 0) - break; - - /* Check whether the initializer execution was interrupted by a fork. */ - if (oldval != newval) - break; - - /* Same generation, some other thread was faster. Wait. */ - lll_futex_wait (once_control, oldval, LLL_PRIVATE); - } - - /* This thread is the first here. Do the initialization. - Register a cleanup handler so that in case the thread gets - interrupted the initialization can be restarted. */ - pthread_cleanup_push (clear_once_control, once_control); - - init_routine (); - - pthread_cleanup_pop (0); - - /* Say that the initialisation is done. */ - *once_control = __fork_generation | 2; - - /* Wake up all other threads. */ - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); - - return 0; -} -weak_alias (__pthread_once, pthread_once) -hidden_def (__pthread_once) diff --git a/sysdeps/unix/sysv/linux/mips/nptl/pthread_once.c b/sysdeps/unix/sysv/linux/mips/nptl/pthread_once.c deleted file mode 100644 index 3e3430d..0000000 --- a/sysdeps/unix/sysv/linux/mips/nptl/pthread_once.c +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright (C) 2003-2014 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Jakub Jelinek , 2003. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library. If not, see - . */ - -#include "pthreadP.h" -#include - - -unsigned long int __fork_generation attribute_hidden; - - -static void -clear_once_control (void *arg) -{ - pthread_once_t *once_control = (pthread_once_t *) arg; - - *once_control = 0; - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); -} - - -int -__pthread_once (once_control, init_routine) - pthread_once_t *once_control; - void (*init_routine) (void); -{ - while (1) - { - int oldval, val, newval; - - val = *once_control; - do - { - /* Check if the initialized has already been done. */ - if ((val & 2) != 0) - return 0; - - oldval = val; - newval = (oldval & 3) | __fork_generation | 1; - val = atomic_compare_and_exchange_val_acq (once_control, newval, - oldval); - } - while (__builtin_expect (val != oldval, 0)); - - /* Check if another thread already runs the initializer. */ - if ((oldval & 1) != 0) - { - /* Check whether the initializer execution was interrupted - by a fork. */ - if (((oldval ^ newval) & -4) == 0) - { - /* Same generation, some other thread was faster. Wait. */ - lll_futex_wait (once_control, newval, LLL_PRIVATE); - continue; - } - } - - /* This thread is the first here. Do the initialization. - Register a cleanup handler so that in case the thread gets - interrupted the initialization can be restarted. */ - pthread_cleanup_push (clear_once_control, once_control); - - init_routine (); - - pthread_cleanup_pop (0); - - - /* Add one to *once_control. */ - atomic_increment (once_control); - - /* Wake up all other threads. */ - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); - break; - } - - return 0; -} -weak_alias (__pthread_once, pthread_once) -hidden_def (__pthread_once) diff --git a/sysdeps/unix/sysv/linux/tile/nptl/pthread_once.c b/sysdeps/unix/sysv/linux/tile/nptl/pthread_once.c deleted file mode 100644 index 1b38999..0000000 --- a/sysdeps/unix/sysv/linux/tile/nptl/pthread_once.c +++ /dev/null @@ -1,94 +0,0 @@ -/* Copyright (C) 2011-2014 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Chris Metcalf , 2011. - Based on work contributed by Jakub Jelinek , 2003. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library. If not, see - . */ - -#include -#include - - -unsigned long int __fork_generation attribute_hidden; - - -static void -clear_once_control (void *arg) -{ - pthread_once_t *once_control = (pthread_once_t *) arg; - - *once_control = 0; - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); -} - - -int -__pthread_once (once_control, init_routine) - pthread_once_t *once_control; - void (*init_routine) (void); -{ - while (1) - { - int oldval, val, newval; - - val = *once_control; - do - { - /* Check if the initialized has already been done. */ - if ((val & 2) != 0) - return 0; - - oldval = val; - newval = (oldval & 3) | __fork_generation | 1; - val = atomic_compare_and_exchange_val_acq (once_control, newval, - oldval); - } - while (__builtin_expect (val != oldval, 0)); - - /* Check if another thread already runs the initializer. */ - if ((oldval & 1) != 0) - { - /* Check whether the initializer execution was interrupted - by a fork. */ - if (((oldval ^ newval) & -4) == 0) - { - /* Same generation, some other thread was faster. Wait. */ - lll_futex_wait (once_control, newval, LLL_PRIVATE); - continue; - } - } - - /* This thread is the first here. Do the initialization. - Register a cleanup handler so that in case the thread gets - interrupted the initialization can be restarted. */ - pthread_cleanup_push (clear_once_control, once_control); - - init_routine (); - - pthread_cleanup_pop (0); - - - /* Add one to *once_control. */ - atomic_increment (once_control); - - /* Wake up all other threads. */ - lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE); - break; - } - - return 0; -} -weak_alias (__pthread_once, pthread_once) -hidden_def (__pthread_once)