mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 10:08:12 +00:00
Kernel/Locking: Add lock rank tracking per thread to find deadlocks
This change adds a static lock hierarchy / ranking to the Kernel with the goal of reducing / finding deadlocks when running with SMP enabled. We have seen quite a few lock ordering deadlocks (locks taken in a different order, on two different code paths). As we properly annotate locks in the system, then these facilities will find these locking protocol violations automatically The `LockRank` enum documents the various locks in the system and their rank. The implementation guarantees that a thread holding one or more locks of a lower rank cannot acquire an additional lock with rank that is greater or equal to any of the currently held locks.
This commit is contained in:
parent
0718afa773
commit
066b0590ec
5 changed files with 128 additions and 0 deletions
|
@ -1226,6 +1226,56 @@ bool Thread::should_be_stopped() const
|
|||
return process().is_stopped();
|
||||
}
|
||||
|
||||
void Thread::track_lock_acquire(LockRank rank)
|
||||
{
|
||||
// Nothing to do for locks without a rank.
|
||||
if (rank == LockRank::None)
|
||||
return;
|
||||
|
||||
if (m_lock_rank_mask != LockRank::None) {
|
||||
// Verify we are only attempting to take a lock of a higher rank.
|
||||
VERIFY(m_lock_rank_mask > rank);
|
||||
}
|
||||
|
||||
m_lock_rank_mask |= rank;
|
||||
}
|
||||
|
||||
void Thread::track_lock_release(LockRank rank)
|
||||
{
|
||||
// Nothing to do for locks without a rank.
|
||||
if (rank == LockRank::None)
|
||||
return;
|
||||
|
||||
using PrimitiveType = UnderlyingType<LockRank>;
|
||||
|
||||
// The rank value from the caller should only contain a single bit, otherwise
|
||||
// we are disabling the tracking for multiple locks at once which will corrupt
|
||||
// the lock tracking mask, and we will assert somewhere else.
|
||||
auto rank_is_a_single_bit = [](auto rank_enum) -> bool {
|
||||
auto rank = static_cast<PrimitiveType>(rank_enum);
|
||||
auto rank_without_least_significant_bit = rank - 1;
|
||||
return (rank & rank_without_least_significant_bit) == 0;
|
||||
};
|
||||
|
||||
// We can't release locks out of order, as that would violate the ranking.
|
||||
// This is validated by toggling the least significant bit of the mask, and
|
||||
// then bit wise or-ing the rank we are trying to release with the resulting
|
||||
// mask. If the rank we are releasing is truly the highest rank then the mask
|
||||
// we get back will be equal to the current mask of stored on the thread.
|
||||
auto rank_is_in_order = [](auto mask_enum, auto rank_enum) -> bool {
|
||||
auto mask = static_cast<PrimitiveType>(mask_enum);
|
||||
auto rank = static_cast<PrimitiveType>(rank_enum);
|
||||
auto mask_without_least_significant_bit = mask - 1;
|
||||
return ((mask & mask_without_least_significant_bit) | rank) == mask;
|
||||
};
|
||||
|
||||
VERIFY(has_flag(m_lock_rank_mask, rank));
|
||||
VERIFY(rank_is_a_single_bit(rank));
|
||||
VERIFY(rank_is_in_order(m_lock_rank_mask, rank));
|
||||
|
||||
m_lock_rank_mask ^= rank;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AK::Formatter<Kernel::Thread>::format(FormatBuilder& builder, const Kernel::Thread& value)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue