1
/*****************************************************************************
3
Copyright (C) 1995, 2009, Innobase Oy. All Rights Reserved.
4
Copyright (C) 2008, Google Inc.
6
Portions of this file contain modifications contributed and copyrighted by
7
Google, Inc. Those modifications are gratefully acknowledged and are described
8
briefly in the InnoDB documentation. The contributions by Google are
9
incorporated with their permission, and subject to the conditions contained in
10
the file COPYING.Google.
12
This program is free software; you can redistribute it and/or modify it under
13
the terms of the GNU General Public License as published by the Free Software
14
Foundation; version 2 of the License.
16
This program is distributed in the hope that it will be useful, but WITHOUT
17
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20
You should have received a copy of the GNU General Public License along with
21
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
22
St, Fifth Floor, Boston, MA 02110-1301 USA
24
*****************************************************************************/
26
/**************************************************//**
28
The wait array used in synchronization primitives
30
Created 9/5/1995 Heikki Tuuri
31
*******************************************************/
35
#include "sync0arr.ic"
38
#include "sync0sync.h"
48
The wait array consists of cells each of which has an
49
an operating system event object created for it. The threads
50
waiting for a mutex, for example, can reserve a cell
51
in the array and suspend themselves to wait for the event
52
to become signaled. When using the wait array, remember to make
53
sure that some thread holding the synchronization object
54
will eventually know that there is a waiter in the array and
55
signal the object, to prevent infinite wait.
56
Why we chose to implement a wait array? First, to make
57
mutexes fast, we had to code our own implementation of them,
58
which only in usually uncommon cases resorts to using
59
slow operating system primitives. Then we had the choice of
60
assigning a unique OS event for each mutex, which would
61
be simpler, or using a global wait array. In some operating systems,
62
the global wait array solution is more efficient and flexible,
63
because we can do with a very small number of OS events,
64
say 200. In NT 3.51, allocating events seems to be a quadratic
65
algorithm, because 10 000 events are created fast, but
66
100 000 events takes a couple of minutes to create.
68
As of 5.0.30 the above mentioned design is changed. Since now
69
OS can handle millions of wait events efficiently, we no longer
70
have this concept of each cell of wait array having one event.
71
Instead, now the event that a thread wants to wait on is embedded
72
in the wait object (mutex or rw_lock). We still keep the global
73
wait array for the sake of diagnostics and also to avoid infinite
74
wait The error_monitor thread scans the global wait array to signal
75
any waiting threads who have missed the signal. */
77
/** A cell where an individual thread may wait suspended
78
until a resource is released. The suspending is implemented
79
using an operating system event semaphore. */
80
struct sync_cell_struct {
81
void* wait_object; /*!< pointer to the object the
82
thread is waiting for; if NULL
83
the cell is free for use */
84
mutex_t* old_wait_mutex; /*!< the latest wait mutex in cell */
85
rw_lock_t* old_wait_rw_lock;
86
/*!< the latest wait rw-lock
88
ulint request_type; /*!< lock type requested on the
90
const char* file; /*!< in debug version file where
92
ulint line; /*!< in debug version line where
94
os_thread_id_t thread; /*!< thread id of this waiting
96
ibool waiting; /*!< TRUE if the thread has already
97
called sync_array_event_wait
99
ib_int64_t signal_count; /*!< We capture the signal_count
100
of the wait_object when we
101
reset the event. This value is
102
then passed on to os_event_wait
103
and we wait only if the event
104
has not been signalled in the
105
period between the reset and
107
time_t reservation_time;/*!< time when the thread reserved
111
/* NOTE: It is allowed for a thread to wait
112
for an event allocated for the array without owning the
113
protecting mutex (depending on the case: OS or database mutex), but
114
all changes (set or reset) to the state of the event must be made
115
while owning the mutex. */
117
/** Synchronization array */
118
struct sync_array_struct {
119
ulint n_reserved; /*!< number of currently reserved
120
cells in the wait array */
121
ulint n_cells; /*!< number of cells in the
123
sync_cell_t* array; /*!< pointer to wait array */
124
ulint protection; /*!< this flag tells which
125
mutex protects the data */
126
mutex_t mutex; /*!< possible database mutex
127
protecting this data structure */
128
os_mutex_t os_mutex; /*!< Possible operating system mutex
129
protecting the data structure.
130
As this data structure is used in
131
constructing the database mutex,
132
to prevent infinite recursion
133
in implementation, we fall back to
135
ulint sg_count; /*!< count of how many times an
136
object has been signalled */
137
ulint res_count; /*!< count of cell reservations
138
since creation of the array */
141
#ifdef UNIV_PFS_MUTEX
142
/* Key to register the mutex with performance schema */
143
UNIV_INTERN mysql_pfs_key_t syn_arr_mutex_key;
146
#ifdef UNIV_SYNC_DEBUG
147
/******************************************************************//**
148
This function is called only in the debug version. Detects a deadlock
149
of one or more threads because of waits of semaphores.
150
@return TRUE if deadlock detected */
153
sync_array_detect_deadlock(
154
/*=======================*/
155
sync_array_t* arr, /*!< in: wait array; NOTE! the caller must
156
own the mutex to array */
157
sync_cell_t* start, /*!< in: cell where recursive search started */
158
sync_cell_t* cell, /*!< in: cell to search */
159
ulint depth); /*!< in: recursion depth */
160
#endif /* UNIV_SYNC_DEBUG */
162
/*****************************************************************//**
163
Gets the nth cell in array.
167
sync_array_get_nth_cell(
168
/*====================*/
169
sync_array_t* arr, /*!< in: sync array */
170
ulint n) /*!< in: index */
173
ut_a(n < arr->n_cells);
175
return(arr->array + n);
178
/******************************************************************//**
179
Reserves the mutex semaphore protecting a sync array. */
184
sync_array_t* arr) /*!< in: sync wait array */
188
protection = arr->protection;
190
if (protection == SYNC_ARRAY_OS_MUTEX) {
191
os_mutex_enter(arr->os_mutex);
192
} else if (protection == SYNC_ARRAY_MUTEX) {
193
mutex_enter(&(arr->mutex));
199
/******************************************************************//**
200
Releases the mutex semaphore protecting a sync array. */
205
sync_array_t* arr) /*!< in: sync wait array */
209
protection = arr->protection;
211
if (protection == SYNC_ARRAY_OS_MUTEX) {
212
os_mutex_exit(arr->os_mutex);
213
} else if (protection == SYNC_ARRAY_MUTEX) {
214
mutex_exit(&(arr->mutex));
220
/*******************************************************************//**
221
Creates a synchronization wait array. It is protected by a mutex
222
which is automatically reserved when the functions operating on it
224
@return own: created wait array */
229
ulint n_cells, /*!< in: number of cells in the array
231
ulint protection) /*!< in: either SYNC_ARRAY_OS_MUTEX or
232
SYNC_ARRAY_MUTEX: determines the type
233
of mutex protecting the data structure */
240
/* Allocate memory for the data structures */
241
arr = static_cast<sync_array_t *>(ut_malloc(sizeof(sync_array_t)));
242
memset(arr, 0x0, sizeof(*arr));
244
sz = sizeof(sync_cell_t) * n_cells;
245
arr->array = static_cast<sync_cell_t *>(ut_malloc(sz));
246
memset(arr->array, 0x0, sz);
248
arr->n_cells = n_cells;
249
arr->protection = protection;
251
/* Then create the mutex to protect the wait array complex */
252
if (protection == SYNC_ARRAY_OS_MUTEX) {
253
arr->os_mutex = os_mutex_create();
254
} else if (protection == SYNC_ARRAY_MUTEX) {
255
mutex_create(syn_arr_mutex_key,
256
&arr->mutex, SYNC_NO_ORDER_CHECK);
264
/******************************************************************//**
265
Frees the resources in a wait array. */
270
sync_array_t* arr) /*!< in, own: sync wait array */
274
ut_a(arr->n_reserved == 0);
276
sync_array_validate(arr);
278
protection = arr->protection;
280
/* Release the mutex protecting the wait array complex */
282
if (protection == SYNC_ARRAY_OS_MUTEX) {
283
os_mutex_free(arr->os_mutex);
284
} else if (protection == SYNC_ARRAY_MUTEX) {
285
mutex_free(&(arr->mutex));
294
/********************************************************************//**
295
Validates the integrity of the wait array. Checks
296
that the number of reserved cells equals the count variable. */
301
sync_array_t* arr) /*!< in: sync wait array */
307
sync_array_enter(arr);
309
for (i = 0; i < arr->n_cells; i++) {
310
cell = sync_array_get_nth_cell(arr, i);
311
if (cell->wait_object != NULL) {
316
ut_a(count == arr->n_reserved);
318
sync_array_exit(arr);
321
/*******************************************************************//**
322
Returns the event that the thread owning the cell waits for. */
327
sync_cell_t* cell) /*!< in: non-empty sync array cell */
329
ulint type = cell->request_type;
331
if (type == SYNC_MUTEX) {
332
return(((mutex_t *) cell->wait_object)->event);
333
} else if (type == RW_LOCK_WAIT_EX) {
334
return(((rw_lock_t *) cell->wait_object)->wait_ex_event);
335
} else { /* RW_LOCK_SHARED and RW_LOCK_EX wait on the same event */
336
return(((rw_lock_t *) cell->wait_object)->event);
340
/******************************************************************//**
341
Reserves a wait array cell for waiting for an object.
342
The event of the cell is reset to nonsignalled state. */
345
sync_array_reserve_cell(
346
/*====================*/
347
sync_array_t* arr, /*!< in: wait array */
348
void* object, /*!< in: pointer to the object to wait for */
349
ulint type, /*!< in: lock request type */
350
const char* file, /*!< in: file where requested */
351
ulint line, /*!< in: line where requested */
352
ulint* index) /*!< out: index of the reserved cell */
361
sync_array_enter(arr);
365
/* Reserve a new cell. */
366
for (i = 0; i < arr->n_cells; i++) {
367
cell = sync_array_get_nth_cell(arr, i);
369
if (cell->wait_object == NULL) {
371
cell->waiting = FALSE;
372
cell->wait_object = object;
374
if (type == SYNC_MUTEX) {
375
cell->old_wait_mutex = static_cast<mutex_struct *>(object);
377
cell->old_wait_rw_lock = static_cast<rw_lock_struct *>(object);
380
cell->request_type = type;
389
sync_array_exit(arr);
391
/* Make sure the event is reset and also store
392
the value of signal_count at which the event
394
event = sync_cell_get_event(cell);
395
cell->signal_count = os_event_reset(event);
397
cell->reservation_time = time(NULL);
399
cell->thread = os_thread_get_curr_id();
405
ut_error; /* No free cell found */
410
/******************************************************************//**
411
This function should be called when a thread starts to wait on
412
a wait array cell. In the debug version this function checks
413
if the wait for a semaphore will result in a deadlock, in which
414
case prints info and asserts. */
417
sync_array_wait_event(
418
/*==================*/
419
sync_array_t* arr, /*!< in: wait array */
420
ulint index) /*!< in: index of the reserved cell */
427
sync_array_enter(arr);
429
cell = sync_array_get_nth_cell(arr, index);
431
ut_a(cell->wait_object);
432
ut_a(!cell->waiting);
433
ut_ad(os_thread_get_curr_id() == cell->thread);
435
event = sync_cell_get_event(cell);
436
cell->waiting = TRUE;
438
#ifdef UNIV_SYNC_DEBUG
440
/* We use simple enter to the mutex below, because if
441
we cannot acquire it at once, mutex_enter would call
442
recursively sync_array routines, leading to trouble.
443
rw_lock_debug_mutex freezes the debug lists. */
445
rw_lock_debug_mutex_enter();
447
if (TRUE == sync_array_detect_deadlock(arr, cell, cell, 0)) {
449
fputs("########################################\n", stderr);
453
rw_lock_debug_mutex_exit();
455
sync_array_exit(arr);
457
os_event_wait_low(event, cell->signal_count);
459
sync_array_free_cell(arr, index);
462
/******************************************************************//**
463
Reports info of a wait array cell. */
466
sync_array_cell_print(
467
/*==================*/
468
FILE* file, /*!< in: file where to print */
469
sync_cell_t* cell) /*!< in: sync cell */
476
type = cell->request_type;
479
"--Thread %lu has waited at %s line %lu"
480
" for %.2f seconds the semaphore:\n",
481
(ulong) os_thread_pf(cell->thread), cell->file,
483
difftime(time(NULL), cell->reservation_time));
485
if (type == SYNC_MUTEX) {
486
/* We use old_wait_mutex in case the cell has already
487
been freed meanwhile */
488
mutex = cell->old_wait_mutex;
491
"Mutex at %p created file %s line %lu, lock var %lu\n"
492
#ifdef UNIV_SYNC_DEBUG
493
"Last time reserved in file %s line %lu, "
494
#endif /* UNIV_SYNC_DEBUG */
495
"waiters flag %lu\n",
496
(void*) mutex, mutex->cfile_name, (ulong) mutex->cline,
497
(ulong) mutex->lock_word,
498
#ifdef UNIV_SYNC_DEBUG
499
mutex->file_name, (ulong) mutex->line,
500
#endif /* UNIV_SYNC_DEBUG */
501
(ulong) mutex->waiters);
503
} else if (type == RW_LOCK_EX
504
|| type == RW_LOCK_WAIT_EX
505
|| type == RW_LOCK_SHARED) {
507
fputs(type == RW_LOCK_EX ? "X-lock on"
508
: type == RW_LOCK_WAIT_EX ? "X-lock (wait_ex) on"
509
: "S-lock on", file);
511
rwlock = cell->old_wait_rw_lock;
514
" RW-latch at %p created in file %s line %lu\n",
515
(void*) rwlock, rwlock->cfile_name,
516
(ulong) rwlock->cline);
517
writer = rw_lock_get_writer(rwlock);
518
if (writer != RW_LOCK_NOT_LOCKED) {
520
"a writer (thread id %lu) has"
521
" reserved it in mode %s",
522
(ulong) os_thread_pf(rwlock->writer_thread),
525
: " wait exclusive\n");
529
"number of readers %lu, waiters flag %lu, "
531
"Last time read locked in file %s line %lu\n"
532
"Last time write locked in file %s line %lu\n",
533
(ulong) rw_lock_get_reader_count(rwlock),
534
(ulong) rwlock->waiters,
536
rwlock->last_s_file_name,
537
(ulong) rwlock->last_s_line,
538
rwlock->last_x_file_name,
539
(ulong) rwlock->last_x_line);
544
if (!cell->waiting) {
545
fputs("wait has ended\n", file);
549
#ifdef UNIV_SYNC_DEBUG
550
/******************************************************************//**
551
Looks for a cell with the given thread id.
552
@return pointer to cell or NULL if not found */
555
sync_array_find_thread(
556
/*===================*/
557
sync_array_t* arr, /*!< in: wait array */
558
os_thread_id_t thread) /*!< in: thread id */
563
for (i = 0; i < arr->n_cells; i++) {
565
cell = sync_array_get_nth_cell(arr, i);
567
if (cell->wait_object != NULL
568
&& os_thread_eq(cell->thread, thread)) {
570
return(cell); /* Found */
574
return(NULL); /* Not found */
577
/******************************************************************//**
578
Recursion step for deadlock detection.
579
@return TRUE if deadlock detected */
582
sync_array_deadlock_step(
583
/*=====================*/
584
sync_array_t* arr, /*!< in: wait array; NOTE! the caller must
585
own the mutex to array */
586
sync_cell_t* start, /*!< in: cell where recursive search
588
os_thread_id_t thread, /*!< in: thread to look at */
589
ulint pass, /*!< in: pass value */
590
ulint depth) /*!< in: recursion depth */
598
/* If pass != 0, then we do not know which threads are
599
responsible of releasing the lock, and no deadlock can
605
new = sync_array_find_thread(arr, thread);
608
/* Stop running of other threads */
610
ut_dbg_stop_threads = TRUE;
613
fputs("########################################\n"
614
"DEADLOCK of threads detected!\n", stderr);
619
ret = sync_array_detect_deadlock(arr, start, new, depth);
628
/******************************************************************//**
629
This function is called only in the debug version. Detects a deadlock
630
of one or more threads because of waits of semaphores.
631
@return TRUE if deadlock detected */
634
sync_array_detect_deadlock(
635
/*=======================*/
636
sync_array_t* arr, /*!< in: wait array; NOTE! the caller must
637
own the mutex to array */
638
sync_cell_t* start, /*!< in: cell where recursive search started */
639
sync_cell_t* cell, /*!< in: cell to search */
640
ulint depth) /*!< in: recursion depth */
644
os_thread_id_t thread;
646
rw_lock_debug_t*debug;
651
ut_ad(cell->wait_object);
652
ut_ad(os_thread_get_curr_id() == start->thread);
657
if (!cell->waiting) {
659
return(FALSE); /* No deadlock here */
662
if (cell->request_type == SYNC_MUTEX) {
664
mutex = cell->wait_object;
666
if (mutex_get_lock_word(mutex) != 0) {
668
thread = mutex->thread_id;
670
/* Note that mutex->thread_id above may be
671
also OS_THREAD_ID_UNDEFINED, because the
672
thread which held the mutex maybe has not
673
yet updated the value, or it has already
674
released the mutex: in this case no deadlock
675
can occur, as the wait array cannot contain
676
a thread with ID_UNDEFINED value. */
678
ret = sync_array_deadlock_step(arr, start, thread, 0,
682
"Mutex %p owned by thread %lu file %s line %lu\n",
683
mutex, (ulong) os_thread_pf(mutex->thread_id),
684
mutex->file_name, (ulong) mutex->line);
685
sync_array_cell_print(stderr, cell);
691
return(FALSE); /* No deadlock */
693
} else if (cell->request_type == RW_LOCK_EX
694
|| cell->request_type == RW_LOCK_WAIT_EX) {
696
lock = cell->wait_object;
698
debug = UT_LIST_GET_FIRST(lock->debug_list);
700
while (debug != NULL) {
702
thread = debug->thread_id;
704
if (((debug->lock_type == RW_LOCK_EX)
705
&& !os_thread_eq(thread, cell->thread))
706
|| ((debug->lock_type == RW_LOCK_WAIT_EX)
707
&& !os_thread_eq(thread, cell->thread))
708
|| (debug->lock_type == RW_LOCK_SHARED)) {
710
/* The (wait) x-lock request can block
711
infinitely only if someone (can be also cell
712
thread) is holding s-lock, or someone
713
(cannot be cell thread) (wait) x-lock, and
714
he is blocked by start thread */
716
ret = sync_array_deadlock_step(
717
arr, start, thread, debug->pass,
721
fprintf(stderr, "rw-lock %p ",
723
sync_array_cell_print(stderr, cell);
724
rw_lock_debug_print(debug);
729
debug = UT_LIST_GET_NEXT(list, debug);
734
} else if (cell->request_type == RW_LOCK_SHARED) {
736
lock = cell->wait_object;
737
debug = UT_LIST_GET_FIRST(lock->debug_list);
739
while (debug != NULL) {
741
thread = debug->thread_id;
743
if ((debug->lock_type == RW_LOCK_EX)
744
|| (debug->lock_type == RW_LOCK_WAIT_EX)) {
746
/* The s-lock request can block infinitely
747
only if someone (can also be cell thread) is
748
holding (wait) x-lock, and he is blocked by
751
ret = sync_array_deadlock_step(
752
arr, start, thread, debug->pass,
759
debug = UT_LIST_GET_NEXT(list, debug);
768
return(TRUE); /* Execution never reaches this line: for compiler
771
#endif /* UNIV_SYNC_DEBUG */
773
/******************************************************************//**
774
Determines if we can wake up the thread waiting for a sempahore. */
777
sync_arr_cell_can_wake_up(
778
/*======================*/
779
sync_cell_t* cell) /*!< in: cell to search */
784
if (cell->request_type == SYNC_MUTEX) {
786
mutex = static_cast<mutex_t *>(cell->wait_object);
788
if (mutex_get_lock_word(mutex) == 0) {
793
} else if (cell->request_type == RW_LOCK_EX) {
795
lock = static_cast<rw_lock_t *>(cell->wait_object);
797
if (lock->lock_word > 0) {
798
/* Either unlocked or only read locked. */
803
} else if (cell->request_type == RW_LOCK_WAIT_EX) {
805
lock = static_cast<rw_lock_t *>(cell->wait_object);
807
/* lock_word == 0 means all readers have left */
808
if (lock->lock_word == 0) {
812
} else if (cell->request_type == RW_LOCK_SHARED) {
813
lock = static_cast<rw_lock_t *>(cell->wait_object);
815
/* lock_word > 0 means no writer or reserved writer */
816
if (lock->lock_word > 0) {
825
/******************************************************************//**
826
Frees the cell. NOTE! sync_array_wait_event frees the cell
830
sync_array_free_cell(
831
/*=================*/
832
sync_array_t* arr, /*!< in: wait array */
833
ulint index) /*!< in: index of the cell in array */
837
sync_array_enter(arr);
839
cell = sync_array_get_nth_cell(arr, index);
841
ut_a(cell->wait_object != NULL);
843
cell->waiting = FALSE;
844
cell->wait_object = NULL;
845
cell->signal_count = 0;
847
ut_a(arr->n_reserved > 0);
850
sync_array_exit(arr);
853
/**********************************************************************//**
854
Increments the signalled count. */
857
sync_array_object_signalled(
858
/*========================*/
859
sync_array_t* arr) /*!< in: wait array */
861
#ifdef HAVE_ATOMIC_BUILTINS
862
(void) os_atomic_increment_ulint(&arr->sg_count, 1);
864
sync_array_enter(arr);
868
sync_array_exit(arr);
872
/**********************************************************************//**
873
If the wakeup algorithm does not work perfectly at semaphore relases,
874
this function will do the waking (see the comment in mutex_exit). This
875
function should be called about every 1 second in the server.
877
Note that there's a race condition between this thread and mutex_exit
878
changing the lock_word and calling signal_object, so sometimes this finds
879
threads to wake up even when nothing has gone wrong. */
882
sync_arr_wake_threads_if_sema_free(void)
883
/*====================================*/
885
sync_array_t* arr = sync_primary_wait_array;
891
sync_array_enter(arr);
896
while (count < arr->n_reserved) {
898
cell = sync_array_get_nth_cell(arr, i);
901
if (cell->wait_object == NULL) {
906
if (sync_arr_cell_can_wake_up(cell)) {
908
event = sync_cell_get_event(cell);
915
sync_array_exit(arr);
918
/**********************************************************************//**
919
Prints warnings of long semaphore waits to stderr.
920
@return TRUE if fatal semaphore wait threshold was exceeded */
923
sync_array_print_long_waits(void)
924
/*=============================*/
928
ibool noticed = FALSE;
930
ulint fatal_timeout = srv_fatal_semaphore_wait_threshold;
933
for (i = 0; i < sync_primary_wait_array->n_cells; i++) {
935
cell = sync_array_get_nth_cell(sync_primary_wait_array, i);
937
if (cell->wait_object != NULL && cell->waiting
938
&& difftime(time(NULL), cell->reservation_time) > 240) {
939
fputs("InnoDB: Warning: a long semaphore wait:\n",
941
sync_array_cell_print(stderr, cell);
945
if (cell->wait_object != NULL && cell->waiting
946
&& difftime(time(NULL), cell->reservation_time)
954
"InnoDB: ###### Starts InnoDB Monitor"
955
" for 30 secs to print diagnostic info:\n");
956
old_val = srv_print_innodb_monitor;
958
/* If some crucial semaphore is reserved, then also the InnoDB
959
Monitor can hang, and we do not get diagnostics. Since in
960
many cases an InnoDB hang is caused by a pwrite() or a pread()
961
call hanging inside the operating system, let us print right
962
now the values of pending calls of these. */
965
"InnoDB: Pending preads %lu, pwrites %lu\n",
966
(ulong)os_file_n_pending_preads,
967
(ulong)os_file_n_pending_pwrites);
969
srv_print_innodb_monitor = TRUE;
970
os_event_set(srv_lock_timeout_thread_event);
972
os_thread_sleep(30000000);
974
srv_print_innodb_monitor = old_val;
976
"InnoDB: ###### Diagnostic info printed"
977
" to the standard error stream\n");
983
/**********************************************************************//**
984
Prints info of the wait array. */
987
sync_array_output_info(
988
/*===================*/
989
FILE* file, /*!< in: file where to print */
990
sync_array_t* arr) /*!< in: wait array; NOTE! caller must own the
998
"OS WAIT ARRAY INFO: reservation count %ld, signal count %ld\n",
999
(long) arr->res_count, (long) arr->sg_count);
1003
while (count < arr->n_reserved) {
1005
cell = sync_array_get_nth_cell(arr, i);
1007
if (cell->wait_object != NULL) {
1009
sync_array_cell_print(file, cell);
1016
/**********************************************************************//**
1017
Prints info of the wait array. */
1020
sync_array_print_info(
1021
/*==================*/
1022
FILE* file, /*!< in: file where to print */
1023
sync_array_t* arr) /*!< in: wait array */
1025
sync_array_enter(arr);
1027
sync_array_output_info(file, arr);
1029
sync_array_exit(arr);