Why do we need a lock to be reentrant? - multithreading

I understand (somewhat) the features of the jdk 5 ReentrantLock here
But why we would want a 're-entrant' lock? i.e if a Thread already has the lock on an Object, why would it need to acquire it again?

Consider this theoretical example: You are using a lock to protect some back-end data while updating some items in a list box in your GUI. You loop through and modify the items. While doing so, the list box fires an event (perhaps a Selection Changed event or something) for which you have a handler registered. This handler also locks the same lock in order to process the new item. If the lock is not recursive, this thread would deadlock on the second attempt to acquire the lock.

Reentrant locks are useful in cases where a resource cannot tolerate all forms of arbitrarily-timed accesses, but can tolerate certain patterns of access which can occur in nested execution contexts. In many cases their usage is unaesthetic and sloppy, but it may be easier to arrange things so that a reentrant lock can be guaranteed to work than it would be to arrange things so as to make one unnecessary.
Note that while many languages default to making locks reentrant, that is not necessarily a good thing. If code acquires a lock and then other code in that thread tries to acquire a token for that same lock, it's clear that that having the second request wait until lock has been released isn't going to be very productive. That does not imply, however, that the second request should allow access to the lock. In many cases a proper course of action would be for the second request to throw an immediate exception (access shouldn't be granted until the lock is released, and that can't happen until either the request is granted (which shouldn't happen) or the code exits some other way (an exception being the most natural choice). Such a situation would apply if the a method which was modifying a lock-guarded data structure called some outside code which wasn't expected to use the data structure while the data structure was in an inconsistent state. If the code unexpectedly does try to use the data structure, having it fail immediately with an exception may be better than having it wait forever for a lock it's never going to get, or blithely proceed into a lock and access invalid data.
There are many cases where code will call nested routines at times when a guarded resource satisfies some but not all of its invariants, and where the outside code may expect the nested routines to make some kinds of changes to it but not others. In such cases, reentrant locks may be appropriate, but care is required to ensure that code doesn't do things it shouldn't. One advantage of reentrant locks is that if code which makes nested calls with the lock held sets flags to indicate its promises/requirements, and code which acquires the lock tests those flags on entry, one can guarantee that the flags will only be manipulated in predictable sequences. Such a thing would not be possible if two different threads were trying to use the resource simultaneously.

Related

What does a mutex lock?

In every tutorial about mutex, mutex is described as a way to prevent for example multiple threads to access the same resources at the same time. But what are those resources. I know that the resources can be a lot of things, like for example variables, but how do i define those variables that shouldnt be used at the same time by another thread? How does Mutex know which variables to "lock"? I dont understand how the compiler can know before executing the code what Mutex should lock between the functions mutex.lock and mutex.release.
The answer depends on how you want to think about it.
At a low level, a mutex locks nothing but itself. Two threads will never be allowed to lock the same mutex at the same time. End of story.
At a higher level, a mutex locks whatever data you want to lock with it. A mutex is a type of advisory lock. It's like a sign hanging on a door knob that says, "in-use, do not enter." It will keep out whoever respects the sign, but it has no actual power to keep anybody out.
If you have some data shared by several threads, and if you don't want any two threads to ever access* the data at the same time, then you can set up a mutex, and declare that, "None shall access these data unless they have the mutex locked." That declaration is what #Wyck called a "protocol" in a comment, above.
It's up to you to ensure that no thread in your program ever accesses the data without holding the mutex locked. I.e., it's up to you to ensure that your code obeys the protocol.
Also note! Nowhere did I mention "method" or "function." There's never any inherent benefit to locking a method or a function. It's always about the data that the method or the function accesses.
* "Access" doesn't just mean "update." If one thread merely tries to read the data while some other thread is in the middle of updating it, the reading thread could see an inconsistent or invalid snapshot of the data, and it could make arbitrarily bad decisions based on what it saw. The consequences could be fatal to the process, or worse.

Make thread wait for condition but allow thread to remain usable while waiting or listening for a signal

Given a situation where thread A had to dispatch work to thread B, is there any synchronisation mechanism that allows thread A to not return, but remain usable for other tasks, until thread B is done, of which then thread A can return?
This is not language specific, but simple c language would be a great choice in responding to this.
This could be absolutely counterintuitive; it actually sounds as such, but I have to ask before presuming...
Please Note This is a made up hypothetical situation that I'm interested in. I am not looking for a solution to an existing problem, so alternative concurrency solutions are completely pointless. I have no code for it, and if I were in it I can think of a few alternative code engineering solutions to avoid this setup. I just wish to know if a thread can be usable, in some way, while waiting for a signal from another thread, and what synchronisation mechanism to use for that.
UPDATE
As I mentioned above, I know how to synchronise threads etc. Im only interested in the situation that I have presented here. Mutexes, semaphores and locks all kinds of mechanisms will all synchronise access to resources, synchronise order of events, synchronise all kinds of concurrently issues, yes. But Im not interested in how to do it properly. I just have this made up situation that I wish to know if it can be addressed with a mechanism as described prior.
UPDATE 2
It seems I have opened up a portal for people that think they are experts in concurrency to teleport and lecture at chance how they think the rest of world does not know how threading works. I simply asked if there is a mechanism for this situation, not a work around solution, not 'the proper way to synchronise', not a better way to do it. I already know what I would do and never be in this made up situation. It's simply hypothetical.
After much research, thought, and overview, I have come to the conclusion that its like asking:
If a calculator has the ability for me simply enter a series of 5 digits and automatically get their sum on the screen.
No, it does not have such a mode ready. But I can still get the sum with a few extra clicks using the plus and eventually the equal button.
If i really wanted a thread that can continue while listening for a condition of some sort, I could easily implement a personal class or object around the OS/kernel/SDK thread or whatever and make use of that.
• So at a low level, my answer is no, there is no such mechanism •
If a thread is waiting, then it's waiting. If it can continue executing then it is not really 'waiting', in the concurrency meaning of waiting. Otherwise there would be some other term for this state (Alert Waiting, anyone?). This is not to say it is not possible, just not with one simple low level predefined mechanism similar to a mutex or semaphore etc. One could wrap the required functionality in some class or object etc.
Having said that, there are Interrupts and Interrupt handlers, which come close to addressing this situation. However, an interrupt has to be defined, with its handler. The interrupts may actually be running on another thread (not to say a thread per interrupt). So a number of objects are involved here.
You have a misunderstanding about how mutexes are typically used.
If you want to do some work, you acquire the mutex to figure out what work you need to do. You do this because "what work you need to do" is shared between the thread that decide what work needed to be done and the thread that's going to do the work. But then you release the mutex that protects "what work you need to do" while you do the work.
Then, when you finish the work, you acquire the mutex that protects your report that the work is done. This is needed because the status of the work is shared with other threads. You set that status to "done" and then you release the mutex.
Notice that no thread holds the mutex for very long, just for the microscopic fraction of a second it needs to check on or modify shared state. So to see if work is done, you can acquire the mutex that protects the reporting of the status of that work, check the status, and then release the mutex. The thread doing the work will not hold that mutex for longer than the tiny fraction of a second it needs to change that status.
If you're holding mutexes so long that you worry at all about waiting for them to be released, you're either doing something wrong or using mutexes in a very atypical way.
So use a mutex to protect the status of the work. If you need to wait for work to be done, also use a condition variable. Only hold that mutex while changing, or checking, the status of the work.
But, If a thread attempts to acquire an already acquired mutex, that thread will be forced to wait until the thread that originally acquired the mutex releases it. So, while that thread is waiting, can it actually be usable. This is where my question is.
If you consider any case where one thread might slow another thread down to be "waiting", then you can never avoid waiting. All that has to happen is one thread accesses memory and that might slow another thread down. So what do you do, never access memory?
When we talk about one thread "waiting" for another, what we mean is waiting for the thread to do actual work. We don't worry about the microscopic overhead of inter-thread synchronization both because there's nothing we can do about it and because it's negligible.
If you literally want to find some way that one thread can never, ever slow another thread down, you'll have to re-design pretty much everything we use threads for.
Update:
For example, consider some code that has a mutex and a boolean. The boolean indicates whether or not the work is done. The "assign work" flow looks like this:
Create a work object with a mutex and a boolean. Set the boolean to false.
Dispatch a thread to work on that object.
The "do work" flow looks like this:
Do work. (The mutex is not held here.)
Acquire mutex.
Set boolean to true.
Release mutex.
The "is work done" flow looks like this:
Acquire mutex.
Copy boolean.
Release mutex.
Look at copied value.
This allows one thread to do work and another thread to check if the work is done any time it wants to while doing other things. The only case where one thread waits for the other is the one-in-a-million case where a thread that needs to check if the work is done happens to check right at the instant that the work has just finished. Even in that case, it will typically block for less than a microsecond as the thread that holds the mutex only needs to set one boolean and release the mutex. And if even that bothers you, most mutexes have a non-blocking "try to lock" function (which you would use in the "check if work is done" flow so that the checking thread never blocks).
And this is the normal way mutexes are used. Actual contention is the exception, not the rule.

Calling action from within lock/semaphore

I have a function which accepts an action. The function obtains a semaphore lock (but for the purposes of the question could also be a monitor lock) and then calls the action.
A code reviewer has stated does not represent an effective way to implement thread-safety because it is prone to deadly embrace. Thread-safe code should be encapsulated but you break this by allowing a third-party to invoke an external action. (It's like raising an event inside a lock.)
Ignoring the encapsulation bit, is there any special case with calling actions from with a lock? My instinct is to say an action is no more likely to incur a deadlock than any other code but before i challenge that, is he right??
The problem with calling external code when lock is acquired is that you cannot guarantee anymore that your code is deadlock-safe.
The caller can do anything in the callback action.
Here is a few examples when action might perform 'dangerous':
Recursive call of a function. Deadlock is possible in that case.
Perform some long running operation. That might downgrade the others threads performance if they need the same synch object (monitor or semaphore).
I believe there are the other negative cases also possible.
The purpose of a monitor (or a semaphore) is to prevent simultaneous entering into the code section, which is definitely should not be run simultaneously. That is not the case for the callback action.
So, there is no good reason to call action within the lock.
I would suggest here instead to call the callback action either before the lock is acquired or after it is released.

Is a lock (threading) atomic?

This may sound like a stupid question, but if one locks a resource in a multi-threaded app, then the operation that happens on the resource, is that done atomically?
I.E.: can the processor be interrupted or can a context switch occur while that resource has a lock on it? If it does, then nothing else can access this resource until it's scheduled back in to finish off it's process. Sounds like an expensive operation.
The processor can very definitely still switch to another thread, yes. Indeed, in most modern computers there can be multiple threads running simultaneously anyway. The locking just makes sure that no other thread can acquire the same lock, so you can make sure that an operation on that resource is atomic in terms of that resource. Code using other resources can operate completely independently.
You should usually lock for short operations wherever possible. You can also choose the granularity of locks... for example, if you have two independent variables in a shared object, you could use two separate locks to protect access to those variables. That will potentially provide better concurrency - but at the same time, more locks means more complexity and more potential for deadlock. There's always a balancing act when it comes to concurrency.
You're exactly right. That's one reason why it's so important to lock for short period of time. However, this isn't as bad as it sounds because no other thread that's waiting on the lock will get scheduled until the thread holding the lock releases it.
Yes, a context switch can definitely occur.
This is exactly why when accessing a shared resource it is important to lock it from another thread as well. When thread A has the lock, thread B cannot access the code locked.
For example if two threads run the following code:
1. lock(l);
2. -- change shared resource S here --
3. unlock(l);
A context switch can occur after step 1, but the other thread cannot hold the lock at that time, and therefore, cannot change the shared resource. If access to the shared resource on one of the threads is done without a lock - bad things can happen!
Regarding the wastefulness, yes, it is a wasteful method. This is why there are methods that try to avoid locks altogether. These methods are called lock-free, and some of them are based on strong locking services such as CAS (Compare-And-Swap) or others.
No, it's not really expensive. There are typically only two possibilities:
1) The system has other things it can do: In this case, the system is still doing useful work with all available cores.
2) The system doesn't have anything else to do: In this case, the thread that holds the lock will be scheduled. A sane system won't leave a core unused while there's a ready-to-run thread that's not scheduled.
So, how can it be expensive? If there's nothing else for the system to do that doesn't require acquiring that lock (or not enough other things to occupy all cores) and the thread holding the lock is not ready-to-run. So that's the case you have to avoid, and the context switch or pre-empt issue doesn't matter (since the thread would be ready-to-run).

Code Re-entrancy vs. Thread Safety

What is the difference between the concepts of "Code Re-entrancy" and "Thread Safety"? As per the link mentioned below, a piece of code can be either of them, both of them or neither of them.
Reentrant and Thread safe code
I was not able to understand the explaination clearly. Help would be appreciated.
Re-entrant code has no state in a single point. You can call the code while something is executing in the code. If the code uses global state, one call can conceivably overwrite the global state, breaking the computation in the other call.
Thread safe code is code with no race conditions or other concurrency issues. A race condition is where the order in which two threads do something affects the computation. A typical concurrency issue is where a change to a shared data structure can be partially completed and left in an inconsistent state. In order to avoid this, you have to use concurrency control mechanisms such as semaphores of mutexes to ensure that nothing else can access the data structure until the operation is completed.
For example, a piece of code can be non re-entrant but thread-safe if it is guarded externally by a mutex but still has a global data structure where the state must be consistent for the entire duration of the call. In this case, the same thread could initiate a call-back into the procedure while still protected by an external coarse-grained mutex. If the call-back occured from within the non re-entrant procedure the call could leave the data structure in a state that could break the computation from the caller's point of view.
A piece of code can be re-entrant but non thread-safe if it can make a non-atomic change to a shared (and sharable) data structure that could be interrupted in the middle of the update leaving the data structure in an incosistent state. In this case another thread accessing the data structure could be affected by the half-changed data structure and either crash or perform an operation that corrupts the data.
That article says:
"a function can be either reentrant, thread-safe, both, or neither."
It also says:
"Non-reentrant functions are thread-unsafe".
I can see how this may cause a muddle. They mean that standard functions documented as not required to be re-entrant are also not required to be thread-safe, which is true of the POSIX libraries iirc (and POSIX declares it to be true of the ANSI/ISO libraries too, ISO having no concept of threads and hence no concept of thread-safety). In other words, "if a function says it is non-reentrant, then it is saying it's thread-unsafe too". That's not a logical necessity, it's just a convention.
Here's some pseudo-code which is thread-safe (well, there's plenty of opportunity for callbacks to create deadlocks due to locking inversion, but let's assume the documentation contains sufficient information for users to avoid that) but not re-entrant. It is supposed to increment the global counter, and perform the callback:
take_global_lock();
int i = get_global_counter();
do_callback(i);
set_global_counter(i+1);
release_global_lock();
If the callback calls this routine again, resulting in another callback, then both levels of callback will get the same parameter (which might be OK, depending on the API), but the counter will only be incremented once (which is almost certainly not the API you want, so it would have to be banned).
That's assuming the lock is recursive, of course. If the lock is non-recursive, then of course the code is non-reentrant anyway, since taking the lock the second time won't work.
Here's some pseudo-code which is "weakly re-entrant" but not thread-safe:
int i = get_global_counter();
do_callback(i);
set_global_counter(get_global_counter()+1);
Now it's fine to call the function from the callback, but it's not safe to call the function concurrently from different threads. It's also not safe to call it from a signal handler, because re-entrancy from a signal handler could likewise break the count if the signal happened to occur at the right time. So the code is non-re-entrant by the proper definition.
Here's some code which arguably is fully re-entrant (except I think the standard distinguishes between reentrant and 'non-interruptible by signals', and I'm not sure where this falls), but still isn't thread-safe:
int i = get_global_counter();
do_callback(i);
disable_signals(); // and any other kind of interrupts on your system
set_global_counter(get_global_counter()+1);
restore_signal_state();
On a single-threaded app, this is fine, assuming that the OS supports disabling everything that needs to be disabled. It prevents re-entrancy from occurring at the critical point. Depending how signals are disabled, it may be safe to call from a signal handler, although in this particular example there's still the issue of the parameter passed to the callback being the same for separate calls. It can still go wrong multi-threaded, though.
In practice, non-thread-safe often implies non-re-entrant, since (informally) anything that can go wrong due to the thread being interrupted by the scheduler, and the function called again from another thread, can also go wrong if the thread is interrupted by a signal, and the function is called again from the signal handler. But then the "fix" to prevent signals (disabling them) is different from the "fix" to prevent concurrency (locks, usually). This is at best a rule of thumb.
Note that I've implied globals here, but exactly the same considerations would apply if the function took as a parameter a pointer to the counter and the lock. It's just that the various cases would be thread-unsafe or non-re-entrant when called with the same parameter, rather than when called at all.

Resources