Jump to content
  • Advertisement
Sign in to follow this  
OpOpOpOp

Are mutexes really fool-proof?

This topic is 1884 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Take a look at the following code. Is it theoreticzally possible for two CPU cores to read the value of Mutex::Locked at the exact same time, both assuming the mutex is unlocked and proceeding to lock the mutex at the same time? If so, what's a good way to work around this problem?

class Mutex
{
	bool Locked;
public:
	Mutex() { Locked = false; }
	void lock() { while (Locked); Locked = true; }
	void unlock() { Locked = false; }
};

// Thread 1:
mutex.lock()
SharedData->operationAlpha();
mutex.unlock();

// Thread 2:
mutex.lock()
SharedData->operationBeta();
mutex.unlock();
Edited by Nüb?ek Pænus

Share this post


Link to post
Share on other sites
Advertisement

Dont use a bool to verify if the mutex is locked or not, that defeat the purpose of the mutex in the first place, you need to use WaitForSingleObject(), not to mention that this while loop will consume 100% of 1 cpu for nothing instead of sleeping.

 

I dont think what you are talking about is possible, since that's exactly what mutex are for, avoid data to be accessed at the same time by different threads or processes.

 

You might also consider using a critical section instead if you don't need to lock between multiples process since they are more lightweight.

Edited by Vortez

Share this post


Link to post
Share on other sites

Dont use a bool to verify if the mutex is locked or not, that defeat the purpose of the mutex in the first place, you need to use WaitForSingleObject(), not to mention that this while loop will consume 100% of 1 cpu for nothing instead of sleeping.

 

I dont think what you are talking about is possible, since that's exactly what mutex are for, avoid data to be accessed at the same time by different threads or processes.

 

You might also consider using a critical section instead if you don't need to lock between multiples process since they are more lightweight.

I'm guessing that's what SDL semaphores are for. Silly me. I never understood why I needed them when I could just make my own Mutex class. Silly, silly me.

 

Guess I'll be looking into that then. Thank you!

Share this post


Link to post
Share on other sites

You might also consider using a critical section instead if you don't need to lock between multiples process since they are more lightweight.


Just a heads-up, talking about critical sections is Windows-only. In Boost::Thread for example a mutex is equivalent to a Windows critical section (it used to be implemented as one but nowadays they use something different that is also extremely lightweight). The more complex mutex is called recursive_mutex (this has nothing to do with interprocess communication, that's another layer of heavyweightedness to add). This kind of mutex can be locked multiple times from the same thread without deadlocking itself.

Edited by BitMaster

Share this post


Link to post
Share on other sites

I'm guessing that's what SDL semaphores are for. Silly me. I never understood why I needed them when I could just make my own Mutex class. Silly, silly me.

 

No, that's what SDL_mutex is for.

 

SDL_semaphore is for something else.

Share this post


Link to post
Share on other sites

I see. I've read about critical sections before. Would either of you happen to know what (if any) are the advantages of using the Win32 API with critical sections as opposed to SDL_mutex or Boost? I'm already using SDL for pretty much everything else but I'll try the other options if there's potential for speed.

Share this post


Link to post
Share on other sites

Dont use a bool to verify if the mutex is locked or not, that defeat the purpose of the mutex in the first place, you need to use WaitForSingleObject(), not to mention that this while loop will consume 100% of 1 cpu for nothing instead of sleeping.

 

This article explains what is going on in the background when you are acquiring a lock: http://en.wikipedia.org/wiki/Test-and-set

 

The test-and-set-operation has to be done in one operation so that there is no chance for another process to come in between.  When you are testing a bool afterwards, you split the operation in more than one part so that another thread can come in between and disturb, and wreck havok.

 

That is why you don't use a bool to verify, as Vortez is saying.

Share this post


Link to post
Share on other sites
Multithreading is far more complex than most people first realize. In the For Beginners forum many people talk about starting out with multithreading, and it is highly discouraged.

Before venturing into the land of locks and mutexes you need to have knowledge not just of the high level programming language, but also a good knowledge of every step of the process, including how the optimizers work, how the operating systems work, how the CPU works, how the processors work, how the hardware caches work, and how the processors maintain synchronization. Any issue at any of those levels can cause subtle but extremely painful bugs in your multithreaded code.

Concepts like memory barriers, memory tearing, memory synchronization and prefetch, atomic operations, and many others all come into play. Failure to understand these can also lead to subtle and extremely painful bugs.

For example, you may run into bugs that only manifest themselves very rarely, and even then only on 6-core processors, or only on machines with specific memory speeds, or in seemingly random times and places that have no discernible patterns.

It is a good thing to learn and it is a valuable skill to have.

Just be aware that the transition from single processing to multiprocessing is one of the largest transitions in most programmers' careers. It is not something you can just try to pick up and suddenly have working correctly; it takes years of careful study and practice to reach a minimal level of competence. Even after you are competent the bugs you inevitably create are some of the most insidious flaws in the software world. Some of the most expensive software rewrites I have seen in the industry were due to multithreading bugs that could not be solved.

Most seasoned veterans (including those who are skilled at multithreading) will generally avoid writing their own multithreaded code, relying on asynchronous calls and external systems rather than go through the pains of writing their own new, untested, and undebugged code. It is a skill that is useful and if you want to pick it up, that is a good thing. Just be prepared for a VERY long and bumpy road ahead.

Share this post


Link to post
Share on other sites

Multithreading is far more complex than first realized. In the For Beginners forum many people talk about starting out with multithreading, and it is highly discouraged.

Before venturing into the land of locks and mutexes you need to have knowledge not just of the high level programming language, but also a good knowledge of every step of the process, including how the optimizers work, how the operating systems work, how the CPU works, how the processors work, how the hardware caches work, and how the processors maintain synchronization. Any issue at any of those levels can cause subtle but extremely painful bugs in your multithreaded code.

Concepts like memory barriers, memory tearing, memory synchronization and prefetch, atomic operations, and many others all come into play. Failure to understand these can also lead to subtle and extremely painful bugs.

For example, you may run into bugs that only manifest themselves very rarely, and even then only on 6-core processors, or only on machines with specific memory speeds, or in seemingly random times and places that have no discernible patterns.

It is a good thing to learn and it is a valuable skill to have.

Just be aware that the transition from single processing to multiprocessing is one of the largest transitions in most programmers' careers. It is not something you can just try to pick up and suddenly have working correctly; it takes years of careful study and practice to reach a minimal level of competence. Even after you are competent the bugs you inevitably create are some of the most insidious flaws in the software world. Some of the most expensive software rewrites I have seen in the industry were due to multithreading bugs that could not be solved.

Most seasoned veterans (including those who are skilled at multithreading) will generally avoid writing their own multithreaded code, relying on asynchronous calls and external systems rather than go through the pains of writing their own new, untested, and undebugged code. It is a skill that is useful and if you want to pick it up, that is a good thing. Just be prepared for a VERY long and bumpy road ahead.

I don't understand how it can be so complicated. I just need to share an array of data between two threads. It's not working as it should so you're probably right, but it's just an array, wrapped around a class with a mutex that locks before and unlocks after every read or write operation. Why would that not work? I understand what memory tearing is, but how could that happen if the data is protected by a mutex?

I wrote my code so that it works both in threads and in a single thread, except either thread freezes after several seconds for no apparent reason, as you've predicted, with no discernible pattern..

Edited by Nüb?ek Pænus

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!