Add APIs to retrieve the thread ID of the owner of a lock. It is commonly needed in error handlers to have to check if the thread on which the error handler was invoked was holding a particular lock when it crashed. Typically, we would want to do this to be able to forcefully release the lock. This is currently difficult to do, as the thread ID which is stored in priority inheriting flag locks is not exposed through the C/C++ APIs. This commit adds news APIs `get_owner_thread_id` (C++) and `flaglock_priority_inheriting_get_owner_thread_id` (C) to retrieve the thread ID of the owner of a lock. Following this commit, error handlers can call `get_owner_thread_id` (or `flaglock_priority_inheriting_get_owner_thread_id`) on a lock and compare the result with `thread_id_get` to know if the thread which crashed owned the lock. Signed-off-by: Hugo Lefeuvre <hugo.lefeuvre@scisemi.com>
diff --git a/sdk/include/locks.h b/sdk/include/locks.h index 1dbab8c..c33474a 100644 --- a/sdk/include/locks.h +++ b/sdk/include/locks.h
@@ -144,6 +144,33 @@ flaglock_upgrade_for_destruction(struct FlagLockState *lock); /** + * Return the thread ID of the owner of the lock. + * + * This is only available for priority inherited locks, as this is the only + * case where we store the thread ID of the owner. + * + * The return value is 0 if the lock is not owned or if called on a + * non-priority inherited flag lock. The return value is undefined if called on + * an uninitialized lock. + * + * This *will* race with succesful `lock` and `unlock` operations on other + * threads, and should thus not be used to check if the lock is owned. + * + * The main use case for this function is in the error handler to check whether + * or not the lock is owned by the thread on which the error handler was + * invoked. In this case we can call this function and compare the result with + * `thread_id_get` to know if the current thread owns the lock. + */ +__always_inline static inline uint16_t +flaglock_priority_inheriting_get_owner_thread_id(struct FlagLockState *lock) +{ + // The lock must be held at this point for the value to be stable so do + // a non-atomic read (simply &ing the lock word would result in a + // libcall for the atomic operation). + return ((*(uint32_t *)&(lock->lockWord)) & 0x0000ffff); +} + +/** * Try to acquire a recursive mutex. This is a priority-inheriting mutex that * can be acquired multiple times by the same thread. *
diff --git a/sdk/include/locks.hh b/sdk/include/locks.hh index 3f7808e..6819f84 100644 --- a/sdk/include/locks.hh +++ b/sdk/include/locks.hh
@@ -93,6 +93,19 @@ { flaglock_upgrade_for_destruction(&state); } + + /** + * Return the thread ID of the owner of the lock. + * + * This is only available for priority inherited locks, as this is the + * only case where we store the thread ID of the owner. See the + * documentation of `flaglock_priority_inheriting_get_owner_thread_id` + * for more information. + */ + __always_inline uint16_t get_owner_thread_id() requires(IsPriorityInherited) + { + return flaglock_priority_inheriting_get_owner_thread_id(&state); + } }; /**
diff --git a/tests/locks-test.cc b/tests/locks-test.cc index 20798c8..7b8db43 100644 --- a/tests/locks-test.cc +++ b/tests/locks-test.cc
@@ -202,6 +202,42 @@ "Unlocking unsets the destruction bit of flag lock"); } + /** + * Test that `get_owner_thread_id` returns the thread ID of the owner + * of the lock. + */ + void test_get_owner_thread_id(FlagLockPriorityInherited &lock) + { + debug_log("Testing that `get_owner_thread_id` works."); + + TEST(lock.get_owner_thread_id() == 0, + "`get_owner_thread_id` does not return 0 when called on an unheld " + "lock"); + + modified = false; + LockGuard g{lock}; + uint16_t ownerThreadId = thread_id_get(); + TEST(lock.get_owner_thread_id() == ownerThreadId, + "`get_owner_thread_id` does not return the thread ID of the lock " + "owner"); + + async([ownerThreadId, &lock]() { + TEST(thread_id_get() != ownerThreadId, + "Async has the same thread ID as the main thread"); + TEST(lock.get_owner_thread_id() == ownerThreadId, + "`get_owner_thread_id` does not return the thread ID of the " + "lock owner when called from a non-owning thread"); + modified = true; + }); + + sleep(1); + while (!modified) + { + debug_log("Other thread not finished, yielding"); + sleep(1); + } + } + void test_recursive_mutex() { static RecursiveMutexState recursiveMutex; @@ -365,6 +401,7 @@ test_lock(flagLock); test_lock(flagLockPriorityInherited); test_lock(ticketLock); + test_get_owner_thread_id(flagLockPriorityInherited); test_flaglock_unlock(); test_trylock(flagLock); test_trylock(flagLockPriorityInherited);