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., 59 Temple
22
Place, Suite 330, Boston, MA 02111-1307 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_SYNC_DEBUG
142
/******************************************************************//**
143
This function is called only in the debug version. Detects a deadlock
144
of one or more threads because of waits of semaphores.
145
@return TRUE if deadlock detected */
148
sync_array_detect_deadlock(
149
/*=======================*/
150
sync_array_t* arr, /*!< in: wait array; NOTE! the caller must
151
own the mutex to array */
152
sync_cell_t* start, /*!< in: cell where recursive search started */
153
sync_cell_t* cell, /*!< in: cell to search */
154
ulint depth); /*!< in: recursion depth */
155
#endif /* UNIV_SYNC_DEBUG */
157
/*****************************************************************//**
158
Gets the nth cell in array.
162
sync_array_get_nth_cell(
163
/*====================*/
164
sync_array_t* arr, /*!< in: sync array */
165
ulint n) /*!< in: index */
168
ut_a(n < arr->n_cells);
170
return(arr->array + n);
173
/******************************************************************//**
174
Reserves the mutex semaphore protecting a sync array. */
179
sync_array_t* arr) /*!< in: sync wait array */
183
protection = arr->protection;
185
if (protection == SYNC_ARRAY_OS_MUTEX) {
186
os_mutex_enter(arr->os_mutex);
187
} else if (protection == SYNC_ARRAY_MUTEX) {
188
mutex_enter(&(arr->mutex));
194
/******************************************************************//**
195
Releases the mutex semaphore protecting a sync array. */
200
sync_array_t* arr) /*!< in: sync wait array */
204
protection = arr->protection;
206
if (protection == SYNC_ARRAY_OS_MUTEX) {
207
os_mutex_exit(arr->os_mutex);
208
} else if (protection == SYNC_ARRAY_MUTEX) {
209
mutex_exit(&(arr->mutex));
215
/*******************************************************************//**
216
Creates a synchronization wait array. It is protected by a mutex
217
which is automatically reserved when the functions operating on it
219
@return own: created wait array */
224
ulint n_cells, /*!< in: number of cells in the array
226
ulint protection) /*!< in: either SYNC_ARRAY_OS_MUTEX or
227
SYNC_ARRAY_MUTEX: determines the type
228
of mutex protecting the data structure */
231
sync_cell_t* cell_array;
237
/* Allocate memory for the data structures */
238
arr = ut_malloc(sizeof(sync_array_t));
240
cell_array = ut_malloc(sizeof(sync_cell_t) * n_cells);
242
arr->n_cells = n_cells;
244
arr->array = cell_array;
245
arr->protection = protection;
249
/* Then create the mutex to protect the wait array complex */
250
if (protection == SYNC_ARRAY_OS_MUTEX) {
251
arr->os_mutex = os_mutex_create(NULL);
252
} else if (protection == SYNC_ARRAY_MUTEX) {
253
mutex_create(&arr->mutex, SYNC_NO_ORDER_CHECK);
258
for (i = 0; i < n_cells; i++) {
259
cell = sync_array_get_nth_cell(arr, i);
260
cell->wait_object = NULL;
261
cell->waiting = FALSE;
262
cell->signal_count = 0;
268
/******************************************************************//**
269
Frees the resources in a wait array. */
274
sync_array_t* arr) /*!< in, own: sync wait array */
278
ut_a(arr->n_reserved == 0);
280
sync_array_validate(arr);
282
protection = arr->protection;
284
/* Release the mutex protecting the wait array complex */
286
if (protection == SYNC_ARRAY_OS_MUTEX) {
287
os_mutex_free(arr->os_mutex);
288
} else if (protection == SYNC_ARRAY_MUTEX) {
289
mutex_free(&(arr->mutex));
298
/********************************************************************//**
299
Validates the integrity of the wait array. Checks
300
that the number of reserved cells equals the count variable. */
305
sync_array_t* arr) /*!< in: sync wait array */
311
sync_array_enter(arr);
313
for (i = 0; i < arr->n_cells; i++) {
314
cell = sync_array_get_nth_cell(arr, i);
315
if (cell->wait_object != NULL) {
320
ut_a(count == arr->n_reserved);
322
sync_array_exit(arr);
325
/*******************************************************************//**
326
Returns the event that the thread owning the cell waits for. */
331
sync_cell_t* cell) /*!< in: non-empty sync array cell */
333
ulint type = cell->request_type;
335
if (type == SYNC_MUTEX) {
336
return(((mutex_t *) cell->wait_object)->event);
337
} else if (type == RW_LOCK_WAIT_EX) {
338
return(((rw_lock_t *) cell->wait_object)->wait_ex_event);
339
} else { /* RW_LOCK_SHARED and RW_LOCK_EX wait on the same event */
340
return(((rw_lock_t *) cell->wait_object)->event);
344
/******************************************************************//**
345
Reserves a wait array cell for waiting for an object.
346
The event of the cell is reset to nonsignalled state. */
349
sync_array_reserve_cell(
350
/*====================*/
351
sync_array_t* arr, /*!< in: wait array */
352
void* object, /*!< in: pointer to the object to wait for */
353
ulint type, /*!< in: lock request type */
354
const char* file, /*!< in: file where requested */
355
ulint line, /*!< in: line where requested */
356
ulint* index) /*!< out: index of the reserved cell */
365
sync_array_enter(arr);
369
/* Reserve a new cell. */
370
for (i = 0; i < arr->n_cells; i++) {
371
cell = sync_array_get_nth_cell(arr, i);
373
if (cell->wait_object == NULL) {
375
cell->waiting = FALSE;
376
cell->wait_object = object;
378
if (type == SYNC_MUTEX) {
379
cell->old_wait_mutex = object;
381
cell->old_wait_rw_lock = object;
384
cell->request_type = type;
393
sync_array_exit(arr);
395
/* Make sure the event is reset and also store
396
the value of signal_count at which the event
398
event = sync_cell_get_event(cell);
399
cell->signal_count = os_event_reset(event);
401
cell->reservation_time = time(NULL);
403
cell->thread = os_thread_get_curr_id();
409
ut_error; /* No free cell found */
414
/******************************************************************//**
415
This function should be called when a thread starts to wait on
416
a wait array cell. In the debug version this function checks
417
if the wait for a semaphore will result in a deadlock, in which
418
case prints info and asserts. */
421
sync_array_wait_event(
422
/*==================*/
423
sync_array_t* arr, /*!< in: wait array */
424
ulint index) /*!< in: index of the reserved cell */
431
sync_array_enter(arr);
433
cell = sync_array_get_nth_cell(arr, index);
435
ut_a(cell->wait_object);
436
ut_a(!cell->waiting);
437
ut_ad(os_thread_get_curr_id() == cell->thread);
439
event = sync_cell_get_event(cell);
440
cell->waiting = TRUE;
442
#ifdef UNIV_SYNC_DEBUG
444
/* We use simple enter to the mutex below, because if
445
we cannot acquire it at once, mutex_enter would call
446
recursively sync_array routines, leading to trouble.
447
rw_lock_debug_mutex freezes the debug lists. */
449
rw_lock_debug_mutex_enter();
451
if (TRUE == sync_array_detect_deadlock(arr, cell, cell, 0)) {
453
fputs("########################################\n", stderr);
457
rw_lock_debug_mutex_exit();
459
sync_array_exit(arr);
461
os_event_wait_low(event, cell->signal_count);
463
sync_array_free_cell(arr, index);
466
/******************************************************************//**
467
Reports info of a wait array cell. */
470
sync_array_cell_print(
471
/*==================*/
472
FILE* file, /*!< in: file where to print */
473
sync_cell_t* cell) /*!< in: sync cell */
480
type = cell->request_type;
483
"--Thread %lu has waited at %s line %lu"
484
" for %.2f seconds the semaphore:\n",
485
(ulong) os_thread_pf(cell->thread), cell->file,
487
difftime(time(NULL), cell->reservation_time));
489
if (type == SYNC_MUTEX) {
490
/* We use old_wait_mutex in case the cell has already
491
been freed meanwhile */
492
mutex = cell->old_wait_mutex;
495
"Mutex at %p created file %s line %lu, lock var %lu\n"
496
#ifdef UNIV_SYNC_DEBUG
497
"Last time reserved in file %s line %lu, "
498
#endif /* UNIV_SYNC_DEBUG */
499
"waiters flag %lu\n",
500
(void*) mutex, mutex->cfile_name, (ulong) mutex->cline,
501
(ulong) mutex->lock_word,
502
#ifdef UNIV_SYNC_DEBUG
503
mutex->file_name, (ulong) mutex->line,
504
#endif /* UNIV_SYNC_DEBUG */
505
(ulong) mutex->waiters);
507
} else if (type == RW_LOCK_EX
508
|| type == RW_LOCK_WAIT_EX
509
|| type == RW_LOCK_SHARED) {
511
fputs(type == RW_LOCK_EX ? "X-lock on" : "S-lock on", file);
513
rwlock = cell->old_wait_rw_lock;
516
" RW-latch at %p created in file %s line %lu\n",
517
(void*) rwlock, rwlock->cfile_name,
518
(ulong) rwlock->cline);
519
writer = rw_lock_get_writer(rwlock);
520
if (writer != RW_LOCK_NOT_LOCKED) {
522
"a writer (thread id %lu) has"
523
" reserved it in mode %s",
524
(ulong) os_thread_pf(rwlock->writer_thread),
527
: " wait exclusive\n");
531
"number of readers %lu, waiters flag %lu, "
533
"Last time read locked in file %s line %lu\n"
534
"Last time write locked in file %s line %lu\n",
535
(ulong) rw_lock_get_reader_count(rwlock),
536
(ulong) rwlock->waiters,
538
rwlock->last_s_file_name,
539
(ulong) rwlock->last_s_line,
540
rwlock->last_x_file_name,
541
(ulong) rwlock->last_x_line);
546
if (!cell->waiting) {
547
fputs("wait has ended\n", file);
551
#ifdef UNIV_SYNC_DEBUG
552
/******************************************************************//**
553
Looks for a cell with the given thread id.
554
@return pointer to cell or NULL if not found */
557
sync_array_find_thread(
558
/*===================*/
559
sync_array_t* arr, /*!< in: wait array */
560
os_thread_id_t thread) /*!< in: thread id */
565
for (i = 0; i < arr->n_cells; i++) {
567
cell = sync_array_get_nth_cell(arr, i);
569
if (cell->wait_object != NULL
570
&& os_thread_eq(cell->thread, thread)) {
572
return(cell); /* Found */
576
return(NULL); /* Not found */
579
/******************************************************************//**
580
Recursion step for deadlock detection.
581
@return TRUE if deadlock detected */
584
sync_array_deadlock_step(
585
/*=====================*/
586
sync_array_t* arr, /*!< in: wait array; NOTE! the caller must
587
own the mutex to array */
588
sync_cell_t* start, /*!< in: cell where recursive search
590
os_thread_id_t thread, /*!< in: thread to look at */
591
ulint pass, /*!< in: pass value */
592
ulint depth) /*!< in: recursion depth */
600
/* If pass != 0, then we do not know which threads are
601
responsible of releasing the lock, and no deadlock can
607
new = sync_array_find_thread(arr, thread);
610
/* Stop running of other threads */
612
ut_dbg_stop_threads = TRUE;
615
fputs("########################################\n"
616
"DEADLOCK of threads detected!\n", stderr);
621
ret = sync_array_detect_deadlock(arr, start, new, depth);
630
/******************************************************************//**
631
This function is called only in the debug version. Detects a deadlock
632
of one or more threads because of waits of semaphores.
633
@return TRUE if deadlock detected */
636
sync_array_detect_deadlock(
637
/*=======================*/
638
sync_array_t* arr, /*!< in: wait array; NOTE! the caller must
639
own the mutex to array */
640
sync_cell_t* start, /*!< in: cell where recursive search started */
641
sync_cell_t* cell, /*!< in: cell to search */
642
ulint depth) /*!< in: recursion depth */
646
os_thread_id_t thread;
648
rw_lock_debug_t*debug;
653
ut_ad(cell->wait_object);
654
ut_ad(os_thread_get_curr_id() == start->thread);
659
if (!cell->waiting) {
661
return(FALSE); /* No deadlock here */
664
if (cell->request_type == SYNC_MUTEX) {
666
mutex = cell->wait_object;
668
if (mutex_get_lock_word(mutex) != 0) {
670
thread = mutex->thread_id;
672
/* Note that mutex->thread_id above may be
673
also OS_THREAD_ID_UNDEFINED, because the
674
thread which held the mutex maybe has not
675
yet updated the value, or it has already
676
released the mutex: in this case no deadlock
677
can occur, as the wait array cannot contain
678
a thread with ID_UNDEFINED value. */
680
ret = sync_array_deadlock_step(arr, start, thread, 0,
684
"Mutex %p owned by thread %lu file %s line %lu\n",
685
mutex, (ulong) os_thread_pf(mutex->thread_id),
686
mutex->file_name, (ulong) mutex->line);
687
sync_array_cell_print(stderr, cell);
693
return(FALSE); /* No deadlock */
695
} else if (cell->request_type == RW_LOCK_EX
696
|| cell->request_type == RW_LOCK_WAIT_EX) {
698
lock = cell->wait_object;
700
debug = UT_LIST_GET_FIRST(lock->debug_list);
702
while (debug != NULL) {
704
thread = debug->thread_id;
706
if (((debug->lock_type == RW_LOCK_EX)
707
&& !os_thread_eq(thread, cell->thread))
708
|| ((debug->lock_type == RW_LOCK_WAIT_EX)
709
&& !os_thread_eq(thread, cell->thread))
710
|| (debug->lock_type == RW_LOCK_SHARED)) {
712
/* The (wait) x-lock request can block
713
infinitely only if someone (can be also cell
714
thread) is holding s-lock, or someone
715
(cannot be cell thread) (wait) x-lock, and
716
he is blocked by start thread */
718
ret = sync_array_deadlock_step(
719
arr, start, thread, debug->pass,
723
fprintf(stderr, "rw-lock %p ",
725
sync_array_cell_print(stderr, cell);
726
rw_lock_debug_print(debug);
731
debug = UT_LIST_GET_NEXT(list, debug);
736
} else if (cell->request_type == RW_LOCK_SHARED) {
738
lock = cell->wait_object;
739
debug = UT_LIST_GET_FIRST(lock->debug_list);
741
while (debug != NULL) {
743
thread = debug->thread_id;
745
if ((debug->lock_type == RW_LOCK_EX)
746
|| (debug->lock_type == RW_LOCK_WAIT_EX)) {
748
/* The s-lock request can block infinitely
749
only if someone (can also be cell thread) is
750
holding (wait) x-lock, and he is blocked by
753
ret = sync_array_deadlock_step(
754
arr, start, thread, debug->pass,
761
debug = UT_LIST_GET_NEXT(list, debug);
770
return(TRUE); /* Execution never reaches this line: for compiler
773
#endif /* UNIV_SYNC_DEBUG */
775
/******************************************************************//**
776
Determines if we can wake up the thread waiting for a sempahore. */
779
sync_arr_cell_can_wake_up(
780
/*======================*/
781
sync_cell_t* cell) /*!< in: cell to search */
786
if (cell->request_type == SYNC_MUTEX) {
788
mutex = cell->wait_object;
790
if (mutex_get_lock_word(mutex) == 0) {
795
} else if (cell->request_type == RW_LOCK_EX) {
797
lock = cell->wait_object;
799
if (lock->lock_word > 0) {
800
/* Either unlocked or only read locked. */
805
} else if (cell->request_type == RW_LOCK_WAIT_EX) {
807
lock = cell->wait_object;
809
/* lock_word == 0 means all readers have left */
810
if (lock->lock_word == 0) {
814
} else if (cell->request_type == RW_LOCK_SHARED) {
815
lock = cell->wait_object;
817
/* lock_word > 0 means no writer or reserved writer */
818
if (lock->lock_word > 0) {
827
/******************************************************************//**
828
Frees the cell. NOTE! sync_array_wait_event frees the cell
832
sync_array_free_cell(
833
/*=================*/
834
sync_array_t* arr, /*!< in: wait array */
835
ulint index) /*!< in: index of the cell in array */
839
sync_array_enter(arr);
841
cell = sync_array_get_nth_cell(arr, index);
843
ut_a(cell->wait_object != NULL);
845
cell->waiting = FALSE;
846
cell->wait_object = NULL;
847
cell->signal_count = 0;
849
ut_a(arr->n_reserved > 0);
852
sync_array_exit(arr);
855
/**********************************************************************//**
856
Increments the signalled count. */
859
sync_array_object_signalled(
860
/*========================*/
861
sync_array_t* arr) /*!< in: wait array */
863
#ifdef HAVE_ATOMIC_BUILTINS
864
(void) os_atomic_increment_ulint(&arr->sg_count, 1);
866
sync_array_enter(arr);
870
sync_array_exit(arr);
874
/**********************************************************************//**
875
If the wakeup algorithm does not work perfectly at semaphore relases,
876
this function will do the waking (see the comment in mutex_exit). This
877
function should be called about every 1 second in the server.
879
Note that there's a race condition between this thread and mutex_exit
880
changing the lock_word and calling signal_object, so sometimes this finds
881
threads to wake up even when nothing has gone wrong. */
884
sync_arr_wake_threads_if_sema_free(void)
885
/*====================================*/
887
sync_array_t* arr = sync_primary_wait_array;
893
sync_array_enter(arr);
898
while (count < arr->n_reserved) {
900
cell = sync_array_get_nth_cell(arr, i);
903
if (cell->wait_object == NULL) {
908
if (sync_arr_cell_can_wake_up(cell)) {
910
event = sync_cell_get_event(cell);
917
sync_array_exit(arr);
920
/**********************************************************************//**
921
Prints warnings of long semaphore waits to stderr.
922
@return TRUE if fatal semaphore wait threshold was exceeded */
925
sync_array_print_long_waits(void)
926
/*=============================*/
930
ibool noticed = FALSE;
932
ulint fatal_timeout = srv_fatal_semaphore_wait_threshold;
935
for (i = 0; i < sync_primary_wait_array->n_cells; i++) {
937
cell = sync_array_get_nth_cell(sync_primary_wait_array, i);
939
if (cell->wait_object != NULL && cell->waiting
940
&& difftime(time(NULL), cell->reservation_time) > 240) {
941
fputs("InnoDB: Warning: a long semaphore wait:\n",
943
sync_array_cell_print(stderr, cell);
947
if (cell->wait_object != NULL && cell->waiting
948
&& difftime(time(NULL), cell->reservation_time)
956
"InnoDB: ###### Starts InnoDB Monitor"
957
" for 30 secs to print diagnostic info:\n");
958
old_val = srv_print_innodb_monitor;
960
/* If some crucial semaphore is reserved, then also the InnoDB
961
Monitor can hang, and we do not get diagnostics. Since in
962
many cases an InnoDB hang is caused by a pwrite() or a pread()
963
call hanging inside the operating system, let us print right
964
now the values of pending calls of these. */
967
"InnoDB: Pending preads %lu, pwrites %lu\n",
968
(ulong)os_file_n_pending_preads,
969
(ulong)os_file_n_pending_pwrites);
971
srv_print_innodb_monitor = TRUE;
972
os_event_set(srv_lock_timeout_thread_event);
974
os_thread_sleep(30000000);
976
srv_print_innodb_monitor = old_val;
978
"InnoDB: ###### Diagnostic info printed"
979
" to the standard error stream\n");
985
/**********************************************************************//**
986
Prints info of the wait array. */
989
sync_array_output_info(
990
/*===================*/
991
FILE* file, /*!< in: file where to print */
992
sync_array_t* arr) /*!< in: wait array; NOTE! caller must own the
1000
"OS WAIT ARRAY INFO: reservation count %ld, signal count %ld\n",
1001
(long) arr->res_count, (long) arr->sg_count);
1005
while (count < arr->n_reserved) {
1007
cell = sync_array_get_nth_cell(arr, i);
1009
if (cell->wait_object != NULL) {
1011
sync_array_cell_print(file, cell);
1018
/**********************************************************************//**
1019
Prints info of the wait array. */
1022
sync_array_print_info(
1023
/*==================*/
1024
FILE* file, /*!< in: file where to print */
1025
sync_array_t* arr) /*!< in: wait array */
1027
sync_array_enter(arr);
1029
sync_array_output_info(file, arr);
1031
sync_array_exit(arr);