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