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