Bug 13684 - local static object variable constructed once but ctors and dtors called multiple times on same memory when called in multiple threads
Summary: local static object variable constructed once but ctors and dtors called mult...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 3.3.3
: P3 enhancement
Target Milestone: 4.0.0
Assignee: Jason Merrill
URL:
Keywords:
: 13487 (view as bug list)
Depends on:
Blocks:
 
Reported: 2004-01-14 17:25 UTC by vijay kumar
Modified: 2014-02-16 13:17 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2004-08-21 00:01:19


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description vijay kumar 2004-01-14 17:25:09 UTC
I have a simple sample code which illustrates the issue. Basically, if multiple 
threads invoke a function which has local static object, the ctor and dtor are 
called multiple times on the same object in non reentrant manner until object 
is fully constructed. Here is a sample program which illustrates the issue




#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

volatile int i;
class A {
    public:
    int i;
    A(int i)
    {
        this->i = i;
        sleep(1);
        printf("I am in A ctor %d this:%p\n", i, this);
    }
    ~A() {
        printf("A dtor %d this:%p\n", i, this);
    }
};
extern "C" void *foo(void *)
{
    printf("before static ctor in foo\n");
    static A a(i++);
    printf("in foo %p a(i) %d\n", &a, a.i);
    return NULL;
}
main()
{
    printf("I am in main\n");
    pthread_t tid;
    pthread_create(&tid,NULL, foo, NULL);
    pthread_create(&tid,NULL, foo, NULL);
    pthread_create(&tid,NULL, foo, NULL);
    pthread_create(&tid,NULL, foo, NULL);
    pthread_create(&tid,NULL, foo, NULL);
    pthread_create(&tid,NULL, foo, NULL);
    pthread_create(&tid,NULL, foo, NULL);
    pthread_create(&tid,NULL, foo, NULL);
    pthread_create(&tid,NULL, foo, NULL);
    pthread_create(&tid,NULL, foo, NULL);
    pthread_create(&tid,NULL, foo, NULL);
    sleep(3);
    foo(NULL);
}


The output of this program is 

I am in main
before static ctor in foo
before static ctor in foo
before static ctor in foo
before static ctor in foo
before static ctor in foo
before static ctor in foo
before static ctor in foo
before static ctor in foo
before static ctor in foo
before static ctor in foo
before static ctor in foo
I am in A ctor 1 this:210b0
I am in A ctor 3 this:210b0
in foo 210b0 a(i) 10
in foo 210b0 a(i) 10
I am in A ctor 4 this:210b0
in foo 210b0 a(i) 10
I am in A ctor 6 this:210b0
in foo 210b0 a(i) 10
I am in A ctor 8 this:210b0
in foo 210b0 a(i) 10
I am in A ctor 9 this:210b0
in foo 210b0 a(i) 10
I am in A ctor 2 this:210b0
in foo 210b0 a(i) 10
I am in A ctor 5 this:210b0
in foo 210b0 a(i) 10
I am in A ctor 0 this:210b0
in foo 210b0 a(i) 10
I am in A ctor 10 this:210b0
in foo 210b0 a(i) 10
I am in A ctor 7 this:210b0
in foo 210b0 a(i) 10
before static ctor in foo
in foo 210b0 a(i) 10
A dtor 10 this:210b0
A dtor 10 this:210b0
A dtor 10 this:210b0
A dtor 10 this:210b0
A dtor 10 this:210b0
A dtor 10 this:210b0
A dtor 10 this:210b0
A dtor 10 this:210b0
A dtor 10 this:210b0
A dtor 10 this:210b0
A dtor 10 this:210b0
Comment 1 Andrew Pinski 2004-01-14 17:30:03 UTC
Basically allocation of static variables in functions are not done thread safe, I do not think there is 
any way that GCC will be able to do this efficiently, I would not use local static variables if you are 
going to use threads.
Comment 2 vijay kumar 2004-01-14 20:55:52 UTC
dont we follow c++ abi specification ?

http://www.armdevzone.com/EABI/GVariables.txt

