1/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
2 Copyright (c) 2012, Monty Program Ab.
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/*
18Read and write locks for Posix threads. All tread must acquire
19all locks it needs through thr_multi_lock() to avoid dead-locks.
20A lock consists of a master lock (THR_LOCK), and lock instances
21(THR_LOCK_DATA).
22Any thread can have any number of lock instances (read and write:s) on
23any lock. All lock instances must be freed.
24Locks are prioritized according to:
25
26The current lock types are:
27
28TL_READ # Low priority read
29TL_READ_WITH_SHARED_LOCKS
30TL_READ_HIGH_PRIORITY # High priority read
31TL_READ_NO_INSERT # Read without concurrent inserts
32TL_WRITE_ALLOW_WRITE # Write lock that allows other writers
33TL_WRITE_CONCURRENT_INSERT
34 # Insert that can be mixed when selects
35TL_WRITE_DELAYED # Used by delayed insert
36 # Allows lower locks to take over
37TL_WRITE_LOW_PRIORITY # Low priority write
38TL_WRITE # High priority write
39TL_WRITE_ONLY # High priority write
40 # Abort all new lock request with an error
41
42Locks are prioritized according to:
43
44WRITE_ALLOW_WRITE, WRITE_CONCURRENT_INSERT, WRITE_DELAYED,
45WRITE_LOW_PRIORITY, READ, WRITE, READ_HIGH_PRIORITY and WRITE_ONLY
46
47Locks in the same privilege level are scheduled in first-in-first-out order.
48
49To allow concurrent read/writes locks, with 'WRITE_CONCURRENT_INSERT' one
50should put a pointer to the following functions in the lock structure:
51(If the pointer is zero (default), the function is not called)
52
53check_status:
54 Before giving a lock of type TL_WRITE_CONCURRENT_INSERT,
55 we check if this function exists and returns 0.
56 If not, then the lock is upgraded to TL_WRITE_LOCK
57 In MyISAM this is a simple check if the insert can be done
58 at the end of the datafile.
59update_status:
60 in thr_reschedule_write_lock(), when an insert delayed thread
61 downgrades TL_WRITE lock to TL_WRITE_DELAYED, to allow SELECT
62 threads to proceed.
63 A storage engine should also call update_status internally
64 in the ::external_lock(F_UNLCK) method.
65 In MyISAM and CSV this functions updates the length of the datafile.
66 MySQL does in some exceptional cases (when doing DLL statements on
67 open tables calls thr_unlock() followed by thr_lock() without calling
68 ::external_lock() in between. In this case thr_unlock() is called with
69 the THR_UNLOCK_UPDATE_STATUS flag and thr_unlock() will call
70 update_status for write locks.
71get_status:
72 When one gets a lock this functions is called.
73 In MyISAM this stores the number of rows and size of the datafile
74 for concurrent reads.
75
76The lock algorithm allows one to have one TL_WRITE_CONCURRENT_INSERT or
77one TL_WRITE_DELAYED lock at the same time as multiple read locks.
78
79In addition, if lock->allow_multiple_concurrent_insert is set then there can
80be any number of TL_WRITE_CONCURRENT_INSERT locks aktive at the same time.
81*/
82
83#if !defined(MAIN) && !defined(DBUG_OFF) && !defined(EXTRA_DEBUG)
84#define FORCE_DBUG_OFF
85#endif
86
87#include "mysys_priv.h"
88
89#include "thr_lock.h"
90#include "mysql/psi/mysql_table.h"
91#include <m_string.h>
92#include <errno.h>
93
94my_bool thr_lock_inited=0;
95ulong locks_immediate = 0L, locks_waited = 0L;
96enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE;
97
98#ifdef WITH_WSREP
99static wsrep_thd_is_brute_force_fun wsrep_thd_is_brute_force= NULL;
100static wsrep_abort_thd_fun wsrep_abort_thd= NULL;
101static my_bool wsrep_debug;
102static my_bool wsrep_convert_LOCK_to_trx;
103static wsrep_on_fun wsrep_on = NULL;
104
105void wsrep_thr_lock_init(
106 wsrep_thd_is_brute_force_fun bf_fun, wsrep_abort_thd_fun abort_fun,
107 my_bool debug, my_bool convert_LOCK_to_trx, wsrep_on_fun on_fun
108) {
109 wsrep_thd_is_brute_force = bf_fun;
110 wsrep_abort_thd = abort_fun;
111 wsrep_debug = debug;
112 wsrep_convert_LOCK_to_trx= convert_LOCK_to_trx;
113 wsrep_on = on_fun;
114}
115#endif
116/* The following constants are only for debug output */
117#define MAX_THREADS 1000
118#define MAX_LOCKS 1000
119
120
121LIST *thr_lock_thread_list; /* List of threads in use */
122ulong max_write_lock_count= ~(ulong) 0L;
123
124static void (*before_lock_wait)(void)= 0;
125static void (*after_lock_wait)(void)= 0;
126
127void thr_set_lock_wait_callback(void (*before_wait)(void),
128 void (*after_wait)(void))
129{
130 before_lock_wait= before_wait;
131 after_lock_wait= after_wait;
132}
133
134static inline mysql_cond_t *get_cond(void)
135{
136 return &my_thread_var->suspend;
137}
138
139
140/*
141 Sort locks in priority order
142
143 LOCK_CMP()
144 A First lock
145 B Second lock
146
147 Return:
148 0 if A >= B
149 1 if A < B
150
151 Priority for locks (decides in which order locks are locked)
152 We want all write locks to be first, followed by read locks.
153 Locks from MERGE tables has a little lower priority than other
154 locks, to allow one to release merge tables without having
155 to unlock and re-lock other locks.
156 The lower the number, the higher the priority for the lock.
157 For MERGE tables we add 2 (THR_LOCK_MERGE_PRIV) to the lock priority.
158 THR_LOCK_LATE_PRIV (1) is used when one locks other tables to be merged
159 with existing locks. This way we prioritize the original locks over the
160 new locks.
161*/
162
163
164static inline int LOCK_CMP(THR_LOCK_DATA *a, THR_LOCK_DATA *b)
165{
166 if (a->lock != b->lock)
167 return a->lock < b->lock;
168
169 if (a->type != b->type)
170 return a->type > b->type;
171
172 return a->priority < b->priority;
173}
174
175
176/*
177 For the future (now the thread specific cond is alloced by my_pthread.c)
178*/
179
180my_bool init_thr_lock()
181{
182 thr_lock_inited=1;
183 return 0;
184}
185
186static inline my_bool
187thr_lock_owner_equal(THR_LOCK_INFO *rhs, THR_LOCK_INFO *lhs)
188{
189 return rhs == lhs;
190}
191
192
193#ifdef EXTRA_DEBUG
194#define MAX_FOUND_ERRORS 10 /* Report 10 first errors */
195static uint found_errors=0;
196
197static int check_lock(struct st_lock_list *list, const char* lock_type,
198 const char *where, my_bool same_owner, my_bool no_cond,
199 my_bool read_lock)
200{
201 THR_LOCK_DATA *data,**prev;
202 uint count=0;
203
204 prev= &list->data;
205 if (list->data)
206 {
207 enum thr_lock_type last_lock_type= list->data->type;
208 THR_LOCK_INFO *first_owner= list->data->owner;
209
210 for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next)
211 {
212 if (data->type == TL_UNLOCK)
213 {
214 fprintf(stderr,
215 "Warning: Found unlocked lock at %s: %s\n",
216 lock_type, where);
217 return 1;
218 }
219 if ((read_lock && data->type > TL_READ_NO_INSERT) ||
220 (!read_lock && data->type <= TL_READ_NO_INSERT))
221 {
222 fprintf(stderr,
223 "Warning: Found %s lock in %s queue at %s: %s\n",
224 read_lock ? "write" : "read",
225 read_lock ? "read" : "write",
226 lock_type, where);
227 return 1;
228 }
229 if (data->type != last_lock_type)
230 last_lock_type=TL_IGNORE;
231 if (data->prev != prev)
232 {
233 fprintf(stderr,
234 "Warning: prev link %d didn't point at previous lock at %s: %s\n",
235 count, lock_type, where);
236 return 1;
237 }
238 if (same_owner &&
239 !thr_lock_owner_equal(data->owner, first_owner) &&
240 last_lock_type != TL_WRITE_ALLOW_WRITE &&
241 last_lock_type != TL_WRITE_CONCURRENT_INSERT)
242 {
243 fprintf(stderr,
244 "Warning: Found locks from different threads for lock '%s' in '%s' at '%s'. org_lock_type: %d last_lock_type: %d new_lock_type: %d\n",
245 data->lock->name ? data->lock->name : "",
246 lock_type, where, list->data->type, last_lock_type,
247 data->type);
248 return 1;
249 }
250 if (no_cond && data->cond)
251 {
252 fprintf(stderr,
253 "Warning: Found active lock with not reset cond %s: %s\n",
254 lock_type,where);
255 return 1;
256 }
257 prev= &data->next;
258 }
259 if (data)
260 {
261 fprintf(stderr,"Warning: found too many locks at %s: %s\n",
262 lock_type,where);
263 return 1;
264 }
265 }
266 if (prev != list->last)
267 {
268 fprintf(stderr,"Warning: last didn't point at last lock at %s: %s\n",
269 lock_type, where);
270 return 1;
271 }
272 return 0;
273}
274
275
276static void check_locks(THR_LOCK *lock, const char *where,
277 enum thr_lock_type type,
278 my_bool allow_no_locks)
279{
280 uint old_found_errors=found_errors;
281 DBUG_ENTER("check_locks");
282
283 if (found_errors < MAX_FOUND_ERRORS)
284 {
285 if (check_lock(&lock->write,"write",where,1,1,0) |
286 check_lock(&lock->write_wait,"write_wait",where,0,0,0) |
287 check_lock(&lock->read,"read",where,0,1,1) |
288 check_lock(&lock->read_wait,"read_wait",where,0,0,1))
289 {
290 DBUG_ASSERT(my_assert_on_error == 0);
291 found_errors++;
292 }
293
294 if (found_errors < MAX_FOUND_ERRORS)
295 {
296 uint count=0, count2= 0;
297 THR_LOCK_DATA *data;
298 for (data=lock->read.data ; data ; data=data->next)
299 {
300 count2++;
301 if (data->type == TL_READ_NO_INSERT)
302 count++;
303 /* Protect against infinite loop. */
304 DBUG_ASSERT(count <= lock->read_no_write_count &&
305 count2 <= MAX_LOCKS);
306 }
307 if (count != lock->read_no_write_count)
308 {
309 found_errors++;
310 fprintf(stderr,
311 "Warning at '%s': Locks read_no_write_count was %u when it should have been %u\n", where, lock->read_no_write_count,count);
312 }
313
314 if (!lock->write.data)
315 {
316 if (!allow_no_locks && !lock->read.data &&
317 (lock->write_wait.data || lock->read_wait.data))
318 {
319 found_errors++;
320 fprintf(stderr,
321 "Warning at '%s': No locks in use but locks are in wait queue\n",
322 where);
323 }
324 if (!lock->write_wait.data)
325 {
326 if (!allow_no_locks && lock->read_wait.data)
327 {
328 found_errors++;
329 fprintf(stderr,
330 "Warning at '%s': No write locks and waiting read locks\n",
331 where);
332 }
333 }
334 else
335 {
336 if (!allow_no_locks &&
337 (((lock->write_wait.data->type == TL_WRITE_CONCURRENT_INSERT ||
338 lock->write_wait.data->type == TL_WRITE_ALLOW_WRITE) &&
339 !lock->read_no_write_count) ||
340 (lock->write_wait.data->type == TL_WRITE_DELAYED &&
341 !lock->read.data)))
342 {
343 found_errors++;
344 fprintf(stderr,
345 "Warning at '%s': Write lock %d waiting while no exclusive read locks\n",where,(int) lock->write_wait.data->type);
346 DBUG_PRINT("warning", ("Warning at '%s': Write lock %d waiting while no exclusive read locks",where,(int) lock->write_wait.data->type));
347 }
348 }
349 }
350 else
351 {
352 /* We have at least one write lock */
353 if (lock->write.data->type == TL_WRITE_CONCURRENT_INSERT)
354 {
355 count= 0;
356 for (data=lock->write.data->next;
357 data && count < MAX_LOCKS;
358 data=data->next)
359 {
360 if (data->type != TL_WRITE_CONCURRENT_INSERT &&
361 data->type != TL_WRITE_ALLOW_WRITE)
362 {
363 fprintf(stderr,
364 "Warning at '%s': Found TL_WRITE_CONCURRENT_INSERT lock mixed with other write lock: %d\n",
365 where, data->type);
366 DBUG_PRINT("warning", ("Warning at '%s': Found TL_WRITE_CONCURRENT_INSERT lock mixed with other write lock: %d",
367 where, data->type));
368 break;
369 }
370 }
371 }
372 if (lock->write_wait.data)
373 {
374 if (!allow_no_locks &&
375 lock->write.data->type == TL_WRITE_ALLOW_WRITE &&
376 lock->write_wait.data->type == TL_WRITE_ALLOW_WRITE)
377 {
378 found_errors++;
379 fprintf(stderr,
380 "Warning at '%s': Found WRITE_ALLOW_WRITE lock waiting for WRITE_ALLOW_WRITE lock\n",
381 where);
382 DBUG_PRINT("warning", ("Warning at '%s': Found WRITE_ALLOW_WRITE lock waiting for WRITE_ALLOW_WRITE lock",
383 where));
384
385 }
386 }
387 if (lock->read.data)
388 {
389 for (data=lock->read.data ; data ; data=data->next)
390 {
391 if (!thr_lock_owner_equal(lock->write.data->owner,
392 data->owner) &&
393 ((lock->write.data->type > TL_WRITE_DELAYED &&
394 lock->write.data->type != TL_WRITE_ONLY) ||
395 ((lock->write.data->type == TL_WRITE_CONCURRENT_INSERT ||
396 lock->write.data->type == TL_WRITE_ALLOW_WRITE) &&
397 data->type == TL_READ_NO_INSERT)))
398 {
399 found_errors++;
400 fprintf(stderr,
401 "Warning at '%s' for lock: %d: Found lock of type %d that is write and read locked. Read_no_write_count: %d\n",
402 where, (int) type, lock->write.data->type,
403 lock->read_no_write_count);
404 DBUG_PRINT("warning",("At '%s' for lock %d: Found lock of type %d that is write and read locked",
405 where, (int) type,
406 lock->write.data->type));
407 }
408 }
409 }
410 if (lock->read_wait.data)
411 {
412 if (!allow_no_locks && lock->write.data->type <= TL_WRITE_DELAYED &&
413 lock->read_wait.data->type <= TL_READ_HIGH_PRIORITY)
414 {
415 found_errors++;
416 fprintf(stderr,
417 "Warning at '%s': Found read lock of type %d waiting for write lock of type %d\n",
418 where,
419 (int) lock->read_wait.data->type,
420 (int) lock->write.data->type);
421 }
422 }
423 }
424 }
425 if (found_errors != old_found_errors)
426 {
427 DBUG_PRINT("error",("Found wrong lock"));
428 }
429 }
430 DBUG_VOID_RETURN;
431}
432
433#else /* EXTRA_DEBUG */
434#define check_locks(A,B,C,D)
435#endif
436
437
438 /* Initialize a lock */
439
440void thr_lock_init(THR_LOCK *lock)
441{
442 DBUG_ENTER("thr_lock_init");
443 bzero((char*) lock,sizeof(*lock));
444 mysql_mutex_init(key_THR_LOCK_mutex, &lock->mutex, MY_MUTEX_INIT_FAST);
445 lock->read.last= &lock->read.data;
446 lock->read_wait.last= &lock->read_wait.data;
447 lock->write_wait.last= &lock->write_wait.data;
448 lock->write.last= &lock->write.data;
449
450 mysql_mutex_lock(&THR_LOCK_lock); /* Add to locks in use */
451 lock->list.data=(void*) lock;
452 thr_lock_thread_list=list_add(thr_lock_thread_list,&lock->list);
453 mysql_mutex_unlock(&THR_LOCK_lock);
454 DBUG_VOID_RETURN;
455}
456
457
458void thr_lock_delete(THR_LOCK *lock)
459{
460 DBUG_ENTER("thr_lock_delete");
461 mysql_mutex_lock(&THR_LOCK_lock);
462 thr_lock_thread_list=list_delete(thr_lock_thread_list,&lock->list);
463 mysql_mutex_unlock(&THR_LOCK_lock);
464 mysql_mutex_destroy(&lock->mutex);
465 DBUG_VOID_RETURN;
466}
467
468
469void thr_lock_info_init(THR_LOCK_INFO *info, struct st_my_thread_var *tmp)
470{
471 if (tmp)
472 tmp= my_thread_var;
473 info->thread= tmp->pthread_self;
474 info->thread_id= tmp->id;
475}
476
477 /* Initialize a lock instance */
478
479void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param)
480{
481 data->lock=lock;
482 data->type=TL_UNLOCK;
483 data->owner= 0; /* no owner yet */
484 data->status_param=param;
485 data->cond=0;
486 data->priority= 0;
487 data->debug_print_param= 0;
488}
489
490
491static inline my_bool
492has_old_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner)
493{
494 for ( ; data ; data=data->next)
495 {
496 if (thr_lock_owner_equal(data->owner, owner))
497 return 1; /* Already locked by thread */
498 }
499 return 0;
500}
501
502static void wake_up_waiters(THR_LOCK *lock);
503
504
505static enum enum_thr_lock_result
506wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
507 my_bool in_wait_list, ulong lock_wait_timeout)
508{
509 struct st_my_thread_var *thread_var= my_thread_var;
510 mysql_cond_t *cond= &thread_var->suspend;
511 struct timespec wait_timeout;
512 enum enum_thr_lock_result result= THR_LOCK_ABORTED;
513 PSI_stage_info old_stage;
514 my_bool use_wait_callbacks= FALSE;
515 DBUG_ENTER("wait_for_lock");
516
517 /*
518 One can use this to signal when a thread is going to wait for a lock.
519 See debug_sync.cc.
520
521 Beware of waiting for a signal here. The lock has acquired its mutex.
522 While waiting on a signal here, the locking thread could not acquire
523 the mutex to release the lock. One could lock up the table
524 completely.
525
526 In detail it works so: When thr_lock() tries to acquire a table
527 lock, it locks the lock->mutex, checks if it can have the lock, and
528 if not, it calls wait_for_lock(). Here it unlocks the table lock
529 while waiting on a condition. The sync point is located before this
530 wait for condition. If we have a waiting action here, we hold the
531 the table locks mutex all the time. Any attempt to look at the table
532 lock by another thread blocks it immediately on lock->mutex. This
533 can easily become an unexpected and unobvious blockage. So be
534 warned: Do not request a WAIT_FOR action for the 'wait_for_lock'
535 sync point unless you really know what you do.
536 */
537 DEBUG_SYNC_C("wait_for_lock");
538
539 if (!in_wait_list)
540 {
541 (*wait->last)=data; /* Wait for lock */
542 data->prev= wait->last;
543 wait->last= &data->next;
544 }
545
546 statistic_increment(locks_waited, &THR_LOCK_lock);
547
548 /* Set up control struct to allow others to abort locks */
549 thread_var->current_mutex= &data->lock->mutex;
550 thread_var->current_cond= cond;
551 data->cond= cond;
552
553 proc_info_hook(NULL, &stage_waiting_for_table_level_lock,
554 &old_stage,
555 __func__, __FILE__, __LINE__);
556
557 /*
558 Since before_lock_wait potentially can create more threads to
559 scheduler work for, we don't want to call the before_lock_wait
560 callback unless it will really start to wait.
561
562 For similar reasons, we do not want to call before_lock_wait and
563 after_lock_wait for each lap around the loop, so we restrict
564 ourselves to call it before_lock_wait once before starting to wait
565 and once after the thread has exited the wait loop.
566 */
567 if ((!thread_var->abort || in_wait_list) && before_lock_wait)
568 {
569 use_wait_callbacks= TRUE;
570 (*before_lock_wait)();
571 }
572
573 set_timespec(wait_timeout, lock_wait_timeout);
574 while (!thread_var->abort || in_wait_list)
575 {
576 int rc= mysql_cond_timedwait(cond, &data->lock->mutex, &wait_timeout);
577 /*
578 We must break the wait if one of the following occurs:
579 - the connection has been aborted (!thread_var->abort), but
580 this is not a delayed insert thread (in_wait_list). For a delayed
581 insert thread the proper action at shutdown is, apparently, to
582 acquire the lock and complete the insert.
583 - the lock has been granted (data->cond is set to NULL by the granter),
584 or the waiting has been aborted (additionally data->type is set to
585 TL_UNLOCK).
586 - the wait has timed out (rc == ETIMEDOUT)
587 Order of checks below is important to not report about timeout
588 if the predicate is true.
589 */
590 if (data->cond == 0)
591 {
592 DBUG_PRINT("thr_lock", ("lock granted/aborted"));
593 break;
594 }
595 if (rc == ETIMEDOUT || rc == ETIME)
596 {
597 /* purecov: begin inspected */
598 DBUG_PRINT("thr_lock", ("lock timed out"));
599 result= THR_LOCK_WAIT_TIMEOUT;
600 break;
601 /* purecov: end */
602 }
603 }
604
605 /*
606 We call the after_lock_wait callback once the wait loop has
607 finished.
608 */
609 if (after_lock_wait && use_wait_callbacks)
610 (*after_lock_wait)();
611
612 DBUG_PRINT("thr_lock", ("aborted: %d in_wait_list: %d",
613 thread_var->abort, in_wait_list));
614
615 if (data->cond || data->type == TL_UNLOCK)
616 {
617 if (data->cond) /* aborted or timed out */
618 {
619 if (((*data->prev)=data->next)) /* remove from wait-list */
620 data->next->prev= data->prev;
621 else
622 wait->last=data->prev;
623 data->type= TL_UNLOCK; /* No lock */
624 check_locks(data->lock, "killed or timed out wait_for_lock", data->type,
625 1);
626 wake_up_waiters(data->lock);
627 }
628 else
629 {
630 DBUG_PRINT("thr_lock", ("lock aborted"));
631 check_locks(data->lock, "aborted wait_for_lock", data->type, 0);
632 }
633 }
634 else
635 {
636 result= THR_LOCK_SUCCESS;
637 if (data->lock->get_status)
638 (*data->lock->get_status)(data->status_param,
639 data->type == TL_WRITE_CONCURRENT_INSERT);
640 check_locks(data->lock,"got wait_for_lock", data->type, 0);
641 }
642 mysql_mutex_unlock(&data->lock->mutex);
643
644 /* The following must be done after unlock of lock->mutex */
645 mysql_mutex_lock(&thread_var->mutex);
646 thread_var->current_mutex= 0;
647 thread_var->current_cond= 0;
648 mysql_mutex_unlock(&thread_var->mutex);
649
650 proc_info_hook(NULL, &old_stage, NULL, __func__, __FILE__, __LINE__);
651
652 DBUG_RETURN(result);
653}
654
655#ifdef WITH_WSREP
656/*
657 * If brute force applier would need to wait for a thr lock,
658 * it needs to make sure that it will get the lock without (too much)
659 * delay.
660 * We identify here the owners of blocking locks and ask them to
661 * abort. We then put our lock request in the first place in the
662 * wait queue. When lock holders abort (one by one) the lock release
663 * algorithm should grant the lock to us. We rely on this and proceed
664 * to wait_for_locks().
665 * wsrep_break_locks() should be called in all the cases, where lock
666 * wait would happen.
667 *
668 * TODO: current implementation might not cover all possible lock wait
669 * situations. This needs an review still.
670 * TODO: lock release, might favor some other lock (instead our bf).
671 * This needs an condition to check for bf locks first.
672 * TODO: we still have a debug fprintf, this should be removed
673 */
674static my_bool
675wsrep_break_lock(
676 THR_LOCK_DATA *data, struct st_lock_list *lock_queue1,
677 struct st_lock_list *wait_queue)
678{
679 if (wsrep_on && wsrep_on(data->owner->mysql_thd) &&
680 wsrep_thd_is_brute_force &&
681 wsrep_thd_is_brute_force(data->owner->mysql_thd, TRUE))
682 {
683 THR_LOCK_DATA *holder;
684
685 /* if locking session conversion to transaction has been enabled,
686 we know that this conflicting lock must be read lock and furthermore,
687 lock holder is read-only. It is safe to wait for him.
688 */
689#ifdef TODO_WHEN_LOCK_TABLES_IS_A_TRANSACTION
690 if (wsrep_convert_LOCK_to_trx &&
691 (THD*)(data->owner->mysql_thd)->in_lock_tables)
692 {
693 if (wsrep_debug)
694 fprintf(stderr,"WSREP wsrep_break_lock read lock untouched\n");
695 return FALSE;
696 }
697#endif
698 if (wsrep_debug)
699 fprintf(stderr,"WSREP wsrep_break_lock aborting locks\n");
700
701 /* aborting lock holder(s) here */
702 for (holder=(lock_queue1) ? lock_queue1->data : NULL;
703 holder;
704 holder=holder->next)
705 {
706 if (!wsrep_thd_is_brute_force(holder->owner->mysql_thd, TRUE))
707 {
708 wsrep_abort_thd(data->owner->mysql_thd,
709 holder->owner->mysql_thd, FALSE);
710 }
711 else
712 {
713 if (wsrep_debug)
714 fprintf(stderr,"WSREP wsrep_break_lock skipping BF lock conflict\n");
715 return FALSE;
716 }
717 }
718
719 /* Add our lock to the head of the wait queue */
720 if (*(wait_queue->last)==wait_queue->data)
721 {
722 wait_queue->last=&data->next;
723 assert(wait_queue->data==0);
724 }
725 else
726 {
727 assert(wait_queue->data!=0);
728 wait_queue->data->prev=&data->next;
729 }
730 data->next=wait_queue->data;
731 data->prev=&wait_queue->data;
732 wait_queue->data=data;
733 data->cond=get_cond();
734
735 statistic_increment(locks_immediate,&THR_LOCK_lock);
736 return TRUE;
737 }
738 return FALSE;
739}
740#endif
741
742static enum enum_thr_lock_result
743thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout)
744{
745 THR_LOCK *lock=data->lock;
746 enum enum_thr_lock_result result= THR_LOCK_SUCCESS;
747 struct st_lock_list *wait_queue;
748 enum thr_lock_type lock_type= data->type;
749#ifdef WITH_WSREP
750 my_bool wsrep_lock_inserted= FALSE;
751#endif
752 MYSQL_TABLE_WAIT_VARIABLES(locker, state) /* no ';' */
753 DBUG_ENTER("thr_lock");
754
755 data->next=0;
756 data->cond=0; /* safety */
757 data->owner= owner; /* Must be reset ! */
758 data->priority&= ~THR_LOCK_LATE_PRIV;
759
760 MYSQL_START_TABLE_LOCK_WAIT(locker, &state, data->m_psi,
761 PSI_TABLE_LOCK, lock_type);
762
763 mysql_mutex_lock(&lock->mutex);
764 DBUG_PRINT("lock",("data:%p thread:%lu lock:%p type: %d",
765 data, (ulong) data->owner->thread_id,
766 lock, (int) lock_type));
767 check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ?
768 "enter read_lock" : "enter write_lock", lock_type, 0);
769 if ((int) lock_type <= (int) TL_READ_NO_INSERT)
770 {
771 /* Request for READ lock */
772 if (lock->write.data)
773 {
774 /*
775 We can allow a read lock even if there is already a
776 write lock on the table if they are owned by the same
777 thread or if they satisfy the following lock
778 compatibility matrix:
779
780 Request
781 /-------
782 H|++++ WRITE_ALLOW_WRITE
783 e|+++- WRITE_CONCURRENT_INSERT
784 l|++++ WRITE_DELAYED
785 d ||||
786 |||\= READ_NO_INSERT
787 ||\ = READ_HIGH_PRIORITY
788 |\ = READ_WITH_SHARED_LOCKS
789 \ = READ
790
791
792 + = Request can be satisfied.
793 - = Request cannot be satisfied.
794
795 READ_NO_INSERT and WRITE_ALLOW_WRITE should in principle
796 be incompatible. However this will cause starvation of
797 LOCK TABLE READ in InnoDB under high write load.
798 See Bug#42147 for more information.
799 */
800
801 DBUG_PRINT("lock",("write locked 1 by thread:%lu",
802 (ulong) lock->write.data->owner->thread_id));
803 if (thr_lock_owner_equal(data->owner, lock->write.data->owner) ||
804 (lock->write.data->type <= TL_WRITE_DELAYED &&
805 (((int) lock_type <= (int) TL_READ_HIGH_PRIORITY) ||
806 (lock->write.data->type != TL_WRITE_CONCURRENT_INSERT))))
807 { /* Already got a write lock */
808 (*lock->read.last)=data; /* Add to running FIFO */
809 data->prev=lock->read.last;
810 lock->read.last= &data->next;
811 if (lock_type == TL_READ_NO_INSERT)
812 lock->read_no_write_count++;
813 check_locks(lock,"read lock with old write lock", lock_type, 0);
814 if (lock->get_status)
815 (*lock->get_status)(data->status_param, 0);
816 statistic_increment(locks_immediate,&THR_LOCK_lock);
817 goto end;
818 }
819 if (lock->write.data->type == TL_WRITE_ONLY)
820 {
821 /* We are not allowed to get a READ lock in this case */
822 data->type=TL_UNLOCK;
823 result= THR_LOCK_ABORTED; /* Can't wait for this one */
824 goto end;
825 }
826 }
827 else if (!lock->write_wait.data ||
828 lock->write_wait.data->type <= TL_WRITE_LOW_PRIORITY ||
829 lock_type == TL_READ_HIGH_PRIORITY ||
830 has_old_lock(lock->read.data, data->owner)) /* Has old read lock */
831 { /* No important write-locks */
832 (*lock->read.last)=data; /* Add to running FIFO */
833 data->prev=lock->read.last;
834 lock->read.last= &data->next;
835 if (lock_type == TL_READ_NO_INSERT)
836 lock->read_no_write_count++;
837 check_locks(lock,"read lock with no write locks", lock_type, 0);
838 if (lock->get_status)
839 (*lock->get_status)(data->status_param, 0);
840 statistic_increment(locks_immediate,&THR_LOCK_lock);
841 goto end;
842 }
843 /*
844 We're here if there is an active write lock or no write
845 lock but a high priority write waiting in the write_wait queue.
846 In the latter case we should yield the lock to the writer.
847 */
848#ifdef WITH_WSREP
849 if (wsrep_break_lock(data, &lock->write, &lock->read_wait))
850 {
851 wsrep_lock_inserted= TRUE;
852 }
853#endif
854
855 wait_queue= &lock->read_wait;
856 }
857 else /* Request for WRITE lock */
858 {
859 if (lock_type == TL_WRITE_DELAYED)
860 {
861 if (lock->write.data && lock->write.data->type == TL_WRITE_ONLY)
862 {
863 data->type=TL_UNLOCK;
864 result= THR_LOCK_ABORTED; /* Can't wait for this one */
865 goto end;
866 }
867 if (lock->write.data || lock->read.data)
868 {
869 /* Add delayed write lock to write_wait queue, and return at once */
870 (*lock->write_wait.last)=data;
871 data->prev=lock->write_wait.last;
872 lock->write_wait.last= &data->next;
873 data->cond=get_cond();
874 /*
875 We don't have to do get_status here as we will do it when we change
876 the delayed lock to a real write lock
877 */
878 statistic_increment(locks_immediate,&THR_LOCK_lock);
879 goto end;
880 }
881 }
882 else if (lock_type == TL_WRITE_CONCURRENT_INSERT && ! lock->check_status)
883 data->type=lock_type= thr_upgraded_concurrent_insert_lock;
884
885 if (lock->write.data) /* If there is a write lock */
886 {
887 if (lock->write.data->type == TL_WRITE_ONLY)
888 {
889 /* purecov: begin tested */
890 /* Allow lock owner to bypass TL_WRITE_ONLY. */
891 if (!thr_lock_owner_equal(data->owner, lock->write.data->owner))
892 {
893 /* We are not allowed to get a lock in this case */
894 data->type=TL_UNLOCK;
895 result= THR_LOCK_ABORTED; /* Can't wait for this one */
896 goto end;
897 }
898 /* purecov: end */
899 }
900
901 /*
902 The idea is to allow us to get a lock at once if we already have
903 a write lock or if there is no pending write locks and if all
904 write locks are of the same type and are either
905 TL_WRITE_ALLOW_WRITE or TL_WRITE_CONCURRENT_INSERT and
906 there is no TL_READ_NO_INSERT lock.
907
908 Note that, since lock requests for the same table are sorted in
909 such way that requests with higher thr_lock_type value come first
910 (with one exception (*)), lock being requested usually (**) has
911 equal or "weaker" type than one which thread might have already
912 acquired.
913 *) The only exception to this rule is case when type of old lock
914 is TL_WRITE_LOW_PRIORITY and type of new lock is changed inside
915 of thr_lock() from TL_WRITE_CONCURRENT_INSERT to TL_WRITE since
916 engine turns out to be not supporting concurrent inserts.
917 Note that since TL_WRITE has the same compatibility rules as
918 TL_WRITE_LOW_PRIORITY (their only difference is priority),
919 it is OK to grant new lock without additional checks in such
920 situation.
921 **) The exceptions are situations when:
922 - when old lock type is TL_WRITE_DELAYED
923 But these should never happen within MariaDB.
924 Therefore it is OK to allow acquiring write lock on the table if
925 this thread already holds some write lock on it.
926
927 (INSERT INTO t1 VALUES (f1()), where f1() is stored function which
928 tries to update t1, is an example of statement which requests two
929 different types of write lock on the same table).
930 */
931 DBUG_ASSERT(! has_old_lock(lock->write.data, data->owner) ||
932 ((lock_type <= lock->write.data->type ||
933 (lock_type == TL_WRITE &&
934 lock->write.data->type == TL_WRITE_LOW_PRIORITY)) &&
935 lock->write.data->type != TL_WRITE_DELAYED));
936
937 if (((lock_type == TL_WRITE_ALLOW_WRITE ||
938 (lock_type == TL_WRITE_CONCURRENT_INSERT &&
939 lock->allow_multiple_concurrent_insert &&
940 !lock->read_no_write_count)) &&
941 ! lock->write_wait.data &&
942 lock->write.data->type == lock_type &&
943 ! lock->read_no_write_count) ||
944 has_old_lock(lock->write.data, data->owner))
945 {
946 DBUG_PRINT("info", ("write_wait.data: %p old_type: %d",
947 lock->write_wait.data,
948 lock->write.data->type));
949
950 (*lock->write.last)=data; /* Add to running fifo */
951 data->prev=lock->write.last;
952 lock->write.last= &data->next;
953 check_locks(lock,"second write lock", lock_type, 0);
954 if (lock->get_status)
955 (*lock->get_status)(data->status_param,
956 lock_type == TL_WRITE_CONCURRENT_INSERT);
957 statistic_increment(locks_immediate,&THR_LOCK_lock);
958 goto end;
959 }
960 DBUG_PRINT("lock",("write locked 2 by thread: %lu",
961 (ulong) lock->write.data->owner->thread_id));
962 }
963 else
964 {
965 DBUG_PRINT("info", ("write_wait.data:%p",
966 lock->write_wait.data));
967 if (!lock->write_wait.data)
968 { /* no scheduled write locks */
969 my_bool concurrent_insert= 0;
970 if (lock_type == TL_WRITE_CONCURRENT_INSERT)
971 {
972 concurrent_insert= 1;
973 if ((*lock->check_status)(data->status_param))
974 {
975 concurrent_insert= 0;
976 data->type=lock_type= thr_upgraded_concurrent_insert_lock;
977 }
978 }
979
980 if (!lock->read.data ||
981 (lock_type <= TL_WRITE_DELAYED &&
982 ((lock_type != TL_WRITE_CONCURRENT_INSERT &&
983 lock_type != TL_WRITE_ALLOW_WRITE) ||
984 !lock->read_no_write_count)))
985 {
986 (*lock->write.last)=data; /* Add as current write lock */
987 data->prev=lock->write.last;
988 lock->write.last= &data->next;
989 if (lock->get_status)
990 (*lock->get_status)(data->status_param, concurrent_insert);
991 check_locks(lock,"only write lock", lock_type, 0);
992 statistic_increment(locks_immediate,&THR_LOCK_lock);
993 goto end;
994 }
995 }
996 DBUG_PRINT("lock",("write locked 3 by thread:%lu type: %d",
997 (ulong) lock->read.data->owner->thread_id,
998 data->type));
999 }
1000#ifdef WITH_WSREP
1001 if (wsrep_break_lock(data, &lock->write, &lock->write_wait))
1002 {
1003 wsrep_lock_inserted= TRUE;
1004 }
1005#endif
1006
1007 wait_queue= &lock->write_wait;
1008 }
1009 /* Can't get lock yet; Wait for it */
1010#ifdef WITH_WSREP
1011 if (wsrep_lock_inserted && wsrep_on(data->owner->mysql_thd))
1012 DBUG_RETURN(wait_for_lock(wait_queue, data, 1, lock_wait_timeout));
1013#endif
1014 result= wait_for_lock(wait_queue, data, 0, lock_wait_timeout);
1015 MYSQL_END_TABLE_LOCK_WAIT(locker);
1016 DBUG_RETURN(result);
1017end:
1018 mysql_mutex_unlock(&lock->mutex);
1019 MYSQL_END_TABLE_LOCK_WAIT(locker);
1020 DBUG_RETURN(result);
1021}
1022
1023
1024static inline void free_all_read_locks(THR_LOCK *lock,
1025 my_bool using_concurrent_insert)
1026{
1027 THR_LOCK_DATA *data=lock->read_wait.data;
1028
1029 check_locks(lock,"before freeing read locks", TL_UNLOCK, 1);
1030
1031 /* move all locks from read_wait list to read list */
1032 (*lock->read.last)=data;
1033 data->prev=lock->read.last;
1034 lock->read.last=lock->read_wait.last;
1035
1036 /* Clear read_wait list */
1037 lock->read_wait.last= &lock->read_wait.data;
1038
1039 do
1040 {
1041 mysql_cond_t *cond= data->cond;
1042 if ((int) data->type == (int) TL_READ_NO_INSERT)
1043 {
1044 if (using_concurrent_insert)
1045 {
1046 /*
1047 We can't free this lock;
1048 Link lock away from read chain back into read_wait chain
1049 */
1050 if (((*data->prev)=data->next))
1051 data->next->prev=data->prev;
1052 else
1053 lock->read.last=data->prev;
1054 *lock->read_wait.last= data;
1055 data->prev= lock->read_wait.last;
1056 lock->read_wait.last= &data->next;
1057 continue;
1058 }
1059 lock->read_no_write_count++;
1060 }
1061 /* purecov: begin inspected */
1062 DBUG_PRINT("lock",("giving read lock to thread: %lu",
1063 (ulong)data->owner->thread_id));
1064 /* purecov: end */
1065 data->cond=0; /* Mark thread free */
1066 mysql_cond_signal(cond);
1067 } while ((data=data->next));
1068 *lock->read_wait.last=0;
1069 if (!lock->read_wait.data)
1070 lock->write_lock_count=0;
1071 check_locks(lock,"after giving read locks", TL_UNLOCK, 0);
1072}
1073
1074 /* Unlock lock and free next thread on same lock */
1075
1076void thr_unlock(THR_LOCK_DATA *data, uint unlock_flags)
1077{
1078 THR_LOCK *lock=data->lock;
1079 enum thr_lock_type lock_type=data->type;
1080 DBUG_ENTER("thr_unlock");
1081 DBUG_PRINT("lock",("data: %p thread: %lu lock: %p",
1082 data, (ulong) data->owner->thread_id,
1083 lock));
1084 mysql_mutex_lock(&lock->mutex);
1085 check_locks(lock,"start of release lock", lock_type, 0);
1086
1087 if (((*data->prev)=data->next)) /* remove from lock-list */
1088 data->next->prev= data->prev;
1089 else if (lock_type <= TL_READ_NO_INSERT)
1090 lock->read.last=data->prev;
1091 else if (lock_type == TL_WRITE_DELAYED && data->cond)
1092 {
1093 /*
1094 This only happens in extreme circumstances when a
1095 write delayed lock that is waiting for a lock
1096 */
1097 lock->write_wait.last=data->prev; /* Put it on wait queue */
1098 }
1099 else
1100 lock->write.last=data->prev;
1101
1102 if (unlock_flags & THR_UNLOCK_UPDATE_STATUS)
1103 {
1104 /* External lock was not called; Update or restore status */
1105 if (lock_type >= TL_WRITE_CONCURRENT_INSERT)
1106 {
1107 if (lock->update_status)
1108 (*lock->update_status)(data->status_param);
1109 }
1110 else
1111 {
1112 if (lock->restore_status)
1113 (*lock->restore_status)(data->status_param);
1114 }
1115 }
1116 if (lock_type == TL_READ_NO_INSERT)
1117 lock->read_no_write_count--;
1118 data->type=TL_UNLOCK; /* Mark unlocked */
1119 wake_up_waiters(lock);
1120 mysql_mutex_unlock(&lock->mutex);
1121 DBUG_VOID_RETURN;
1122}
1123
1124
1125/**
1126 @brief Wake up all threads which pending requests for the lock
1127 can be satisfied.
1128
1129 @param lock Lock for which threads should be woken up
1130
1131*/
1132
1133static void wake_up_waiters(THR_LOCK *lock)
1134{
1135 THR_LOCK_DATA *data;
1136 enum thr_lock_type lock_type;
1137 DBUG_ENTER("wake_up_waiters");
1138
1139 check_locks(lock, "before waking up waiters", TL_UNLOCK, 1);
1140 if (!lock->write.data) /* If no active write locks */
1141 {
1142 data=lock->write_wait.data;
1143 if (!lock->read.data) /* If no more locks in use */
1144 {
1145 /* Release write-locks with TL_WRITE or TL_WRITE_ONLY priority first */
1146 if (data &&
1147 (data->type != TL_WRITE_LOW_PRIORITY || !lock->read_wait.data ||
1148 lock->read_wait.data->type < TL_READ_HIGH_PRIORITY))
1149 {
1150 if (lock->write_lock_count++ > max_write_lock_count)
1151 {
1152 /* Too many write locks in a row; Release all waiting read locks */
1153 lock->write_lock_count=0;
1154 if (lock->read_wait.data)
1155 {
1156 DBUG_PRINT("info",("Freeing all read_locks because of max_write_lock_count"));
1157 free_all_read_locks(lock,0);
1158 goto end;
1159 }
1160 }
1161 for (;;)
1162 {
1163 if (((*data->prev)=data->next)) /* remove from wait-list */
1164 data->next->prev= data->prev;
1165 else
1166 lock->write_wait.last=data->prev;
1167 (*lock->write.last)=data; /* Put in execute list */
1168 data->prev=lock->write.last;
1169 data->next=0;
1170 lock->write.last= &data->next;
1171 if (data->type == TL_WRITE_CONCURRENT_INSERT &&
1172 (*lock->check_status)(data->status_param))
1173 data->type=TL_WRITE; /* Upgrade lock */
1174 /* purecov: begin inspected */
1175 DBUG_PRINT("lock",("giving write lock of type %d to thread: %lu",
1176 data->type, (ulong) data->owner->thread_id));
1177 /* purecov: end */
1178 {
1179 mysql_cond_t *cond= data->cond;
1180 data->cond=0; /* Mark thread free */
1181 mysql_cond_signal(cond); /* Start waiting thread */
1182 }
1183 if (data->type != TL_WRITE_ALLOW_WRITE ||
1184 !lock->write_wait.data ||
1185 lock->write_wait.data->type != TL_WRITE_ALLOW_WRITE)
1186 break;
1187 data=lock->write_wait.data; /* Free this too */
1188 }
1189 if (data->type >= TL_WRITE_LOW_PRIORITY)
1190 goto end;
1191 /* Release possible read locks together with the write lock */
1192 }
1193 if (lock->read_wait.data)
1194 free_all_read_locks(lock,
1195 data &&
1196 (data->type == TL_WRITE_CONCURRENT_INSERT ||
1197 data->type == TL_WRITE_ALLOW_WRITE));
1198 else
1199 {
1200 DBUG_PRINT("lock",("No waiting read locks to free"));
1201 }
1202 }
1203 else if (data &&
1204 (lock_type=data->type) <= TL_WRITE_DELAYED &&
1205 ((lock_type != TL_WRITE_CONCURRENT_INSERT &&
1206 lock_type != TL_WRITE_ALLOW_WRITE) ||
1207 !lock->read_no_write_count))
1208 {
1209 /*
1210 For DELAYED, ALLOW_READ, WRITE_ALLOW_WRITE or CONCURRENT_INSERT locks
1211 start WRITE locks together with the READ locks
1212 */
1213 if (lock_type == TL_WRITE_CONCURRENT_INSERT &&
1214 (*lock->check_status)(data->status_param))
1215 {
1216 data->type=TL_WRITE; /* Upgrade lock */
1217 if (lock->read_wait.data)
1218 free_all_read_locks(lock,0);
1219 goto end;
1220 }
1221 do {
1222 mysql_cond_t *cond= data->cond;
1223 if (((*data->prev)=data->next)) /* remove from wait-list */
1224 data->next->prev= data->prev;
1225 else
1226 lock->write_wait.last=data->prev;
1227 (*lock->write.last)=data; /* Put in execute list */
1228 data->prev=lock->write.last;
1229 lock->write.last= &data->next;
1230 data->next=0; /* Only one write lock */
1231 data->cond=0; /* Mark thread free */
1232 mysql_cond_signal(cond); /* Start waiting thread */
1233 } while (lock_type == TL_WRITE_ALLOW_WRITE &&
1234 (data=lock->write_wait.data) &&
1235 data->type == TL_WRITE_ALLOW_WRITE);
1236 if (lock->read_wait.data)
1237 free_all_read_locks(lock,
1238 (lock_type == TL_WRITE_CONCURRENT_INSERT ||
1239 lock_type == TL_WRITE_ALLOW_WRITE));
1240 }
1241 else if (!data && lock->read_wait.data)
1242 free_all_read_locks(lock,0);
1243 }
1244end:
1245 check_locks(lock, "after waking up waiters", TL_UNLOCK, 0);
1246 DBUG_VOID_RETURN;
1247}
1248
1249
1250/*
1251 Get all locks in a specific order to avoid dead-locks
1252 Sort according to lock position and put write_locks before read_locks if
1253 lock on same lock. Locks on MERGE tables has lower priority than other
1254 locks of the same type. See comment for lock_priority.
1255*/
1256
1257static void sort_locks(THR_LOCK_DATA **data,uint count)
1258{
1259 THR_LOCK_DATA **pos,**end,**prev,*tmp;
1260
1261 /* Sort locks with insertion sort (fast because almost always few locks) */
1262
1263 for (pos=data+1,end=data+count; pos < end ; pos++)
1264 {
1265 tmp= *pos;
1266 if (LOCK_CMP(tmp,pos[-1]))
1267 {
1268 prev=pos;
1269 do {
1270 prev[0]=prev[-1];
1271 } while (--prev != data && LOCK_CMP(tmp,prev[-1]));
1272 prev[0]=tmp;
1273 }
1274 }
1275}
1276
1277
1278enum enum_thr_lock_result
1279thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_INFO *owner,
1280 ulong lock_wait_timeout)
1281{
1282 THR_LOCK_DATA **pos, **end, **first_lock;
1283 DBUG_ENTER("thr_multi_lock");
1284 DBUG_PRINT("lock",("data: %p count: %d", data, count));
1285
1286 if (count > 1)
1287 sort_locks(data,count);
1288 else if (count == 0)
1289 DBUG_RETURN(THR_LOCK_SUCCESS);
1290
1291 /* lock everything */
1292 DEBUG_SYNC_C("thr_multi_lock_before_thr_lock");
1293 for (pos=data,end=data+count; pos < end ; pos++)
1294 {
1295 enum enum_thr_lock_result result= thr_lock(*pos, owner, lock_wait_timeout);
1296 if (result != THR_LOCK_SUCCESS)
1297 { /* Aborted */
1298 thr_multi_unlock(data,(uint) (pos-data), 0);
1299 /* Mark all requested locks as TL_UNLOCK (to simplify lock checking) */
1300 for ( ; pos < end ; pos++)
1301 (*pos)->type= TL_UNLOCK;
1302 DBUG_RETURN(result);
1303 }
1304#ifdef MAIN
1305 printf("Thread: %s Got lock:%p type: %d\n",my_thread_name(),
1306 pos[0]->lock, pos[0]->type); fflush(stdout);
1307#endif
1308 }
1309 DEBUG_SYNC_C("thr_multi_lock_after_thr_lock");
1310
1311 /*
1312 Call start_trans for all locks.
1313 If we lock the same table multiple times, we must use the same
1314 status_param; We ensure this by calling copy_status() for all
1315 copies of the same tables.
1316 */
1317 if ((*data)->lock->start_trans)
1318 ((*data)->lock->start_trans)((*data)->status_param);
1319 for (first_lock=data, pos= data+1 ; pos < end ; pos++)
1320 {
1321 /* Get the current status (row count, checksum, trid etc) */
1322 if ((*pos)->lock->start_trans)
1323 (*(*pos)->lock->start_trans)((*pos)->status_param);
1324 /*
1325 If same table as previous table use pointer to previous status
1326 information to ensure that all read/write tables shares same
1327 state.
1328 */
1329 if (pos[0]->lock == pos[-1]->lock && pos[0]->lock->copy_status)
1330 (pos[0]->lock->copy_status)((*pos)->status_param,
1331 (*first_lock)->status_param);
1332 else
1333 {
1334 /* Different lock, use this as base for next lock */
1335 first_lock= pos;
1336 }
1337 }
1338 DBUG_RETURN(THR_LOCK_SUCCESS);
1339}
1340
1341
1342/**
1343 Merge two sets of locks.
1344
1345 @param data All locks. First old locks, then new locks.
1346 @param old_count Original number of locks. These are first in 'data'.
1347 @param new_count How many new locks
1348
1349 The merge is needed if the new locks contains same tables as the old
1350 locks, in which case we have to ensure that same tables shares the
1351 same status (as after a thr_multi_lock()).
1352*/
1353
1354void thr_merge_locks(THR_LOCK_DATA **data, uint old_count, uint new_count)
1355{
1356 THR_LOCK_DATA **pos, **end, **first_lock= 0;
1357 DBUG_ENTER("thr_merge_lock");
1358
1359 /* Remove marks on old locks to make them sort before new ones */
1360 for (pos=data, end= pos + old_count; pos < end ; pos++)
1361 (*pos)->priority&= ~THR_LOCK_LATE_PRIV;
1362
1363 /* Mark new locks with LATE_PRIV to make them sort after org ones */
1364 for (pos=data + old_count, end= pos + new_count; pos < end ; pos++)
1365 (*pos)->priority|= THR_LOCK_LATE_PRIV;
1366
1367 sort_locks(data, old_count + new_count);
1368
1369 for (pos=data ; pos < end ; pos++)
1370 {
1371 /* Check if lock was unlocked before */
1372 if (pos[0]->type == TL_UNLOCK || ! pos[0]->lock->fix_status)
1373 {
1374 DBUG_PRINT("info", ("lock skipped. unlocked: %d fix_status: %d",
1375 pos[0]->type == TL_UNLOCK,
1376 pos[0]->lock->fix_status == 0));
1377 continue;
1378 }
1379
1380 /*
1381 If same table as previous table use pointer to previous status
1382 information to ensure that all read/write tables shares same
1383 state.
1384 */
1385 if (first_lock && pos[0]->lock == first_lock[0]->lock)
1386 (pos[0]->lock->fix_status)((*first_lock)->status_param,
1387 (*pos)->status_param);
1388 else
1389 {
1390 /* Different lock, use this as base for next lock */
1391 first_lock= pos;
1392 (pos[0]->lock->fix_status)((*first_lock)->status_param, 0);
1393 }
1394 }
1395 DBUG_VOID_RETURN;
1396}
1397
1398
1399/* Unlock all locks */
1400
1401void thr_multi_unlock(THR_LOCK_DATA **data,uint count, uint unlock_flags)
1402{
1403 THR_LOCK_DATA **pos,**end;
1404 DBUG_ENTER("thr_multi_unlock");
1405 DBUG_PRINT("lock",("data: %p count: %d flags: %u", data, count,
1406 unlock_flags));
1407
1408 for (pos=data,end=data+count; pos < end ; pos++)
1409 {
1410#ifdef MAIN
1411 printf("Thread: %s Rel lock: %p type: %d\n",
1412 my_thread_name(), pos[0]->lock, pos[0]->type);
1413 fflush(stdout);
1414#endif
1415 if ((*pos)->type != TL_UNLOCK)
1416 thr_unlock(*pos, unlock_flags);
1417 else
1418 {
1419 DBUG_PRINT("lock",("Free lock: data: %p thread:%lu lock: %p",
1420 *pos, (ulong) (*pos)->owner->thread_id,
1421 (*pos)->lock));
1422 }
1423 }
1424 DBUG_VOID_RETURN;
1425}
1426
1427/*
1428 Abort all threads waiting for a lock. The lock will be upgraded to
1429 TL_WRITE_ONLY to abort any new accesses to the lock
1430*/
1431
1432void thr_abort_locks(THR_LOCK *lock, my_bool upgrade_lock)
1433{
1434 THR_LOCK_DATA *data;
1435 DBUG_ENTER("thr_abort_locks");
1436 mysql_mutex_lock(&lock->mutex);
1437
1438 for (data=lock->read_wait.data; data ; data=data->next)
1439 {
1440 data->type=TL_UNLOCK; /* Mark killed */
1441 /* It's safe to signal the cond first: we're still holding the mutex. */
1442 mysql_cond_signal(data->cond);
1443 data->cond=0; /* Removed from list */
1444 }
1445 for (data=lock->write_wait.data; data ; data=data->next)
1446 {
1447 data->type=TL_UNLOCK;
1448 mysql_cond_signal(data->cond);
1449 data->cond=0;
1450 }
1451 lock->read_wait.last= &lock->read_wait.data;
1452 lock->write_wait.last= &lock->write_wait.data;
1453 lock->read_wait.data=lock->write_wait.data=0;
1454 if (upgrade_lock && lock->write.data)
1455 lock->write.data->type=TL_WRITE_ONLY;
1456 mysql_mutex_unlock(&lock->mutex);
1457 DBUG_VOID_RETURN;
1458}
1459
1460
1461/*
1462 Abort all locks for specific table/thread combination
1463
1464 This is used to abort all locks for a specific thread
1465*/
1466
1467my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread_id)
1468{
1469 THR_LOCK_DATA *data;
1470 my_bool found= FALSE;
1471 DBUG_ENTER("thr_abort_locks_for_thread");
1472
1473 mysql_mutex_lock(&lock->mutex);
1474 for (data= lock->read_wait.data; data ; data= data->next)
1475 {
1476 if (data->owner->thread_id == thread_id) /* purecov: tested */
1477 {
1478 DBUG_PRINT("info",("Aborting read-wait lock"));
1479 data->type= TL_UNLOCK; /* Mark killed */
1480 /* It's safe to signal the cond first: we're still holding the mutex. */
1481 found= TRUE;
1482 mysql_cond_signal(data->cond);
1483 data->cond= 0; /* Removed from list */
1484
1485 if (((*data->prev)= data->next))
1486 data->next->prev= data->prev;
1487 else
1488 lock->read_wait.last= data->prev;
1489 }
1490 }
1491 for (data= lock->write_wait.data; data ; data= data->next)
1492 {
1493 if (data->owner->thread_id == thread_id) /* purecov: tested */
1494 {
1495 DBUG_PRINT("info",("Aborting write-wait lock"));
1496 data->type= TL_UNLOCK;
1497 found= TRUE;
1498 mysql_cond_signal(data->cond);
1499 data->cond= 0;
1500
1501 if (((*data->prev)= data->next))
1502 data->next->prev= data->prev;
1503 else
1504 lock->write_wait.last= data->prev;
1505 }
1506 }
1507 wake_up_waiters(lock);
1508 mysql_mutex_unlock(&lock->mutex);
1509 DBUG_RETURN(found);
1510}
1511
1512
1513/*
1514 Downgrade a WRITE_* to a lower WRITE level
1515 SYNOPSIS
1516 thr_downgrade_write_lock()
1517 in_data Lock data of thread downgrading its lock
1518 new_lock_type New write lock type
1519 RETURN VALUE
1520 NONE
1521 DESCRIPTION
1522 This can be used to downgrade a lock already owned. When the downgrade
1523 occurs also other waiters, both readers and writers can be allowed to
1524 start.
1525 The previous lock is often TL_WRITE_ONLY but can also be
1526 TL_WRITE. The normal downgrade variants are:
1527 TL_WRITE_ONLY => TL_WRITE after a short exclusive lock while holding a
1528 write table lock
1529 TL_WRITE_ONLY => TL_WRITE_ALLOW_WRITE After a short exclusive lock after
1530 already earlier having dongraded lock to TL_WRITE_ALLOW_WRITE
1531 The implementation is conservative and rather don't start rather than
1532 go on unknown paths to start, the common cases are handled.
1533
1534 NOTE:
1535 In its current implementation it is only allowed to downgrade from
1536 TL_WRITE_ONLY. In this case there are no waiters. Thus no wake up
1537 logic is required.
1538*/
1539
1540void thr_downgrade_write_lock(THR_LOCK_DATA *in_data,
1541 enum thr_lock_type new_lock_type)
1542{
1543 THR_LOCK *lock=in_data->lock;
1544#ifdef DBUG_ASSERT_EXISTS
1545 enum thr_lock_type old_lock_type= in_data->type;
1546#endif
1547 DBUG_ENTER("thr_downgrade_write_only_lock");
1548
1549 mysql_mutex_lock(&lock->mutex);
1550 DBUG_ASSERT(old_lock_type == TL_WRITE_ONLY);
1551 DBUG_ASSERT(old_lock_type > new_lock_type);
1552 in_data->type= new_lock_type;
1553 check_locks(lock,"after downgrading lock", old_lock_type, 0);
1554
1555 mysql_mutex_unlock(&lock->mutex);
1556 DBUG_VOID_RETURN;
1557}
1558
1559/* Upgrade a WRITE_DELAY lock to a WRITE_LOCK */
1560
1561my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data,
1562 enum thr_lock_type new_lock_type,
1563 ulong lock_wait_timeout)
1564{
1565 THR_LOCK *lock=data->lock;
1566 enum enum_thr_lock_result res;
1567 DBUG_ENTER("thr_upgrade_write_delay_lock");
1568
1569 mysql_mutex_lock(&lock->mutex);
1570 if (data->type == TL_UNLOCK || data->type >= TL_WRITE_LOW_PRIORITY)
1571 {
1572 mysql_mutex_unlock(&lock->mutex);
1573 DBUG_RETURN(data->type == TL_UNLOCK); /* Test if Aborted */
1574 }
1575 check_locks(lock,"before upgrading lock", data->type, 0);
1576 /* TODO: Upgrade to TL_WRITE_CONCURRENT_INSERT in some cases */
1577 data->type= new_lock_type; /* Upgrade lock */
1578
1579 /* Check if someone has given us the lock */
1580 if (!data->cond)
1581 {
1582 if (!lock->read.data) /* No read locks */
1583 { /* We have the lock */
1584 if (data->lock->get_status)
1585 (*data->lock->get_status)(data->status_param, 0);
1586 mysql_mutex_unlock(&lock->mutex);
1587 if (lock->start_trans)
1588 (*lock->start_trans)(data->status_param);
1589 DBUG_RETURN(0);
1590 }
1591
1592 if (((*data->prev)=data->next)) /* remove from lock-list */
1593 data->next->prev= data->prev;
1594 else
1595 lock->write.last=data->prev;
1596
1597 if ((data->next=lock->write_wait.data)) /* Put first in lock_list */
1598 data->next->prev= &data->next;
1599 else
1600 lock->write_wait.last= &data->next;
1601 data->prev= &lock->write_wait.data;
1602 lock->write_wait.data=data;
1603 check_locks(lock,"upgrading lock", new_lock_type, 0);
1604 }
1605 else
1606 {
1607 check_locks(lock,"waiting for lock", new_lock_type, 0);
1608 }
1609 res= wait_for_lock(&lock->write_wait, data, 1, lock_wait_timeout);
1610 if (res == THR_LOCK_SUCCESS && lock->start_trans)
1611 DBUG_RETURN((*lock->start_trans)(data->status_param));
1612 DBUG_RETURN(0);
1613}
1614
1615
1616/* downgrade a WRITE lock to a WRITE_DELAY lock if there is pending locks */
1617
1618my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data,
1619 ulong lock_wait_timeout)
1620{
1621 THR_LOCK *lock=data->lock;
1622 enum thr_lock_type write_lock_type;
1623 DBUG_ENTER("thr_reschedule_write_lock");
1624
1625 mysql_mutex_lock(&lock->mutex);
1626 if (!lock->read_wait.data) /* No waiting read locks */
1627 {
1628 mysql_mutex_unlock(&lock->mutex);
1629 DBUG_RETURN(0);
1630 }
1631
1632 write_lock_type= data->type;
1633 data->type=TL_WRITE_DELAYED;
1634 if (lock->update_status)
1635 (*lock->update_status)(data->status_param);
1636 if (((*data->prev)=data->next)) /* remove from lock-list */
1637 data->next->prev= data->prev;
1638 else
1639 lock->write.last=data->prev;
1640
1641 if ((data->next=lock->write_wait.data)) /* Put first in lock_list */
1642 data->next->prev= &data->next;
1643 else
1644 lock->write_wait.last= &data->next;
1645 data->prev= &lock->write_wait.data;
1646 data->cond=get_cond(); /* This was zero */
1647 lock->write_wait.data=data;
1648 free_all_read_locks(lock,0);
1649
1650 mysql_mutex_unlock(&lock->mutex);
1651 DBUG_RETURN(thr_upgrade_write_delay_lock(data, write_lock_type,
1652 lock_wait_timeout));
1653}
1654
1655
1656#include <my_sys.h>
1657
1658static void thr_print_lock(const char* name,struct st_lock_list *list)
1659{
1660 THR_LOCK_DATA *data,**prev;
1661 uint count=0;
1662
1663 if (list->data)
1664 {
1665 printf("%-10s: ",name);
1666 prev= &list->data;
1667 for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next)
1668 {
1669 printf("%p (%lu:%d); ", data, (ulong) data->owner->thread_id,
1670 (int) data->type);
1671 if (data->prev != prev)
1672 printf("\nWarning: prev didn't point at previous lock\n");
1673 prev= &data->next;
1674 }
1675 puts("");
1676 if (prev != list->last)
1677 printf("Warning: last didn't point at last lock\n");
1678 }
1679}
1680
1681void thr_print_locks(void)
1682{
1683 LIST *list;
1684 uint count=0;
1685
1686 mysql_mutex_lock(&THR_LOCK_lock);
1687 puts("Current active THR (table level locks):");
1688 for (list= thr_lock_thread_list; list && count++ < MAX_THREADS;
1689 list= list_rest(list))
1690 {
1691 THR_LOCK *lock=(THR_LOCK*) list->data;
1692 mysql_mutex_lock(&lock->mutex);
1693 if ((lock->write.data || lock->read.data ||
1694 lock->write_wait.data || lock->read_wait.data))
1695 {
1696 printf("lock: %p:", lock);
1697 if ((lock->write_wait.data || lock->read_wait.data) &&
1698 (! lock->read.data && ! lock->write.data))
1699 printf(" WARNING: ");
1700 if (lock->write.data)
1701 printf(" write");
1702 if (lock->write_wait.data)
1703 printf(" write_wait");
1704 if (lock->read.data)
1705 printf(" read");
1706 if (lock->read_wait.data)
1707 printf(" read_wait");
1708 puts("");
1709 thr_print_lock("write",&lock->write);
1710 thr_print_lock("write_wait",&lock->write_wait);
1711 thr_print_lock("read",&lock->read);
1712 thr_print_lock("read_wait",&lock->read_wait);
1713 puts("");
1714 }
1715 mysql_mutex_unlock(&lock->mutex);
1716 }
1717 fflush(stdout);
1718 mysql_mutex_unlock(&THR_LOCK_lock);
1719}
1720
1721
1722/*****************************************************************************
1723** Test of thread locks
1724****************************************************************************/
1725
1726#ifdef MAIN
1727
1728struct st_test {
1729 uint lock_nr;
1730 enum thr_lock_type lock_type;
1731};
1732
1733THR_LOCK locks[6]; /* Number of locks +1 */
1734
1735struct st_test test_0[] = {{0,TL_READ}}; /* One lock */
1736struct st_test test_1[] = {{0,TL_READ},{0,TL_WRITE}}; /* Read and write lock of lock 0 */
1737struct st_test test_2[] = {{1,TL_WRITE},{0,TL_READ},{2,TL_READ}};
1738struct st_test test_3[] = {{2,TL_WRITE},{1,TL_READ},{0,TL_READ}}; /* Deadlock with test_2 ? */
1739struct st_test test_4[] = {{0,TL_WRITE},{0,TL_READ},{0,TL_WRITE},{0,TL_READ}};
1740struct st_test test_5[] = {{0,TL_READ},{1,TL_READ},{2,TL_READ},{3,TL_READ}}; /* Many reads */
1741struct st_test test_6[] = {{0,TL_WRITE},{1,TL_WRITE},{2,TL_WRITE},{3,TL_WRITE}}; /* Many writes */
1742struct st_test test_7[] = {{3,TL_READ}};
1743struct st_test test_8[] = {{1,TL_READ_NO_INSERT},{2,TL_READ_NO_INSERT},{3,TL_READ_NO_INSERT}}; /* Should be quick */
1744struct st_test test_9[] = {{4,TL_READ_HIGH_PRIORITY}};
1745struct st_test test_10[] ={{4,TL_WRITE}};
1746struct st_test test_11[] = {{0,TL_WRITE_LOW_PRIORITY},{1,TL_WRITE_LOW_PRIORITY},{2,TL_WRITE_LOW_PRIORITY},{3,TL_WRITE_LOW_PRIORITY}}; /* Many writes */
1747struct st_test test_12[] = {{0,TL_WRITE_CONCURRENT_INSERT},{1,TL_WRITE_CONCURRENT_INSERT},{2,TL_WRITE_CONCURRENT_INSERT},{3,TL_WRITE_CONCURRENT_INSERT}};
1748struct st_test test_13[] = {{0,TL_WRITE_CONCURRENT_INSERT},{1,TL_READ}};
1749struct st_test test_14[] = {{0,TL_WRITE_ALLOW_WRITE},{1,TL_READ}};
1750struct st_test test_15[] = {{0,TL_WRITE_ALLOW_WRITE},{1,TL_WRITE_ALLOW_WRITE}};
1751
1752struct st_test *tests[] = {test_0,test_1,test_2,test_3,test_4,test_5,test_6,
1753 test_7,test_8,test_9,test_10,test_11,test_12,
1754 test_13,test_14,test_15};
1755int lock_counts[]= {sizeof(test_0)/sizeof(struct st_test),
1756 sizeof(test_1)/sizeof(struct st_test),
1757 sizeof(test_2)/sizeof(struct st_test),
1758 sizeof(test_3)/sizeof(struct st_test),
1759 sizeof(test_4)/sizeof(struct st_test),
1760 sizeof(test_5)/sizeof(struct st_test),
1761 sizeof(test_6)/sizeof(struct st_test),
1762 sizeof(test_7)/sizeof(struct st_test),
1763 sizeof(test_8)/sizeof(struct st_test),
1764 sizeof(test_9)/sizeof(struct st_test),
1765 sizeof(test_10)/sizeof(struct st_test),
1766 sizeof(test_11)/sizeof(struct st_test),
1767 sizeof(test_12)/sizeof(struct st_test),
1768 sizeof(test_13)/sizeof(struct st_test),
1769 sizeof(test_14)/sizeof(struct st_test),
1770 sizeof(test_15)/sizeof(struct st_test)
1771};
1772
1773
1774static mysql_cond_t COND_thread_count;
1775static mysql_mutex_t LOCK_thread_count;
1776static uint thread_count;
1777static ulong sum=0;
1778
1779#define MAX_LOCK_COUNT 8
1780#define TEST_TIMEOUT 100000
1781
1782/* The following functions is for WRITE_CONCURRENT_INSERT */
1783
1784static void test_get_status(void* param __attribute__((unused)),
1785 my_bool concurrent_insert __attribute__((unused)))
1786{
1787}
1788
1789static void test_update_status(void* param __attribute__((unused)))
1790{
1791}
1792
1793static void test_copy_status(void* to __attribute__((unused)) ,
1794 void *from __attribute__((unused)))
1795{
1796}
1797
1798static my_bool test_check_status(void* param __attribute__((unused)))
1799{
1800 return 0;
1801}
1802
1803
1804static void *test_thread(void *arg)
1805{
1806 int i,j,param=*((int*) arg);
1807 THR_LOCK_DATA data[MAX_LOCK_COUNT];
1808 THR_LOCK_INFO lock_info;
1809 THR_LOCK_DATA *multi_locks[MAX_LOCK_COUNT];
1810 my_thread_init();
1811
1812 printf("Thread %s (%d) started\n",my_thread_name(),param); fflush(stdout);
1813
1814 thr_lock_info_init(&lock_info, 0);
1815 for (i=0; i < lock_counts[param] ; i++)
1816 thr_lock_data_init(locks+tests[param][i].lock_nr,data+i,NULL);
1817 for (j=1 ; j < 10 ; j++) /* try locking 10 times */
1818 {
1819 for (i=0; i < lock_counts[param] ; i++)
1820 { /* Init multi locks */
1821 multi_locks[i]= &data[i];
1822 data[i].type= tests[param][i].lock_type;
1823 }
1824 thr_multi_lock(multi_locks, lock_counts[param], &lock_info, TEST_TIMEOUT);
1825 mysql_mutex_lock(&LOCK_thread_count);
1826 {
1827 int tmp=rand() & 7; /* Do something from 0-2 sec */
1828 if (tmp == 0)
1829 sleep(1);
1830 else if (tmp == 1)
1831 sleep(2);
1832 else
1833 {
1834 ulong k;
1835 for (k=0 ; k < (ulong) (tmp-2)*100000L ; k++)
1836 sum+=k;
1837 }
1838 }
1839 mysql_mutex_unlock(&LOCK_thread_count);
1840 thr_multi_unlock(multi_locks,lock_counts[param], THR_UNLOCK_UPDATE_STATUS);
1841 }
1842
1843 printf("Thread %s (%d) ended\n",my_thread_name(),param); fflush(stdout);
1844 thr_print_locks();
1845 mysql_mutex_lock(&LOCK_thread_count);
1846 thread_count--;
1847 mysql_cond_signal(&COND_thread_count); /* Tell main we are ready */
1848 mysql_mutex_unlock(&LOCK_thread_count);
1849 my_thread_end();
1850 return 0;
1851}
1852
1853
1854int main(int argc __attribute__((unused)),char **argv __attribute__((unused)))
1855{
1856 pthread_t tid;
1857 pthread_attr_t thr_attr;
1858 int param[array_elements(lock_counts)], error;
1859 uint i;
1860 MY_INIT(argv[0]);
1861 if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '#')
1862 DBUG_PUSH(argv[1]+2);
1863
1864 printf("Main thread: %s\n",my_thread_name());
1865
1866 if ((error= mysql_cond_init(0, &COND_thread_count, NULL)))
1867 {
1868 fprintf(stderr, "Got error: %d from mysql_cond_init (errno: %d)",
1869 error,errno);
1870 exit(1);
1871 }
1872 if ((error= mysql_mutex_init(0, &LOCK_thread_count, MY_MUTEX_INIT_FAST)))
1873 {
1874 fprintf(stderr, "Got error: %d from mysql_cond_init (errno: %d)",
1875 error,errno);
1876 exit(1);
1877 }
1878
1879 for (i=0 ; i < array_elements(locks) ; i++)
1880 {
1881 thr_lock_init(locks+i);
1882 locks[i].check_status= test_check_status;
1883 locks[i].update_status=test_update_status;
1884 locks[i].copy_status= test_copy_status;
1885 locks[i].get_status= test_get_status;
1886 locks[i].allow_multiple_concurrent_insert= 1;
1887 }
1888 if ((error=pthread_attr_init(&thr_attr)))
1889 {
1890 fprintf(stderr,"Got error: %d from pthread_attr_init (errno: %d)",
1891 error,errno);
1892 exit(1);
1893 }
1894 if ((error=pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED)))
1895 {
1896 fprintf(stderr,
1897 "Got error: %d from pthread_attr_setdetachstate (errno: %d)",
1898 error,errno);
1899 exit(1);
1900 }
1901#ifndef pthread_attr_setstacksize /* void return value */
1902 if ((error=pthread_attr_setstacksize(&thr_attr,65536L)))
1903 {
1904 fprintf(stderr,"Got error: %d from pthread_attr_setstacksize (errno: %d)",
1905 error,errno);
1906 exit(1);
1907 }
1908#endif
1909#ifdef HAVE_THR_SETCONCURRENCY
1910 (void) thr_setconcurrency(2);
1911#endif
1912 for (i=0 ; i < array_elements(lock_counts) ; i++)
1913 {
1914 param[i]= i;
1915
1916 if ((error= mysql_mutex_lock(&LOCK_thread_count)))
1917 {
1918 fprintf(stderr, "Got error: %d from mysql_mutex_lock (errno: %d)",
1919 error, errno);
1920 exit(1);
1921 }
1922 if ((error= mysql_thread_create(0,
1923 &tid, &thr_attr, test_thread,
1924 (void*) &param[i])))
1925 {
1926 fprintf(stderr, "Got error: %d from mysql_thread_create (errno: %d)\n",
1927 error, errno);
1928 mysql_mutex_unlock(&LOCK_thread_count);
1929 exit(1);
1930 }
1931 thread_count++;
1932 mysql_mutex_unlock(&LOCK_thread_count);
1933 }
1934
1935 pthread_attr_destroy(&thr_attr);
1936 if ((error= mysql_mutex_lock(&LOCK_thread_count)))
1937 fprintf(stderr, "Got error: %d from mysql_mutex_lock\n", error);
1938 while (thread_count)
1939 {
1940 if ((error= mysql_cond_wait(&COND_thread_count, &LOCK_thread_count)))
1941 fprintf(stderr, "Got error: %d from mysql_cond_wait\n", error);
1942 }
1943 if ((error= mysql_mutex_unlock(&LOCK_thread_count)))
1944 fprintf(stderr, "Got error: %d from mysql_mutex_unlock\n", error);
1945 for (i=0 ; i < array_elements(locks) ; i++)
1946 thr_lock_delete(locks+i);
1947#ifdef EXTRA_DEBUG
1948 if (found_errors)
1949 printf("Got %d warnings\n",found_errors);
1950 else
1951#endif
1952 printf("Test succeeded\n");
1953 mysql_cond_destroy(&COND_thread_count);
1954 mysql_mutex_destroy(&LOCK_thread_count);
1955 my_end(MY_CHECK_ERROR);
1956 return 0;
1957}
1958
1959#endif /* MAIN */
1960