7

All the documentation I've read on the pthreads mutex states only that a mutex prevents multiple threads from accessing shared memory, but how do you specify in the program what exactly is that? Is it all the global variables in the program, the variables being accessed between the locking and unlocking functions, or... ? Everything I've found on pthreads, including the examples, is irritatingly vague.

dreta
  • 1,016
  • 3
  • 13
  • 22
  • 4
    It's up to you. Generally speaking, you lock a mutex, manipulate a particular variable, and then unlock the mutex. – Oliver Charlesworth Oct 03 '13 at 08:07
  • The section of code after the mutex lock call and before the unlock call, called the critical section. No matter what you do in that code. – fkl Oct 03 '13 at 08:09
  • 1
    @fayyazkl so i just lock the mutex, work on some variables, and all these variables are locked? like the compiler or what ever figures what parts of memory to lock based on what variables i use in that part of code between locking and unlocking? – dreta Oct 03 '13 at 08:10
  • @dreta: No, it's not the compiler. It's up to you to ensure that all threads that access those variables only do so once they have acquired the mutex lock. – Oliver Charlesworth Oct 03 '13 at 08:11
  • Yeah. It is important to get the concept right. The assumption here is that the OTHER threads in your program execute the same code. When they reach the same mutex lock statement, they will be block, until the earlier thread unlocks. So it is sequence of execution. No one prevents you from changing that variable if you access it in some other section of code without mutex call and change it. – fkl Oct 03 '13 at 08:14
  • So many answers and none refers to [Mutual exclusion](http://en.wikipedia.org/wiki/Mutual_exclusion) on Wikipedia ... – nosid Oct 03 '13 at 08:18
  • @dreta NO. No variables are ever locked. It's up to use to ensure that shared variables are only accessed in critical sections controlled by the same mutex. – James Kanze Oct 03 '13 at 08:19
  • @nosid Perhaps because it's almost never appropriate to refer to Wikipedia. Wikipedia is an Encyclopedia, not a source. A reference to Butenhof's book, however, would be in order. – James Kanze Oct 03 '13 at 08:23
  • @JamesKanze: At least it would be helpful to mention that _mutex_ is short for _mutual exclusion_. – nosid Oct 03 '13 at 08:34
  • In and out of itself, a mutex is just a token. Locking the mutex is acquiring this token (only one person can hold onto it at once). And then, *as a convention*, you establish: *only the thread holding onto the yellow token is authorized to access the memory area here*. Of course, any rogue thread may access it without bothering about respecting the convention: it's called a bug. – Matthieu M. Oct 03 '13 at 09:45

7 Answers7

16

a mutex prevents multiple threads from accessing shared memory

The above is an incorrect statement. By itself, a mutex does not do that. It lets you build code that prevents multiple threads from accessing shared memory or other resources concurrently, but it does not lock anything by itself.

You can build a program that uses a mutex to prevent multiple threads from executing specific pieces of code concurrently. If these pieces of code happen to be accessing a shared memory region, and no other code would try accessing that region concurrently without locking the mutex, then the effect would be that of "locking" that region of memory.

w5m
  • 2,286
  • 3
  • 34
  • 46
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    +1 precisely what the students usually mistook it for and even the OP was getting confused with. – fkl Oct 03 '13 at 08:15
  • dasb, I would perhaps: s/does not lock anything by itself/does not lock anything **but** itself/ - it clearly _does_ lock itself. – paxdiablo Oct 03 '13 at 08:25
  • 1
    So i can use a mutex to prevent concurrent execution of various pieces of code. This explains it and makes mutexes so much more useful for me, thanks a lot! – dreta Oct 03 '13 at 08:30
  • @paxdiablo I was planning to say that the mutex itself gets locked, but then I realized as I was typing this up that "locking" in "locking the mutex" and "locking a region of memory" mean different things. The mutex is locked for real, while the memory is locked only as long as your code agrees not to touch that memory as long as the mutex is locked. It is because of this distinction that I did not mention the locking of the mutex itself: as far as the understanding of the memory locking goes, it is an insignificant detail. – Sergey Kalinichenko Oct 03 '13 at 08:33
  • dasb, no probs, you've explained your reasoning well. – paxdiablo Oct 03 '13 at 08:36
  • 1
    E.g. you can also use a mutex to lock a file, if you so desire. As long as all users of the file know that they must lock that mutex, it works. But it's cooperative sharing. – MSalters Oct 03 '13 at 14:01
  • 1
    @MSalters And the mutex must be somehow shared among the different processes. In Unix (I don't know about Windows), a mutex is first and foremost an in-memory object (so you don't have to do a context switch if it is uncontested), which means that to protect file accesses, it has to be in shared memory. – James Kanze Oct 03 '13 at 14:43
  • @JamesKanze: "Shared across different processes" is just a special case of "all users of the file must lock the same mutex". – MSalters Oct 03 '13 at 14:49
10

A mutex doesn't lock memory, it "locks" a part of the execution path, and synchronizes memory (but when locking and when unlocking). What is guaranteed is that if one thread holds the mutex, no other threads can acquire it, and any thread attempting to acquire it will be blocked until it is released.

It is also guaranteed that any memory accesses (read or write) will be ordered with respect to acquiring or releasing the mutex; in other words, that any reads made while the mutex is held will reflect any changes made before the mutex was acquired (including those made in a different thread), and that any modifications made while the mutex is held will be potentially visible to all other threads at the latest when the mutex is released. (The other threads will, of course, have to ensure that their memory reads see up to date values for this to work.)

For more information, you really should read Programming with POSIX Threads, by David Butenhof. It's the reference, and explains them in detail.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Importnat: a mutex forces _all_ memory accesses to be synchronized, because it doesn't know _what_ it is protecting. Sometimes that hurts, but on the other hand it makes it a lot easier to protect a `std::deque`. – MSalters Oct 03 '13 at 14:03
  • "Sometimes that hurts"? Do you know of a processor where you can insert fences or membars which don't affect all of the memory? – James Kanze Oct 03 '13 at 14:41
  • No, but I know a lot of CPU's with registers. If a register contains the live copy of a variable which is unrelated to the mutex, the compiler still has to write it back to memory before the barrier, and retrieve it afterwards. Those are real writes and real reads, and must be done even for an uncontested mutex. – MSalters Oct 03 '13 at 14:47
  • @MSalters Good point. I was thinking in terms of actual memory, but it's true that the compiler can often keep things in registers. – James Kanze Oct 03 '13 at 15:06
3

Strictly speaking, a mutex only locks/unlocks itself. What shared resources it protects depends entirely on how you use it. You use mutexes (or more generally any synchronisation primitives) to establish a protocol for yourself to safely access shared resources such as data.

For example, you have an array double d[10] which is to be access by different threads. To protect this from concurrent modifications, you can create one mutex, let's say mutex_for_d, and program your code so that every time any code accesses d, it will lock mutex_for_d first. That way, access to d is protected by the mutex.

Alternatively, you could decide that each element of the array will be synchronised separately - and have an array of mutexes, always locking the one for the element you access.

This is purely your own protocol and you must make sure you stick to it - if you forget to lock the mutex in one function which modifies d, the program will still run, but can potentially introduce a data race. For this reason, you will normally want to hide shared data behind a class interface which will guarantee correct locking, something like this:

struct SharedArray
{
  double get(size_t idx) const { std::lock_guard<std::mutex> lock(mut); return d[idx]; }
  void set(size_t idx, double v) { std::lock_guard<std::mutex> lock(mut); d[idx] = v; }
private:
  double d[10];
  std::mutex mut;
};
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
1

See this code:

bool initialized_array = false;

int some_array[10];

void do_some_initialization() { ... };

int get_array_element(int i)
{
    if (!initialized_array) {
        do_some_initialization();
        initialized_array = true;
    }
    return some_array[i];
}

As you can see, the variables initialized_array and some_array are deeply related, but the relationship exists only in the code that handles them both.

That's how mutexes are associated with shared memory; the association happens because you write code that does so. There is no way to say "this mutex protects that shared object", the programmer has to make sure every time the shared object is accessed by a thread, it also performs the synchronization on the correct mutex.

DanielKO
  • 4,422
  • 19
  • 29
1

When locked, a mutex prevents any other thread from obtaining a lock on that same mutex. So, if you want some particular data to be thread safe (i.e., a critical section of memory), you should obtain a lock on the mutex before reading from it or writing to it, then release the lock when finished. The mutex itself does not refer to any particular section of memory. It is simply a tool you can use.

Dave
  • 4,282
  • 2
  • 19
  • 24
1

Mutex locks a piece of code. For example:

mutex.lock();
    //some code here
mutex.unlock();

If one thread enters the code above it locks it, until it is done with it. At the same time no other thread can execute this piece of code.

mutex and threads independence

Community
  • 1
  • 1
cpp
  • 3,743
  • 3
  • 24
  • 38
1

A mutex locks itself and that's all it locks. It does not lock other memory, it does not lock code. You can design your code so that something other than just the mutex is protected but that's down to how you design the code, not a feature of the mutex itself.

You can tell it doesn't lock data memory because you can freely modify the memory when the mutex is locked by someone else just by not using the mutex:

thread1:            thread2:
    lock mtx            set i to 4  // i is not protected here
    set i to 7
    unlock mtx

To say it locks code is also not quite right since you can run all sort of different sections of code under the control of a single mutex.

And, if someone manages to get to the code within a mutex block without first claiming the mutex, it can freely run the code even when someone else has the mutex locked:

threadN:
    if thread_id == 2: goto skip_label1
    lock mtx
  :skip1_label1

    set i to 7              // not as protected as you think

    if thread_id == 2: goto skip_label2
    unlock mtx
  :skip1_label2
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • That's not all it does, since it also ensures memory synchronization. (The code implementing the lock and unlock commands will contain the necessary fence or membar instructions to ensure full ordering with respect to acquiring or freeing the mutex.) – James Kanze Oct 03 '13 at 08:26
  • @James, I'll change it to "all it locks" rather than "all it does". Obviously there's a lot of stuff happening under the covers but that has nothing to do with what gets locked. Those are implementation details. – paxdiablo Oct 03 '13 at 08:28