Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mutex assert in boost regex constructor

I'm using boost 1.47 for Arm, with the Code Sourcery C++ compiler (4.5.1), crosscompiling from Windows 7 targeting Ubuntu.

When we compile the debug version (i.e. asserts are enabled), there is an assert triggered:

pthread_mutex_lock.c:62: __pthread_mutex_lock: Assertion 'mutex->__data.__owner == 0' failed.

Compiling in release mode, the assert is not triggered and the program works fine (as far as we can tell).

This is happening under a Ubuntu 10.x Arm board.

So, it appears that the pthread_mutex_lock thinks the mutex was set by a different thread than the current one. At this point in my program, we're still single threaded, verified by printing out pthread_self in main and just before the regex constructor is called. That is, it should not have failed the assertion.

Below is the snippet of code that triggers the problem.

// Set connection server address and port from a URL
bool MyHttpsXmlClient::set_server_url(const std::string& server_url)
{
#ifdef BOOST_HAS_THREADS
cout <<"Boost has threads" << endl;
#else
cout <<"WARNING: boost does not support threads" << endl;
#endif
#ifdef PTHREAD_MUTEX_INITIALIZER
    cout << "pthread mutex initializer" << endl;
#endif
{
        pthread_t id = pthread_self();
        printf("regex: Current threadid: %d\n",id);
}
const boost::regex e("^((http|https)://)?([^:]*)(:([0-9]*))?"); // 2: service, 3: host, 5: port // <-- dies in here

I've confirmed that BOOST_HAS_THREADS is set, as is PTHREAD_MUTEX_INITIALIZER.

I tried following the debugger though boost but it's templated code and it was rather difficult to follow the assembly, but we basically die in do_assign (roughtly line 380 in basic_regex.hpp)

basic_regex& assign(const charT* p1,
                      const charT* p2,
                      flag_type f = regex_constants::normal)
{
  return do_assign(p1, p2, f);
}

the templated code is:

// out of line members;
// these are the only members that mutate the basic_regex object,
// and are designed to provide the strong exception guarentee
// (in the event of a throw, the state of the object remains unchanged).
//
template <class charT, class traits>
basic_regex<charT, traits>& basic_regex<charT, traits>::do_assign(const charT* p1,
                    const charT* p2,
                    flag_type f)
{
   shared_ptr<re_detail::basic_regex_implementation<charT, traits> > temp;
   if(!m_pimpl.get())
   {
      temp = shared_ptr<re_detail::basic_regex_implementation<charT, traits> >(new re_detail::basic_regex_implementation<charT, traits>());
   }
   else
   {
      temp = shared_ptr<re_detail::basic_regex_implementation<charT, traits> >(new re_detail::basic_regex_implementation<charT, traits>(m_pimpl->m_ptraits));
   }
   temp->assign(p1, p2, f);
   temp.swap(m_pimpl);
   return *this;
}

I'm not sure what component is actually using the mutex--does anyone know?

In the debugger, I could retrieve the address for the variable mutex and then inspect (mutex->__data.__owner). I got the offsets from the compiler header file bits/pthreadtypes.h, which shows:

/* Data structures for mutex handling.  The structure of the attribute
   type is not exposed on purpose.  */
typedef union
{
  struct __pthread_mutex_s
  {
    int __lock;
    unsigned int __count;
    int __owner;
    /* KIND must stay at this position in the structure to maintain
       binary compatibility.  */
    int __kind;
    unsigned int __nusers;
    __extension__ union
    {
      int __spins;
      __pthread_slist_t __list;
    };
  } __data;
  char __size[__SIZEOF_PTHREAD_MUTEX_T];
  long int __align;

I used these offsets to inspect the data in memory. The values did not make sense: For instance, the __data.__lock field (an int) is 0xb086b580. The __count (an unsigned int) is 0x6078af00, and __owner (an int) is 0x6078af00.

This leads me to think that somehow initialization of this mutex was not performed. Either that or it was completely corrupted, but I'm leaning to missed initialization because when I linked with debug boost libraries, there was no assert.

Now, I'm assuming that whatever mutex that is being queried, is some global/static that is used to make regex threadsafe, and that somehow it was not initialized.

  • Has anyone encountered anything similar? Is there some extra step needed for Ubuntu to ensure mutex initialization?
  • Is my implementation assumption correct?
  • If it is correct, can someone point me to where this mutex is declared, and where it's initialization is occurring
  • any suggestions on further debugging steps? I'm thinking I might have to somehow download the source and rebuild with tracing in there (hoping StackOverflow can help me before I get to this point)
like image 969
TheDuke Avatar asked Oct 24 '12 20:10

TheDuke


2 Answers

One of the first things to check when a really REALLY peculiar runtime crash appears in a well-known, well-tested library like boost is whether there's a header/library configuration mismatch. IMHO, putting _DEBUG or NDEBUG in headers, especially within the structures in a way that affects their binary layout, is an anti-pattern. Ideally we should be able to use the same .lib whether we define _DEBUG, DEBUG, Debug, Debug, NDEBUG, or whatever (so that we can select the .lib based on whether we want to have debug symbols or not, not whether it matches header defines). Unfortunately this isn't always the case.

like image 133
Ted Middleton Avatar answered Sep 25 '22 02:09

Ted Middleton


I used these offsets to inspect the data in memory. The values did not make sense: For instance, the __data.__lock field (an int) is 0xb086b580. The __count (an unsigned > int) is 0x6078af00, and __owner (an int) is 0x6078af00.

This sounds like different parts of your code have different views on how large various structures should be. Some things to check:

  • Is there any #define which enlarges a data structure, but is not consistently set throughout your code base? (On Windows, _SECURE_SCL is infamous for this kind of bugs)
  • Do you do any structure packing? If you set #pragma pack anywhere in a header and forget to unset it at the end of the header, any data structures included after that will have a different layout than elsewhere in your program.
like image 30
tbleher Avatar answered Sep 27 '22 02:09

tbleher