From osa@freebsd.org.ru  Sun Dec  3 15:37:08 2000
Return-Path: <osa@freebsd.org.ru>
Received: from freebsd.org.ru (sweet.etrust.ru [194.84.67.5])
	by hub.freebsd.org (Postfix) with ESMTP id 243D037B400
	for <FreeBSD-gnats-submit@freebsd.org>; Sun,  3 Dec 2000 15:37:07 -0800 (PST)
Received: by freebsd.org.ru (Postfix, from userid 1000)
	id 9CBCC73; Mon,  4 Dec 2000 02:37:04 +0300 (MSK)
Message-Id: <20001203233704.9CBCC73@freebsd.org.ru>
Date: Mon,  4 Dec 2000 02:37:04 +0300 (MSK)
From: osa@FreeBSD.org.ru
Reply-To: osa@FreeBSD.org.ru
To: FreeBSD-gnats-submit@freebsd.org
Subject: C++ bug (please see the body)
X-Send-Pr-Version: 3.2

>Number:         23252
>Category:       gnu
>Synopsis:       C++ bug (please see the body)
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    obrien
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Dec 03 15:40:01 PST 2000
>Closed-Date:    Fri Jan 5 23:35:36 PST 2001
>Last-Modified:  Wed Oct 26 06:28:25 GMT 2005
>Originator:     Sergey A. Osokin
>Release:        FreeBSD 4.2-BETA i386
>Organization:
Russian FreeBSD Team
>Environment:

	FreeBSD 4.2-BETA i386 (after 12.11.2000), FreeBSD-4.2-BETA previous 02.11.2000 are not affected

>Description:

	C++ bug. Please see the source code.

>How-To-Repeat:

	Here is a special code.

/*--------------------------------------------------------*/

#include <stdio.h>
#include <assert.h>
#include <string>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>

#define Debug(x)	printf x

extern "C" {
typedef void *(*_THR_C_FUNC)(void *args);
}
typedef void *(*_THR_FUNC)(void *args);

/*-----------------------------------------------------------------*/
class    Mutex
{
public:
  Mutex() { assert(::pthread_mutex_init(&this->lock_, 0) == 0); }
  ~Mutex (void) { assert(::pthread_mutex_destroy(&this->lock_)==0); }
  int acquire (void) { return ::pthread_mutex_lock(&this->lock_); }
  int release (void) { return ::pthread_mutex_unlock (&this->lock_); }
  pthread_mutex_t lock_;
};

/*-----------------------------------------------------------------*/
class Condition
{
public:
  Condition (Mutex &m);
  ~Condition (void);
  int wait (void);
  void signal (void);
protected:
  pthread_cond_t cond_;
  Mutex &mutex_;
};

Condition::Condition (Mutex &m) : mutex_ (m)
{
  assert (pthread_cond_init(&this->cond_, 0) == 0);
}

Condition::~Condition (void)
{
   
    while(::pthread_cond_destroy(&this->cond_) == -1 && errno == EBUSY)
      {
        assert(::pthread_cond_broadcast(&this->cond_) == 0);
#ifdef __linux__
	::sched_yield ();
#else
        ::pthread_yield();
#endif
      }
}

int Condition::wait (void)
{
  return ::pthread_cond_wait(&this->cond_, &this->mutex_.lock_);
}

void Condition::signal (void)
{
  assert(::pthread_cond_signal(&this->cond_) == 0);
}

/*-----------------------------------------------------------------*/
class Guard
{
public:
  Guard (Mutex &l);
  ~Guard (void);
private:
  Mutex *lock_;
};
Guard::Guard (Mutex &l)
  : lock_ (&l)
{
  this->lock_->acquire ();
}
Guard::~Guard (void)
{
  this->lock_->release ();
}

/*-----------------------------------------------------------------*/
class _Base_Thread_Adapter
{
public:
  _Base_Thread_Adapter (_THR_FUNC user_func, void *arg);
  void *invoke (void);
  _THR_C_FUNC entry_point (void) { return entry_point_; }

private:
  _THR_FUNC user_func_;
  void *arg_;
  _THR_C_FUNC entry_point_;
};

extern "C" void * _thread_adapter (void *args)
{
  _Base_Thread_Adapter *thread_args = (_Base_Thread_Adapter*)args;
  void *status = thread_args->invoke ();
  return status;
}

_Base_Thread_Adapter::_Base_Thread_Adapter (_THR_FUNC user_func, void *arg)
  : user_func_ (user_func), arg_ (arg), entry_point_ (_thread_adapter)
{
}

void *
_Base_Thread_Adapter::invoke (void)
{
  void *(*func)(void *) = this->user_func_;
  void *arg = this->arg_;
  delete this;
  return func(arg);
}

/*-----------------------------------------------------------------*/
class SS {
  public:
    void spawn();
    static void run();
    static void *WThread( void *data );
};
  
/*---------------------------------------------------------------------*/
static Mutex CMutex;
static Condition Cond(CMutex);
static Mutex m1;

/*---------------------------------------------------------------------*/
#define REL(m,n) assert(m.release() != -1)
#define ACQ(m,n) assert(m.acquire() != -1)
      
/*---------------------------------------------------------------------*/
void *
SS::WThread( void *data )
{
    Cond.signal();
    Debug(("run thread...\n"));
    SS::run();
    Debug(("thread ended\n"));
    return NULL;
}

/*---------------------------------------------------------------------*/
int thr_create (_THR_FUNC func, void *args)
{
  _Base_Thread_Adapter *thread_args;
  thread_args  = new  _Base_Thread_Adapter(func, args);
  pthread_attr_t attr;
  if (::pthread_attr_init (&attr) != 0)
      return -1;
  ::pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  pthread_t thr_id;
  assert( ::pthread_create (&thr_id, &attr,
                    thread_args->entry_point(), thread_args) == 0 );
  ::pthread_attr_destroy (&attr);
}
/*---------------------------------------------------------------------*/
void
SS::spawn()
{
#ifdef BAD
    int rc;
    Guard guard(m1);   // !!!
#else
    Guard guard(m1);   // !!!
    int rc;
#endif
    pthread_attr_t attr;
    if (::pthread_attr_init (&attr) != 0) return;
    ::pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    thr_create(SS::WThread, (void *)0);
    ::pthread_attr_destroy (&attr);
    ACQ(CMutex, "CMutex");
    rc = Cond.wait();
    if( rc == -1 )
      Debug(("Cond wait failed: %s\n", strerror(errno)));
    REL(CMutex, "CMutex"); 
}

/*---------------------------------------------------------------------*/
void
SS::run()
{
    string s;	// !!!
    string s1;  // !!!
    sleep(1);
}

/*=====================================================================*/
static void sp_call(SS *ss)
{
    string s;	// !!!
    ss->spawn();
}

/*------------------------------------------------------------------*/
int main(int argc, char **argv)
{
    SS ss;
    sp_call(&ss);
    sleep(2);
    Debug(("Exitting...\n"));
    sleep(3);
    return 0;
}

/*--------------------------------------------------------*/

And here is a makefile for gmake:

Goal: bad good

bad: tt.cpp
	$(CXX) -DBAD tt.cpp -pthread  -g -o bad
	$(CXX) -S -DBAD tt.cpp -pthread  -g -o bad.s

good: tt.cpp
	$(CXX) tt.cpp -pthread  -g -o good
	$(CXX) -S tt.cpp -pthread  -g -o good.s


Put the source code in the one directory and say:
$ gmake
After build a programms bad and good, try to run:

% ./good
run thread...
thread ended
Exitting...
% ./bad
run thread...
Segmentation fault (core dumped)

>Fix:



>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: gnats-admin->obrien 
Responsible-Changed-By: sobomax 
Responsible-Changed-When: Tue Dec 5 09:38:50 PST 2000 
Responsible-Changed-Why:  
Over to c++ maintainer. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=23252 

From: "Daniel M. Eischen" <eischen@vigrid.com>
To: freebsd-gnats-submit@FreeBSD.org, osa@FreeBSD.org.ru
Cc:  
Subject: Re: pending/23252: C++ bug (please see the body)
Date: Fri, 15 Dec 2000 12:02:11 -0500

 I've analyzed this PR and am the opinion that it is not a threads library
 bug.  It seems to have something to do with either the compiler (libgcc2)
 or the implementation of string in libstdc++.  In the sample program included
 with the original PR submittal, there are the following functions:
 
 void
 SS::run()
 {
     string s;   // !!!
     string s1;  // !!!
     sleep(1);
 }
 
 static void sp_call(SS *ss)
 {
     string s;   // !!!
     ss->spawn();
 }
 
 SS::run() is called from a thread that is created by the "ss->spawn()" call in
 sp_call().  sp_call initializes/allocates the local string s, then creates a
 thread that calls SS::run().  SS::run() is called which also initializes/allocates
 2 strings (s and s1).  The thread then sleeps and a thread switch back to the
 main thread occurs.  ss->spawn returns and the local string in sp_call() is
 then finalized.  When the spawned thread returns from the sleep it also tries
 to finalize the two strings (s and s1).  But it seems that the finalization
 of the string in sp_call() has corrupted one of the strings in SS::run().
 

