1/*
2 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
3 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Copyright 2002-2009, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
5 * Distributed under the terms of the MIT License.
6 *
7 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
8 * Distributed under the terms of the NewOS License.
9 */
10
11
12#include <locks.h>
13
14#include <stdlib.h>
15#include <string.h>
16
17#include <syscalls.h>
18#include <user_mutex_defs.h>
19
20
21#define MAX_UNSUCCESSFUL_SPINS	100
22
23
24extern int32 __gCPUCount;
25
26
27// #pragma mark - mutex
28
29
30void
31__mutex_init(mutex *lock, const char *name)
32{
33	lock->name = name;
34	lock->lock = 0;
35	lock->flags = 0;
36}
37
38
39void
40__mutex_init_etc(mutex *lock, const char *name, uint32 flags)
41{
42	lock->name = (flags & MUTEX_FLAG_CLONE_NAME) != 0 ? strdup(name) : name;
43	lock->lock = 0;
44	lock->flags = flags;
45
46	if (__gCPUCount < 2)
47		lock->flags &= ~uint32(MUTEX_FLAG_ADAPTIVE);
48}
49
50
51void
52__mutex_destroy(mutex *lock)
53{
54	if ((lock->flags & MUTEX_FLAG_CLONE_NAME) != 0)
55		free(const_cast<char*>(lock->name));
56}
57
58
59status_t
60__mutex_lock(mutex *lock)
61{
62	uint32 count = 0;
63	const uint32 kMaxCount
64		= (lock->flags & MUTEX_FLAG_ADAPTIVE) != 0 ? MAX_UNSUCCESSFUL_SPINS : 1;
65
66	int32 oldValue;
67	do {
68		oldValue = atomic_test_and_set(&lock->lock, B_USER_MUTEX_LOCKED, 0);
69		if (oldValue == 0 || (oldValue & B_USER_MUTEX_DISABLED) != 0)
70			return B_OK;
71	} while (count++ < kMaxCount && (oldValue & B_USER_MUTEX_WAITING) != 0);
72
73	// we have to call the kernel
74	status_t error;
75	do {
76		error = _kern_mutex_lock(&lock->lock, lock->name, 0, 0);
77	} while (error == B_INTERRUPTED);
78
79	return error;
80}
81
82
83void
84__mutex_unlock(mutex *lock)
85{
86	// clear the locked flag
87	int32 oldValue = atomic_and(&lock->lock, ~(int32)B_USER_MUTEX_LOCKED);
88	if ((oldValue & B_USER_MUTEX_WAITING) != 0
89			&& (oldValue & B_USER_MUTEX_DISABLED) == 0) {
90		_kern_mutex_unblock(&lock->lock, 0);
91	}
92
93	if ((oldValue & B_USER_MUTEX_LOCKED) == 0) {
94#if 0
95		debugger("mutex was not actually locked!");
96#else
97		// The above happens too often at present (see bug #18451).
98		_kern_debug_output("libroot __mutex_unlock: mutex was not actually locked!\n");
99#endif
100	}
101}
102