MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
thread_utils.cc
1 /* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 // First include (the generated) my_config.h, to get correct platform defines.
17 #include "my_config.h"
18 #include <gtest/gtest.h>
19 
20 #include "thread_utils.h"
21 #include "mysql/psi/mysql_thread.h"
22 
23 namespace thread {
24 
25 /*
26  On windows we need an open handle to the thread in order to join it.
27  The three instances of Notification are used as follows:
28  - The main thread starts sub-thread, waits for m_thread_started.
29  - The main thread picks up the thread id, and does OpenThread to get a handle.
30  - The main thread tells the sub-thread that it can continue
31  (notifies m_thread_continue)
32  - The main thread waits until the sub-thread is actually running
33  (waits for m_thread_running) before destroying all objects.
34 
35  All this is to work around a bug in pthread_create() in mysys/my_winthread,
36  which closes the thread handle (it has nowhere to store it).
37  When we later call pthread_join() we try to re-open a handle.
38  This will fail if the thread has already finished, and then the join will fail.
39  */
41 {
42 public:
43  Thread_start_arg(Thread *thread) : m_thread(thread) {}
44 
45  Notification m_thread_started;
46  Notification m_thread_continue;
47  Notification m_thread_running;
48  Thread *m_thread;
49 };
50 
51 namespace {
52 extern "C"
53 void *thread_start_routine(void *arg)
54 {
55  Thread_start_arg *start_arg= (Thread_start_arg*) arg;
56  Thread::run_wrapper(start_arg);
57  return NULL;
58 }
59 
60 // We cannot use ASSERT_FALSE in constructors/destructors,
61 // so we add a local helper routine.
62 #define LOCAL_ASSERT_FALSE(arg) assert_false(arg, __LINE__)
63 void assert_false(int arg, int line)
64 {
65  ASSERT_FALSE(arg) << "failed with arg " << arg << " at line " << line;
66 }
67 
68 } // namespace
69 
70 Thread::~Thread()
71 {
72 #ifdef __WIN__
73  if (m_thread_handle != NULL)
74  CloseHandle(m_thread_handle);
75 #endif
76 }
77 
78 
79 int Thread::start()
80 {
81  Thread_start_arg start_arg(this);
82  const int retval=
83  pthread_create(&m_thread_id, NULL, thread_start_routine, &start_arg);
84  if (retval != 0)
85  {
86  ADD_FAILURE() << " could not start thread, errno: " << errno;
87  return retval;
88  }
89 
90  start_arg.m_thread_started.wait_for_notification();
91 #ifdef __WIN__
92  m_thread_handle= OpenThread(SYNCHRONIZE, FALSE, m_thread_id);
93  if (m_thread_handle == NULL)
94  {
95  DWORD lasterror= GetLastError();
96  ADD_FAILURE()
97  << " could not open thread id " << m_thread_id
98  << " GetLastError: " << lasterror
99  ;
100  }
101 #endif
102  start_arg.m_thread_continue.notify();
103  start_arg.m_thread_running.wait_for_notification();
104  return retval;
105 }
106 
107 
108 #ifdef __WIN__
109 void Thread::join()
110 {
111  DWORD ret= WaitForSingleObject(m_thread_handle, INFINITE);
112  if (ret != WAIT_OBJECT_0)
113  {
114  DWORD lasterror= GetLastError();
115  ADD_FAILURE()
116  << " could not join thread id " << m_thread_id
117  << " handle " << m_thread_handle
118  << " GetLastError: " << lasterror
119  ;
120  }
121  CloseHandle(m_thread_handle);
122  m_thread_handle= NULL;
123 }
124 #else
125 void Thread::join()
126 {
127  const int failed= pthread_join(m_thread_id, NULL);
128  if (failed)
129  {
130  ADD_FAILURE()
131  << " could not join thread id " << m_thread_id
132  << " failed: " << failed << " errno: " << errno
133  ;
134  }
135 }
136 #endif
137 
138 
139 void Thread::run_wrapper(Thread_start_arg *start_arg)
140 {
141  const my_bool error= my_thread_init();
142  ASSERT_FALSE(error);
143  Thread *thread= start_arg->m_thread;
144  start_arg->m_thread_started.notify();
145  start_arg->m_thread_continue.wait_for_notification();
146  start_arg->m_thread_running.notify();
147  thread->run();
148  my_thread_end();
149 }
150 
151 
152 Mutex_lock::Mutex_lock(mysql_mutex_t *mutex) : m_mutex(mutex)
153 {
154  mysql_mutex_lock(m_mutex);
155 }
156 
157 
158 Mutex_lock::~Mutex_lock()
159 {
160  const int failed= mysql_mutex_unlock(m_mutex);
161  LOCAL_ASSERT_FALSE(failed);
162 }
163 
164 
165 Notification::Notification() : m_notified(FALSE)
166 {
167  const int failed1= mysql_cond_init(0, &m_cond, NULL);
168  LOCAL_ASSERT_FALSE(failed1);
169  const int failed2= mysql_mutex_init(0, &m_mutex, MY_MUTEX_INIT_FAST);
170  LOCAL_ASSERT_FALSE(failed2);
171 }
172 
173 Notification::~Notification()
174 {
175  mysql_mutex_destroy(&m_mutex);
176  mysql_cond_destroy(&m_cond);
177 }
178 
179 bool Notification::has_been_notified()
180 {
181  Mutex_lock lock(&m_mutex);
182  return m_notified;
183 }
184 
185 void Notification::wait_for_notification()
186 {
187  Mutex_lock lock(&m_mutex);
188  while (!m_notified)
189  {
190  const int failed= mysql_cond_wait(&m_cond, &m_mutex);
191  ASSERT_FALSE(failed);
192  }
193 }
194 
195 void Notification::notify()
196 {
197  Mutex_lock lock(&m_mutex);
198  m_notified= TRUE;
199  const int failed= mysql_cond_broadcast(&m_cond);
200  ASSERT_FALSE(failed);
201 }
202 
203 
204 } // namespace thread