MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ThreadConfig.cpp
1 /*
2  Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 #include "ThreadConfig.hpp"
19 #include "Emulator.hpp"
20 #include "GlobalData.hpp"
21 #include "TimeQueue.hpp"
22 #include "TransporterRegistry.hpp"
23 #include "FastScheduler.hpp"
24 #include "pc.hpp"
25 
26 #include <GlobalSignalNumbers.h>
27 #include <BlockNumbers.h>
28 
29 #include <NdbSleep.h>
30 #include <NdbTick.h>
31 #include <NdbOut.hpp>
32 #include <WatchDog.hpp>
33 
34 #include <EventLogger.hpp>
35 extern EventLogger * g_eventLogger;
36 
37 #include <signaldata/StartOrd.hpp>
38 
39 ThreadConfig::ThreadConfig()
40 {
41 }
42 
43 ThreadConfig::~ThreadConfig()
44 {
45 }
46 
47 void
48 ThreadConfig::init()
49 {
50 }
51 
57 inline
58 void
59 ThreadConfig::scanTimeQueue()
60 {
61  unsigned int maxCounter;
62  Uint64 currMilliSecond;
63  maxCounter = 0;
64  currMilliSecond = NdbTick_CurrentMillisecond();
65  if (currMilliSecond < globalData.internalMillisecCounter) {
66 //--------------------------------------------------------------------
67 // This could occur around 2036 or if the operator decides to change
68 // time backwards. We cannot know how long time has past since last
69 // time and we make a best try with 0 milliseconds.
70 //--------------------------------------------------------------------
71  g_eventLogger->warning("Time moved backwards with %llu ms",
72  globalData.internalMillisecCounter-currMilliSecond);
73  globalData.internalMillisecCounter = currMilliSecond;
74  }//if
75  if (currMilliSecond > (globalData.internalMillisecCounter + 1500)) {
76 //--------------------------------------------------------------------
77 // Time has moved forward more than a second. Either it could happen
78 // if operator changed the time or if the OS has misbehaved badly.
79 // We set the new time to one second from the past.
80 //--------------------------------------------------------------------
81  g_eventLogger->warning("Time moved forward with %llu ms",
82  currMilliSecond-globalData.internalMillisecCounter);
83  globalData.internalMillisecCounter = currMilliSecond - 1000;
84  }//if
85  while (((currMilliSecond - globalData.internalMillisecCounter) > 0) &&
86  (maxCounter < 20)){
87  globalData.internalMillisecCounter++;
88  maxCounter++;
89  globalTimeQueue.scanTable();
90  }//while
91 }//ThreadConfig::scanTimeQueue()
92 
93 
94 //--------------------------------------------------------------------
95 // ipControlLoop -- The main loop of ndb.
96 // Handles the scheduling of signal execution and input/output
97 // One lap in the loop should take approximately 10 milli seconds
98 // If the jobbuffer is empty and the laptime is less than 10 milliseconds
99 // at the end of the loop
100 // the TransporterRegistry is called in order to sleep on the IO ports
101 // waiting for another incoming signal to wake us up.
102 // The timeout value in this call is calculated as (10 ms - laptime)
103 // This would make ndb use less cpu while improving response time.
104 //--------------------------------------------------------------------
105 void ThreadConfig::ipControlLoop(NdbThread*, Uint32 thread_index)
106 {
107  globalEmulatorData.theConfiguration->setAllLockCPU(true);
108 
109  Uint32 execute_loop_constant =
110  globalEmulatorData.theConfiguration->schedulerExecutionTimer();
111  Uint32 min_spin_time =
112  globalEmulatorData.theConfiguration->schedulerSpinTimer();
113  struct MicroSecondTimer start_micro, end_micro, statistics_start_micro;
114  struct MicroSecondTimer yield_micro;
115  Uint32 no_exec_loops = 0;
116  Uint32 no_extra_loops = 0;
117  Uint32 tot_exec_time = 0;
118  Uint32 tot_extra_time = 0;
119  Uint32 timeOutMillis;
120  Uint32 micros_passed;
121  bool spinning;
122  bool yield_flag= FALSE;
123  int res1 = 0;
124  int res2 = 0;
125  int res3 = 0;
126  Uint32 i = 0;
127  Uint32 exec_again;
128 
129 //--------------------------------------------------------------------
130 // initialise the counter that keeps track of the current millisecond
131 //--------------------------------------------------------------------
132  globalData.internalMillisecCounter = NdbTick_CurrentMillisecond();
133 
134  Uint32 *watchCounter = globalData.getWatchDogPtr();
135  globalEmulatorData.theWatchDog->registerWatchedThread(watchCounter, 0);
136 
137  res1 = NdbTick_getMicroTimer(&start_micro);
138  yield_micro = statistics_start_micro = end_micro = start_micro;
139  while (1)
140  {
141  timeOutMillis = 0;
142 //--------------------------------------------------------------------
143 // We send all messages buffered during execution of job buffers
144 //--------------------------------------------------------------------
145  globalData.incrementWatchDogCounter(6);
146  globalTransporterRegistry.performSend();
147 
148 //--------------------------------------------------------------------
149 // Now it is time to check all interfaces. We will send all buffers
150 // plus checking for any received messages.
151 //--------------------------------------------------------------------
152  if (i++ >= 20)
153  {
154  execute_loop_constant =
155  globalEmulatorData.theConfiguration->schedulerExecutionTimer();
156  min_spin_time =
157  globalEmulatorData.theConfiguration->schedulerSpinTimer();
158  globalData.incrementWatchDogCounter(5);
159  globalTransporterRegistry.update_connections();
160  i = 0;
161  }
162  spinning = false;
163  do
164  {
165 //--------------------------------------------------------------------
166 // We scan the time queue to see if there are any timed signals that
167 // is now ready to be executed.
168 //--------------------------------------------------------------------
169  globalData.incrementWatchDogCounter(2);
170  scanTimeQueue();
171 
172  if (LEVEL_IDLE == globalData.highestAvailablePrio)
173  {
174 //--------------------------------------------------------------------
175 // The buffers are empty, we need to wait for a while until we continue.
176 // We cannot wait forever since we can also have timed events.
177 //--------------------------------------------------------------------
178 // We set the time to sleep on sockets before waking up to 10
179 // milliseconds unless we have set spin timer to be larger than 0. In
180 // this case we spin checking for events on the transporter until we
181 // have expired the spin time.
182 //--------------------------------------------------------------------
183  timeOutMillis = 10;
184  if (min_spin_time && !yield_flag)
185  {
186  if (spinning)
187  res2 = NdbTick_getMicroTimer(&end_micro);
188  if (!(res1 + res2))
189  {
190  micros_passed =
191  (Uint32)NdbTick_getMicrosPassed(start_micro, end_micro);
192  if (micros_passed < min_spin_time)
193  timeOutMillis = 0;
194  }
195  }
196  }
197  if (spinning && timeOutMillis > 0 && i++ >= 20)
198  {
199  globalData.incrementWatchDogCounter(5);
200  globalTransporterRegistry.update_connections();
201  i = 0;
202  }
203 
204 //--------------------------------------------------------------------
205 // Perform receive before entering execute loop
206 //--------------------------------------------------------------------
207  globalData.incrementWatchDogCounter(7);
208  {
209  bool poll_flag;
210  if (yield_flag)
211  {
212  globalEmulatorData.theConfiguration->yield_main(thread_index, TRUE);
213  poll_flag= globalTransporterRegistry.pollReceive(timeOutMillis);
214  globalEmulatorData.theConfiguration->yield_main(thread_index, FALSE);
215  res3= NdbTick_getMicroTimer(&yield_micro);
216  }
217  else
218  poll_flag= globalTransporterRegistry.pollReceive(timeOutMillis);
219  if (poll_flag)
220  {
221  globalData.incrementWatchDogCounter(8);
222  globalTransporterRegistry.performReceive();
223  }
224  yield_flag= FALSE;
225  }
226  spinning = true;
227  globalScheduler.postPoll();
228 //--------------------------------------------------------------------
229 // In an idle system we will use this loop to wait either for external
230 // signal received or a message generated by the time queue.
231 //--------------------------------------------------------------------
232  } while (LEVEL_IDLE == globalData.highestAvailablePrio);
233 //--------------------------------------------------------------------
234 // Get current microsecond to ensure we will continue executing
235 // signals for at least a configured time while there are more
236 // signals to receive.
237 //--------------------------------------------------------------------
238  res1= NdbTick_getMicroTimer(&start_micro);
239  if ((res1 + res3) ||
240  ((Uint32)NdbTick_getMicrosPassed(start_micro, yield_micro) > 10000))
241  yield_flag= TRUE;
242  exec_again= 0;
243  do
244  {
245 //--------------------------------------------------------------------
246 // This is where the actual execution of signals occur. We execute
247 // until all buffers are empty or until we have executed 2048 signals.
248 //--------------------------------------------------------------------
249  globalScheduler.doJob();
250  if (unlikely(globalData.theRestartFlag == perform_stop))
251  goto out;
252 //--------------------------------------------------------------------
253 // Get timer after executing this set of jobs. If we have passed the
254 // maximum execution time we will break out of the loop always
255 // otherwise we will check for new received signals before executing
256 // the send of the buffers.
257 // By setting exec_loop_constant to 0 we go back to the traditional
258 // algorithm of sending once per receive instance.
259 //--------------------------------------------------------------------
260  if (!execute_loop_constant && !min_spin_time)
261  break;
262  res2= NdbTick_getMicroTimer(&end_micro);
263  if (res2)
264  break;
265  micros_passed = (Uint32)NdbTick_getMicrosPassed(start_micro, end_micro);
266  tot_exec_time += micros_passed;
267  if (no_exec_loops++ >= 8192)
268  {
269  Uint32 expired_time =
270  (Uint32)NdbTick_getMicrosPassed(statistics_start_micro, end_micro);
271  statistics_start_micro = end_micro;
272  globalScheduler.reportThreadConfigLoop(expired_time,
273  execute_loop_constant,
274  &no_exec_loops,
275  &tot_exec_time,
276  &no_extra_loops,
277  &tot_extra_time);
278  }
279  /*
280  Continue our execution if micros_passed since last round is smaller
281  than the configured constant. Given that we don't recall the
282  actual start time of this loop we insert an extra check to ensure we
283  don't enter an eternal loop here. We'll never execute more than
284  3 times before sending.
285  */
286  if (micros_passed > execute_loop_constant || (exec_again > 1))
287  break;
288  exec_again++;
289 //--------------------------------------------------------------------
290 // There were still time for execution left, we check if there are
291 // signals newly received on the transporters and if so we execute one
292 // more round before sending the buffered signals.
293 //--------------------------------------------------------------------
294  globalData.incrementWatchDogCounter(7);
295  if (!globalTransporterRegistry.pollReceive(0))
296  break;
297 
298  no_extra_loops++;
299  tot_extra_time += micros_passed;
300  start_micro = end_micro;
301  globalData.incrementWatchDogCounter(8);
302  globalTransporterRegistry.performReceive();
303  } while (1);
304  }
305 out:
306  globalData.incrementWatchDogCounter(6);
307  globalTransporterRegistry.performSend();
308 
309  globalEmulatorData.theWatchDog->unregisterWatchedThread(0);
310 
311 }//ThreadConfig::ipControlLoop()
312 
313 int
314 ThreadConfig::doStart(NodeState::StartLevel startLevel){
315 
316  SignalHeader sh;
317  memset(&sh, 0, sizeof(SignalHeader));
318 
319  sh.theVerId_signalNumber = GSN_START_ORD;
320  sh.theReceiversBlockNumber = CMVMI;
321  sh.theSendersBlockRef = 0;
322  sh.theTrace = 0;
323  sh.theSignalId = 0;
324  sh.theLength = StartOrd::SignalLength;
325 
326  union {
327  Uint32 theData[25];
328  StartOrd startOrd;
329  };
330  startOrd.restartInfo = 0;
331 
332  Uint32 secPtrI[3];
333  globalScheduler.execute(&sh, JBA, theData, secPtrI);
334  return 0;
335 }
336