kernel: Allocate user_mutex_contexts when adding the second thread to a team.

This allows us to gracefully fail if memory allocation fails,
rather than panic()ing. If a single-threaded application tries
to wait on a non-shared user_mutex in single-threaded mode, it
will now be dropped into the debugger.
This commit is contained in:
Augustin Cavalier
2026-03-04 21:42:03 -05:00
parent e6569ec320
commit 7ef695df5c
3 changed files with 23 additions and 19 deletions
+2
View File
@@ -7,6 +7,7 @@
#include <SupportDefs.h> #include <SupportDefs.h>
#include <thread_types.h>
#ifdef __cplusplus #ifdef __cplusplus
@@ -17,6 +18,7 @@ struct user_mutex_context;
void user_mutex_init(); void user_mutex_init();
void delete_user_mutex_context(struct user_mutex_context* context); void delete_user_mutex_context(struct user_mutex_context* context);
status_t allocate_team_user_mutex_context(Team* team);
status_t _user_mutex_lock(int32* mutex, const char* name, uint32 flags, status_t _user_mutex_lock(int32* mutex, const char* name, uint32 flags,
bigtime_t timeout); bigtime_t timeout);
+13 -19
View File
@@ -219,31 +219,25 @@ user_mutex_init()
} }
struct user_mutex_context* status_t
get_team_user_mutex_context() allocate_team_user_mutex_context(Team* team)
{ {
struct user_mutex_context* context = team->AssertLocked();
thread_get_current_thread()->team->user_mutex_context; ASSERT(team->user_mutex_context == NULL);
if (context != NULL)
return context;
Team* team = thread_get_current_thread()->team; struct user_mutex_context* context = new(std::nothrow) user_mutex_context;
TeamLocker teamLocker(team);
if (team->user_mutex_context != NULL)
return team->user_mutex_context;
context = new(std::nothrow) user_mutex_context;
if (context == NULL) if (context == NULL)
return NULL; return B_NO_MEMORY;
context->lock = RW_LOCK_INITIALIZER("user mutex table"); context->lock = RW_LOCK_INITIALIZER("user mutex table");
if (context->table.Init() != B_OK) { status_t status = context->table.Init();
if (status != B_OK) {
delete context; delete context;
return NULL; return status;
} }
team->user_mutex_context = context; team->user_mutex_context = context;
return context; return B_OK;
} }
@@ -469,10 +463,10 @@ struct UserMutexContextFetcher {
fAddress(0) fAddress(0)
{ {
if (!fShared) { if (!fShared) {
fContext = get_team_user_mutex_context(); fContext = thread_get_current_thread()->team->user_mutex_context;
if (fContext == NULL) { if (fContext == NULL) {
panic("UserMutexContext allocation failed!"); _user_debugger("single-threaded team attempted mutex operation");
fInitStatus = B_NO_MEMORY; fInitStatus = EDEADLK;
return; return;
} }
+8
View File
@@ -46,6 +46,7 @@
#include <tls.h> #include <tls.h>
#include <user_runtime.h> #include <user_runtime.h>
#include <user_thread.h> #include <user_thread.h>
#include <user_mutex.h>
#include <vfs.h> #include <vfs.h>
#include <vm/vm.h> #include <vm/vm.h>
#include <vm/VMAddressSpace.h> #include <vm/VMAddressSpace.h>
@@ -1084,6 +1085,13 @@ thread_create_thread(const ThreadCreationAttributes& attributes, bool kernel)
bool debugNewThread = false; bool debugNewThread = false;
if (!kernel) { if (!kernel) {
// ensure there's a user_mutex_context, if this isn't the main thread
if (team->main_thread != NULL && team->user_mutex_context == NULL) {
status_t status = allocate_team_user_mutex_context(team);
if (status != B_OK)
return status;
}
// allocate the user_thread structure, if not already allocated // allocate the user_thread structure, if not already allocated
if (thread->user_thread == NULL) { if (thread->user_thread == NULL) {
thread->user_thread = team_allocate_user_thread(team); thread->user_thread = team_allocate_user_thread(team);