39

C++14 seems to have omitted a mechanism for checking whether an std::mutex is locked or not. See this SO question:

https://stackoverflow.com/questions/21892934/how-to-assert-if-a-stdmutex-is-locked

There are several ways around this, e.g. by using;

std::mutex::try_lock()
std::unique_lock::owns_lock()

But neither of these are particularly satisfying solutions.

try_lock() is permitted to return a false negative and has undefined behaviour if the current thread has locked the mutex. It also has side-effects. owns_lock() requires the construction of a unique_lock on top of the original std::mutex.

Obviously I could roll my own, but I'd rather understand the motivations for the current interface.

The ability to check the status of a mutex (e.g. std::mutex::is_locked()) does not seem like an esoteric request to me, so I suspect the Standard Committee deliberately omitted this feature rather than it being an oversight.

Why?

Edit: Ok so maybe this use case isn't as common as I had expected, so I'll illustrate my particular scenario. I have a machine learning algorithm which is distributed on multiple threads. Each thread operates asynchronously, and returns to a master pool once it has completed an optimisation problem.

It then locks a master mutex. The thread must then pick a new parent from which to mutate an offspring, but may only pick from parents which do not currently have offspring that are being optimised by other threads. I therefore need to perform a search to find parents that are not currently locked by another thread. There is no risk of the status of the mutex changing during the search, as the master thread mutex is locked. Obviously there's other solutions (I'm currently using a boolean flag) but I thought the mutex offers a logical solution to this problem, as it exists for the purpose of inter-thread synchronization.

12
  • 51
    You can't really reasonably check whether a mutex is locked, because one nanosecond after the check it can get unlocked or locked. So if you wrote "if (mutex_is_locked ()) ... " then mutex_is_locked could return the correct result, but by the time the "if" is executed, it is wrong.
    – gnasher729
    Commented Sep 11, 2016 at 13:33
  • 1
    This^. What useful information do you hope to get from is_locked?
    – Useless
    Commented Sep 11, 2016 at 13:34
  • 3
    This feels like an XY-problem. Why are you trying to prevent reuse of the parents only while a child is being generated? Do you have a requirement that any parent may only have exactly one offspring? Your lock will not prevent that. Don't you have clear generations? If not, are you aware that individuals that can be optimized faster have higher fitness, since they can be selected more often/earlier? If you do use generations, why don't you select all parents up front, then let the threads retrieve parents from a queue? Is generating offspring really so expensive that you need multiple threads?
    – amon
    Commented Sep 11, 2016 at 14:01
  • 10
    @quant - I don't see why your parent object mutexes in your example application need to be mutexes at all: if you have a master mutex which is locked whenever they are set, you can just use a boolean variable to indicate their status. Commented Sep 11, 2016 at 14:12
  • 4
    I disagree with the last sentence of the question. A simple boolean value is far cleaner than a mutex here. Make it an atomic bool if you don't want to lock the master mutex for "returning" a parent. Commented Sep 12, 2016 at 9:42

7 Answers 7

61

I can see at least two severe problems with the suggested operation.

The first one was already mentioned in a comment by @gnasher729:

You can't really reasonably check whether a mutex is locked, because one nanosecond after the check it can get unlocked or locked. So if you wrote if (mutex_is_locked ()) … then mutex_is_locked could return the correct result, but by the time the if is executed, it is wrong.

The only way to be sure that the “is currently locked” property of a mutex doesn't change is to, well, lock it yourself.

The second problem I see is that unless you lock a mutex, your thread doesn't synchronize with the thread that had previously locked the mutex. Therefore, it isn't even well-defined to speak about “before” and “after” and whether the mutex is locked or not is kind of asking whether Schrödiger's cat is currently alive without attempting to open the box.

If I understand correctly, then both problems would be moot in your particular case thanks to the master mutex being locked. But this doesn't seem like a particularly common case to me so I think that the committee did the right thing by not adding a function that might be somewhat useful in very special scenarios and cause damage in all others. (In the spirit of: “Make interfaces easy to use correctly and difficult to use incorrectly.”)

And if I may say, I think that the setup you currently have is not the most elegant and could be refactored to avoid the problem altogether. For example, instead of the master thread checking all potential parents for one that is not currently locked, why not maintain a queue of ready parents? If a thread wants to optimize another one, it pops the next one off the queue and as soon as it has new parents, it adds them to the queue. That way, you don't even need the master thread as a coordinator.

7
  • 15
    @quant: If you have two purposes to queue parents, you can do so with two queues....
    – user122173
    Commented Sep 11, 2016 at 17:40
  • 6
    But it is reasonable to ask whether the current thread has locked the mutex. Commented May 18, 2018 at 19:20
  • 1
    @LimitedAtonement Not really. To do this mutex has to store additional information (thread id), making it slower. Recursive mutexes do this already, you should them instead.
    – user289860
    Commented Sep 6, 2018 at 12:04
  • 5
    The answers here seem to presuppose that the only reason might want to determine whether a mutex is locked is to affect the programs' control flow. This does have the mentioned hazards. But the reason I want to be able to inspect whether a mutex is locked is for debugging by observing the mutex's status from another thread. Commented Apr 8, 2020 at 19:52
  • 3
    The severely problematic operation is one you need in order to assert liberally, because in that code style, you often need to assert that someone up the call stack has locked something.
    – arnt
    Commented Aug 18, 2021 at 8:36
9

It seems that you are using the secondary mutexes not to lock access to an optimisation problem, but to determine whether an optimisation problem is being optimised right now or not.

That is completely unnecessary. I'd have a list of problems that need optimising, a list of problems being optimised right now, and a list of problems that have been optimised. (Don't take "list" literally, take it to mean "any appropriate data structure).

The operations of adding a new problem to the list of unoptimised problems, or moving a problem from one list to the next, would be done under protection of the single "master" mutex.

3
  • 1
    You don't think that an object of type std::mutex is appropriate for such a data structure?
    – quant
    Commented Sep 11, 2016 at 18:31
  • 2
    @quant - no. std::mutex relies on an operating-system defined mutex implementation that may well take resources (e.g. handles) that are limited and slow to allocate and/or operate on. Using a single mutex to lock access to an internal data structure is likely to be much more efficient, and possibly more scalable too. Commented Sep 11, 2016 at 18:44
  • 1
    Also consider Condition Variables. They can make a lot of data structures like this really easy.
    – Cort Ammon
    Commented Sep 12, 2016 at 0:26
2

In addition to the two reasons given in 5gon12eder's answer above, I'd like to add that it is neither necessary nor desirable.

If you are already holding a mutex, then you had better know that you're holding it! You don't need to ask. Just like with owning a block of memory or any other resource, you should know exactly whether or not you own it, and when it's appropriate to release/delete the resource.
If that is not the case, your program is designed badly, and you are heading for trouble.

If you need to access the shared resource protected by the mutex, and you are not already holding the mutex, then you need to acquire the mutex. There's no other option, otherwise your program logic is not correct.
You might find blocking acceptable or inacceptable, in either case lock() or try_lock() will give the behavior you want. All you need to know, positively, and without doubt, is whether you successfully acquired the mutex (the return value of try_lock tells you). It's inconsequential whether someone else holds it or whether you got a spurious failure.

In every other case, bluntly, it's none of your business. You don't need to know, and you shouldn't know, or make assumptions (for the timeliness and sync issues mentioned in the other question).

5
  • 1
    What if I want to perform a ranking operation on the resources that are currently available for locking?
    – quant
    Commented Sep 11, 2016 at 16:40
  • But is that something that is realistic to happen? I'd deem that rather unusual. I would say either resources already have some intrinsic kind of ranking, then you will need to do (acquire the lock for) the more important one first. Example: Must update physics simulation prior to rendering. Or, the ranking is more or less deliberate, then you can as well try_lock the first resource, and if that one fails, try the second. Example: Three persistent, pooled connections to the database server, and you need to use one to send a command.
    – Damon
    Commented Sep 11, 2016 at 17:23
  • 4
    @quant - "a ranking operation on the resources that are currently available for locking" -- in general, doing this kind of thing is a really easy and quick way to write code that deadlocks in a way you struggle to figure out. Making locks acquisition and release deterministic is, in almost all cases, the best policy. Searching for a lock based on a criterion that could change is asking for trouble. Commented Sep 11, 2016 at 18:41
  • @PeriataBreatta My program is intentionally indeterminate. I see now that this attribute is not common, so I can understand the omission of features like is_locked() that might facilitate such behaviour.
    – quant
    Commented Sep 11, 2016 at 18:43
  • @quant ranking and locking are entirely separate problems. If you want to somehow sort or reorder a queue with a lock, lock it, sort it, then unlock it. If you need is_locked, then a much better solution to your problem exists than the one you have in mind.
    – Peter
    Commented Sep 12, 2016 at 18:33
2

As others said, there's no use case where is_locked on a mutex is of any benefit, that's why the function does not exist.

The case you're having a problem with is incredibly common, it's basically what worker threads do, which are one of the, if not the most common implementation of threads.

You have a shelf with 10 boxes on it. You have 4 workers working with these boxes. How do you make sure the 4 workers work on different boxes? The first worker takes a box off the shelf before they start working on it. The second worker sees 9 boxes on the shelf.

There are no mutexes to lock the boxes, so seeing the state of the imaginary mutex on the box is not necessary, and abusing a mutex as a boolean is just wrong. The mutex locks the shelf.

4
  • 2
    Being able to assert() on the state of a mutex is a use case.
    – arnt
    Commented Aug 18, 2021 at 8:42
  • What about the use case of a non-blocking lock()? If I can't lock the mutex immediately, fail instead of blocking until it unlocks. The engineering I have to do to limit the number of locked threads seems unnecessary if I could just fail the thread and try again later. Commented Feb 8, 2022 at 22:18
  • @TopherIsSwell is_locked() is useless for that purpose because it doesn't allow you to lock or fail in an atomic operation. What you need is try_lock(), which exists because it is useful.
    – Peter
    Commented Feb 10, 2022 at 14:24
  • try_lock() is exactly what I was after. Thank you. Commented Feb 14, 2022 at 18:40
1

I want to add a use-case for this: It would enable an internal function to ensure as a precondition / assertion that the caller is indeed holding the lock.

For classes with several such internal functions, and possibly many public functions calling them, it could ensure, that someone adding another public function calling the internal one did indeed acquire the lock.

class SynchronizedClass
{

public:

void publicFunc()
{
  std::lock_guard<std::mutex>(_mutex);

  internalFuncA();
}

// A lot of code

void newPublicFunc()
{
  internalFuncA(); // whops, forgot to acquire the lock
}


private:

void internalFuncA()
{
  assert(_mutex.is_locked_by_this_thread());

  doStuffWithLockedResource();
}

};
1
  • In my experience, which I'll freely admit includes very little OOP, it's best to have a single context where you do locking. So there would be methods that assume no thread safety, methods that do locking/unlocking, methods that are only called on locked data structures (utilities for the thread safe functions). But every rule about best practice always has exceptions. Commented Feb 8, 2022 at 22:25
0

You may be wanting to use atomic_flag with the default memory order. It doesn't have data races and never throws exceptions like mutex does with multiple unlock calls (and aborts uncontrollably, I might add...). Alternatively, there is atomic (e.g. atomic[bool] or atomic[int] (with triangle brackets, not [])), which has nice functions like load and compare_exchange_strong.

-3

My use case for also wanting is_locked() involves using mutexes for automatic cleanup by the OS if an application crashes.

Process #1: 1) takes mutex (to avoid multiple processes being a "master") 2) sets information in shared memory 3) Normally, clears information 4) releases mutex

If process #1 crashes, the mutex is released by the OS, but the shared memory information remains.

Pocess #2 wants to validate the shared memory information, but it is not valid if the mutex is no longer set, but has no interest in being a "master".

2

Not the answer you're looking for? Browse other questions tagged or ask your own question.