Comment 3 Andrew Pinski 2004-07-15 01:07:31 UTC
*** Bug 13487 has been marked as a duplicate of this bug. ***
Comment 4 gianni 2004-07-15 02:16:36 UTC
Andrew wrote:
> Basically allocation of static variables in functions are not done thread 
> safe, I do not think there is any way that GCC will be able to do this 
> efficiently, I would not use local static variables if you are 
> going to use threads.

Actually, it can be quite efficient on some platforms.  Also, since this is a
one-time thing, efficiency is not that big a deal.

This posting from Zack Weinberg indicates pretty much how it could be done in a
way that more-or-less works as efficiently as it does without thread safety.
http://gcc.gnu.org/ml/gcc/2002-08/msg00284.html
Comment 5 Wolfgang Bangerth 2004-07-15 02:42:53 UTC
It's not quite a one-time thing, of course: every time you run over 
a static variable, you have to check whether it has already been  
initialized. If this check has to be guarded by a lock, it gets vastly 
more expensive than just checking for zero-or-one. 
 
Nevertheless, I believe that this should eventually be implemented. 
 
W. 
Comment 6 gianni 2004-07-15 05:11:14 UTC
> It's not quite a one-time thing, of course: every time you run over 
> a static variable, you have to check whether it has already been  
> initialized.

I suspect that this is done with the current gcc.

> If this check has to be guarded by a lock, it gets vastly 
> more expensive than just checking for zero-or-one. 

I don't understand why you think so.

Zack's suggestion (and this concept is also used in lots of other code) is this:

  if (guard) {
    if (__cxa_guard_acquire (&guard)) {
      // construct variable.
      __cxa_guard_release (&guard)
    }
  }

The first conditional is not thread safe, however, once the object is correctly
initialized the first conditional will be false and hence it will render code
that is practically the same speed as non thread-safe code (code size will be
larger, obviously!).


 
Comment 7 merkert 2004-07-15 11:07:28 UTC
The cost of static local variables that are objects is actually quite expensive
in general because it requires testing whether or not the object has been
constructed. This is has nothing to do with thread safety. Using the
double-checked locking as is indicated allows for thread-safe initialization
that is no more costly than normal initialization with the exception of having
to acquire a mutex. 

The code as indicated is not quite correct though, because initialization
requires a global mutex to avoid a deadlock.

So initialization needs to be done like this:

  static volatile bool guard=true;
  if (guard) {
    if (__cxa_guard_acquire (&global_guard)) {
      if (guard) {
        // construct variable.
        guard = false;
      }
      __cxa_guard_release (&global_guard)
    }
  }

Also, the guard boolean needs to be volatile.
Comment 8 gianni 2004-07-15 13:54:28 UTC
Zack's code may have been missing the details but the code below is still
perfectly valid.  

  if (guard) {
    if (__cxa_guard_acquire (&guard)) {
      // construct variable.
      __cxa_guard_release (&guard)
    }
  }

If you take these definitions for aquire/release functions.

  bool __cxa_guard_acquire ( volatile bool * pguard )
  {
     __lock_reentrant_mutex( & global_mutex );
     if ( ! * pguard )
     { 
        __unlock_reentrant_mutex( &global_mutex );
        return false;
     }
     return true;
  }

  void __cxa_guard_release (bool * guard)
  {
      * guard = false;
      __unlock_reentrant_mutex( &global_mutex );
  }


I'd imagine that the sense of the guard is more like "is initialized" so the
code below is more like somthing that could be plugged in.

 if (! is_initialized) {
    if (__cxa_guard_acquire(is_initialized)) {
      // construct variable.
      __cxa_guard_release(is_initialized)
    }
  }

And these would be the corresponding aquire/release functions.

  bool __cxa_guard_acquire ( volatile bool & is_initialized )
  {
     __lock_reentrant_mutex( & global_mutex );
     if ( ! is_initialized )
     { 
        __unlock_reentrant_mutex( &global_mutex );
        return true;
     }
     return false;
  }

  void __cxa_guard_release( volatile bool & is_initialized )
  {
      is_initialized = true;
      __unlock_reentrant_mutex( &global_mutex );
  }
Comment 9 GCC Commits 2004-08-28 02:34:01 UTC
Subject: Bug 13684

