Boost logo

Boost :

From: David Bien (davidbien_at_[hidden])
Date: 2024-04-26 18:13:22


It seems there is some type of race condition involved. It both works, fails, and hangs regardless of BOOST_THREAD_HAS_EINTR_BUG defined. Weird cuz I never saw anything of the sort in weeks of testing under Windows, and a couple days of testing under MacOS - both using boost synchronization objects.

When it aborts the return is EBUSY.

I am going to change it to use STL just as a data point. The condition variable/mutex pattern here:

~CShareMgr()
{
  {
    boost::lock_guard< boost::mutex > lock( m_cvMutex );
    m_fRunning = false;
  }
  m_cv.notify_one();
  if ( m_thread.joinable() ) // wait on cleanup thread here.
  {
    m_thread.join();
  }
}
void CShareMgr::_CleanupThread()
{
  while ( true )
  {
    boost::unique_lock< boost::mutex > lock( m_cvMutex );
    if ( m_cv.wait_for( lock, boost::chrono::minutes( 1 ), [ this ] { return !m_fRunning; } ) )
      return;
    lock.unlock();

    _CleanupThreadVolatile();
  }
}
void CShareMgr::_CleanupThreadVolatile() volatile
{
  std::vector<std::shared_ptr<volatile CShare>> vecShares;
  std::vector<vTyShareKey> vecExpiredKeys;

  // 1) Create a vector of shared ptr of shares.
  std::chrono::minutes nMinutesCleanupShare;
  {
    _TyLockingPtrRead lockRead(*this, m_mapMutex);
    for (auto& pair : lockRead->m_map)
      vecShares.push_back(pair.second);
    nMinutesCleanupShare = lockRead->m_nMinutesCleanupShare;
  } // 2) Let the lockRead go out of scope.

  // 3) Peruse the array of shares backward adding keys of expired shares to vecExpiredKeys
  auto now = std::chrono::system_clock::now();
  for (auto it = vecShares.rbegin(); it != vecShares.rend(); ++it)
  {
    if (now - (*it)->GetLastAccessed() > nMinutesCleanupShare)
      vecExpiredKeys.push_back((*it)->GetShareKey());
  }

  // 4) Use the write lock to lock the map and then remove all expired share from the map
  _TyLockingPtrWrite lockWrite(*this, m_mapMutex);
  for (const auto& key : vecExpiredKeys)
    lockWrite->m_map.erase(key);
}


Perhaps there is something wrong with my usage above?

FYI: I use http://erdani.org/publications/cuj-02-2001.php.html cuz I've been bitten before... 😉.
[http://i.cmpnet.com/ddj/digital/ddj.gif]<http://erdani.org/publications/cuj-02-2001.php.html>
volatile: The Multithreaded Programmer's Best Friend<http://erdani.org/publications/cuj-02-2001.php.html>
The volatile keyword was devised to prevent compiler optimizations that might render code incorrect in the presence of certain asynchronous events.
erdani.org


________________________________
From: Boost <boost-bounces_at_[hidden]> on behalf of Peter Dimov via Boost <boost_at_[hidden]>
Sent: Friday, April 26, 2024 10:24 AM
To: boost_at_[hidden] <boost_at_[hidden]>
Cc: Peter Dimov <pdimov_at_[hidden]>
Subject: Re: [boost] BOOST_THREAD_HAS_EINTR_BUG...

Vinícius dos Santos Oliveira wrote:
> Em sex., 26 de abr. de 2024 às 13:51, David Bien via Boost
> <boost_at_[hidden]> escreveu:
> > Under Linux when BOOST_THREAD_HAS_EINTR_BUG is defined we
> SIGABRT at this:
> >
> > ~mutex()
> > {
> > BOOST_VERIFY(!posix::pthread_mutex_destroy(&m));
> > }

What is the return value of pthread_mutex_destroy here?

> How is the code for BOOST_THREAD_HAS_NO_EINTR_BUG? Are UNIX signals
> being involved?

It's used here: https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fboostorg%2Fthread%2Fblob%2Faec18d337f41d8e3081ee65f5cf3b5090179ab0e%2Finclude%2Fboost%2Fthread%2Fpthread%2Fpthread_helpers.hpp%23L17&data=05%7C02%7C%7C8a131f3fbb1546d5138208dc6615d40e%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C638497491169778552%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=X%2B767f7giYai7qMLtBCTd65AfHC2HKYAnCAHMciSoqU%3D&reserved=0<https://github.com/boostorg/thread/blob/aec18d337f41d8e3081ee65f5cf3b5090179ab0e/include/boost/thread/pthread/pthread_helpers.hpp#L17>

Basically, the relevant code is:

#ifdef BOOST_THREAD_HAS_EINTR_BUG

    BOOST_FORCEINLINE BOOST_THREAD_DISABLE_THREAD_SAFETY_ANALYSIS
    int pthread_mutex_destroy(pthread_mutex_t* m)
    {
      int ret;
      do
      {
          ret = ::pthread_mutex_destroy(m);
      } while (ret == EINTR);
      return ret;
    }

#else

    BOOST_FORCEINLINE BOOST_THREAD_DISABLE_THREAD_SAFETY_ANALYSIS
    int pthread_mutex_destroy(pthread_mutex_t* m)
    {
      return ::pthread_mutex_destroy(m);
    }

#endif

The only scenario in which the former can return nonzero when the latter doesn't
would be if it returns EINTR, and then tries to destroy the mutex again, and then
get EINVAL because the mutex has already been destroyed.

But this doesn't make sense because in this case the other function would have
returned EINTR, which is also nonzero, so it should also fail the VERIFY.

So I'm at a loss here. More printf debugging (printing the return values in both
cases) is needed.



_______________________________________________
Unsubscribe & other changes: https://na01.safelinks.protection.outlook.com/?url=http%3A%2F%2Flists.boost.org%2Fmailman%2Flistinfo.cgi%2Fboost&data=05%7C02%7C%7C8a131f3fbb1546d5138208dc6615d40e%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C638497491169793535%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=%2BSvaxKUj05Yr91tn80uRbt%2BCRgK%2BnXuViPDrBTOVIU4%3D&reserved=0<http://lists.boost.org/mailman/listinfo.cgi/boost>


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk