This PR replaces RecursiveMutex CTxMemPool::cs
with Mutex CTxMemPool:cs
.
All of the related code branches are covered by appropriate lock assertions to insure that the mutex locking policy has not been changed by accident
Related to #19303.
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
Reviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
From IRC:
<jeremyrubin> I think it’s basically getting rid of a recursive mutex for code that’s still designed to take a recursive mutex <gwillen> it’s better than a true recursive mutex because it’s not possible to recurse by accident, you have to declare at call time which behavior you want (although better if you had to declare statically at compile time) <sipa> it looks like that <jeremyrubin> The correct refactor would be to make the code not do anything fancy with locks, or to just leave it <jeremyrubin> gwillen: I think the chances of a bug or error in custom logic is higher than a recursive mutex <jeremyrubin> accidental recursion seems unlikely… <jeremyrubin> and accidental recursion would be a bug lock or no <gwillen> (sorry I mean, accidental mutex recursion, that is, calling a function while holding a mutex, not expecting the callee to also lock it, resulting in the callee violating the caller’s invariants) <gwillen> (this is the fundamental problem of recursive mutexes) <gwillen> (and I assume the motivation behind hebasto’s refactor)
Correct, @gwillen :)
Currently this adds a lot of code complexity. Also, it adds mental complexity to write code that doesn’t crash or deadlock/UB whereas the RecursiveMutex
just works (TM).
Maybe there is a way to achieve the same without the added complexity? For example, always force the caller to take the lock for the right scope. This would also solve issues of non-atomic RPC responses or at make them more visible. Though, it makes caller code slightly more verbose.
Currently this adds a lot of code complexity. Also, it adds mental complexity to write code that doesn’t crash or deadlock/UB whereas the
RecursiveMutex
just works (TM).
A function that locks RecursiveMutex
there are no guarantees that protected invariants are held before locking. This adds mental complexity to read code.
Maybe there is a way to achieve the same without the added complexity? For example, always force the caller to take the lock for the right scope. This would also solve issues of non-atomic RPC responses or at make them more visible.
I assume these steps are much easier for non-recursive mutex, no?
@hebasto Do you have any thoughts on “function takes bool locked
” versus “split function into _locked and _unlocked variants”?
In some of the cases here, I see that the latter would require some (maybe substantial) refactoring to avoid code duplication, and I assume that’s why you didn’t go that route. But that would not only eliminate conditional locking (which is scary), it would probably allow the use of RAII locks. I’m curious to hear your thoughts.
@hebasto Do you have any thoughts on “function takes
bool locked
” versus “split function into _locked and _unlocked variants”?
For new code I prefer clean “split function into _locked and _unlocked variants” like #19238 (review). Unfortunately, that is not the case in this PR.
In some of the cases here, I see that the latter would require some (maybe substantial) refactoring to avoid code duplication, and I assume that’s why you didn’t go that route.
You are correct. I’ve used the “function takes bool locked
” approach in 72f7486b5ebe96762c5d5a68849c61e58c812ffd and 21787abedd7a0808c0175a0b1d795df97fd3b970 as a quick-and-dirty solution to fix broken tests.
But that would not only eliminate conditional locking (which is scary), it would probably allow the use of RAII locks. I’m curious to hear your thoughts.
Hmm…
In 21787abedd7a0808c0175a0b1d795df97fd3b970 I’ve had to drop the RAII lock in favor of a pair ENTER_CRITICAL_SECTION()
- LEAVE_CRITICAL_SECTION()
. That is why I dont’t like this approach.