marsh
marsh

Reputation: 2720

Mutex lock on separate classes

What is the correct use of a Mutex? How do I know what it is protecting? Does it stop all threads from running for the locked period?

For example I have a singleton that contains a list of objects.

class Foo
{
    // Pretend this is a singleton.
    std::list<Object>* GetObjects() { return &objects; }
    void Reset() { for ( auto iter = objects.begin(); iter != objects.end(); ++iter ) { delete *iter; } }
    std::list<Object> objects;
};

In a separate class I have a update method. This is run in a seperate thread.

class Bar
{
    void Update()
    {
        std::list<Object>* objects Foo.GetObjects(); 
        for(auto iter = objects.begin(); iter != objects .end(); ++iter )
            iter->SetTransform(50,50,50);
    }
};

My Problem is that I want to call Reset() on Foo, and delete all the objects. But my Bar class is updating all these objects. So if it is in the middle of a update method it may be trying to modify a object that has been deleted.

How do I prevent this?

Can I just create a Mutex in reset function and lock it before deleting and unlock it after? Something like this:

 void Reset()
 {
     Mutex mutex; mutex.Lock()
     for ( auto iter = objects.begin(); iter != objects.end(); ++iter )
         delete *iter;
     mutex.Unlock();
}

Does this let the other class know these resources are locked? I am not sure how the mutex knows what information to protect. How does it know that it needs to lock the list in the singleton class.

Btw this is all pseudo code so there is syntax errors and missing information.

Upvotes: 2

Views: 3389

Answers (2)

basav
basav

Reputation: 1495

In simple terms, a mutex is a locking mechanism.

say, you have a resource, in your case list<object> objects; Now , If multiple threads end up operating on this list concurrently, then, the outcome of these operations could be unexpected.

so, basically mutex gives us a mechanism that ensures that only one thread operated on the protected resource one at a time.

Implementation:

In Linux kernel, a thread is modeled by a task_struct object.And mutex implementation is as follows:

struct mutex {
  /* 1: unlocked, 0: locked, negative: locked, possible waiters */
  atomic_t                count;
  spinlock_t              wait_lock;
  struct list_head        wait_list;
 #if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER)
  struct task_struct      *owner;
 #endif
}

So, first thread to try to lock mutex becomes the owner and everyone else is added to wait_list of that mutex.

Once the owner releases it, the next task from the wait_list gets the next chance.

Note that

in case of mutex, whoever locks it has to unlock it,

else everyone else will be locked forever, also known as dreadlock.

Upvotes: 3

i_am_jorf
i_am_jorf

Reputation: 54600

What is the correct use of a Mutex?

To synchronize access to a shared resource.

How do I know what it is protecting?

By reading the code. Whatever is accessed when the lock is acquired is what is being protected.

Does it stop all threads from running for the locked period?

No. Only people who wait on the Mutex are "stopped". A thread will wait when you call Lock() if some other thread already has the lock. It will wait until whoever has the lock calls Unlock(). If nobody has the lock then the thread calling Lock() acquires it and does not wait. Multiple threads may be waiting if you have a lock and who gets it when it is unlocked is not necessarily defined. Refer to the documentation for your particular framework.

How do I prevent this? Can I just create a Mutex in reset function and lock it before deleting and unlock it after?

You can prevent simultaneous access to a shared resource by multiple threads if you acquire and release the lock around access to that shared resource. So, yes, you can lock and unlock around your reset code, but you need to do it in all the places that access your shared resource.

In this case objects is your shared resource. If you acquire the lock in the place you delete the contents of objects, you must also acquire the lock in places where you're using it. Since you give out a reference via GetObjects() you would also need to have callers of that realize access needs to be synchronized via this mutex.

An alternative scheme would be to get rid of GetObjects() and have other methods, for example Object *get(int index), that get / set / manipulate the data inside the lock and never actually give out a raw reference to objects.

Does this let the other class know these resources are locked? I am not sure how the mutex knows what information to protect.

No. The Mutex doesn't know anything. You must impose the proper semantics on the code, either by documenting it for callers or thunking all access to the shared resource through an object that knows to acquire the Mutex.

Upvotes: 2

Related Questions