1813d4cbeSIngo Weinhold /*
20ab9f280SAugustin Cavalier * Copyright 2023, Haiku, Inc. All rights reserved.
39dd4d2ddSJérôme Duval * Copyright 2018, Jérôme Duval, jerome.duval@gmail.com.
4d6d439f3SHamish Morrison * Copyright 2015, Hamish Morrison, hamishm53@gmail.com.
5813d4cbeSIngo Weinhold * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
6813d4cbeSIngo Weinhold * Distributed under the terms of the MIT License.
7813d4cbeSIngo Weinhold */
8813d4cbeSIngo Weinhold
9813d4cbeSIngo Weinhold
10813d4cbeSIngo Weinhold #include <user_mutex.h>
11813d4cbeSIngo Weinhold #include <user_mutex_defs.h>
12813d4cbeSIngo Weinhold
13813d4cbeSIngo Weinhold #include <condition_variable.h>
14813d4cbeSIngo Weinhold #include <kernel.h>
15813d4cbeSIngo Weinhold #include <lock.h>
16813d4cbeSIngo Weinhold #include <smp.h>
17813d4cbeSIngo Weinhold #include <syscall_restart.h>
18813d4cbeSIngo Weinhold #include <util/AutoLock.h>
1993d7d1c5SAugustin Cavalier #include <util/ThreadAutoLock.h>
20813d4cbeSIngo Weinhold #include <util/OpenHashTable.h>
21813d4cbeSIngo Weinhold #include <vm/vm.h>
22813d4cbeSIngo Weinhold #include <vm/VMArea.h>
23fb688aa1SAugustin Cavalier #include <arch/generic/user_memory.h>
24813d4cbeSIngo Weinhold
25813d4cbeSIngo Weinhold
260ab9f280SAugustin Cavalier /*! One UserMutexEntry corresponds to one mutex address.
270ab9f280SAugustin Cavalier *
280ab9f280SAugustin Cavalier * The mutex's "waiting" state is controlled by the rw_lock: a waiter acquires
290ab9f280SAugustin Cavalier * a "read" lock before initiating a wait, and an unblocker acquires a "write"
300ab9f280SAugustin Cavalier * lock. That way, unblockers can be sure that no waiters will start waiting
310ab9f280SAugustin Cavalier * during unblock, and they can thus safely (without races) unset WAITING.
320ab9f280SAugustin Cavalier */
3313491fd2SAugustin Cavalier struct UserMutexEntry {
3493d7d1c5SAugustin Cavalier generic_addr_t address;
3513491fd2SAugustin Cavalier UserMutexEntry* hash_next;
3613491fd2SAugustin Cavalier int32 ref_count;
3713491fd2SAugustin Cavalier
380ab9f280SAugustin Cavalier rw_lock lock;
39813d4cbeSIngo Weinhold ConditionVariable condition;
40813d4cbeSIngo Weinhold };
41813d4cbeSIngo Weinhold
42813d4cbeSIngo Weinhold struct UserMutexHashDefinition {
4393d7d1c5SAugustin Cavalier typedef generic_addr_t KeyType;
44813d4cbeSIngo Weinhold typedef UserMutexEntry ValueType;
45813d4cbeSIngo Weinhold
HashKeyUserMutexHashDefinition4693d7d1c5SAugustin Cavalier size_t HashKey(generic_addr_t key) const
47813d4cbeSIngo Weinhold {
48813d4cbeSIngo Weinhold return key >> 2;
49813d4cbeSIngo Weinhold }
50813d4cbeSIngo Weinhold
HashUserMutexHashDefinition51813d4cbeSIngo Weinhold size_t Hash(const UserMutexEntry* value) const
52813d4cbeSIngo Weinhold {
53813d4cbeSIngo Weinhold return HashKey(value->address);
54813d4cbeSIngo Weinhold }
55813d4cbeSIngo Weinhold
CompareUserMutexHashDefinition5693d7d1c5SAugustin Cavalier bool Compare(generic_addr_t key, const UserMutexEntry* value) const
57813d4cbeSIngo Weinhold {
58813d4cbeSIngo Weinhold return value->address == key;
59813d4cbeSIngo Weinhold }
60813d4cbeSIngo Weinhold
GetLinkUserMutexHashDefinition61813d4cbeSIngo Weinhold UserMutexEntry*& GetLink(UserMutexEntry* value) const
62813d4cbeSIngo Weinhold {
6313491fd2SAugustin Cavalier return value->hash_next;
64813d4cbeSIngo Weinhold }
65813d4cbeSIngo Weinhold };
66813d4cbeSIngo Weinhold
67813d4cbeSIngo Weinhold typedef BOpenHashTable<UserMutexHashDefinition> UserMutexTable;
68813d4cbeSIngo Weinhold
69813d4cbeSIngo Weinhold
7093d7d1c5SAugustin Cavalier struct user_mutex_context {
7193d7d1c5SAugustin Cavalier UserMutexTable table;
7293d7d1c5SAugustin Cavalier rw_lock lock;
7393d7d1c5SAugustin Cavalier };
7493d7d1c5SAugustin Cavalier static user_mutex_context sSharedUserMutexContext;
75c5a0df24SAugustin Cavalier static const char* kUserMutexEntryType = "umtx entry";
76813d4cbeSIngo Weinhold
77813d4cbeSIngo Weinhold
78cf1b26a9SAugustin Cavalier // #pragma mark - user atomics
79cf1b26a9SAugustin Cavalier
80cf1b26a9SAugustin Cavalier
81cf1b26a9SAugustin Cavalier static int32
user_atomic_or(int32 * value,int32 orValue,bool isWired)82fb688aa1SAugustin Cavalier user_atomic_or(int32* value, int32 orValue, bool isWired)
83cf1b26a9SAugustin Cavalier {
84fb688aa1SAugustin Cavalier int32 result;
85fb688aa1SAugustin Cavalier if (isWired) {
86*6f88de11SAugustin Cavalier arch_cpu_enable_user_access();
87fb688aa1SAugustin Cavalier result = atomic_or(value, orValue);
88*6f88de11SAugustin Cavalier arch_cpu_disable_user_access();
89cf1b26a9SAugustin Cavalier return result;
90cf1b26a9SAugustin Cavalier }
91cf1b26a9SAugustin Cavalier
92fb688aa1SAugustin Cavalier return user_access([=, &result] {
93fb688aa1SAugustin Cavalier result = atomic_or(value, orValue);
94fb688aa1SAugustin Cavalier }) ? result : INT32_MIN;
95cf1b26a9SAugustin Cavalier }
96cf1b26a9SAugustin Cavalier
97cf1b26a9SAugustin Cavalier
98cf1b26a9SAugustin Cavalier static int32
user_atomic_and(int32 * value,int32 andValue,bool isWired)99fb688aa1SAugustin Cavalier user_atomic_and(int32* value, int32 andValue, bool isWired)
100cf1b26a9SAugustin Cavalier {
101fb688aa1SAugustin Cavalier int32 result;
102fb688aa1SAugustin Cavalier if (isWired) {
103*6f88de11SAugustin Cavalier arch_cpu_enable_user_access();
104fb688aa1SAugustin Cavalier result = atomic_and(value, andValue);
105*6f88de11SAugustin Cavalier arch_cpu_disable_user_access();
106cf1b26a9SAugustin Cavalier return result;
107cf1b26a9SAugustin Cavalier }
108cf1b26a9SAugustin Cavalier
109fb688aa1SAugustin Cavalier return user_access([=, &result] {
110fb688aa1SAugustin Cavalier result = atomic_and(value, andValue);
111fb688aa1SAugustin Cavalier }) ? result : INT32_MIN;
112fb688aa1SAugustin Cavalier }
113fb688aa1SAugustin Cavalier
114cf1b26a9SAugustin Cavalier
115cf1b26a9SAugustin Cavalier static int32
user_atomic_get(int32 * value,bool isWired)116fb688aa1SAugustin Cavalier user_atomic_get(int32* value, bool isWired)
117cf1b26a9SAugustin Cavalier {
118fb688aa1SAugustin Cavalier int32 result;
119fb688aa1SAugustin Cavalier if (isWired) {
120*6f88de11SAugustin Cavalier arch_cpu_enable_user_access();
121fb688aa1SAugustin Cavalier result = atomic_get(value);
122*6f88de11SAugustin Cavalier arch_cpu_disable_user_access();
123cf1b26a9SAugustin Cavalier return result;
124cf1b26a9SAugustin Cavalier }
125cf1b26a9SAugustin Cavalier
126fb688aa1SAugustin Cavalier return user_access([=, &result] {
127fb688aa1SAugustin Cavalier result = atomic_get(value);
128fb688aa1SAugustin Cavalier }) ? result : INT32_MIN;
129fb688aa1SAugustin Cavalier }
130fb688aa1SAugustin Cavalier
131fb688aa1SAugustin Cavalier
132fb688aa1SAugustin Cavalier static int32
user_atomic_test_and_set(int32 * value,int32 newValue,int32 testAgainst,bool isWired)133fb688aa1SAugustin Cavalier user_atomic_test_and_set(int32* value, int32 newValue, int32 testAgainst,
134fb688aa1SAugustin Cavalier bool isWired)
135fb688aa1SAugustin Cavalier {
136fb688aa1SAugustin Cavalier int32 result;
137fb688aa1SAugustin Cavalier if (isWired) {
138*6f88de11SAugustin Cavalier arch_cpu_enable_user_access();
139fb688aa1SAugustin Cavalier result = atomic_test_and_set(value, newValue, testAgainst);
140*6f88de11SAugustin Cavalier arch_cpu_disable_user_access();
141fb688aa1SAugustin Cavalier return result;
142fb688aa1SAugustin Cavalier }
143fb688aa1SAugustin Cavalier
144fb688aa1SAugustin Cavalier return user_access([=, &result] {
145fb688aa1SAugustin Cavalier result = atomic_test_and_set(value, newValue, testAgainst);
146fb688aa1SAugustin Cavalier }) ? result : INT32_MIN;
147fb688aa1SAugustin Cavalier }
148fb688aa1SAugustin Cavalier
149cf1b26a9SAugustin Cavalier
15093d7d1c5SAugustin Cavalier // #pragma mark - user mutex context
15193d7d1c5SAugustin Cavalier
15293d7d1c5SAugustin Cavalier
153c5a0df24SAugustin Cavalier static int
dump_user_mutex(int argc,char ** argv)154c5a0df24SAugustin Cavalier dump_user_mutex(int argc, char** argv)
155c5a0df24SAugustin Cavalier {
156c5a0df24SAugustin Cavalier if (argc != 2) {
157c5a0df24SAugustin Cavalier print_debugger_command_usage(argv[0]);
158c5a0df24SAugustin Cavalier return 0;
159c5a0df24SAugustin Cavalier }
160c5a0df24SAugustin Cavalier
161c5a0df24SAugustin Cavalier addr_t threadID = parse_expression(argv[1]);
162c5a0df24SAugustin Cavalier if (threadID == 0)
163c5a0df24SAugustin Cavalier return 0;
164c5a0df24SAugustin Cavalier
165c5a0df24SAugustin Cavalier Thread* thread = Thread::GetDebug(threadID);
166c5a0df24SAugustin Cavalier if (thread == NULL) {
167c5a0df24SAugustin Cavalier kprintf("no such thread\n");
168c5a0df24SAugustin Cavalier return 0;
169c5a0df24SAugustin Cavalier }
170c5a0df24SAugustin Cavalier
171c5a0df24SAugustin Cavalier if (thread->wait.type != THREAD_BLOCK_TYPE_CONDITION_VARIABLE) {
172c5a0df24SAugustin Cavalier kprintf("thread is not blocked on cvar (thus not user_mutex)\n");
173c5a0df24SAugustin Cavalier return 0;
174c5a0df24SAugustin Cavalier }
175c5a0df24SAugustin Cavalier
176c5a0df24SAugustin Cavalier ConditionVariable* variable = (ConditionVariable*)thread->wait.object;
177c5a0df24SAugustin Cavalier if (variable->ObjectType() != kUserMutexEntryType) {
178c5a0df24SAugustin Cavalier kprintf("thread is not blocked on user_mutex\n");
179c5a0df24SAugustin Cavalier return 0;
180c5a0df24SAugustin Cavalier }
181c5a0df24SAugustin Cavalier
182c5a0df24SAugustin Cavalier UserMutexEntry* entry = (UserMutexEntry*)variable->Object();
183c5a0df24SAugustin Cavalier
184c5a0df24SAugustin Cavalier const bool physical = (sSharedUserMutexContext.table.Lookup(entry->address) == entry);
185c5a0df24SAugustin Cavalier kprintf("user mutex entry %p\n", entry);
186c5a0df24SAugustin Cavalier kprintf(" address: 0x%" B_PRIxPHYSADDR " (%s)\n", entry->address,
187c5a0df24SAugustin Cavalier physical ? "physical" : "virtual");
188c5a0df24SAugustin Cavalier kprintf(" refcount: %" B_PRId32 "\n", entry->ref_count);
189c5a0df24SAugustin Cavalier kprintf(" lock: %p\n", &entry->lock);
190c5a0df24SAugustin Cavalier
191c5a0df24SAugustin Cavalier int32 mutex = 0;
192c5a0df24SAugustin Cavalier status_t status = B_ERROR;
193c5a0df24SAugustin Cavalier if (!physical) {
194c5a0df24SAugustin Cavalier status = debug_memcpy(thread->team->id, &mutex,
195c5a0df24SAugustin Cavalier (void*)entry->address, sizeof(mutex));
196c5a0df24SAugustin Cavalier }
197c5a0df24SAugustin Cavalier
198c5a0df24SAugustin Cavalier if (status == B_OK)
199c5a0df24SAugustin Cavalier kprintf(" mutex: 0x%" B_PRIx32 "\n", mutex);
200c5a0df24SAugustin Cavalier
201c5a0df24SAugustin Cavalier entry->condition.Dump();
202c5a0df24SAugustin Cavalier
203c5a0df24SAugustin Cavalier return 0;
204c5a0df24SAugustin Cavalier }
205c5a0df24SAugustin Cavalier
206c5a0df24SAugustin Cavalier
20793d7d1c5SAugustin Cavalier void
user_mutex_init()20893d7d1c5SAugustin Cavalier user_mutex_init()
20993d7d1c5SAugustin Cavalier {
21093d7d1c5SAugustin Cavalier sSharedUserMutexContext.lock = RW_LOCK_INITIALIZER("shared user mutex table");
21193d7d1c5SAugustin Cavalier if (sSharedUserMutexContext.table.Init() != B_OK)
21293d7d1c5SAugustin Cavalier panic("user_mutex_init(): Failed to init table!");
213c5a0df24SAugustin Cavalier
214c5a0df24SAugustin Cavalier add_debugger_command_etc("user_mutex", &dump_user_mutex,
215c5a0df24SAugustin Cavalier "Dump user-mutex info",
216c5a0df24SAugustin Cavalier "<thread>\n"
217c5a0df24SAugustin Cavalier "Prints info about the user-mutex a thread is blocked on.\n"
218c5a0df24SAugustin Cavalier " <thread> - Thread ID that is blocked on a user mutex\n", 0);
21993d7d1c5SAugustin Cavalier }
22093d7d1c5SAugustin Cavalier
22193d7d1c5SAugustin Cavalier
22293d7d1c5SAugustin Cavalier struct user_mutex_context*
get_team_user_mutex_context()22393d7d1c5SAugustin Cavalier get_team_user_mutex_context()
22493d7d1c5SAugustin Cavalier {
22593d7d1c5SAugustin Cavalier struct user_mutex_context* context =
22693d7d1c5SAugustin Cavalier thread_get_current_thread()->team->user_mutex_context;
22793d7d1c5SAugustin Cavalier if (context != NULL)
22893d7d1c5SAugustin Cavalier return context;
22993d7d1c5SAugustin Cavalier
23093d7d1c5SAugustin Cavalier Team* team = thread_get_current_thread()->team;
23193d7d1c5SAugustin Cavalier TeamLocker teamLocker(team);
23293d7d1c5SAugustin Cavalier if (team->user_mutex_context != NULL)
23393d7d1c5SAugustin Cavalier return team->user_mutex_context;
23493d7d1c5SAugustin Cavalier
23593d7d1c5SAugustin Cavalier context = new(std::nothrow) user_mutex_context;
23693d7d1c5SAugustin Cavalier if (context == NULL)
23793d7d1c5SAugustin Cavalier return NULL;
23893d7d1c5SAugustin Cavalier
23993d7d1c5SAugustin Cavalier context->lock = RW_LOCK_INITIALIZER("user mutex table");
24093d7d1c5SAugustin Cavalier if (context->table.Init() != B_OK) {
24193d7d1c5SAugustin Cavalier delete context;
24293d7d1c5SAugustin Cavalier return NULL;
24393d7d1c5SAugustin Cavalier }
24493d7d1c5SAugustin Cavalier
24593d7d1c5SAugustin Cavalier team->user_mutex_context = context;
24693d7d1c5SAugustin Cavalier return context;
24793d7d1c5SAugustin Cavalier }
24893d7d1c5SAugustin Cavalier
24993d7d1c5SAugustin Cavalier
25093d7d1c5SAugustin Cavalier void
delete_user_mutex_context(struct user_mutex_context * context)25193d7d1c5SAugustin Cavalier delete_user_mutex_context(struct user_mutex_context* context)
25293d7d1c5SAugustin Cavalier {
25393d7d1c5SAugustin Cavalier if (context == NULL)
25493d7d1c5SAugustin Cavalier return;
25593d7d1c5SAugustin Cavalier
25693d7d1c5SAugustin Cavalier // This should be empty at this point in team destruction.
25793d7d1c5SAugustin Cavalier ASSERT(context->table.IsEmpty());
25893d7d1c5SAugustin Cavalier delete context;
25993d7d1c5SAugustin Cavalier }
260cf1b26a9SAugustin Cavalier
261cf1b26a9SAugustin Cavalier
26213491fd2SAugustin Cavalier static UserMutexEntry*
get_user_mutex_entry(struct user_mutex_context * context,generic_addr_t address,bool noInsert=false,bool alreadyLocked=false)26393d7d1c5SAugustin Cavalier get_user_mutex_entry(struct user_mutex_context* context,
26493d7d1c5SAugustin Cavalier generic_addr_t address, bool noInsert = false, bool alreadyLocked = false)
265813d4cbeSIngo Weinhold {
2660ab9f280SAugustin Cavalier ReadLocker tableReadLocker;
2670ab9f280SAugustin Cavalier if (!alreadyLocked)
26893d7d1c5SAugustin Cavalier tableReadLocker.SetTo(context->lock, false);
2690ab9f280SAugustin Cavalier
27093d7d1c5SAugustin Cavalier UserMutexEntry* entry = context->table.Lookup(address);
27113491fd2SAugustin Cavalier if (entry != NULL) {
27213491fd2SAugustin Cavalier atomic_add(&entry->ref_count, 1);
27313491fd2SAugustin Cavalier return entry;
2740ab9f280SAugustin Cavalier } else if (noInsert)
2750ab9f280SAugustin Cavalier return entry;
2760ab9f280SAugustin Cavalier
2770ab9f280SAugustin Cavalier tableReadLocker.Unlock();
27893d7d1c5SAugustin Cavalier WriteLocker tableWriteLocker(context->lock);
2790ab9f280SAugustin Cavalier
28093d7d1c5SAugustin Cavalier entry = context->table.Lookup(address);
2810ab9f280SAugustin Cavalier if (entry != NULL) {
2820ab9f280SAugustin Cavalier atomic_add(&entry->ref_count, 1);
2830ab9f280SAugustin Cavalier return entry;
28413491fd2SAugustin Cavalier }
28513491fd2SAugustin Cavalier
28613491fd2SAugustin Cavalier entry = new(std::nothrow) UserMutexEntry;
28713491fd2SAugustin Cavalier if (entry == NULL)
28813491fd2SAugustin Cavalier return entry;
28913491fd2SAugustin Cavalier
29013491fd2SAugustin Cavalier entry->address = address;
29113491fd2SAugustin Cavalier entry->ref_count = 1;
2920ab9f280SAugustin Cavalier rw_lock_init(&entry->lock, "UserMutexEntry lock");
293c5a0df24SAugustin Cavalier entry->condition.Init(entry, kUserMutexEntryType);
29413491fd2SAugustin Cavalier
29593d7d1c5SAugustin Cavalier context->table.Insert(entry);
29613491fd2SAugustin Cavalier return entry;
297813d4cbeSIngo Weinhold }
298813d4cbeSIngo Weinhold
299813d4cbeSIngo Weinhold
30013491fd2SAugustin Cavalier static void
put_user_mutex_entry(struct user_mutex_context * context,UserMutexEntry * entry)30193d7d1c5SAugustin Cavalier put_user_mutex_entry(struct user_mutex_context* context, UserMutexEntry* entry)
302813d4cbeSIngo Weinhold {
3030ab9f280SAugustin Cavalier if (entry == NULL)
3040ab9f280SAugustin Cavalier return;
3050ab9f280SAugustin Cavalier
30693d7d1c5SAugustin Cavalier const generic_addr_t address = entry->address;
30713491fd2SAugustin Cavalier if (atomic_add(&entry->ref_count, -1) != 1)
30813491fd2SAugustin Cavalier return;
309813d4cbeSIngo Weinhold
31093d7d1c5SAugustin Cavalier WriteLocker tableWriteLocker(context->lock);
31113491fd2SAugustin Cavalier
31213491fd2SAugustin Cavalier // Was it removed & deleted while we were waiting for the lock?
31393d7d1c5SAugustin Cavalier if (context->table.Lookup(address) != entry)
31413491fd2SAugustin Cavalier return;
31513491fd2SAugustin Cavalier
31613491fd2SAugustin Cavalier // Or did someone else acquire a reference to it?
31713491fd2SAugustin Cavalier if (atomic_get(&entry->ref_count) > 0)
31813491fd2SAugustin Cavalier return;
31913491fd2SAugustin Cavalier
32093d7d1c5SAugustin Cavalier context->table.Remove(entry);
3210ab9f280SAugustin Cavalier tableWriteLocker.Unlock();
3220ab9f280SAugustin Cavalier
3230ab9f280SAugustin Cavalier rw_lock_destroy(&entry->lock);
32413491fd2SAugustin Cavalier delete entry;
325813d4cbeSIngo Weinhold }
326813d4cbeSIngo Weinhold
327813d4cbeSIngo Weinhold
328813d4cbeSIngo Weinhold static status_t
user_mutex_wait_locked(UserMutexEntry * entry,uint32 flags,bigtime_t timeout,ReadLocker & locker)3290ab9f280SAugustin Cavalier user_mutex_wait_locked(UserMutexEntry* entry,
3300ab9f280SAugustin Cavalier uint32 flags, bigtime_t timeout, ReadLocker& locker)
331813d4cbeSIngo Weinhold {
33213491fd2SAugustin Cavalier ConditionVariableEntry waiter;
33313491fd2SAugustin Cavalier entry->condition.Add(&waiter);
33413491fd2SAugustin Cavalier locker.Unlock();
335813d4cbeSIngo Weinhold
3360ab9f280SAugustin Cavalier return waiter.Wait(flags, timeout);
337813d4cbeSIngo Weinhold }
338813d4cbeSIngo Weinhold
339d6d439f3SHamish Morrison
340901b48c2SAugustin Cavalier static bool
user_mutex_prepare_to_lock(UserMutexEntry * entry,int32 * mutex,bool isWired)341fb688aa1SAugustin Cavalier user_mutex_prepare_to_lock(UserMutexEntry* entry, int32* mutex, bool isWired)
342d6d439f3SHamish Morrison {
3430ab9f280SAugustin Cavalier ASSERT_READ_LOCKED_RW_LOCK(&entry->lock);
3440ab9f280SAugustin Cavalier
345cf1b26a9SAugustin Cavalier int32 oldValue = user_atomic_or(mutex,
346fb688aa1SAugustin Cavalier B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING, isWired);
3476f3f29c7SAugustin Cavalier if ((oldValue & B_USER_MUTEX_LOCKED) == 0
348d6d439f3SHamish Morrison || (oldValue & B_USER_MUTEX_DISABLED) != 0) {
3490ab9f280SAugustin Cavalier // possibly unset waiting flag
3500ab9f280SAugustin Cavalier if ((oldValue & B_USER_MUTEX_WAITING) == 0) {
3510ab9f280SAugustin Cavalier rw_lock_read_unlock(&entry->lock);
3520ab9f280SAugustin Cavalier rw_lock_write_lock(&entry->lock);
3530ab9f280SAugustin Cavalier if (entry->condition.EntriesCount() == 0)
354fb688aa1SAugustin Cavalier user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING, isWired);
3550ab9f280SAugustin Cavalier rw_lock_write_unlock(&entry->lock);
3560ab9f280SAugustin Cavalier rw_lock_read_lock(&entry->lock);
3570ab9f280SAugustin Cavalier }
358901b48c2SAugustin Cavalier return true;
359d6d439f3SHamish Morrison }
360d6d439f3SHamish Morrison
361901b48c2SAugustin Cavalier return false;
362901b48c2SAugustin Cavalier }
363901b48c2SAugustin Cavalier
364901b48c2SAugustin Cavalier
365901b48c2SAugustin Cavalier static status_t
user_mutex_lock_locked(UserMutexEntry * entry,int32 * mutex,uint32 flags,bigtime_t timeout,ReadLocker & locker,bool isWired)3660ab9f280SAugustin Cavalier user_mutex_lock_locked(UserMutexEntry* entry, int32* mutex,
367fb688aa1SAugustin Cavalier uint32 flags, bigtime_t timeout, ReadLocker& locker, bool isWired)
368901b48c2SAugustin Cavalier {
369fb688aa1SAugustin Cavalier if (user_mutex_prepare_to_lock(entry, mutex, isWired))
370901b48c2SAugustin Cavalier return B_OK;
371901b48c2SAugustin Cavalier
3720ab9f280SAugustin Cavalier status_t error = user_mutex_wait_locked(entry, flags, timeout, locker);
3730ab9f280SAugustin Cavalier
3740ab9f280SAugustin Cavalier // possibly unset waiting flag
3750ab9f280SAugustin Cavalier if (error != B_OK && entry->condition.EntriesCount() == 0) {
3760ab9f280SAugustin Cavalier WriteLocker writeLocker(entry->lock);
3770ab9f280SAugustin Cavalier if (entry->condition.EntriesCount() == 0)
378fb688aa1SAugustin Cavalier user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING, isWired);
3790ab9f280SAugustin Cavalier }
3800ab9f280SAugustin Cavalier
3810ab9f280SAugustin Cavalier return error;
382813d4cbeSIngo Weinhold }
383813d4cbeSIngo Weinhold
384813d4cbeSIngo Weinhold
385813d4cbeSIngo Weinhold static void
user_mutex_unblock(UserMutexEntry * entry,int32 * mutex,uint32 flags,bool isWired)386fb688aa1SAugustin Cavalier user_mutex_unblock(UserMutexEntry* entry, int32* mutex, uint32 flags, bool isWired)
387813d4cbeSIngo Weinhold {
3880ab9f280SAugustin Cavalier WriteLocker entryLocker(entry->lock);
3890ab9f280SAugustin Cavalier if (entry->condition.EntriesCount() == 0) {
3900ab9f280SAugustin Cavalier // Nobody is actually waiting at present.
391fb688aa1SAugustin Cavalier user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING, isWired);
392fb67dbf0SHamish Morrison return;
393fb67dbf0SHamish Morrison }
394fb67dbf0SHamish Morrison
395ca458a2bSAugustin Cavalier int32 oldValue = 0;
396ca458a2bSAugustin Cavalier if ((flags & B_USER_MUTEX_UNBLOCK_ALL) == 0) {
39713491fd2SAugustin Cavalier // This is not merely an unblock, but a hand-off.
398fb688aa1SAugustin Cavalier oldValue = user_atomic_or(mutex, B_USER_MUTEX_LOCKED, isWired);
3996f3f29c7SAugustin Cavalier if ((oldValue & B_USER_MUTEX_LOCKED) != 0)
4006f3f29c7SAugustin Cavalier return;
401ca458a2bSAugustin Cavalier }
4020dffa8d4SIngo Weinhold
403813d4cbeSIngo Weinhold if ((flags & B_USER_MUTEX_UNBLOCK_ALL) != 0
4040dffa8d4SIngo Weinhold || (oldValue & B_USER_MUTEX_DISABLED) != 0) {
405b3b7b893SAugustin Cavalier // unblock all waiting threads
40613491fd2SAugustin Cavalier entry->condition.NotifyAll(B_OK);
40713491fd2SAugustin Cavalier } else {
408b3b7b893SAugustin Cavalier if (!entry->condition.NotifyOne(B_OK))
409798b7733SAugustin Cavalier user_atomic_and(mutex, ~(int32)B_USER_MUTEX_LOCKED, isWired);
410813d4cbeSIngo Weinhold }
411fb67dbf0SHamish Morrison
41213491fd2SAugustin Cavalier if (entry->condition.EntriesCount() == 0)
413fb688aa1SAugustin Cavalier user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING, isWired);
414813d4cbeSIngo Weinhold }
415813d4cbeSIngo Weinhold
416813d4cbeSIngo Weinhold
417813d4cbeSIngo Weinhold static status_t
user_mutex_sem_acquire_locked(UserMutexEntry * entry,int32 * sem,uint32 flags,bigtime_t timeout,ReadLocker & locker,bool isWired)4180ab9f280SAugustin Cavalier user_mutex_sem_acquire_locked(UserMutexEntry* entry, int32* sem,
419fb688aa1SAugustin Cavalier uint32 flags, bigtime_t timeout, ReadLocker& locker, bool isWired)
420d6d439f3SHamish Morrison {
421d6d439f3SHamish Morrison // The semaphore may have been released in the meantime, and we also
422d6d439f3SHamish Morrison // need to mark it as contended if it isn't already.
423fb688aa1SAugustin Cavalier int32 oldValue = user_atomic_get(sem, isWired);
424d6d439f3SHamish Morrison while (oldValue > -1) {
425fb688aa1SAugustin Cavalier int32 value = user_atomic_test_and_set(sem, oldValue - 1, oldValue, isWired);
426d6d439f3SHamish Morrison if (value == oldValue && value > 0)
427d6d439f3SHamish Morrison return B_OK;
428d6d439f3SHamish Morrison oldValue = value;
429d6d439f3SHamish Morrison }
430d6d439f3SHamish Morrison
4310ab9f280SAugustin Cavalier return user_mutex_wait_locked(entry, flags,
43213491fd2SAugustin Cavalier timeout, locker);
433d6d439f3SHamish Morrison }
434d6d439f3SHamish Morrison
435d6d439f3SHamish Morrison
436d6d439f3SHamish Morrison static void
user_mutex_sem_release(UserMutexEntry * entry,int32 * sem,bool isWired)437fb688aa1SAugustin Cavalier user_mutex_sem_release(UserMutexEntry* entry, int32* sem, bool isWired)
438d6d439f3SHamish Morrison {
4392cc89328SAugustin Cavalier WriteLocker entryLocker(entry->lock);
440b3b7b893SAugustin Cavalier if (entry->condition.NotifyOne(B_OK) == 0) {
441d6d439f3SHamish Morrison // no waiters - mark as uncontended and release
442fb688aa1SAugustin Cavalier int32 oldValue = user_atomic_get(sem, isWired);
443d6d439f3SHamish Morrison while (true) {
444d6d439f3SHamish Morrison int32 inc = oldValue < 0 ? 2 : 1;
445fb688aa1SAugustin Cavalier int32 value = user_atomic_test_and_set(sem, oldValue + inc, oldValue, isWired);
446d6d439f3SHamish Morrison if (value == oldValue)
447d6d439f3SHamish Morrison return;
448d6d439f3SHamish Morrison oldValue = value;
449d6d439f3SHamish Morrison }
450d6d439f3SHamish Morrison }
451d6d439f3SHamish Morrison
45213491fd2SAugustin Cavalier if (entry->condition.EntriesCount() == 0) {
453d6d439f3SHamish Morrison // mark the semaphore uncontended
454fb688aa1SAugustin Cavalier user_atomic_test_and_set(sem, 0, -1, isWired);
455d6d439f3SHamish Morrison }
456d6d439f3SHamish Morrison }
457d6d439f3SHamish Morrison
458d6d439f3SHamish Morrison
459fb688aa1SAugustin Cavalier // #pragma mark - syscalls
460fb688aa1SAugustin Cavalier
461fb688aa1SAugustin Cavalier
462fb688aa1SAugustin Cavalier struct UserMutexContextFetcher {
UserMutexContextFetcherUserMutexContextFetcher463fb688aa1SAugustin Cavalier UserMutexContextFetcher(int32* mutex, uint32 flags)
464fb688aa1SAugustin Cavalier :
465fb688aa1SAugustin Cavalier fInitStatus(B_OK),
466fb688aa1SAugustin Cavalier fShared((flags & B_USER_MUTEX_SHARED) != 0),
467fb688aa1SAugustin Cavalier fAddress(0)
468fb688aa1SAugustin Cavalier {
469fb688aa1SAugustin Cavalier if (!fShared) {
470fb688aa1SAugustin Cavalier fContext = get_team_user_mutex_context();
471fb688aa1SAugustin Cavalier if (fContext == NULL) {
472fb688aa1SAugustin Cavalier fInitStatus = B_NO_MEMORY;
473fb688aa1SAugustin Cavalier return;
474fb688aa1SAugustin Cavalier }
475fb688aa1SAugustin Cavalier
476fb688aa1SAugustin Cavalier fAddress = (addr_t)mutex;
477fb688aa1SAugustin Cavalier } else {
478fb688aa1SAugustin Cavalier fContext = &sSharedUserMutexContext;
479fb688aa1SAugustin Cavalier
480fb688aa1SAugustin Cavalier // wire the page and get the physical address
481fb688aa1SAugustin Cavalier fInitStatus = vm_wire_page(B_CURRENT_TEAM, (addr_t)mutex, true,
482fb688aa1SAugustin Cavalier &fWiringInfo);
483fb688aa1SAugustin Cavalier if (fInitStatus != B_OK)
484fb688aa1SAugustin Cavalier return;
485fb688aa1SAugustin Cavalier fAddress = fWiringInfo.physicalAddress;
486fb688aa1SAugustin Cavalier }
487fb688aa1SAugustin Cavalier }
488fb688aa1SAugustin Cavalier
~UserMutexContextFetcherUserMutexContextFetcher489fb688aa1SAugustin Cavalier ~UserMutexContextFetcher()
490fb688aa1SAugustin Cavalier {
491fb688aa1SAugustin Cavalier if (fInitStatus != B_OK)
492fb688aa1SAugustin Cavalier return;
493fb688aa1SAugustin Cavalier
494fb688aa1SAugustin Cavalier if (fShared)
495fb688aa1SAugustin Cavalier vm_unwire_page(&fWiringInfo);
496fb688aa1SAugustin Cavalier }
497fb688aa1SAugustin Cavalier
InitCheckUserMutexContextFetcher498fb688aa1SAugustin Cavalier status_t InitCheck() const
499fb688aa1SAugustin Cavalier { return fInitStatus; }
500fb688aa1SAugustin Cavalier
ContextUserMutexContextFetcher501fb688aa1SAugustin Cavalier struct user_mutex_context* Context() const
502fb688aa1SAugustin Cavalier { return fContext; }
503fb688aa1SAugustin Cavalier
AddressUserMutexContextFetcher504fb688aa1SAugustin Cavalier generic_addr_t Address() const
505fb688aa1SAugustin Cavalier { return fAddress; }
506fb688aa1SAugustin Cavalier
IsWiredUserMutexContextFetcher507fb688aa1SAugustin Cavalier bool IsWired() const
508fb688aa1SAugustin Cavalier { return fShared; }
509fb688aa1SAugustin Cavalier
510fb688aa1SAugustin Cavalier private:
511fb688aa1SAugustin Cavalier status_t fInitStatus;
512fb688aa1SAugustin Cavalier bool fShared;
513fb688aa1SAugustin Cavalier struct user_mutex_context* fContext;
514fb688aa1SAugustin Cavalier VMPageWiringInfo fWiringInfo;
515fb688aa1SAugustin Cavalier generic_addr_t fAddress;
516fb688aa1SAugustin Cavalier };
517fb688aa1SAugustin Cavalier
518fb688aa1SAugustin Cavalier
519d6d439f3SHamish Morrison static status_t
user_mutex_lock(int32 * mutex,const char * name,uint32 flags,bigtime_t timeout)520813d4cbeSIngo Weinhold user_mutex_lock(int32* mutex, const char* name, uint32 flags, bigtime_t timeout)
521813d4cbeSIngo Weinhold {
522fb688aa1SAugustin Cavalier UserMutexContextFetcher contextFetcher(mutex, flags);
523fb688aa1SAugustin Cavalier if (contextFetcher.InitCheck() != B_OK)
524fb688aa1SAugustin Cavalier return contextFetcher.InitCheck();
525813d4cbeSIngo Weinhold
526813d4cbeSIngo Weinhold // get the lock
527fb688aa1SAugustin Cavalier UserMutexEntry* entry = get_user_mutex_entry(contextFetcher.Context(),
528fb688aa1SAugustin Cavalier contextFetcher.Address());
5290ab9f280SAugustin Cavalier if (entry == NULL)
5300ab9f280SAugustin Cavalier return B_NO_MEMORY;
531fb688aa1SAugustin Cavalier status_t error = B_OK;
532813d4cbeSIngo Weinhold {
5330ab9f280SAugustin Cavalier ReadLocker entryLocker(entry->lock);
5340ab9f280SAugustin Cavalier error = user_mutex_lock_locked(entry, mutex,
535fb688aa1SAugustin Cavalier flags, timeout, entryLocker, contextFetcher.IsWired());
536813d4cbeSIngo Weinhold }
537fb688aa1SAugustin Cavalier put_user_mutex_entry(contextFetcher.Context(), entry);
538813d4cbeSIngo Weinhold
539813d4cbeSIngo Weinhold return error;
540813d4cbeSIngo Weinhold }
541813d4cbeSIngo Weinhold
542813d4cbeSIngo Weinhold
543813d4cbeSIngo Weinhold static status_t
user_mutex_switch_lock(int32 * fromMutex,uint32 fromFlags,int32 * toMutex,const char * name,uint32 toFlags,bigtime_t timeout)54493d7d1c5SAugustin Cavalier user_mutex_switch_lock(int32* fromMutex, uint32 fromFlags,
54593d7d1c5SAugustin Cavalier int32* toMutex, const char* name, uint32 toFlags, bigtime_t timeout)
546813d4cbeSIngo Weinhold {
547fb688aa1SAugustin Cavalier UserMutexContextFetcher fromFetcher(fromMutex, fromFlags);
548fb688aa1SAugustin Cavalier if (fromFetcher.InitCheck() != B_OK)
549fb688aa1SAugustin Cavalier return fromFetcher.InitCheck();
55093d7d1c5SAugustin Cavalier
551fb688aa1SAugustin Cavalier UserMutexContextFetcher toFetcher(toMutex, toFlags);
552fb688aa1SAugustin Cavalier if (toFetcher.InitCheck() != B_OK)
553fb688aa1SAugustin Cavalier return toFetcher.InitCheck();
554813d4cbeSIngo Weinhold
555813d4cbeSIngo Weinhold // unlock the first mutex and lock the second one
5560ab9f280SAugustin Cavalier UserMutexEntry* fromEntry = NULL,
557fb688aa1SAugustin Cavalier *toEntry = get_user_mutex_entry(toFetcher.Context(), toFetcher.Address());
5580ab9f280SAugustin Cavalier if (toEntry == NULL)
5590ab9f280SAugustin Cavalier return B_NO_MEMORY;
560fb688aa1SAugustin Cavalier status_t error = B_OK;
561813d4cbeSIngo Weinhold {
5620ab9f280SAugustin Cavalier ConditionVariableEntry waiter;
563901b48c2SAugustin Cavalier
5640ab9f280SAugustin Cavalier bool alreadyLocked = false;
5650ab9f280SAugustin Cavalier {
5660ab9f280SAugustin Cavalier ReadLocker entryLocker(toEntry->lock);
567fb688aa1SAugustin Cavalier alreadyLocked = user_mutex_prepare_to_lock(toEntry, toMutex,
568fb688aa1SAugustin Cavalier toFetcher.IsWired());
5690ab9f280SAugustin Cavalier if (!alreadyLocked)
5700ab9f280SAugustin Cavalier toEntry->condition.Add(&waiter);
5710ab9f280SAugustin Cavalier }
572813d4cbeSIngo Weinhold
573fb688aa1SAugustin Cavalier const int32 oldValue = user_atomic_and(fromMutex, ~(int32)B_USER_MUTEX_LOCKED,
574fb688aa1SAugustin Cavalier fromFetcher.IsWired());
575fb688aa1SAugustin Cavalier if ((oldValue & B_USER_MUTEX_WAITING) != 0) {
576fb688aa1SAugustin Cavalier fromEntry = get_user_mutex_entry(fromFetcher.Context(),
577fb688aa1SAugustin Cavalier fromFetcher.Address(), true);
578fb688aa1SAugustin Cavalier if (fromEntry != NULL) {
579fb688aa1SAugustin Cavalier user_mutex_unblock(fromEntry, fromMutex, fromFlags,
580fb688aa1SAugustin Cavalier fromFetcher.IsWired());
581fb688aa1SAugustin Cavalier }
582fb688aa1SAugustin Cavalier }
5830ab9f280SAugustin Cavalier
5840ab9f280SAugustin Cavalier if (!alreadyLocked)
58593d7d1c5SAugustin Cavalier error = waiter.Wait(toFlags, timeout);
586813d4cbeSIngo Weinhold }
587fb688aa1SAugustin Cavalier put_user_mutex_entry(fromFetcher.Context(), fromEntry);
588fb688aa1SAugustin Cavalier put_user_mutex_entry(toFetcher.Context(), toEntry);
589813d4cbeSIngo Weinhold
590813d4cbeSIngo Weinhold return error;
591813d4cbeSIngo Weinhold }
592813d4cbeSIngo Weinhold
593813d4cbeSIngo Weinhold
594813d4cbeSIngo Weinhold status_t
_user_mutex_lock(int32 * mutex,const char * name,uint32 flags,bigtime_t timeout)595813d4cbeSIngo Weinhold _user_mutex_lock(int32* mutex, const char* name, uint32 flags,
596813d4cbeSIngo Weinhold bigtime_t timeout)
597813d4cbeSIngo Weinhold {
598813d4cbeSIngo Weinhold if (mutex == NULL || !IS_USER_ADDRESS(mutex) || (addr_t)mutex % 4 != 0)
599813d4cbeSIngo Weinhold return B_BAD_ADDRESS;
600813d4cbeSIngo Weinhold
601813d4cbeSIngo Weinhold syscall_restart_handle_timeout_pre(flags, timeout);
602813d4cbeSIngo Weinhold
603813d4cbeSIngo Weinhold status_t error = user_mutex_lock(mutex, name, flags | B_CAN_INTERRUPT,
604813d4cbeSIngo Weinhold timeout);
605813d4cbeSIngo Weinhold
606813d4cbeSIngo Weinhold return syscall_restart_handle_timeout_post(error, timeout);
607813d4cbeSIngo Weinhold }
608813d4cbeSIngo Weinhold
609813d4cbeSIngo Weinhold
610813d4cbeSIngo Weinhold status_t
_user_mutex_unblock(int32 * mutex,uint32 flags)6116f3f29c7SAugustin Cavalier _user_mutex_unblock(int32* mutex, uint32 flags)
612813d4cbeSIngo Weinhold {
613813d4cbeSIngo Weinhold if (mutex == NULL || !IS_USER_ADDRESS(mutex) || (addr_t)mutex % 4 != 0)
614813d4cbeSIngo Weinhold return B_BAD_ADDRESS;
615813d4cbeSIngo Weinhold
616fb688aa1SAugustin Cavalier UserMutexContextFetcher contextFetcher(mutex, flags);
617fb688aa1SAugustin Cavalier if (contextFetcher.InitCheck() != B_OK)
618fb688aa1SAugustin Cavalier return contextFetcher.InitCheck();
619fb688aa1SAugustin Cavalier struct user_mutex_context* context = contextFetcher.Context();
620813d4cbeSIngo Weinhold
6210ab9f280SAugustin Cavalier // In the case where there is no entry, we must hold the read lock until we
6220ab9f280SAugustin Cavalier // unset WAITING, because otherwise some other thread could initiate a wait.
62393d7d1c5SAugustin Cavalier ReadLocker tableReadLocker(context->lock);
624fb688aa1SAugustin Cavalier UserMutexEntry* entry = get_user_mutex_entry(context,
625fb688aa1SAugustin Cavalier contextFetcher.Address(), true, true);
6260ab9f280SAugustin Cavalier if (entry == NULL) {
627fb688aa1SAugustin Cavalier user_atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING, contextFetcher.IsWired());
6280ab9f280SAugustin Cavalier tableReadLocker.Unlock();
6290ab9f280SAugustin Cavalier } else {
6300ab9f280SAugustin Cavalier tableReadLocker.Unlock();
631fb688aa1SAugustin Cavalier user_mutex_unblock(entry, mutex, flags, contextFetcher.IsWired());
632813d4cbeSIngo Weinhold }
63393d7d1c5SAugustin Cavalier put_user_mutex_entry(context, entry);
634813d4cbeSIngo Weinhold
635813d4cbeSIngo Weinhold return B_OK;
636813d4cbeSIngo Weinhold }
637813d4cbeSIngo Weinhold
638813d4cbeSIngo Weinhold
639813d4cbeSIngo Weinhold status_t
_user_mutex_switch_lock(int32 * fromMutex,uint32 fromFlags,int32 * toMutex,const char * name,uint32 toFlags,bigtime_t timeout)64093d7d1c5SAugustin Cavalier _user_mutex_switch_lock(int32* fromMutex, uint32 fromFlags,
64193d7d1c5SAugustin Cavalier int32* toMutex, const char* name, uint32 toFlags, bigtime_t timeout)
642813d4cbeSIngo Weinhold {
643813d4cbeSIngo Weinhold if (fromMutex == NULL || !IS_USER_ADDRESS(fromMutex)
644813d4cbeSIngo Weinhold || (addr_t)fromMutex % 4 != 0 || toMutex == NULL
645813d4cbeSIngo Weinhold || !IS_USER_ADDRESS(toMutex) || (addr_t)toMutex % 4 != 0) {
646813d4cbeSIngo Weinhold return B_BAD_ADDRESS;
647813d4cbeSIngo Weinhold }
648813d4cbeSIngo Weinhold
64993d7d1c5SAugustin Cavalier return user_mutex_switch_lock(fromMutex, fromFlags, toMutex, name,
65093d7d1c5SAugustin Cavalier toFlags | B_CAN_INTERRUPT, timeout);
651813d4cbeSIngo Weinhold }
652d6d439f3SHamish Morrison
653d6d439f3SHamish Morrison
654d6d439f3SHamish Morrison status_t
_user_mutex_sem_acquire(int32 * sem,const char * name,uint32 flags,bigtime_t timeout)655d6d439f3SHamish Morrison _user_mutex_sem_acquire(int32* sem, const char* name, uint32 flags,
656d6d439f3SHamish Morrison bigtime_t timeout)
657d6d439f3SHamish Morrison {
658d6d439f3SHamish Morrison if (sem == NULL || !IS_USER_ADDRESS(sem) || (addr_t)sem % 4 != 0)
659d6d439f3SHamish Morrison return B_BAD_ADDRESS;
660d6d439f3SHamish Morrison
661d6d439f3SHamish Morrison syscall_restart_handle_timeout_pre(flags, timeout);
662d6d439f3SHamish Morrison
663f64c46e6SAugustin Cavalier UserMutexContextFetcher contextFetcher(sem, flags);
664f64c46e6SAugustin Cavalier if (contextFetcher.InitCheck() != B_OK)
665f64c46e6SAugustin Cavalier return contextFetcher.InitCheck();
666f64c46e6SAugustin Cavalier struct user_mutex_context* context = contextFetcher.Context();
66793d7d1c5SAugustin Cavalier
668f64c46e6SAugustin Cavalier UserMutexEntry* entry = get_user_mutex_entry(context, contextFetcher.Address());
6690ab9f280SAugustin Cavalier if (entry == NULL)
6700ab9f280SAugustin Cavalier return B_NO_MEMORY;
671f64c46e6SAugustin Cavalier status_t error;
672d6d439f3SHamish Morrison {
6730ab9f280SAugustin Cavalier ReadLocker entryLocker(entry->lock);
6740ab9f280SAugustin Cavalier error = user_mutex_sem_acquire_locked(entry, sem,
675f64c46e6SAugustin Cavalier flags | B_CAN_INTERRUPT, timeout, entryLocker, contextFetcher.IsWired());
676d6d439f3SHamish Morrison }
67793d7d1c5SAugustin Cavalier put_user_mutex_entry(context, entry);
678d6d439f3SHamish Morrison
679d6d439f3SHamish Morrison return syscall_restart_handle_timeout_post(error, timeout);
680d6d439f3SHamish Morrison }
681d6d439f3SHamish Morrison
682d6d439f3SHamish Morrison
683d6d439f3SHamish Morrison status_t
_user_mutex_sem_release(int32 * sem,uint32 flags)684f64c46e6SAugustin Cavalier _user_mutex_sem_release(int32* sem, uint32 flags)
685d6d439f3SHamish Morrison {
686d6d439f3SHamish Morrison if (sem == NULL || !IS_USER_ADDRESS(sem) || (addr_t)sem % 4 != 0)
687d6d439f3SHamish Morrison return B_BAD_ADDRESS;
688d6d439f3SHamish Morrison
689f64c46e6SAugustin Cavalier UserMutexContextFetcher contextFetcher(sem, flags);
690f64c46e6SAugustin Cavalier if (contextFetcher.InitCheck() != B_OK)
691f64c46e6SAugustin Cavalier return contextFetcher.InitCheck();
692f64c46e6SAugustin Cavalier struct user_mutex_context* context = contextFetcher.Context();
693d6d439f3SHamish Morrison
69493d7d1c5SAugustin Cavalier UserMutexEntry* entry = get_user_mutex_entry(context,
695f64c46e6SAugustin Cavalier contextFetcher.Address());
696d6d439f3SHamish Morrison {
697f64c46e6SAugustin Cavalier user_mutex_sem_release(entry, sem, contextFetcher.IsWired());
698d6d439f3SHamish Morrison }
69993d7d1c5SAugustin Cavalier put_user_mutex_entry(context, entry);
700d6d439f3SHamish Morrison
701d6d439f3SHamish Morrison return B_OK;
702d6d439f3SHamish Morrison }
703