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, &param);
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