1 /*
2  * Copyright (c) 2017 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <kernel.h>
8 #include <ksched.h>
9 #include <wait_q.h>
10 #include <posix/pthread.h>
11 
12 struct k_spinlock z_pthread_spinlock;
13 
14 int64_t timespec_to_timeoutms(const struct timespec *abstime);
15 
16 #define MUTEX_MAX_REC_LOCK 32767
17 
18 /*
19  *  Default mutex attrs.
20  */
21 static const pthread_mutexattr_t def_attr = {
22 	.type = PTHREAD_MUTEX_DEFAULT,
23 };
24 
acquire_mutex(pthread_mutex_t * m,k_timeout_t timeout)25 static int acquire_mutex(pthread_mutex_t *m, k_timeout_t timeout)
26 {
27 	int rc = 0;
28 	k_spinlock_key_t key = k_spin_lock(&z_pthread_spinlock);
29 
30 	if (m->lock_count == 0U && m->owner == NULL) {
31 		m->lock_count++;
32 		m->owner = pthread_self();
33 
34 		k_spin_unlock(&z_pthread_spinlock, key);
35 		return 0;
36 	} else if (m->owner == pthread_self()) {
37 		if (m->type == PTHREAD_MUTEX_RECURSIVE &&
38 		    m->lock_count < MUTEX_MAX_REC_LOCK) {
39 			m->lock_count++;
40 			rc = 0;
41 		} else if (m->type == PTHREAD_MUTEX_ERRORCHECK) {
42 			rc = EDEADLK;
43 		} else {
44 			rc = EINVAL;
45 		}
46 
47 		k_spin_unlock(&z_pthread_spinlock, key);
48 		return rc;
49 	}
50 
51 	if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
52 		k_spin_unlock(&z_pthread_spinlock, key);
53 		return EINVAL;
54 	}
55 
56 	rc = z_pend_curr(&z_pthread_spinlock, key, &m->wait_q, timeout);
57 	if (rc != 0) {
58 		rc = ETIMEDOUT;
59 	}
60 
61 	return rc;
62 }
63 
64 /**
65  * @brief Lock POSIX mutex with non-blocking call.
66  *
67  * See IEEE 1003.1
68  */
pthread_mutex_trylock(pthread_mutex_t * m)69 int pthread_mutex_trylock(pthread_mutex_t *m)
70 {
71 	return acquire_mutex(m, K_NO_WAIT);
72 }
73 
74 /**
75  * @brief Lock POSIX mutex with timeout.
76  *
77  *
78  * See IEEE 1003.1
79  */
pthread_mutex_timedlock(pthread_mutex_t * m,const struct timespec * abstime)80 int pthread_mutex_timedlock(pthread_mutex_t *m,
81 			    const struct timespec *abstime)
82 {
83 	int32_t timeout = (int32_t)timespec_to_timeoutms(abstime);
84 	return acquire_mutex(m, K_MSEC(timeout));
85 }
86 
87 /**
88  * @brief Intialize POSIX mutex.
89  *
90  * See IEEE 1003.1
91  */
pthread_mutex_init(pthread_mutex_t * m,const pthread_mutexattr_t * attr)92 int pthread_mutex_init(pthread_mutex_t *m,
93 				     const pthread_mutexattr_t *attr)
94 {
95 	const pthread_mutexattr_t *mattr;
96 
97 	m->owner = NULL;
98 	m->lock_count = 0U;
99 
100 	mattr = (attr == NULL) ? &def_attr : attr;
101 
102 	m->type = mattr->type;
103 
104 	z_waitq_init(&m->wait_q);
105 
106 	return 0;
107 }
108 
109 
110 /**
111  * @brief Lock POSIX mutex with blocking call.
112  *
113  * See IEEE 1003.1
114  */
pthread_mutex_lock(pthread_mutex_t * m)115 int pthread_mutex_lock(pthread_mutex_t *m)
116 {
117 	return acquire_mutex(m, K_FOREVER);
118 }
119 
120 /**
121  * @brief Unlock POSIX mutex.
122  *
123  * See IEEE 1003.1
124  */
pthread_mutex_unlock(pthread_mutex_t * m)125 int pthread_mutex_unlock(pthread_mutex_t *m)
126 {
127 	k_spinlock_key_t key = k_spin_lock(&z_pthread_spinlock);
128 
129 	k_tid_t thread;
130 
131 	if (m->owner != pthread_self()) {
132 		k_spin_unlock(&z_pthread_spinlock, key);
133 		return EPERM;
134 	}
135 
136 	if (m->lock_count == 0U) {
137 		k_spin_unlock(&z_pthread_spinlock, key);
138 		return EINVAL;
139 	}
140 
141 	m->lock_count--;
142 
143 	if (m->lock_count == 0U) {
144 		thread = z_unpend_first_thread(&m->wait_q);
145 		if (thread) {
146 			m->owner = (pthread_t)thread;
147 			m->lock_count++;
148 			arch_thread_return_value_set(thread, 0);
149 			z_ready_thread(thread);
150 			z_reschedule(&z_pthread_spinlock, key);
151 			return 0;
152 		}
153 		m->owner = NULL;
154 
155 	}
156 	k_spin_unlock(&z_pthread_spinlock, key);
157 	return 0;
158 }
159 
160 /**
161  * @brief Destroy POSIX mutex.
162  *
163  * See IEEE 1003.1
164  */
pthread_mutex_destroy(pthread_mutex_t * m)165 int pthread_mutex_destroy(pthread_mutex_t *m)
166 {
167 	ARG_UNUSED(m);
168 	return 0;
169 }
170 
171 /**
172  * @brief Read protocol attribute for mutex.
173  *
174  * See IEEE 1003.1
175  */
pthread_mutexattr_getprotocol(const pthread_mutexattr_t * attr,int * protocol)176 int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr,
177 				  int *protocol)
178 {
179 	*protocol = PTHREAD_PRIO_NONE;
180 	return 0;
181 }
182 
183 /**
184  * @brief Read type attribute for mutex.
185  *
186  * See IEEE 1003.1
187  */
pthread_mutexattr_gettype(const pthread_mutexattr_t * attr,int * type)188 int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type)
189 {
190 	*type = attr->type;
191 	return 0;
192 }
193 
194 /**
195  * @brief Set type attribute for mutex.
196  *
197  * See IEEE 1003.1
198  */
pthread_mutexattr_settype(pthread_mutexattr_t * attr,int type)199 int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
200 {
201 	int retc = EINVAL;
202 
203 	if ((type == PTHREAD_MUTEX_NORMAL) ||
204 	    (type == PTHREAD_MUTEX_RECURSIVE) ||
205 	    (type == PTHREAD_MUTEX_ERRORCHECK)) {
206 		attr->type = type;
207 		retc = 0;
208 	}
209 
210 	return retc;
211 }
212