From: "Greg Thompson" <johnnyteardrop@hotmail.com>
To: freebsd-gnats-submit@FreeBSD.org, osa@FreeBSD.org.ru
Cc:  
Subject: Re: pending/23252: C++ bug (please see the body)
Date: Thu, 21 Dec 2000 14:16:42 -0500

 enclosed is another test program for this problem. execute it a bunch of 
 times.  you'll see three or four different things depending on what causes 
 death first:
 
 these two mean an exception warped from one thread to another:
 
 $ ./testex
 B catches Z
 Abort trap (core dumped)
 or:
 $ ./testex
 A catches Y
 Abort trap (core dumped)
 
 this means that unexpected() was called by the runtime:
 
 $ ./testex
 unexpected exception thrown
 Abort trap (core dumped)
 
 and this means that the bit of unwinding code blew it:
 
 $ ./testex
 Segmentation fault (core dumped)
 
 it dies in:
 
         leal 4(%edi),%edx
         movl (%edx),%eax
         addl $4,%eax
         movl (%eax),%edx
         movl (%edx),%ecx
         movl %ecx,(%eax)
 
 because edx is zero.
 
 /*
 * testex.cpp
 *
 * this dumb program spins off a few threads that just throw and catch some
 * exceptions.  it's supposed to run silently forever.  it'll print some 
 stuff
 * and exit if one of two things happens:
 *
 * 1) "unexpected()" is invoked by the runtime
 * 2) the wrong exception is caught in a thread
 */
 
 #include <stdio.h> // 11 15 37
 #include <stdlib.h>
 #include <pthread.h>
 #include <new>
 #include <exception>
 
 
 // this is the exception thrown by instances of threadA
 class exZ : public std::exception {
 
 public:
 
     inline exZ(void) : std::exception() { }
     virtual ~exZ(void);
 
 }; // class exZ
 
 exZ::~exZ(
     void
     )
 {
 }
 
 
 // this is the exception thrown by instances of threadB
 class exY : public std::exception {
 
 public:
 
     inline exY(void) : std::exception() { }
     virtual ~exY(void);
 
 }; // class exY
 
 exY::~exY(
     void
     )
 {
 }
 
 
 // this is a synchronization device that mixes up the scheduling of threadA
 // instances
 class Flipper {
 
 public:
     Flipper(void);
     ~Flipper(void);
     void Acquire(void);
     void Release(void);
 
 private:
     pthread_t			 _owner;
     pthread_mutex_t		 _m;
     pthread_t			 _wanter;
     pthread_cond_t		*_wc;
 
 }; // class Flipper
 
 
 Flipper::Flipper(
     void
     ) : _owner(static_cast<pthread_t>(0)), 
 _wanter(static_cast<pthread_t>(0)),
 	_wc(NULL)
 {
     pthread_mutex_init(&_m, NULL);
 }
 
 
 Flipper::~Flipper(
     void
     )
 {
     pthread_mutex_destroy(&_m);
 }
 
 
 void Flipper::Acquire(
     void
     )
 {
     pthread_t			 me = pthread_self();
 
     if (_owner == me)
 	abort();
 
     pthread_mutex_lock(&_m);
 
     if (_owner == 0) {
 
 	_owner = me;
 
     } else {
 
 	pthread_cond_t		 myc;
 
 	pthread_cond_init(&myc, NULL);
 
 	_wanter = me;
 	_wc = &myc;
 
 	while (_owner != me)
 	    pthread_cond_wait(&myc, &_m);
 
 	pthread_cond_destroy(&myc);
 
     }
 
     pthread_mutex_unlock(&_m);
 }
 
 
 void Flipper::Release(
     void
     )
 {
     pthread_mutex_lock(&_m);
 
     if (_wanter == 0) {
 
 	_owner = static_cast<pthread_t>(0);
 
     } else {
 
 	pthread_cond_t		*c = _wc;
 
 	_owner = _wanter;
 	_wanter = 0;
 	_wc = NULL;
 
 	pthread_cond_signal(c);
 
     }
 
     pthread_mutex_unlock(&_m);
 }
 
 
 // a stack object to automate flippage
 class AutoFlip {
 
 public:
     inline AutoFlip(Flipper *flip) : _flip(flip) {
 	_flip->Acquire();
     }
 
     inline ~AutoFlip(void) {
 	_flip->Release();
     }
 
 private:
     Flipper			*_flip;
 
 }; // class AutoFlip
 
 
 
 // threadA's main processing loop.  pretty dumb.  it just trades off with
 // another instance of threadA.
 static void *threadAMain(
     void			*arg
     ) throw()
 {
     Flipper			*flip = reinterpret_cast<Flipper *>(arg);
 
     do {
 
 	try {
 	    AutoFlip		 f(flip);
 
 	    //printf("A throws Z\n");
 	    throw exZ();
 
 	} catch (const exZ &z) {
 	    //printf("A catches Z\n");
 	} catch (const exY &y) {
 	    printf("A catches Y\n");
 	    abort();
 	}
 
     } while(true);
 
     return NULL;
 }
 
 
 
 // threadB's main processing loop.  even dumber.  it just throws exceptions
 // at itself.
 static void *threadBMain(
     void			*arg
     ) throw()
 {
     do {
 
 	try {
 	    //printf("B throws Y\n");
 	    throw exY();
 	} catch (const exY &y) {
 	    //printf("B catches Y\n");
 	} catch (const exZ &z) {
 	    printf("B catches Z\n");
 	    abort();
 	}
 
     } while(true);
 
     return NULL;
 }
 
 
 // handle unexpected exceptions (there shouldn't be any)
 static void unexpectedHandler(
     void
     )
 {
     printf("unexpected exception thrown\n");
     terminate();
 }
 
 
 // program entry point.  the join() will never complete, so there's no point
 // in cleaning up the flipper.
 int main(
     int				 argc,
     char			*argv[]
     ) throw()
 {
     Flipper			*f;
     pthread_t			 thread;
 
     set_unexpected(unexpectedHandler);
 
     f = new Flipper();
     pthread_create(&thread, NULL, threadAMain, f);
     pthread_create(&thread, NULL, threadAMain, f);
     pthread_create(&thread, NULL, threadBMain, NULL);
 
     pthread_join(thread, NULL);
 
     return 0;
 }
 _________________________________________________________________
 Get your FREE download of MSN Explorer at http://explorer.msn.com
 
 

From: "Alexander N. Kabaev" <ak03@gte.com>
To: freebsd-gnats-submit@FreeBSD.ORG
Cc: "David E. O'Brien" <obrien@FreeBSD.ORG>
Subject: Re: pending/23252: C++ bug (please see the body)
Date: Thu, 21 Dec 2000 17:50:34 -0500 (EST)

 The following hack stops test case from coredumping.
 
 a) cd /usr/src/gnu/lib/libgcc_r
 b) change CFLAGS+=-D_PTHREADS  CFLAGS+=-D_PTHREADS -DGTHREAD_USE_WEAK=1
 c) make all install
 d) cp /usr/lib/libgcc/libgcc_r_pic.a /usr/lib/libgcc/libgcc_pic.a
 e) cd /usr/src/gnu/lib/libstdc++; make clean all install
 
 David, why can't we build libgcc_pic.a with -D_PTHREADS -DGTHREAD_USE_WEAK=1
 always and get rid of libgcc_r_pic.a altogether? I just finished buildworld
 after doing just that and everything seems to work so far. I will continue my
 testing and will let you know if something breaks.
 
 

From: "David O'Brien" <obrien@FreeBSD.ORG>
To: "Alexander N. Kabaev" <ak03@gte.com>
Cc: freebsd-gnats-submit@FreeBSD.ORG
Subject: Re: pending/23252: C++ bug (please see the body)
Date: Thu, 21 Dec 2000 15:05:32 -0800

 On Thu, Dec 21, 2000 at 05:50:34PM -0500, Alexander N. Kabaev wrote:
 > David, why can't we build libgcc_pic.a with -D_PTHREADS -DGTHREAD_USE_WEAK=1
 > always and get rid of libgcc_r_pic.a altogether? I just finished buildworld
 
 I am working on something simular.
 
State-Changed-From-To: open->closed 
State-Changed-By: obrien 
State-Changed-When: Fri Jan 5 23:35:36 PST 2001 
State-Changed-Why:  
fix committed to -current and will be MFC'ed on the fast track 

http://www.freebsd.org/cgi/query-pr.cgi?pr=23252 
>Unformatted:
