views:

83

answers:

2

I have a multithreaded server application that needs mutex locks over some shared memory.

The shared memory are basically sTL maps etc.

Much of the time I'm just reading from the map. But, I also need to occasionally add to it.

e.g. typedef std::map MessageMap; MessageMap msgmap; boost:shared_mutex access_;

void ProcessMessage(Message* message)
{
  //  Access message... read some stuff from it  message->...

  UUID id = message->GetSessionID();

  // Need to obtain a lock here. (shared lock? multiple readers)
  // How is that done?
  boost::interprocess::scoped_lock(access_);

  // Do some readonly stuff with msgmap
  MessageMap::iterator it = msgmap.find();
  // 

  // Do some stuff...

  // Ok, after all that I decide that I need to add an entry to the map.
  // how do I upgrade the shared lock that I currently have?
  boost::interprocess::upgradable_lock


  // And then later forcibly release the upgrade lock or upgrade and shared lock if I'm not looking
  // at the map anymore.
  // I like the idea of using scoped lock in case an exception is thrown, I am sure that
  // all locks are released.
}

EDIT: I might be confusing the different lock types.

Whats the difference between shared/upgrade and exclusive. i.e. I don't understand the explanation. It sounds like if you just want to allow lots of readers, a shared access is all you want to obtain. And to write to your shared memory you just need upgrade access. Or do you need exclusive? The explanation in boost is anything but clear.

Is upgrade access obtained because you might write. But shared means you definately won't write is that what it means?

EDIT: Let me explain what I'm wanting to do with a little more clarity. I'm not yet happy with the answers.

Here is the example all over again but with an example of some code that I'm using as well. Just an illustration, not the actual code.

typedef boost::shared_mutex Mutex;
typedef boost::shared_lock<Mutex> ReadLock;
typedef boost::unique_lock<Mutex> WriteLock;
Mutex mutex;
typedef map<int, int> MapType;    // Your map type may vary, just change the typedef
MapType mymap;

void threadoolthread() // There could be 10 of these.
{   
    // Add elements to map here
    int k = 4;   // assume we're searching for keys equal to 4
    int v = 0;   // assume we want the value 0 associated with the key of 4

    ReadLock read(mutex); // Is this correct?
    MapType::iterator lb = mymap.lower_bound(k);
    if(lb != mymap.end() && !(mymap.key_comp()(k, lb->first)))
    {
        // key already exists
    }
    else
    {
        // Acquire an upgrade lock yes?  How do I upgrade the shared lock that I already        have?
        // I think then sounds like I need to upgrade the upgrade lock to exclusive is that correct as well?

        // Assuming I've got the exclusive lock, no other thread in the thread pool will be able to insert.
        // the key does not exist in the map
        // add it to the map
        {
          WriteLock write(mutex, boost::adopt_lock_t());  // Is this also correct?
          mymap.insert(lb, MapType::value_type(k, v));    // Use lb as a hint to insert,
                                                        // so it can avoid another lookup
        }
        // I'm now free to do other things here yes?  what kind of lock do I have here, if any?  does the readlock still exist?
    }
+2  A: 
ybungalobill
Sorry I think maybe i want to use boost::upgrade_lock and boost::upgrade_to_unique_lock. Maybe you can explain the difference between them. See my edited question.
Matt H
@Matt H: see edited answer.
ybungalobill
With UpgradeLockable there can be only one thread in that state but several can be in SharedLockable is that correct? so in order for an upgrade lock to become a unique lock all shared locks need to be unlocked correct?
Matt H
@Matt: "With UpgradeLockable there ..." only one in unique state and many in upgradable or shared. "so in order for an upgrade ..." Yep.
ybungalobill
+1  A: 

You don't want boost-interprocess if you're just using a single process. As the library name implies, it's used for Inter-Process Communication (IPC). You most likely want to use the boost-thread mutex and locking concepts.

#include <boost/thread/locks.hpp>  
#include <boost/thread/shared_mutex.hpp>  

int
main()
{
    typedef boost::shared_mutex Mutex;
    typedef boost::shared_lock<Mutex> ReadLock;
    typedef boost::unique_lock<Mutex> WriteLock;
    Mutex mutex;

    {
        // acquire read lock
        ReadLock read( mutex );

        // do something to read resource
    }

    {
        // acquire write lock
        WriteLock write( mutex, boost::adopt_lock_t() );

        // do something to write resource
    }
}

there's a post on the boost mailing list explaining this as well.

Sam Miller
Edit: thanks Sam. Can you have a look at my example above that I've edited at the bottom of the question. Would that be the correct usage?
Matt H
@Matt, in your case since the read lock is unconditionally acquired when entering `threadoolthread`, I think you want to use `boost::upgrade_to_unique_lock` that ybungalobill has [described](http://stackoverflow.com/questions/3896717/example-of-how-to-use-boost-upgradeable-mutexes/3896816#3896816). Though, I have never used `boost::adopt_lock_t()` so I'm not familiar with the behavior, maybe it works.
Sam Miller
@Sam, wouldn't using upgrade_to_unique_lock at the beginning of the thread pool function mean that only one thread in the thread pool could run at a time?
Matt H
@Matt yes, that is correct. Which is why you would not want to do that. Acquire the exclusive lock by upgrading the read lock only at the scope you need it.
Sam Miller