CVSROOT:	/cvs/gcc
Module name:	gcc
Changes by:	jason@gcc.gnu.org	2004-08-28 02:33:54

Modified files:
	gcc            : ChangeLog c-common.c c-common.h c-opts.c c.opt 
	                 gimplify.c gthr-dce.h gthr.h gthr-posix.h 
	                 gthr-single.h gthr-solaris.h gthr-vxworks.h 
	                 gthr-win32.h tsystem.h 
	gcc/config/i386: gthr-win32.c 
	gcc/cp         : ChangeLog cp-tree.h decl.c decl2.c tree.c 
	gcc/doc        : invoke.texi 
	libstdc++-v3   : ChangeLog 
	libstdc++-v3/libsupc++: guard.cc 

Log message:
	PR c++/13684
	* cp/decl.c (expand_static_init): Use thread-safety API.
	(register_dtor_fn): Return the call, don't expand it.
	* cp/tree.c (add_stmt_to_compound): New fn.
	(stabilize_call): Use it.
	* gimplify.c (gimplify_cleanup_point_expr): Handle CLEANUP_EH_ONLY.
	(gimple_push_cleanup): Add eh_only parm.
	(gimplify_target_expr): Pass it.
	* c.opt (-fno-threadsafe-statics): New option.
	* c-opts.c (c_common_handle_option): Handle it.
	* c-common.h (flag_threadsafe_statics): Declare it.
	* c-common.c (flag_threadsafe_statics): Record it.
	* doc/invoke.texi: Document it.
	* tsystem.h (_GNU_SOURCE): Define.
	* gthr-posix.h (__gthread_recursive_mutex_t): New typedef.
	(__GTHREAD_RECURSIVE_MUTEX_INIT): New macro.
	(__GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION): New macro.
	(__gthread_recursive_mutex_init_function): New fn.
	(__gthread_recursive_mutex_lock): New fn.
	(__gthread_recursive_mutex_trylock): New fn.
	(__gthread_recursive_mutex_unlock): New fn.
	* gthr-solaris.h, gthr-single.h, gthr-dce.h: Likewise.
	* gthr-win32.h, gthr-vxworks.h: Likewise.
	* gthr.h: Document.
	
	* libsupc++/guard.cc (static_mutex): Internal class implementing a
	recursive mutex which controls initialization of local statics.
	(__gnu_cxx::recursive_init): New exception class.
	(__cxa_guard_acquire): Deal with locking and recursion detection.
	(acquire_1, __cxa_guard_abort, __cxa_guard_release): Likewise.

Patches:
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/ChangeLog.diff?cvsroot=gcc&r1=2.5120&r2=2.5121
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/c-common.c.diff?cvsroot=gcc&r1=1.561&r2=1.562
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/c-common.h.diff?cvsroot=gcc&r1=1.258&r2=1.259
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/c-opts.c.diff?cvsroot=gcc&r1=1.124&r2=1.125
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/c.opt.diff?cvsroot=gcc&r1=1.32&r2=1.33
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gimplify.c.diff?cvsroot=gcc&r1=2.67&r2=2.68
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr-dce.h.diff?cvsroot=gcc&r1=1.20&r2=1.21
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr.h.diff?cvsroot=gcc&r1=1.15&r2=1.16
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr-posix.h.diff?cvsroot=gcc&r1=1.28&r2=1.29
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr-single.h.diff?cvsroot=gcc&r1=1.12&r2=1.13
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr-solaris.h.diff?cvsroot=gcc&r1=1.15&r2=1.16
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr-vxworks.h.diff?cvsroot=gcc&r1=1.12&r2=1.13
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr-win32.h.diff?cvsroot=gcc&r1=1.23&r2=1.24
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/tsystem.h.diff?cvsroot=gcc&r1=1.11&r2=1.12
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/config/i386/gthr-win32.c.diff?cvsroot=gcc&r1=1.7&r2=1.8
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/ChangeLog.diff?cvsroot=gcc&r1=1.4313&r2=1.4314
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/cp-tree.h.diff?cvsroot=gcc&r1=1.1035&r2=1.1036
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/decl.c.diff?cvsroot=gcc&r1=1.1281&r2=1.1282
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/decl2.c.diff?cvsroot=gcc&r1=1.737&r2=1.738
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/tree.c.diff?cvsroot=gcc&r1=1.406&r2=1.407
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/doc/invoke.texi.diff?cvsroot=gcc&r1=1.516&r2=1.517
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/ChangeLog.diff?cvsroot=gcc&r1=1.2644&r2=1.2645
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/libsupc++/guard.cc.diff?cvsroot=gcc&r1=1.4&r2=1.5

Comment 10 Andrew Pinski 2004-08-28 10:18:49 UTC
Fixed.
Comment 11 gianni 2004-08-28 19:04:12 UTC
(In reply to comment #10)
> Fixed.

Very cool. Much appreciated.
Comment 12 Wu Yongwei 2004-08-31 06:41:13 UTC
It looks to me that the solution applies the Double-Checked Locking Pattern, 
and could be unsafe on some multi-processor platforms, most notably Alpha and 
IA-64.
Comment 13 GCC Commits 2004-09-01 16:58:15 UTC
Subject: Bug 13684

CVSROOT:	/cvs/gcc
Module name:	gcc
Branch: 	gcc-3_4-rhl-branch
Changes by:	jakub@gcc.gnu.org	2004-09-01 16:58:05

Modified files:
	gcc            : ChangeLog c-common.c c-common.h c-opts.c c.opt 
	                 gthr-dce.h gthr-posix.h gthr-single.h 
	                 gthr-solaris.h gthr-vxworks.h gthr.h tsystem.h 
	gcc/cp         : ChangeLog cp-tree.h decl.c decl2.c tree.c 
	gcc/doc        : invoke.texi 
	libstdc++-v3   : ChangeLog 
	libstdc++-v3/libsupc++: guard.cc 

Log message:
	2004-08-27  Jason Merrill  <jason@redhat.com>
	
	PR c++/13684
	* cp/decl.c (expand_static_init): Use thread-safety API.
	(register_dtor_fn): Return the call, don't expand it.
	* cp/tree.c (add_stmt_to_compound): New fn.
	(stabilize_call): Use it.
	* c.opt (-fno-threadsafe-statics): New option.
	* c-opts.c (c_common_handle_option): Handle it.
	* c-common.h (flag_threadsafe_statics): Declare it.
	* c-common.c (flag_threadsafe_statics): Record it.
	* doc/invoke.texi: Document it.
	* tsystem.h (_GNU_SOURCE): Define.
	* gthr-posix.h (__gthread_recursive_mutex_t): New typedef.
	(__GTHREAD_RECURSIVE_MUTEX_INIT): New macro.
	(__GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION): New macro.
	(__gthread_recursive_mutex_init_function): New fn.
	(__gthread_recursive_mutex_lock): New fn.
	(__gthread_recursive_mutex_trylock): New fn.
	(__gthread_recursive_mutex_unlock): New fn.
	* gthr-solaris.h, gthr-single.h, gthr-dce.h: Likewise.
	* gthr-vxworks.h: Likewise.
	* gthr.h: Document.
	
	* libsupc++/guard.cc (static_mutex): Internal class implementing a
	recursive mutex which controls initialization of local statics.
	(__gnu_cxx::recursive_init): New exception class.
	(__cxa_guard_acquire): Deal with locking and recursion detection.
	(acquire_1, __cxa_guard_abort, __cxa_guard_release): Likewise.

Patches:
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/ChangeLog.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=2.2326.2.399.2.36&r2=2.2326.2.399.2.37
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/c-common.c.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.476.4.6.2.3&r2=1.476.4.6.2.4
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/c-common.h.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.216.2.4&r2=1.216.2.4.2.1
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/c-opts.c.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.96.4.7.2.1&r2=1.96.4.7.2.2
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/c.opt.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.17.12.2.2.1&r2=1.17.12.2.2.2
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr-dce.h.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.20&r2=1.20.28.1
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr-posix.h.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.27&r2=1.27.18.1
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr-single.h.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.12&r2=1.12.38.1
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr-solaris.h.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.15&r2=1.15.28.1
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr-vxworks.h.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.12&r2=1.12.28.1
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/gthr.h.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.13&r2=1.13.28.1
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/tsystem.h.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.11&r2=1.11.14.1
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/ChangeLog.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.3892.2.99.2.10&r2=1.3892.2.99.2.11
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/cp-tree.h.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.946.4.11.2.5&r2=1.946.4.11.2.6
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/decl.c.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.1174.2.19.2.6&r2=1.1174.2.19.2.7
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/decl2.c.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.695.4.7.2.3&r2=1.695.4.7.2.4
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/tree.c.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.360.4.5.2.3&r2=1.360.4.5.2.4
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/doc/invoke.texi.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.390.2.24.2.6&r2=1.390.2.24.2.7
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/ChangeLog.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.2224.2.77.2.8&r2=1.2224.2.77.2.9
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libstdc++-v3/libsupc++/guard.cc.diff?cvsroot=gcc&only_with_tag=gcc-3_4-rhl-branch&r1=1.1&r2=1.1.56.1

Comment 14 Jason Merrill 2004-09-04 18:06:30 UTC
Subject: Re:  local static object variable constructed once
 but ctors and dtors called multiple times on same memory when called in
 multiple threads

On 31 Aug 2004 06:41:13 -0000, "adah at netstd dot com" <gcc-bugzilla@gcc.gnu.org> wrote:

> It looks to me that the solution applies the Double-Checked Locking Pattern, 
> and could be unsafe on some multi-processor platforms, most notably Alpha and 
> IA-64.

Hmm, indeed.  As I understand it from some googling, particularly this thread:

  http://www.google.com/groups?threadm=6kuldj$4sk%40bmtlh10.bnr.ca

the problem is that modern architectures such as the ones you mention can
reorder stores and loads so that another processor could see the guard
variable set, but the controlled variable not yet fully initialized.  The
cheapest workaround is to add memory barriers in the initialization path if
the architecture reorders stores (SPARC also does this) and in the
already-initialized path if it reorders loads.

Unfortunately, pthreads (for instance) don't have a portable way of forcing
a memory barrier other than with a mutex.

I suppose the simplest fix is just to remove the check in the
compiler-generated code, but having all calls to functions with initialized
static locals go through the same mutex seems rather unfortunate.  Hmm.

Jason
Comment 15 Wu Yongwei 2004-09-05 03:19:29 UTC
I myself have had headaches about this problem (unsafe DLCP) too.  Indeed, I do 
not have problems with DLCP, since I have access only to IA-32 platforms, but I 
really like to make my code more portable.

Currently there is no known way to make memory barriers portable.  So I guess 
it might be a good idea that GCC provide built-in functions to do the job.  On 
IA-32 platforms it is a just a null operation, but on other systems it might 
really do significant stuff.
Comment 16 davids 2005-02-02 23:22:22 UTC
This is not a GCC bug and should not be fixed in GCC. The bug is in the test
code which accesses an object that is shared by multiple threads without proper
mutexes. Period. End of story.

The correct fix is to acquire a mutex before calling a function that might
accessed shared data (and constructing a static object counts). This should be
common sense, and it's surprising to me that it is not. Anyone can create code
that fails if you call functions that manipulate shared data without holding any
mutexes.

DS
Comment 17 gianni 2005-02-03 01:56:13 UTC
> This is not a GCC bug and should not be fixed in GCC. The bug is in the test
> code which accesses an object that is shared by multiple threads without proper
> mutexes. Period. End of story.

This approach makes it very difficult to use library code that would otherwise
be thread safe.  Since there is no standard C++ API for threading, resolving
this issue would otherwise cause a significant dependantcy on external
libraries, not to mention the potential bugs that arise from ad-hoc fixes. (like
deadlocks).

The only way to truly resolve this is to ensure proper functioning code is to
have the compiler generate code that adheres to the letter of the C++ standard,
even in a multi-threaded environment.

Why do you object to havingthe compiler generate MT safe code ?
Comment 18 Wu Yongwei 2005-02-03 03:30:56 UTC
I am not David but let me try to name some possible objections.

* The current code is unsafe on some architectures (DLCP is unsafe)
* For cross-compiler code, users SHOULD have already locked a mutex before 
entering the function that might have race conditions: so there will be 
unnecessary overhead
* If users do not use a mutex, if might well be the case that the specific 
function will be called by only one thread; or that the function has been 
called at least once in the initialization (one thread only) code; and 
the "fix" creates unnecessary overhead again

Granted, there is no standard C++ API for threading. But when users write a 
multi-threaded program, they have already begun to use non-standard threading 
APIs. They might well use the APIs to protect what this fix tries to do.
Comment 19 Wu Yongwei 2005-02-03 03:42:46 UTC
I want to emphasize here again one principle of C and C++: Trust the 
programmers, and allow them to do low-level tunings for performance. Or what is 
the purpose of C++ (when compared with "high-level" languages like Python)? 
This "fix" rid the programmers of their right to choose the way they want.

Unless the future C++ standard demands protection in such cases, I do not think 
the compiler-provided mechanism a good idea.
Comment 20 Dhruv Matani 2005-04-13 16:56:11 UTC
(In reply to comment #19)
> I want to emphasize here again one principle of C and C++: Trust the 
> programmers, and allow them to do low-level tunings for performance. Or what is 
> the purpose of C++ (when compared with "high-level" languages like Python)? 
> This "fix" rid the programmers of their right to choose the way they want.
> 
> Unless the future C++ standard demands protection in such cases, I do not think 
> the compiler-provided mechanism a good idea.

I would agree with you.

Btw, what is the approach adopted in case the app. is a single threaded one? Are
the locks still taken in this case? Also, if it is an mt-app. but the programmer
is sure that that particula function will NOT be reentrant, why should he pay
the penalty of a lock or/and a check every time the function is called?

Stroustrup continuously emphasised that C++ was designed to be as fast if not
faster than C in most respects, and I guess that's why C++ is gaining
popularity. If it were to use the java approach then it would be just another
bloat-language....

-Dhruv.
Comment 21 Jason Merrill 2005-04-14 07:38:58 UTC
Subject: Re:  local static object variable constructed once
 but ctors and dtors called multiple times on same memory when called in
 multiple threads

DCL with explicit memory barriers is safe.  That's what I'm using.  But
don't take my word for it, see the "DCL is Broken" Declaration:

  http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Scroll down to "Making it work with explicit memory barriers".

The fast path does not use a lock.  There, the only overhead compared to
the old code is a function call and a read/read hoist memory barrier on
targets where the memory barrier is necessary; targets that do not reorder
loads have no overhead at all.

It is impossible for users to implement this protection as efficiently as
the compiler can.

The C++ committee is working on improving thread support in C++; when this
particular topic came up the other day, there was general support for
having the compiler guarantee that the initialization be threadsafe, though
possibly with a keyword to either enable or disable this guarantee.
Comment 22 Wu Yongwei 2005-04-18 09:06:58 UTC
Function calls, memory barriers, (and "lock" operations?) are all overheads. I 
would like that

* GCC provide extensions so that GCC users can use memory barriers and 
threading calls in a platform-independent way, and
*
  - Revert the patch, or at least
  - Provide a command-line option (or pragma/attribute?) to disable/enable 
static initialization protection.

With the former, users can implement the protection as efficiently as
the compiler can (correct me if I am wrong). With the latter, the new GCC is 
still usable in some applications that require performance no less than C.

I am really worried that with the improvements introduced to GCC beginning with 
GCC 3, some of my applications are running slower and slower, though some 
problems only show up on Windows.
Comment 23 Jason Merrill 2005-04-18 18:28:27 UTC
Subject: Re:  local static object variable constructed once
 but ctors and dtors called multiple times on same memory when called in
 multiple threads

On 18 Apr 2005 09:07:00 -0000, "adah at netstd dot com" <gcc-bugzilla@gcc.gnu.org> wrote:

> I would like that
>
> * GCC provide extensions so that GCC users can use memory barriers and 
> threading calls in a platform-independent way, and

I'd like that too.

>   - Provide a command-line option (or pragma/attribute?) to disable/enable 
> static initialization protection.

Already there: -fno-threadsafe-statics.

Jason
Comment 24 Jackie Rosen 2014-02-16 13:17:32 UTC Comment hidden (spam)