3

Suppose we have the following (pseudo) code:

using UpgradeLock = boost::upgrade_lock<boost::shared_mutex>;
using UpgradeToUniqueLock = boost::upgrade_to_unique_lock<boost::shared_mutex>;

boost::shared_mutex mtx;

void DeleteTable(<tbl>) {
  UpgradeLock lock(mtx);
  if (<the table exists>) {
    UpgradeToUniqueLock up(lock); // (!)
    // delete the table
  }
}

And suppose two threads have just entered into this function and both reached the statement, marked with (!).

I can't figure out what will happen then. I have the following options:

  1. One thread cannot acquire exclusive access until the second one releases its UpgradeLock, but it cannot until it gets exclusive access too. Deadlock.
  2. One thread acquires exclusive access, while another one is being suspended at the line with mark (!).

I guess the second option is what probably will happen, but in this case it comes out, that even having locked environment, we have to recheck that our data hasn't been changed "outside", i.e. we have to use the notorious DCLP. Am I right?

I haven't found any plausible info about it, so don't blame me a lot :)

Alexey
  • 1,198
  • 1
  • 15
  • 35

1 Answers1

1

If I correctly understand you, then the number 2 is going to happen.

If you are using Reader/Writer Locks you must stick to the protocol which assumes, that as long as multiple readers share the lock, they are only allowed to do read access. If they need to acquire some writing privileges, they must upgrade the lock.

Now in you case it can happen that multiple threads enter the if-statement and wait for lock acquisition. That's true and they will all get the lock one after the other. The problem is that right after the lock acquisition it might happen that another thread already delete-ed the table.

That's why you might need the double-checked locking pattern.

From here you have multiple options:

option 1: check if table is pointing to NULL and do nothing

option 2: just call delete again, if the pointer is set to NULL or nullptr after you call delete C++ is fine with that (if delete comes from the standard lib) => and will just do nothing. Just for reference read:

http://en.cppreference.com/w/cpp/language/delete

if expression evaluates to a null pointer value, no destructors are called, and the deallocation function is not called.

  UpgradeLock lock(mtx);

  if (<the table exists>) 
  {
     UpgradeToUniqueLock up(lock); // (!)
     if(<table still exists>)
     {
       // delete the table
       // and set the pointer to nullptr
     }
   } 

but according to option 2 the following snippet is just fine if delete is implemented by STL: UpgradeLock lock(mtx);

  if (<the table exists>) 
  {
     UpgradeToUniqueLock up(lock); // (!)   
     // delete the table
     // and set the pointer to nullptr

  } 

option 3: Use std::shared_ptr or boost::shared_ptr instead. They are synchronized. So you even don't need locks, just call ptr.reset() from multiple threads and that's it.

ovanes
  • 5,483
  • 2
  • 34
  • 60
  • Thank you for the answer, but the things turn out the different way. I didn't know that, but ONLY ONE thread can acquire UpgradeLock, therefore the situation, I described above, merely cannot happen. See: http://www.boost.org/doc/libs/1_44_0/doc/html/thread/synchronization.html#thread.synchronization.mutex_concepts.upgrade_lockable – Alexey Nov 14 '17 at 09:38
  • @Alexey you are right. This is a very elegant solution. I will correct my answer. – ovanes Nov 14 '17 at 11:41