1/*****************************************************************************
2
3Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2017, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/**************************************************//**
21@file os/os0thread.cc
22The interface to the operating system thread control primitives
23
24Created 9/8/1995 Heikki Tuuri
25*******************************************************/
26
27#include "ha_prototypes.h"
28
29#include "os0thread.h"
30#include "ut0new.h"
31#include "srv0srv.h"
32#include "os0event.h"
33#include <map>
34
35/** Number of threads active. */
36ulint os_thread_count;
37
38/***************************************************************//**
39Compares two thread ids for equality.
40@return TRUE if equal */
41ibool
42os_thread_eq(
43/*=========*/
44 os_thread_id_t a, /*!< in: OS thread or thread id */
45 os_thread_id_t b) /*!< in: OS thread or thread id */
46{
47#ifdef _WIN32
48 if (a == b) {
49 return(TRUE);
50 }
51
52 return(FALSE);
53#else
54 if (pthread_equal(a, b)) {
55 return(TRUE);
56 }
57
58 return(FALSE);
59#endif
60}
61
62/****************************************************************//**
63Converts an OS thread id to a ulint. It is NOT guaranteed that the ulint is
64unique for the thread though!
65@return thread identifier as a number */
66ulint
67os_thread_pf(
68/*=========*/
69 os_thread_id_t a) /*!< in: OS thread identifier */
70{
71 return((ulint) a);
72}
73
74/*****************************************************************//**
75Returns the thread identifier of current thread. Currently the thread
76identifier in Unix is the thread handle itself. Note that in HP-UX
77pthread_t is a struct of 3 fields.
78@return current thread identifier */
79os_thread_id_t
80os_thread_get_curr_id(void)
81/*=======================*/
82{
83#ifdef _WIN32
84 return(GetCurrentThreadId());
85#else
86 return(pthread_self());
87#endif
88}
89
90/****************************************************************//**
91Creates a new thread of execution. The execution starts from
92the function given.
93NOTE: We count the number of threads in os_thread_exit(). A created
94thread should always use that to exit so thatthe thread count will be
95decremented.
96We do not return an error code because if there is one, we crash here. */
97os_thread_t
98os_thread_create_func(
99/*==================*/
100 os_thread_func_t func, /*!< in: pointer to function
101 from which to start */
102 void* arg, /*!< in: argument to start
103 function */
104 os_thread_id_t* thread_id) /*!< out: id of the created
105 thread, or NULL */
106{
107 os_thread_id_t new_thread_id;
108
109#ifdef _WIN32
110 HANDLE handle;
111
112 handle = CreateThread(NULL, /* no security attributes */
113 0, /* default size stack */
114 func,
115 arg,
116 0, /* thread runs immediately */
117 &new_thread_id);
118
119 if (!handle) {
120 /* If we cannot start a new thread, life has no meaning. */
121 ib::fatal() << "CreateThread returned " << GetLastError();
122 }
123
124 CloseHandle(handle);
125
126 my_atomic_addlint(&os_thread_count, 1);
127
128 return((os_thread_t)new_thread_id);
129#else /* _WIN32 else */
130
131 pthread_attr_t attr;
132
133 pthread_attr_init(&attr);
134
135 my_atomic_addlint(&os_thread_count, 1);
136
137 int ret = pthread_create(&new_thread_id, &attr, func, arg);
138
139 ut_a(ret == 0);
140
141 pthread_attr_destroy(&attr);
142
143#endif /* not _WIN32 */
144
145 ut_a(os_thread_count <= srv_max_n_threads);
146
147 /* Return the thread_id if the caller requests it. */
148 if (thread_id != NULL) {
149 *thread_id = new_thread_id;
150 }
151 return((os_thread_t)new_thread_id);
152}
153
154/** Waits until the specified thread completes and joins it.
155Its return value is ignored.
156@param[in,out] thread thread to join */
157void
158os_thread_join(
159 os_thread_id_t thread)
160{
161#ifdef _WIN32
162 /* Do nothing. */
163#else
164#ifdef UNIV_DEBUG
165 const int ret =
166#endif /* UNIV_DEBUG */
167 pthread_join(thread, NULL);
168
169 /* Waiting on already-quit threads is allowed. */
170 ut_ad(ret == 0 || ret == ESRCH);
171#endif /* _WIN32 */
172}
173
174/** Exits the current thread.
175@param[in] detach if true, the thread will be detached right before
176exiting. If false, another thread is responsible for joining this thread */
177ATTRIBUTE_NORETURN
178void
179os_thread_exit(bool detach)
180{
181#ifdef UNIV_DEBUG_THREAD_CREATION
182 ib::info() << "Thread exits, id "
183 << os_thread_pf(os_thread_get_curr_id());
184#endif
185
186#ifdef UNIV_PFS_THREAD
187 pfs_delete_thread();
188#endif
189
190 my_atomic_addlint(&os_thread_count, ulint(-1));
191
192#ifdef _WIN32
193 ExitThread(0);
194#else
195 if (detach) {
196 pthread_detach(pthread_self());
197 }
198 pthread_exit(NULL);
199#endif
200}
201
202/*****************************************************************//**
203Advises the os to give up remainder of the thread's time slice. */
204void
205os_thread_yield(void)
206/*=================*/
207{
208#if defined(_WIN32)
209 SwitchToThread();
210#else
211 sched_yield();
212#endif
213}
214
215/*****************************************************************//**
216The thread sleeps at least the time given in microseconds. */
217void
218os_thread_sleep(
219/*============*/
220 ulint tm) /*!< in: time in microseconds */
221{
222#ifdef _WIN32
223 Sleep((DWORD) tm / 1000);
224#elif defined(HAVE_NANOSLEEP)
225 struct timespec t;
226
227 t.tv_sec = tm / 1000000;
228 t.tv_nsec = (tm % 1000000) * 1000;
229
230 ::nanosleep(&t, NULL);
231#else
232 struct timeval t;
233
234 t.tv_sec = tm / 1000000;
235 t.tv_usec = tm % 1000000;
236
237 select(0, NULL, NULL, NULL, &t);
238#endif /* _WIN32 */
239}
240