1

I'm just learning about locks for the first time prior to taking an OS class for the first time. I originally thought that locks would literally "lock some resource" where you would need to specify the resource (perhaps by pointer to the address of the resource in memory), but after reading through a couple really basic implementations of spin-locks (say, the unix-like training OS "xv6"'s version):

http://pages.cs.wisc.edu/~skobov/cs537/P3/xv6/kernel/spinlock.h http://pages.cs.wisc.edu/~skobov/cs537/P3/xv6/kernel/spinlock.c

As well as this previous stack overflow question: (What part of memory does a mutex lock? (pthreads))

I think I had it all wrong.

It seems to me instead that locks are effectively just a boolean flag like variable that temporarily (or indefinitely) blocks execution of some code that would utilize a resource, but only where another thread actually also attempts to acquire the lock (where in that second thread attempting to acquire the lock as well, that blocking of the second thread has the side effect of that second thread not being able to utilize the resource until the lock is released by the first thread). So now I'm wondering instead: if a poorly designed thread that uses no mutexes and simply attempts to utilize a resource that another well designed thread held a lock on, is the poorly designed thread able to access the resource regardless (by simply ignoring the mutex -- which I'm now thinking acts as a flag a thread should look at, but has the opportunity to ignore)?

If that's the case, then why do we implement locks as sophisticated boolean variables such that all threads must use the locks as opposed to a lock that instead prevents access to a memory region?

Since I'm relatively new to all this, I appreciate any reasonable terminology edit recommendations if I'm stating my question incorrectly as well an answer!

Thank you very much!

--edit, Thank you all for the prompt and helpful responses!

  • Your understanding that *all* the threads should properly implement access to shared resources is correct. And there is no such a thing as "lock that instead prevents access to a memory region" - this will require special hardware support. – Eugene Sh. Jun 29 '20 at 16:53
  • The phenomenon has been observed before. Paul McKenney has called RCU "synchronization by social engineering", and pointed out that locks require the same diligence of all callers to lock the mutex. More modern languages like Rust can give you an API where you only gain access to an object by locking it, and you automatically lose access when unlocking. – EOF Jun 29 '20 at 17:05
  • Re, "...some code that would utilize a resource..." Also, don't forget that a thread is "utilizing" a resource even if it only just _looks_ at the data in memory. The real reason why we use mutexes is to ensure that no thread will ever see shared variables in some bad/inconsistent/half-way-done state that was caused by the action of some other thread. Sometimes, just _seeing_ the variables in an inconsistent state could cause a program thread to follow a bad pointer and segfault the program, or issue an inappropriate "correction" to the rocket guidance system, hang in an infinite loop, etc. – Solomon Slow Jun 29 '20 at 18:31

1 Answers1

3

If that's the case, then why do we implement locks as sophisticated boolean variables such that all threads must use the locks as opposed to a lock that instead prevents access to a memory region?

A lot of reasons:

  1. What if the thing you're controlling access to isn't a memory region? What if it's a file or a network connection?

  2. How would the compiler know when it was going to access a region of protected memory? Would the compiler have to assume that any memory access anywhere might synchronize with other threads? That would make many optimizations impossible, including storing possibly shared variables in registers which is pretty critical.

  3. Would hardware have to support locking memory on any granularity? How would it know what memory is associated with an object? Consider a linked list. Would you have to lock every bit of memory associated with that linked list and every object in it? When you add or remove an object from the list, do you have to change what memory is protected? Won't that be both expensive and extremely difficult to use?

  4. How would it know when to release a lock? Say you access some area of memory that needs protection and then later you access some other area of memory. How would the implementation know whether other threads could be allowed to access that area in-between those two accesses? The implementation would need to know whether the code accessing that region was or wasn't relying on a consistent view of the shared state over those two accesses. How could it know that? Get it wrong by keeping the lock and concurrency suffers. Get it wrong by releasing the lock in-between the two accesses, and the code can behave unpredictably.

And so on.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278