MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
srv0conc.cc
Go to the documentation of this file.
1 /*****************************************************************************
2 
3 Copyright (c) 2011, 2012, Oracle and/or its affiliates. All Rights Reserved.
4 
5 Portions of this file contain modifications contributed and copyrighted by
6 Google, Inc. Those modifications are gratefully acknowledged and are described
7 briefly in the InnoDB documentation. The contributions by Google are
8 incorporated with their permission, and subject to the conditions contained in
9 the file COPYING.Google.
10 
11 Portions of this file contain modifications contributed and copyrighted
12 by Percona Inc.. Those modifications are
13 gratefully acknowledged and are described briefly in the InnoDB
14 documentation. The contributions by Percona Inc. are incorporated with
15 their permission, and subject to the conditions contained in the file
16 COPYING.Percona.
17 
18 This program is free software; you can redistribute it and/or modify it under
19 the terms of the GNU General Public License as published by the Free Software
20 Foundation; version 2 of the License.
21 
22 This program is distributed in the hope that it will be useful, but WITHOUT
23 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
24 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
25 
26 You should have received a copy of the GNU General Public License along with
27 this program; if not, write to the Free Software Foundation, Inc.,
28 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
29 
30 *****************************************************************************/
31 
32 /**************************************************/
40 #include "srv0srv.h"
41 #include "sync0sync.h"
42 #include "trx0trx.h"
43 
44 #include "mysql/plugin.h"
45 
48 UNIV_INTERN ulong srv_n_free_tickets_to_enter = 500;
49 
50 #ifdef HAVE_ATOMIC_BUILTINS
51 
52 UNIV_INTERN ulong srv_adaptive_max_sleep_delay = 150000;
53 #endif /* HAVE_ATOMIC_BUILTINS */
54 
55 UNIV_INTERN ulong srv_thread_sleep_delay = 10000;
56 
57 
62 UNIV_INTERN ulint srv_max_n_threads = 0;
63 
68 UNIV_INTERN ulong srv_thread_concurrency = 0;
69 
70 #ifndef HAVE_ATOMIC_BUILTINS
71 
73 static os_fast_mutex_t srv_conc_mutex;
74 
76 typedef UT_LIST_NODE_T(struct srv_conc_slot_t) srv_conc_node_t;
77 
81  ibool reserved;
83  ibool wait_ended;
88  srv_conc_node_t srv_conc_queue;
89 };
90 
92 typedef UT_LIST_BASE_NODE_T(srv_conc_slot_t) srv_conc_queue_t;
93 
94 static srv_conc_queue_t srv_conc_queue;
95 
97 static srv_conc_slot_t* srv_conc_slots;
98 
99 #if defined(UNIV_PFS_MUTEX)
100 /* Key to register srv_conc_mutex_key with performance schema */
101 UNIV_INTERN mysql_pfs_key_t srv_conc_mutex_key;
102 #endif /* UNIV_PFS_MUTEX */
103 
104 #endif /* !HAVE_ATOMIC_BUILTINS */
105 
107 struct srv_conc_t {
108  char pad[64 - (sizeof(ulint) + sizeof(lint))];
109 
115  volatile lint n_active;
116 
119  volatile lint n_waiting;
120 };
121 
122 /* Control variables for tracking concurrency. */
123 static srv_conc_t srv_conc;
124 
125 /*********************************************************************/
127 void
129 /*===============*/
130 {
131 #ifndef HAVE_ATOMIC_BUILTINS
132  ulint i;
133 
134  /* Init the server concurrency restriction data structures */
135 
136  os_fast_mutex_init(srv_conc_mutex_key, &srv_conc_mutex);
137 
138  UT_LIST_INIT(srv_conc_queue);
139 
140  srv_conc_slots = static_cast<srv_conc_slot_t*>(
141  mem_zalloc(OS_THREAD_MAX_N * sizeof(*srv_conc_slots)));
142 
143  for (i = 0; i < OS_THREAD_MAX_N; i++) {
144  srv_conc_slot_t* conc_slot = &srv_conc_slots[i];
145 
146  conc_slot->event = os_event_create();
147  ut_a(conc_slot->event);
148  }
149 #endif /* !HAVE_ATOMIC_BUILTINS */
150 }
151 
152 /*********************************************************************/
154 void
156 /*===============*/
157 {
158 #ifndef HAVE_ATOMIC_BUILTINS
159  os_fast_mutex_free(&srv_conc_mutex);
160  mem_free(srv_conc_slots);
161  srv_conc_slots = NULL;
162 #endif /* !HAVE_ATOMIC_BUILTINS */
163 }
164 
165 #ifdef HAVE_ATOMIC_BUILTINS
166 /*********************************************************************/
168 static
169 void
170 srv_enter_innodb_with_tickets(
171 /*==========================*/
172  trx_t* trx)
174 {
175  trx->declared_to_be_inside_innodb = TRUE;
177 }
178 
179 /*********************************************************************/
191 static
192 void
193 srv_conc_enter_innodb_with_atomics(
194 /*===============================*/
195  trx_t* trx)
197 {
198  ulint n_sleeps = 0;
199  ibool notified_mysql = FALSE;
200 
202 
203  for (;;) {
204  ulint sleep_in_us;
205 
206  if (srv_conc.n_active < (lint) srv_thread_concurrency) {
207  ulint n_active;
208 
209  /* Check if there are any free tickets. */
210  n_active = os_atomic_increment_lint(
211  &srv_conc.n_active, 1);
212 
213  if (n_active <= srv_thread_concurrency) {
214 
215  srv_enter_innodb_with_tickets(trx);
216 
217  if (notified_mysql) {
218 
219  (void) os_atomic_decrement_lint(
220  &srv_conc.n_waiting, 1);
221 
222  thd_wait_end(trx->mysql_thd);
223  }
224 
225  if (srv_adaptive_max_sleep_delay > 0) {
226  if (srv_thread_sleep_delay > 20
227  && n_sleeps == 1) {
228 
230  }
231 
232  if (srv_conc.n_waiting == 0) {
234  }
235  }
236 
237  return;
238  }
239 
240  /* Since there were no free seats, we relinquish
241  the overbooked ticket. */
242 
243  (void) os_atomic_decrement_lint(
244  &srv_conc.n_active, 1);
245  }
246 
247  if (!notified_mysql) {
248  (void) os_atomic_increment_lint(
249  &srv_conc.n_waiting, 1);
250 
251  /* Release possible search system latch this
252  thread has */
253 
254  if (trx->has_search_latch) {
256  }
257 
258  thd_wait_begin(trx->mysql_thd, THD_WAIT_USER_LOCK);
259 
260  notified_mysql = TRUE;
261  }
262 
263  trx->op_info = "sleeping before entering InnoDB";
264 
265  sleep_in_us = srv_thread_sleep_delay;
266 
267  /* Guard against overflow when adaptive sleep delay is on. */
268 
269  if (srv_adaptive_max_sleep_delay > 0
270  && sleep_in_us > srv_adaptive_max_sleep_delay) {
271 
272  sleep_in_us = srv_adaptive_max_sleep_delay;
273  srv_thread_sleep_delay = sleep_in_us;
274  }
275 
276  os_thread_sleep(sleep_in_us);
277 
278  trx->op_info = "";
279 
280  ++n_sleeps;
281 
282  if (srv_adaptive_max_sleep_delay > 0 && n_sleeps > 1) {
284  }
285  }
286 }
287 
288 /*********************************************************************/
290 static
291 void
292 srv_conc_exit_innodb_with_atomics(
293 /*==============================*/
294  trx_t* trx)
295 {
296  trx->n_tickets_to_enter_innodb = 0;
297  trx->declared_to_be_inside_innodb = FALSE;
298 
299  (void) os_atomic_decrement_lint(&srv_conc.n_active, 1);
300 }
301 #else
302 /*********************************************************************/
304 static
305 void
306 srv_conc_exit_innodb_without_atomics(
307 /*=================================*/
308  trx_t* trx)
309 {
310  srv_conc_slot_t* slot;
311 
312  os_fast_mutex_lock(&srv_conc_mutex);
313 
314  ut_ad(srv_conc.n_active > 0);
315  srv_conc.n_active--;
316  trx->declared_to_be_inside_innodb = FALSE;
317  trx->n_tickets_to_enter_innodb = 0;
318 
319  slot = NULL;
320 
321  if (srv_conc.n_active < (lint) srv_thread_concurrency) {
322  /* Look for a slot where a thread is waiting and no other
323  thread has yet released the thread */
324 
325  for (slot = UT_LIST_GET_FIRST(srv_conc_queue);
326  slot != NULL && slot->wait_ended == TRUE;
327  slot = UT_LIST_GET_NEXT(srv_conc_queue, slot)) {
328 
329  /* No op */
330  }
331 
332  if (slot != NULL) {
333  slot->wait_ended = TRUE;
334 
335  /* We increment the count on behalf of the released
336  thread */
337 
338  srv_conc.n_active++;
339  }
340  }
341 
342  os_fast_mutex_unlock(&srv_conc_mutex);
343 
344  if (slot != NULL) {
345  os_event_set(slot->event);
346  }
347 }
348 
349 /*********************************************************************/
351 static
352 void
353 srv_conc_enter_innodb_without_atomics(
354 /*==================================*/
355  trx_t* trx)
357 {
358  ulint i;
359  srv_conc_slot_t* slot = NULL;
360  ibool has_slept = FALSE;
361 
362  os_fast_mutex_lock(&srv_conc_mutex);
363 retry:
364  if (UNIV_UNLIKELY(trx->declared_to_be_inside_innodb)) {
365  os_fast_mutex_unlock(&srv_conc_mutex);
366  ut_print_timestamp(stderr);
367  fputs(" InnoDB: Error: trying to declare trx"
368  " to enter InnoDB, but\n"
369  "InnoDB: it already is declared.\n", stderr);
370  trx_print(stderr, trx, 0);
371  putc('\n', stderr);
372  return;
373  }
374 
375  ut_ad(srv_conc.n_active >= 0);
376 
377  if (srv_conc.n_active < (lint) srv_thread_concurrency) {
378 
379  srv_conc.n_active++;
380  trx->declared_to_be_inside_innodb = TRUE;
382 
383  os_fast_mutex_unlock(&srv_conc_mutex);
384 
385  return;
386  }
387 
388  /* If the transaction is not holding resources, let it sleep
389  for srv_thread_sleep_delay microseconds, and try again then */
390 
391  if (!has_slept && !trx->has_search_latch
392  && NULL == UT_LIST_GET_FIRST(trx->lock.trx_locks)) {
393 
394  has_slept = TRUE; /* We let it sleep only once to avoid
395  starvation */
396 
397  srv_conc.n_waiting++;
398 
399  os_fast_mutex_unlock(&srv_conc_mutex);
400 
401  trx->op_info = "sleeping before joining InnoDB queue";
402 
403  /* Peter Zaitsev suggested that we take the sleep away
404  altogether. But the sleep may be good in pathological
405  situations of lots of thread switches. Simply put some
406  threads aside for a while to reduce the number of thread
407  switches. */
408  if (srv_thread_sleep_delay > 0) {
410  }
411 
412  trx->op_info = "";
413 
414  os_fast_mutex_lock(&srv_conc_mutex);
415 
416  srv_conc.n_waiting--;
417 
418  goto retry;
419  }
420 
421  /* Too many threads inside: put the current thread to a queue */
422 
423  for (i = 0; i < OS_THREAD_MAX_N; i++) {
424  slot = srv_conc_slots + i;
425 
426  if (!slot->reserved) {
427 
428  break;
429  }
430  }
431 
432  if (i == OS_THREAD_MAX_N) {
433  /* Could not find a free wait slot, we must let the
434  thread enter */
435 
436  srv_conc.n_active++;
437  trx->declared_to_be_inside_innodb = TRUE;
438  trx->n_tickets_to_enter_innodb = 0;
439 
440  os_fast_mutex_unlock(&srv_conc_mutex);
441 
442  return;
443  }
444 
445  /* Release possible search system latch this thread has */
446  if (trx->has_search_latch) {
448  }
449 
450  /* Add to the queue */
451  slot->reserved = TRUE;
452  slot->wait_ended = FALSE;
453 
454  UT_LIST_ADD_LAST(srv_conc_queue, srv_conc_queue, slot);
455 
456  os_event_reset(slot->event);
457 
458  srv_conc.n_waiting++;
459 
460  os_fast_mutex_unlock(&srv_conc_mutex);
461 
462  /* Go to wait for the event; when a thread leaves InnoDB it will
463  release this thread */
464 
465  ut_ad(!trx->has_search_latch);
466 #ifdef UNIV_SYNC_DEBUG
467  ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
468 #endif /* UNIV_SYNC_DEBUG */
469  trx->op_info = "waiting in InnoDB queue";
470 
471  thd_wait_begin(trx->mysql_thd, THD_WAIT_USER_LOCK);
472 
473  os_event_wait(slot->event);
474  thd_wait_end(trx->mysql_thd);
475 
476  trx->op_info = "";
477 
478  os_fast_mutex_lock(&srv_conc_mutex);
479 
480  srv_conc.n_waiting--;
481 
482  /* NOTE that the thread which released this thread already
483  incremented the thread counter on behalf of this thread */
484 
485  slot->reserved = FALSE;
486 
487  UT_LIST_REMOVE(srv_conc_queue, srv_conc_queue, slot);
488 
489  trx->declared_to_be_inside_innodb = TRUE;
491 
492  os_fast_mutex_unlock(&srv_conc_mutex);
493 }
494 #endif /* HAVE_ATOMIC_BUILTINS */
495 
496 /*********************************************************************/
499 UNIV_INTERN
500 void
502 /*==================*/
503  trx_t* trx)
505 {
506 #ifdef UNIV_SYNC_DEBUG
507  ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
508 #endif /* UNIV_SYNC_DEBUG */
509 
510 #ifdef HAVE_ATOMIC_BUILTINS
511  srv_conc_enter_innodb_with_atomics(trx);
512 #else
513  srv_conc_enter_innodb_without_atomics(trx);
514 #endif /* HAVE_ATOMIC_BUILTINS */
515 }
516 
517 /*********************************************************************/
520 UNIV_INTERN
521 void
523 /*========================*/
524  trx_t* trx)
526 {
527 #ifdef UNIV_SYNC_DEBUG
528  ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
529 #endif /* UNIV_SYNC_DEBUG */
530 
531  if (!srv_thread_concurrency) {
532 
533  return;
534  }
535 
536  ut_ad(srv_conc.n_active >= 0);
537 
538 #ifdef HAVE_ATOMIC_BUILTINS
539  (void) os_atomic_increment_lint(&srv_conc.n_active, 1);
540 #else
541  os_fast_mutex_lock(&srv_conc_mutex);
542  ++srv_conc.n_active;
543  os_fast_mutex_unlock(&srv_conc_mutex);
544 #endif /* HAVE_ATOMIC_BUILTINS */
545 
546  trx->n_tickets_to_enter_innodb = 1;
547  trx->declared_to_be_inside_innodb = TRUE;
548 }
549 
550 /*********************************************************************/
553 UNIV_INTERN
554 void
556 /*=======================*/
557  trx_t* trx)
559 {
560  if ((trx->mysql_thd != NULL
562  || trx->declared_to_be_inside_innodb == FALSE) {
563 
564  return;
565  }
566 
567 #ifdef HAVE_ATOMIC_BUILTINS
568  srv_conc_exit_innodb_with_atomics(trx);
569 #else
570  srv_conc_exit_innodb_without_atomics(trx);
571 #endif /* HAVE_ATOMIC_BUILTINS */
572 
573 #ifdef UNIV_SYNC_DEBUG
574  ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch));
575 #endif /* UNIV_SYNC_DEBUG */
576 }
577 
578 /*********************************************************************/
580 UNIV_INTERN
581 ulint
583 /*==============================*/
584 {
585  return(srv_conc.n_waiting);
586 }
587 
588 /*********************************************************************/
590 UNIV_INTERN
591 ulint
593 /*==============================*/
594 {
595  return(srv_conc.n_active);
596  }
597