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-1335  USA */
16 
17 /*
18 Read and write locks for Posix threads. All tread must acquire
19 all locks it needs through thr_multi_lock() to avoid dead-locks.
20 A lock consists of a master lock (THR_LOCK), and lock instances
21 (THR_LOCK_DATA).
22 Any thread can have any number of lock instances (read and write:s) on
23 any lock. All lock instances must be freed.
24 Locks are prioritized according to:
25 
26 The current lock types are:
27 
28 TL_READ                 # Low priority read
29 TL_READ_WITH_SHARED_LOCKS
30 TL_READ_HIGH_PRIORITY	# High priority read
31 TL_READ_NO_INSERT	# Read without concurrent inserts
32 TL_WRITE_ALLOW_WRITE	# Write lock that allows other writers
33 TL_WRITE_CONCURRENT_INSERT
34 			# Insert that can be mixed when selects
35 TL_WRITE_DELAYED	# Used by delayed insert
36 			# Allows lower locks to take over
37 TL_WRITE_LOW_PRIORITY	# Low priority write
38 TL_WRITE		# High priority write
39 TL_WRITE_ONLY		# High priority write
40 			# Abort all new lock request with an error
41 
42 Locks are prioritized according to:
43 
44 WRITE_ALLOW_WRITE, WRITE_CONCURRENT_INSERT, WRITE_DELAYED,
45 WRITE_LOW_PRIORITY, READ, WRITE, READ_HIGH_PRIORITY and WRITE_ONLY
46 
47 Locks in the same privilege level are scheduled in first-in-first-out order.
48 
49 To allow concurrent read/writes locks, with 'WRITE_CONCURRENT_INSERT' one
50 should put a pointer to the following functions in the lock structure:
51 (If the pointer is zero (default), the function is not called)
52 
53 check_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.
59 update_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.
71 get_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 
76 The lock algorithm allows one to have one TL_WRITE_CONCURRENT_INSERT or
77 one TL_WRITE_DELAYED lock at the same time as multiple read locks.
78 
79 In addition, if lock->allow_multiple_concurrent_insert is set then there can
80 be 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 
94 my_bool thr_lock_inited=0;
95 ulong locks_immediate = 0L, locks_waited = 0L;
96 enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE;
97 
98 #ifdef WITH_WSREP
99 static wsrep_thd_is_brute_force_fun wsrep_thd_is_brute_force= NULL;
100 static wsrep_abort_thd_fun wsrep_abort_thd= NULL;
101 static my_bool wsrep_debug;
102 static my_bool wsrep_convert_LOCK_to_trx;
103 static wsrep_on_fun wsrep_on = NULL;
104 
wsrep_thr_lock_init(wsrep_thd_is_brute_force_fun bf_fun,wsrep_abort_thd_fun abort_fun,my_bool debug,my_bool convert_LOCK_to_trx,wsrep_on_fun on_fun)105 void 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 
121 LIST *thr_lock_thread_list;			/* List of threads in use */
122 ulong max_write_lock_count= ~(ulong) 0L;
123 
124 static void (*before_lock_wait)(void)= 0;
125 static void (*after_lock_wait)(void)= 0;
126 
thr_set_lock_wait_callback(void (* before_wait)(void),void (* after_wait)(void))127 void 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 
get_cond(void)134 static 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 
LOCK_CMP(THR_LOCK_DATA * a,THR_LOCK_DATA * b)164 static 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 
init_thr_lock()180 my_bool init_thr_lock()
181 {
182   thr_lock_inited=1;
183   return 0;
184 }
185 
186 static inline my_bool
thr_lock_owner_equal(THR_LOCK_INFO * rhs,THR_LOCK_INFO * lhs)187 thr_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 */
195 static uint found_errors=0;
196 
check_lock(struct st_lock_list * list,const char * lock_type,const char * where,my_bool same_owner,my_bool no_cond,my_bool read_lock)197 static 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 
check_locks(THR_LOCK * lock,const char * where,enum thr_lock_type type,my_bool allow_no_locks)276 static 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 
thr_lock_init(THR_LOCK * lock)440 void 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 
thr_lock_delete(THR_LOCK * lock)458 void 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 
thr_lock_info_init(THR_LOCK_INFO * info,struct st_my_thread_var * tmp)469 void 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 
thr_lock_data_init(THR_LOCK * lock,THR_LOCK_DATA * data,void * param)479 void 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 
491 static inline my_bool
has_old_lock(THR_LOCK_DATA * data,THR_LOCK_INFO * owner)492 has_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 
502 static void wake_up_waiters(THR_LOCK *lock);
503 
504 
505 static enum enum_thr_lock_result
wait_for_lock(struct st_lock_list * wait,THR_LOCK_DATA * data,my_bool in_wait_list,ulong lock_wait_timeout)506 wait_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  */
674 static my_bool
wsrep_break_lock(THR_LOCK_DATA * data,struct st_lock_list * lock_queue1,struct st_lock_list * wait_queue)675 wsrep_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 
742 static enum enum_thr_lock_result
thr_lock(THR_LOCK_DATA * data,THR_LOCK_INFO * owner,ulong lock_wait_timeout)743 thr_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);
1017 end:
1018   mysql_mutex_unlock(&lock->mutex);
1019   MYSQL_END_TABLE_LOCK_WAIT(locker);
1020   DBUG_RETURN(result);
1021 }
1022 
1023 
free_all_read_locks(THR_LOCK * lock,my_bool using_concurrent_insert)1024 static 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 
thr_unlock(THR_LOCK_DATA * data,uint unlock_flags)1076 void 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 
wake_up_waiters(THR_LOCK * lock)1133 static 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   }
1244 end:
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 
sort_locks(THR_LOCK_DATA ** data,uint count)1257 static 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 
1278 enum enum_thr_lock_result
thr_multi_lock(THR_LOCK_DATA ** data,uint count,THR_LOCK_INFO * owner,ulong lock_wait_timeout)1279 thr_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 
thr_merge_locks(THR_LOCK_DATA ** data,uint old_count,uint new_count)1354 void 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 
thr_multi_unlock(THR_LOCK_DATA ** data,uint count,uint unlock_flags)1401 void 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 
thr_abort_locks(THR_LOCK * lock,my_bool upgrade_lock)1432 void 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 
thr_abort_locks_for_thread(THR_LOCK * lock,my_thread_id thread_id)1467 my_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 
thr_downgrade_write_lock(THR_LOCK_DATA * in_data,enum thr_lock_type new_lock_type)1540 void 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 
thr_upgrade_write_delay_lock(THR_LOCK_DATA * data,enum thr_lock_type new_lock_type,ulong lock_wait_timeout)1561 my_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 
thr_reschedule_write_lock(THR_LOCK_DATA * data,ulong lock_wait_timeout)1618 my_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 
thr_print_lock(const char * name,struct st_lock_list * list)1658 static 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 
thr_print_locks(void)1681 void 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 
1728 struct st_test {
1729   uint lock_nr;
1730   enum thr_lock_type lock_type;
1731 };
1732 
1733 THR_LOCK locks[6];			/* Number of locks +1 */
1734 
1735 struct st_test test_0[] = {{0,TL_READ}};	/* One lock */
1736 struct st_test test_1[] = {{0,TL_READ},{0,TL_WRITE}}; /* Read and write lock of lock 0 */
1737 struct st_test test_2[] = {{1,TL_WRITE},{0,TL_READ},{2,TL_READ}};
1738 struct st_test test_3[] = {{2,TL_WRITE},{1,TL_READ},{0,TL_READ}}; /* Deadlock with test_2 ? */
1739 struct st_test test_4[] = {{0,TL_WRITE},{0,TL_READ},{0,TL_WRITE},{0,TL_READ}};
1740 struct st_test test_5[] = {{0,TL_READ},{1,TL_READ},{2,TL_READ},{3,TL_READ}}; /* Many reads */
1741 struct st_test test_6[] = {{0,TL_WRITE},{1,TL_WRITE},{2,TL_WRITE},{3,TL_WRITE}}; /* Many writes */
1742 struct st_test test_7[] = {{3,TL_READ}};
1743 struct st_test test_8[] = {{1,TL_READ_NO_INSERT},{2,TL_READ_NO_INSERT},{3,TL_READ_NO_INSERT}};	/* Should be quick */
1744 struct st_test test_9[] = {{4,TL_READ_HIGH_PRIORITY}};
1745 struct st_test test_10[] ={{4,TL_WRITE}};
1746 struct 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 */
1747 struct st_test test_12[] = {{0,TL_WRITE_CONCURRENT_INSERT},{1,TL_WRITE_CONCURRENT_INSERT},{2,TL_WRITE_CONCURRENT_INSERT},{3,TL_WRITE_CONCURRENT_INSERT}};
1748 struct st_test test_13[] = {{0,TL_WRITE_CONCURRENT_INSERT},{1,TL_READ}};
1749 struct st_test test_14[] = {{0,TL_WRITE_ALLOW_WRITE},{1,TL_READ}};
1750 struct st_test test_15[] = {{0,TL_WRITE_ALLOW_WRITE},{1,TL_WRITE_ALLOW_WRITE}};
1751 
1752 struct 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};
1755 int 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 
1774 static mysql_cond_t COND_thread_count;
1775 static mysql_mutex_t LOCK_thread_count;
1776 static uint thread_count;
1777 static 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 
test_get_status(void * param,my_bool concurrent_insert)1784 static void test_get_status(void* param __attribute__((unused)),
1785                             my_bool concurrent_insert __attribute__((unused)))
1786 {
1787 }
1788 
test_update_status(void * param)1789 static void test_update_status(void* param __attribute__((unused)))
1790 {
1791 }
1792 
test_copy_status(void * to,void * from)1793 static void test_copy_status(void* to __attribute__((unused)) ,
1794 			     void *from __attribute__((unused)))
1795 {
1796 }
1797 
test_check_status(void * param)1798 static my_bool test_check_status(void* param __attribute__((unused)))
1799 {
1800   return 0;
1801 }
1802 
1803 
test_thread(void * arg)1804 static 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 
main(int argc,char ** argv)1854 int 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