[Boost-bugs] [Boost C++ Libraries] #5516: Upgrade lock is not acquired when previous upgrade lock releases if another read lock is present

Subject: [Boost-bugs] [Boost C++ Libraries] #5516: Upgrade lock is not acquired when previous upgrade lock releases if another read lock is present
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2011-05-04 08:55:07


#5516: Upgrade lock is not acquired when previous upgrade lock releases if another
read lock is present
-----------------------------------+----------------------------------------
 Reporter: fred@… | Owner: anthonyw
     Type: Bugs | Status: new
Milestone: To Be Determined | Component: threads
  Version: Boost 1.42.0 | Severity: Problem
 Keywords: |
-----------------------------------+----------------------------------------
 Thread 1 acquires a read lock on a shared mutex[[BR]]
 Thread 2 acquires an upgrade lock on the shared mutex[[BR]]
 Thread 3 tries to acquire an upgrade lock on the shared mutex, waits for
 it[[BR]]
 Thread 2 releases the lock

 At this point, I would expect thread 3 to acquire the lock, but it does
 not do it until thread 1 releases its read lock.

 This seems to be caused by the "shared_mutex
 ::unlock_upgrade()" method in thread/pthread/shared_mutex.hpp, that only
 calls "release_waiters()" if it is the last reader, ignoring that another
 upgrade lock might be waiting to take the lock.

 I suppose it could be fixed by maintaining a count of the number of
 threads waiting for an upgrade lock, and to signal the shared condition if
 this is non-zero.

 Here is an example program:


 {{{
 #include <iostream>
 #include <boost/thread.hpp>

 class Test
 {
 public:
   void m1()
   {
     std::cout << "m1 - Trying to take an shared lock" << std::endl;
     boost::shared_lock<boost::shared_mutex> lock(_mutex);
     std::cout << "m1 - Took a shared lock" << std::endl;
     sleep(5);
     std::cout << "m1 - Released the lock" << std::endl;
   }

   void m2()
   {
     sleep(1);
     std::cout << "m2 - Trying to take an upgradable lock" << std::endl;
     boost::upgrade_lock<boost::shared_mutex> lock(_mutex);
     std::cout << "m2 - Took an upgradable lock" << std::endl;
     sleep(1);
     std::cout << "m2 - Released an upgradable lock" << std::endl;
   }

   void m3()
   {
     sleep(2);
     std::cout << "m3 - Trying to take an upgradable lock" << std::endl;
     boost::upgrade_lock<boost::shared_mutex> lock(_mutex);
     std::cout << "m3 - Took an upgradable lock" << std::endl;

     std::cout << "m3 - Releasing locks" << std::endl;
   }

   void run()
   {
     boost::thread t1(&Test::m1, this);
     boost::thread t2(&Test::m2, this);
     m3();
     t1.join();
     t2.join();
   }

 private:
   boost::shared_mutex _mutex;
 };

 int main()
 {
   Test test;
   test.run();

   return 0;
 }
 }}}

 Results:

 {{{
 m1 - Trying to take an shared lock
 m1 - Took a shared lock
 m2 - Trying to take an upgradable lock
 m2 - Took an upgradable lock
 m3 - Trying to take an upgradable lock
 m2 - Released an upgradable lock
 m1 - Released the lock
 m3 - Took an upgradable lock
 m3 - Releasing locks
 }}}

 Expected:

 {{{
 m1 - Trying to take an shared lock
 m1 - Took a shared lock
 m2 - Trying to take an upgradable lock
 m2 - Took an upgradable lock
 m3 - Trying to take an upgradable lock
 m2 - Released an upgradable lock
 m3 - Took an upgradable lock
 m3 - Releasing locks
 m1 - Released the lock
 }}}

 Tested in Boost 1.42, visually checked that Trunk would have the same
 behaviour.

-- 
Ticket URL: <https://svn.boost.org/trac/boost/ticket/5516>
Boost C++ Libraries <http://www.boost.org/>
Boost provides free peer-reviewed portable C++ source libraries.

This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:06 UTC