Bug 20536

Summary: basic_string class fails on MP under RH 7.0 (with updates)
Product: [Retired] Red Hat Linux Reporter: Need Real Name <maciekk>
Component: glibcAssignee: Jakub Jelinek <jakub>
Status: CLOSED CURRENTRELEASE QA Contact: Aaron Brown <abrown>
Severity: high Docs Contact:
Priority: medium    
Version: 7.0CC: fweimer
Target Milestone: ---   
Target Release: ---   
Hardware: i386   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2003-04-22 01:32:50 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Need Real Name 2000-11-08 20:12:21 UTC
Hi,

I seem to have confirmed that the basic_string class in the C++ library
is not thread safe.  I was worried about the calls to string::Rep::grab()
on the static "nilRep" object during string costruction.  The referece
count
in the Rep data structure is not guarded by a lock, so it may get out of
sunc with actual uses of the string in multiple threads.  Bad things happen
when string::Rep::release() is called on "nilRep" when its referece count
is messed up.

Conditions for reproducing this bug:

1) Compile this code with:
   
   g++ -Wall -O0 -ggdb libtest.cxx -static -lpthread

   Static linking of pthread is essential, as is the -O0 flag.

2) Run this cade on a multi-prcessor machine running Linux Red Hat 7.0.

   Tool Versions (RPM):
	gcc-2.96-54
	gcc-c++-2.96-54
	glibc-2.1.94-3
	glibc-devel-2.1.94-3

   I am using a dual-processor Dell PowerEdge 1300.

   I have not been able to reproduce this bug on a uniprocessor.
   Either it takes too long for the right scheduling combination to
   happen, or there is something with thread scheduling on a
   uniprocessor that prevents threads from simultaneously incrementing
   the ref count and leaving it in bad state.


I'm including the code that reproduces the bug.  As is, the program should
never terminate.  Acutally, the program dies with an error message
similar to this:

Thread #0 starting
Thread #0 forked
Thread #1 forked
Thread #1 starting
Thread #1: length = 134955868
Thread #0: length = 134955868
Thread #2 forked
Thread #2 starting
Thread #2: length = 134955868

Regards,
Maciek Kozyrczak
maciekk

//*****************************************************************************
// The bug-causing code
//*****************************************************************************

#define _REENTRANT
#define _THREAD_SAFE
#define _PTHREADS
#define _POSIX_THREADS
#define _POSIX_THREAD_SAFE_FUNCTIONS

#include <cerrno>
#include <string>
using namespace std;

extern "C" {
#include <pthread.h>
#include <time.h>
}

//*****************************************************************************

// Lock for cerr
pthread_mutex_t cerr_lock = PTHREAD_MUTEX_INITIALIZER;

//*****************************************************************************

void *
handler(void *args)
{
  int thr_num = (int)args;

  pthread_mutex_lock(&cerr_lock);
  cerr << "Thread #" << thr_num << " starting" << endl;
  pthread_mutex_unlock(&cerr_lock);

  //  pthread_mutex_t sleep_lock = PTHREAD_MUTEX_INITIALIZER;
  //  pthread_cond_t sleep_cond = PTHREAD_COND_INITIALIZER;

  while (1) {
    string foo;

    if (foo.length() != 0) {
      pthread_mutex_lock(&cerr_lock);
      cerr << "Thread #" << thr_num << ": length = " << foo.length() <<
endl;
      pthread_mutex_unlock(&cerr_lock);
      exit (1);
    }
  }

  return (void*)0;
}

//*****************************************************************************

int
main(int argc, char *argv[])
{
  int i;

  // Fork some threads.
  // The total number of threads running in the program is going to be 4.
  for (i = 0; i < 99; i++) {
    // Fork a child thread to execute the work of the handler
    pthread_t child_thread;
    while (1) {
      pthread_attr_t child_attr;
      pthread_attr_init(&child_attr);
      pthread_attr_setdetachstate(&child_attr,PTHREAD_CREATE_DETACHED);
      int rc = pthread_create(&child_thread,&child_attr,handler,(void*)i);
      if (rc == 0) break;
      if (rc == EAGAIN) continue;
      // Error in the thread fork.
      cerr << __FILE__ << ":" << __LINE__ << ": Error forking thread" <<
endl;
      exit(1);
    }
    pthread_mutex_lock(&cerr_lock);
    cerr << "Thread #" << i << " forked" << endl;
    pthread_mutex_unlock(&cerr_lock);
  }

  // Get the main thread executing the same handler function.
  handler((void*)i);

  // Sanity check
  cerr << "Done with main()" << endl;

  return 0;
}

//*****************************************************************************

Comment 1 Jakub Jelinek 2000-11-08 21:01:44 UTC
I see you're using endl a lot which has threading bug in libstdc++.
Please try libstdc++*-2.96-6?.*.rpm from rawhide.

Comment 2 Need Real Name 2000-11-08 21:13:58 UTC
Regarding the use of "endl":
	I replaced all "endl" with "\n", but still see the same behavior.

By the way, I know how to fix this bug on my system, but it's not a good
long-term solution.  The fix reaffirms the basic_string class.

FIX:	Change bastring.cc to initialize "nilRep" with a "true" instead of a
"false".  This causes its
	reference count to no be touched, which fixes the bug.  It does, however cause
empty
	strings to clone themselves all over the place.


Comment 3 Ulrich Drepper 2003-04-22 01:32:50 UTC
This shouldn't be a problem in the current code in RHL9 anymore.  I tried your
test code.  RHL9 has a completely different C++ library which is also thread
safe.  If you still have problems please open a new bug.