Bug 20536 - basic_string class fails on MP under RH 7.0 (with updates)
basic_string class fails on MP under RH 7.0 (with updates)
Status: CLOSED CURRENTRELEASE
Product: Red Hat Linux
Classification: Retired
Component: glibc (Show other bugs)
7.0
i386 Linux
medium Severity high
: ---
: ---
Assigned To: Jakub Jelinek
Aaron Brown
:
Depends On:
Blocks:
  Show dependency treegraph
 
Reported: 2000-11-08 15:12 EST by Need Real Name
Modified: 2016-11-24 10:22 EST (History)
1 user (show)

See Also:
Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
Environment:
Last Closed: 2003-04-21 21:32:50 EDT
Type: ---
Regression: ---
Mount Type: ---
Documentation: ---
CRM:
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---


Attachments (Terms of Use)

  None (edit)
Description Need Real Name 2000-11-08 15:12:21 EST
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@corelation.com

//*****************************************************************************
// 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 16:01:44 EST
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 16:13:58 EST
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-21 21:32:50 EDT
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.

Note You need to log in before you can comment on or make changes to this bug.