1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 /**************************************************************************
25 Generic threads interface
26
27 **************************************************************************/
28
29 #pragma once
30
31 #include "tscore/ink_hrtime.h"
32 #include "tscore/ink_defs.h"
33 #include <sched.h>
34
35 //////////////////////////////////////////////////////////////////////////////
36 //
37 // The POSIX threads interface
38 //
39 //////////////////////////////////////////////////////////////////////////////
40
41 #if defined(POSIX_THREAD)
42 #include <pthread.h>
43 #include <csignal>
44 #include <semaphore.h>
45
46 #if HAVE_PTHREAD_NP_H
47 #include <pthread_np.h>
48 #endif
49
50 #if HAVE_SYS_PRCTL_H && defined(PR_SET_NAME)
51 #include <sys/prctl.h>
52 #endif
53
54 #define INK_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
55 #define INK_THREAD_STACK_MIN PTHREAD_STACK_MIN
56
57 typedef pthread_t ink_thread;
58 typedef pthread_cond_t ink_cond;
59 typedef pthread_key_t ink_thread_key;
60
61 // Darwin has a sem_init stub that returns ENOSYS. Rather than bodge around doing runtime
62 // detection, just emulate on Darwin.
63 #if defined(darwin)
64 #define TS_EMULATE_ANON_SEMAPHORES 1
65 #endif
66
67 struct ink_semaphore {
68 #if TS_EMULATE_ANON_SEMAPHORES
69 sem_t *sema;
70 int64_t semid;
71 #else
72 sem_t sema;
73 #endif
74
75 sem_t *
getink_semaphore76 get()
77 {
78 #if TS_EMULATE_ANON_SEMAPHORES
79 return sema;
80 #else
81 return &sema;
82 #endif
83 }
84 };
85
86 #endif /* #if defined(POSIX_THREAD) */
87
88 /*******************************************************************
89 *** Condition variables
90 ******************************************************************/
91
92 #ifdef POSIX_THREAD_10031c
93 typedef struct timespec ink_timestruc;
94 #else
95 typedef timestruc_t ink_timestruc;
96 #endif
97
98 #include <cerrno>
99 #include "tscore/ink_mutex.h"
100 #include "tscore/ink_assert.h"
101
102 //////////////////////////////////////////////////////////////////////////////
103 //
104 // The POSIX threads interface
105 //
106 //////////////////////////////////////////////////////////////////////////////
107 #if defined(POSIX_THREAD)
108
109 static inline void
ink_thread_key_create(ink_thread_key * key,void (* destructor)(void * value))110 ink_thread_key_create(ink_thread_key *key, void (*destructor)(void *value))
111 {
112 ink_assert(!pthread_key_create(key, destructor));
113 }
114
115 static inline void
ink_thread_setspecific(ink_thread_key key,void * value)116 ink_thread_setspecific(ink_thread_key key, void *value)
117 {
118 ink_assert(!pthread_setspecific(key, value));
119 }
120
121 static inline void *
ink_thread_getspecific(ink_thread_key key)122 ink_thread_getspecific(ink_thread_key key)
123 {
124 return pthread_getspecific(key);
125 }
126
127 static inline void
ink_thread_key_delete(ink_thread_key key)128 ink_thread_key_delete(ink_thread_key key)
129 {
130 ink_assert(!pthread_key_delete(key));
131 }
132
133 static inline void
ink_thread_create(ink_thread * tid,void * (* f)(void *),void * a,int detached,size_t stacksize,void * stack)134 ink_thread_create(ink_thread *tid, void *(*f)(void *), void *a, int detached, size_t stacksize, void *stack)
135 {
136 ink_thread t;
137 int ret;
138 pthread_attr_t attr;
139
140 if (tid == nullptr) {
141 tid = &t;
142 }
143
144 pthread_attr_init(&attr);
145 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
146
147 if (stacksize) {
148 if (stack) {
149 pthread_attr_setstack(&attr, stack, stacksize);
150 } else {
151 pthread_attr_setstacksize(&attr, stacksize);
152 }
153 }
154
155 if (detached) {
156 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
157 }
158
159 ret = pthread_create(tid, &attr, f, a);
160 if (ret != 0) {
161 ink_abort("pthread_create() failed: %s (%d)", strerror(ret), ret);
162 }
163 pthread_attr_destroy(&attr);
164 }
165
166 static inline void
ink_thread_cancel(ink_thread who)167 ink_thread_cancel(ink_thread who)
168 {
169 #if defined(freebsd)
170 (void)who;
171 ink_assert(!"not supported");
172 #else
173 int ret = pthread_cancel(who);
174 ink_assert(ret == 0);
175 #endif
176 }
177
178 static inline void *
ink_thread_join(ink_thread t)179 ink_thread_join(ink_thread t)
180 {
181 void *r;
182 ink_assert(!pthread_join(t, &r));
183 return r;
184 }
185
186 static inline ink_thread
ink_thread_self()187 ink_thread_self()
188 {
189 return (pthread_self());
190 }
191
192 static inline ink_thread
ink_thread_null()193 ink_thread_null()
194 {
195 // The implementation of ink_thread (the alias of pthread_t) is different on platforms
196 // - e.g. `struct pthread *` on Unix and `unsigned long int` on Linux
197 // NOLINTNEXTLINE(modernize-use-nullptr)
198 return (ink_thread)0;
199 }
200
201 static inline int
ink_thread_get_priority(ink_thread t,int * priority)202 ink_thread_get_priority(ink_thread t, int *priority)
203 {
204 #if defined(freebsd)
205 (void)t;
206 (void)priority;
207 ink_assert(!"not supported");
208 return -1;
209 #else
210 int policy;
211 struct sched_param param;
212 int res = pthread_getschedparam(t, &policy, ¶m);
213 *priority = param.sched_priority;
214 return res;
215 #endif
216 }
217
218 static inline int
ink_thread_sigsetmask(int how,const sigset_t * set,sigset_t * oset)219 ink_thread_sigsetmask(int how, const sigset_t *set, sigset_t *oset)
220 {
221 return (pthread_sigmask(how, set, oset));
222 }
223
224 static inline int
ink_thread_kill(ink_thread t,int sig)225 ink_thread_kill(ink_thread t, int sig)
226 {
227 return pthread_kill(t, sig);
228 }
229
230 /*******************************************************************
231 * Posix Semaphores
232 ******************************************************************/
233
234 void ink_sem_init(ink_semaphore *sp, unsigned int count);
235 void ink_sem_destroy(ink_semaphore *sp);
236 void ink_sem_wait(ink_semaphore *sp);
237 bool ink_sem_trywait(ink_semaphore *sp);
238 void ink_sem_post(ink_semaphore *sp);
239
240 /*******************************************************************
241 * Posix Condition Variables
242 ******************************************************************/
243
244 static inline void
ink_cond_init(ink_cond * cp)245 ink_cond_init(ink_cond *cp)
246 {
247 ink_assert(pthread_cond_init(cp, nullptr) == 0);
248 }
249
250 static inline void
ink_cond_destroy(ink_cond * cp)251 ink_cond_destroy(ink_cond *cp)
252 {
253 ink_assert(pthread_cond_destroy(cp) == 0);
254 }
255
256 static inline void
ink_cond_wait(ink_cond * cp,ink_mutex * mp)257 ink_cond_wait(ink_cond *cp, ink_mutex *mp)
258 {
259 ink_assert(pthread_cond_wait(cp, mp) == 0);
260 }
261
262 static inline int
ink_cond_timedwait(ink_cond * cp,ink_mutex * mp,ink_timestruc * t)263 ink_cond_timedwait(ink_cond *cp, ink_mutex *mp, ink_timestruc *t)
264 {
265 int err;
266 while (EINTR == (err = pthread_cond_timedwait(cp, mp, t)))
267 ;
268 #if defined(freebsd) || defined(openbsd)
269 ink_assert((err == 0) || (err == ETIMEDOUT));
270 #else
271 ink_assert((err == 0) || (err == ETIME) || (err == ETIMEDOUT));
272 #endif
273 return err;
274 }
275
276 static inline void
ink_cond_signal(ink_cond * cp)277 ink_cond_signal(ink_cond *cp)
278 {
279 ink_assert(pthread_cond_signal(cp) == 0);
280 }
281
282 static inline void
ink_cond_broadcast(ink_cond * cp)283 ink_cond_broadcast(ink_cond *cp)
284 {
285 ink_assert(pthread_cond_broadcast(cp) == 0);
286 }
287
288 static inline void
ink_thr_yield()289 ink_thr_yield()
290 {
291 ink_assert(!sched_yield());
292 }
293
294 static inline void
ink_thread_exit(void * status)295 ink_thread_exit(void *status)
296 {
297 pthread_exit(status);
298 }
299
300 // This define is from Linux's <sys/prctl.h> and is most likely very
301 // Linux specific... Feel free to add support for other platforms
302 // that has a feature to give a thread specific name / tag.
303 static inline void
ink_set_thread_name(const char * name ATS_UNUSED)304 ink_set_thread_name(const char *name ATS_UNUSED)
305 {
306 #if defined(HAVE_PTHREAD_SETNAME_NP_1)
307 pthread_setname_np(name);
308 #elif defined(HAVE_PTHREAD_SETNAME_NP_2)
309 pthread_setname_np(pthread_self(), name);
310 #elif defined(HAVE_PTHREAD_SET_NAME_NP_2)
311 pthread_set_name_np(pthread_self(), name);
312 #elif defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_NAME)
313 prctl(PR_SET_NAME, name, 0, 0, 0);
314 #endif
315 }
316
317 static inline void
ink_get_thread_name(char * name,size_t len)318 ink_get_thread_name(char *name, size_t len)
319 {
320 #if defined(HAVE_PTHREAD_GETNAME_NP)
321 pthread_getname_np(pthread_self(), name, len);
322 #elif defined(HAVE_PTHREAD_GET_NAME_NP)
323 pthread_get_name_np(pthread_self(), name, len);
324 #elif defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_NAME)
325 prctl(PR_GET_NAME, name, 0, 0, 0);
326 #else
327 snprintf(name, len, "0x%" PRIx64, (uint64_t)ink_thread_self());
328 #endif
329 }
330
331 #endif /* #if defined(POSIX_THREAD) */
332