thr_lock.c 57 KB
Newer Older
1
/*
Sergei Golubchik's avatar
Sergei Golubchik committed
2
   Copyright (c) 2000, 2011, Oracle and/or its affiliates
Sergei Golubchik's avatar
Sergei Golubchik committed
3
   Copyright (c) 2012, Monty Program Ab
unknown's avatar
unknown committed
4 5 6

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
7
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
8 9

   This program is distributed in the hope that it will be useful,
unknown's avatar
unknown committed
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
unknown's avatar
unknown committed
11 12 13 14 15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
16
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
unknown's avatar
unknown committed
17 18 19 20 21 22 23 24 25 26 27 28

/*
Read and write locks for Posix threads. All tread must acquire
all locks it needs through thr_multi_lock() to avoid dead-locks.
A lock consists of a master lock (THR_LOCK), and lock instances
(THR_LOCK_DATA).
Any thread can have any number of lock instances (read and write:s) on
any lock. All lock instances must be freed.
Locks are prioritized according to:

The current lock types are:

29
TL_READ                 # Low priority read
unknown's avatar
unknown committed
30
TL_READ_WITH_SHARED_LOCKS
unknown's avatar
unknown committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44
TL_READ_HIGH_PRIORITY	# High priority read
TL_READ_NO_INSERT	# Read without concurrent inserts
TL_WRITE_ALLOW_WRITE	# Write lock that allows other writers
TL_WRITE_CONCURRENT_INSERT
			# Insert that can be mixed when selects
TL_WRITE_DELAYED	# Used by delayed insert
			# Allows lower locks to take over
TL_WRITE_LOW_PRIORITY	# Low priority write
TL_WRITE		# High priority write
TL_WRITE_ONLY		# High priority write
			# Abort all new lock request with an error

Locks are prioritized according to:

45
WRITE_ALLOW_WRITE, WRITE_CONCURRENT_INSERT, WRITE_DELAYED,
unknown's avatar
unknown committed
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
WRITE_LOW_PRIORITY, READ, WRITE, READ_HIGH_PRIORITY and WRITE_ONLY

Locks in the same privilege level are scheduled in first-in-first-out order.

To allow concurrent read/writes locks, with 'WRITE_CONCURRENT_INSERT' one
should put a pointer to the following functions in the lock structure:
(If the pointer is zero (default), the function is not called)

check_status:
	 Before giving a lock of type TL_WRITE_CONCURRENT_INSERT,
         we check if this function exists and returns 0.
	 If not, then the lock is upgraded to TL_WRITE_LOCK
	 In MyISAM this is a simple check if the insert can be done
	 at the end of the datafile.
update_status:
61 62 63 64 65 66
        in thr_reschedule_write_lock(), when an insert delayed thread
        downgrades TL_WRITE lock to TL_WRITE_DELAYED, to allow SELECT
        threads to proceed.
        A storage engine should also call update_status internally
        in the ::external_lock(F_UNLCK) method.
        In MyISAM and CSV this functions updates the length of the datafile.
67 68 69 70 71
        MySQL does in some exceptional cases (when doing DLL statements on
        open tables calls thr_unlock() followed by thr_lock() without calling
        ::external_lock() in between. In this case thr_unlock() is called with
        the THR_UNLOCK_UPDATE_STATUS flag and thr_unlock() will call
        update_status for write locks.
unknown's avatar
unknown committed
72 73 74 75 76
get_status:
	When one gets a lock this functions is called.
	In MyISAM this stores the number of rows and size of the datafile
	for concurrent reads.

77 78
The lock algorithm allows one to have one TL_WRITE_CONCURRENT_INSERT or
one TL_WRITE_DELAYED lock at the same time as multiple read locks.
unknown's avatar
unknown committed
79

unknown's avatar
unknown committed
80 81
In addition, if lock->allow_multiple_concurrent_insert is set then there can
be any number of TL_WRITE_CONCURRENT_INSERT locks aktive at the same time.
unknown's avatar
unknown committed
82 83 84
*/

#if !defined(MAIN) && !defined(DBUG_OFF) && !defined(EXTRA_DEBUG)
85
#define FORCE_DBUG_OFF
unknown's avatar
unknown committed
86 87 88
#endif

#include "mysys_priv.h"
unknown's avatar
unknown committed
89

unknown's avatar
unknown committed
90 91 92 93 94
#include "thr_lock.h"
#include <m_string.h>
#include <errno.h>

my_bool thr_lock_inited=0;
unknown's avatar
unknown committed
95
ulong locks_immediate = 0L, locks_waited = 0L;
96
enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE;
unknown's avatar
unknown committed
97 98

/* The following constants are only for debug output */
99 100
#define MAX_THREADS 1000
#define MAX_LOCKS   1000
unknown's avatar
unknown committed
101 102


unknown's avatar
unknown committed
103
LIST *thr_lock_thread_list;			/* List of threads in use */
unknown's avatar
unknown committed
104 105
ulong max_write_lock_count= ~(ulong) 0L;

106 107 108 109 110 111 112 113 114 115
static void (*before_lock_wait)(void)= 0;
static void (*after_lock_wait)(void)= 0;

void thr_set_lock_wait_callback(void (*before_wait)(void),
                                void (*after_wait)(void))
{
  before_lock_wait= before_wait;
  after_lock_wait= after_wait;
}

Marc Alff's avatar
Marc Alff committed
116
static inline mysql_cond_t *get_cond(void)
unknown's avatar
unknown committed
117 118 119 120
{
  return &my_thread_var->suspend;
}

121 122

/*
123 124 125 126 127 128 129 130 131 132
  Sort locks in priority order

  LOCK_CMP()
    A       First lock
    B       Second lock

  Return:
      0 if A >= B
      1 if A < B

133 134 135 136 137 138 139 140 141 142 143 144 145
  Priority for locks (decides in which order locks are locked)
  We want all write locks to be first, followed by read locks.
  Locks from MERGE tables has a little lower priority than other
  locks, to allow one to release merge tables without having
  to unlock and re-lock other locks.
  The lower the number, the higher the priority for the lock.
  For MERGE tables we add 2 (THR_LOCK_MERGE_PRIV) to the lock priority.
  THR_LOCK_LATE_PRIV (1) is used when one locks other tables to be merged
  with existing locks. This way we prioritize the original locks over the
  new locks.
*/


146 147 148 149
static inline int LOCK_CMP(THR_LOCK_DATA *a, THR_LOCK_DATA *b)
{
  if (a->lock != b->lock)
    return a->lock < b->lock;
150

151 152
  if (a->type != b->type)
    return a->type > b->type;
153

154 155
  return a->priority < b->priority;
}
156 157


unknown's avatar
unknown committed
158
/*
159
  For the future (now the thread specific cond is alloced by my_pthread.c)
unknown's avatar
unknown committed
160 161 162 163 164 165 166 167
*/

my_bool init_thr_lock()
{
  thr_lock_inited=1;
  return 0;
}

168
static inline my_bool
169
thr_lock_owner_equal(THR_LOCK_INFO *rhs, THR_LOCK_INFO *lhs)
170 171 172 173 174
{
  return rhs == lhs;
}


unknown's avatar
unknown committed
175
#ifdef EXTRA_DEBUG
176 177
#define MAX_FOUND_ERRORS	10		/* Report 10 first errors */
static uint found_errors=0;
unknown's avatar
unknown committed
178 179

static int check_lock(struct st_lock_list *list, const char* lock_type,
180 181
		      const char *where, my_bool same_owner, my_bool no_cond,
                      my_bool read_lock)
unknown's avatar
unknown committed
182 183 184 185 186 187 188
{
  THR_LOCK_DATA *data,**prev;
  uint count=0;

  prev= &list->data;
  if (list->data)
  {
189
    enum thr_lock_type last_lock_type= list->data->type;
Sergei Golubchik's avatar
Sergei Golubchik committed
190
    THR_LOCK_INFO *first_owner= list->data->owner;
unknown's avatar
unknown committed
191 192 193

    for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next)
    {
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
      if (data->type == TL_UNLOCK)
      {
	fprintf(stderr,
		"Warning: Found unlocked lock at %s: %s\n",
                lock_type, where);
        return 1;
      }
      if ((read_lock && data->type > TL_READ_NO_INSERT) ||
          (!read_lock && data->type <= TL_READ_NO_INSERT))
      {
	fprintf(stderr,
		"Warning: Found %s lock in %s queue at %s: %s\n",
                read_lock ? "write" : "read",
                read_lock ? "read" : "write",
                lock_type, where);
        return 1;
      }
unknown's avatar
unknown committed
211 212 213 214 215 216 217 218 219
      if (data->type != last_lock_type)
	last_lock_type=TL_IGNORE;
      if (data->prev != prev)
      {
	fprintf(stderr,
		"Warning: prev link %d didn't point at previous lock at %s: %s\n",
		count, lock_type, where);
	return 1;
      }
220 221
      if (same_owner &&
          !thr_lock_owner_equal(data->owner, first_owner) &&
unknown's avatar
unknown committed
222 223
	  last_lock_type != TL_WRITE_ALLOW_WRITE &&
          last_lock_type != TL_WRITE_CONCURRENT_INSERT)
unknown's avatar
unknown committed
224 225
      {
	fprintf(stderr,
226 227 228 229
		"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",
                data->lock->name ? data->lock->name : "",
		lock_type, where, list->data->type, last_lock_type,
                data->type);
unknown's avatar
unknown committed
230 231
	return 1;
      }
unknown's avatar
unknown committed
232 233 234 235 236 237 238
      if (no_cond && data->cond)
      {
	fprintf(stderr,
		"Warning: Found active lock with not reset cond %s: %s\n",
		lock_type,where);
	return 1;
      }
unknown's avatar
unknown committed
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
      prev= &data->next;
    }
    if (data)
    {
      fprintf(stderr,"Warning: found too many locks at %s: %s\n",
	      lock_type,where);
      return 1;
    }
  }
  if (prev != list->last)
  {
    fprintf(stderr,"Warning: last didn't point at last lock at %s: %s\n",
	    lock_type, where);
    return 1;
  }
  return 0;
}

257

unknown's avatar
unknown committed
258
static void check_locks(THR_LOCK *lock, const char *where,
259
                        enum thr_lock_type type,
unknown's avatar
unknown committed
260 261
			my_bool allow_no_locks)
{
262
  uint old_found_errors=found_errors;
263 264
  DBUG_ENTER("check_locks");

265
  if (found_errors < MAX_FOUND_ERRORS)
unknown's avatar
unknown committed
266
  {
267 268 269 270 271 272
    if (check_lock(&lock->write,"write",where,1,1,0) |
	check_lock(&lock->write_wait,"write_wait",where,0,0,0) |
	check_lock(&lock->read,"read",where,0,1,1) |
	check_lock(&lock->read_wait,"read_wait",where,0,0,1))
    {
      DBUG_ASSERT(my_assert_on_error == 0);
273
      found_errors++;
274
    }
unknown's avatar
unknown committed
275

276
    if (found_errors < MAX_FOUND_ERRORS)
unknown's avatar
unknown committed
277
    {
278
      uint count=0, count2= 0;
unknown's avatar
unknown committed
279 280 281
      THR_LOCK_DATA *data;
      for (data=lock->read.data ; data ; data=data->next)
      {
282
        count2++;
unknown's avatar
unknown committed
283
	if (data->type == TL_READ_NO_INSERT)
unknown's avatar
unknown committed
284
	  count++;
285
        /* Protect against infinite loop. */
286 287
        DBUG_ASSERT(count <= lock->read_no_write_count &&
                    count2 <= MAX_LOCKS);
unknown's avatar
unknown committed
288 289 290
      }
      if (count != lock->read_no_write_count)
      {
291
	found_errors++;
unknown's avatar
unknown committed
292 293 294 295 296 297 298 299 300
	fprintf(stderr,
		"Warning at '%s': Locks read_no_write_count was %u when it should have been %u\n", where, lock->read_no_write_count,count);
      }      

      if (!lock->write.data)
      {
	if (!allow_no_locks && !lock->read.data &&
	    (lock->write_wait.data || lock->read_wait.data))
	{
301
	  found_errors++;
unknown's avatar
unknown committed
302 303 304 305 306 307 308 309
	  fprintf(stderr,
		  "Warning at '%s': No locks in use but locks are in wait queue\n",
		  where);
	}
	if (!lock->write_wait.data)
	{
	  if (!allow_no_locks && lock->read_wait.data)
	  {
310
	    found_errors++;
unknown's avatar
unknown committed
311 312 313 314 315 316 317 318 319 320 321 322 323 324
	    fprintf(stderr,
		    "Warning at '%s': No write locks and waiting read locks\n",
		    where);
	  }
	}
	else
	{
	  if (!allow_no_locks &&
	      (((lock->write_wait.data->type == TL_WRITE_CONCURRENT_INSERT ||
		 lock->write_wait.data->type == TL_WRITE_ALLOW_WRITE) &&
		!lock->read_no_write_count) ||
	       (lock->write_wait.data->type == TL_WRITE_DELAYED &&
		!lock->read.data)))
	  {
325
	    found_errors++;
unknown's avatar
unknown committed
326 327
	    fprintf(stderr,
		    "Warning at '%s': Write lock %d waiting while no exclusive read locks\n",where,(int) lock->write_wait.data->type);
328
            DBUG_PRINT("warning", ("Warning at '%s': Write lock %d waiting while no exclusive read locks\n",where,(int) lock->write_wait.data->type));
unknown's avatar
unknown committed
329 330 331 332
	  }
	}	      
      }
      else
unknown's avatar
unknown committed
333 334 335 336 337
      {
        /* We have at least one write lock */
        if (lock->write.data->type == TL_WRITE_CONCURRENT_INSERT)
        {
          THR_LOCK_DATA *data;
338 339 340 341
          uint count= 0;
          for (data=lock->write.data->next;
               data && count < MAX_LOCKS;
               data=data->next)
unknown's avatar
unknown committed
342 343 344 345
          {
            if (data->type != TL_WRITE_CONCURRENT_INSERT)
            {
              fprintf(stderr,
346 347 348 349
                      "Warning at '%s': Found TL_WRITE_CONCURRENT_INSERT lock mixed with other write lock: %d\n",
                      where, data->type);
              DBUG_PRINT("warning", ("Warning at '%s': Found TL_WRITE_CONCURRENT_INSERT lock mixed with other write lock: %d\n",
                                     where, data->type));
unknown's avatar
unknown committed
350 351 352 353
              break;
            }
          }
        }
unknown's avatar
unknown committed
354 355 356 357 358 359
	if (lock->write_wait.data)
	{
	  if (!allow_no_locks && 
	      lock->write.data->type == TL_WRITE_ALLOW_WRITE &&
	      lock->write_wait.data->type == TL_WRITE_ALLOW_WRITE)
	  {
360
	    found_errors++;
unknown's avatar
unknown committed
361 362 363
	    fprintf(stderr,
		    "Warning at '%s': Found WRITE_ALLOW_WRITE lock waiting for WRITE_ALLOW_WRITE lock\n",
		    where);
364 365 366
            DBUG_PRINT("warning", ("Warning at '%s': Found WRITE_ALLOW_WRITE lock waiting for WRITE_ALLOW_WRITE lock\n",
                                   where));

unknown's avatar
unknown committed
367 368 369 370
	  }
	}
	if (lock->read.data)
	{
371 372 373 374 375
          THR_LOCK_DATA *data;
          for (data=lock->read.data ; data ; data=data->next)
          {
            if (!thr_lock_owner_equal(lock->write.data->owner,
                                      data->owner) &&
376 377 378 379
	      ((lock->write.data->type > TL_WRITE_DELAYED &&
		lock->write.data->type != TL_WRITE_ONLY) ||
	       ((lock->write.data->type == TL_WRITE_CONCURRENT_INSERT ||
		 lock->write.data->type == TL_WRITE_ALLOW_WRITE) &&
380 381 382 383 384 385 386 387 388 389 390 391
                data->type == TL_READ_NO_INSERT)))
            {
              found_errors++;
              fprintf(stderr,
                      "Warning at '%s' for lock: %d: Found lock of type %d that is write and read locked. Read_no_write_count: %d\n",
                      where, (int) type, lock->write.data->type,
                      lock->read_no_write_count);
              DBUG_PRINT("warning",("At '%s' for lock %d: Found lock of type %d that is write and read locked\n",
                                    where, (int) type,
                                    lock->write.data->type));
            }
          }
unknown's avatar
unknown committed
392 393 394 395 396 397
	}
	if (lock->read_wait.data)
	{
	  if (!allow_no_locks && lock->write.data->type <= TL_WRITE_DELAYED &&
	      lock->read_wait.data->type <= TL_READ_HIGH_PRIORITY)
	  {
398
	    found_errors++;
unknown's avatar
unknown committed
399 400 401 402 403 404 405 406 407
	    fprintf(stderr,
		    "Warning at '%s': Found read lock of type %d waiting for write lock of type %d\n",
		    where,
		    (int) lock->read_wait.data->type,
		    (int) lock->write.data->type);
	  }
	}
      }
    }
408
    if (found_errors != old_found_errors)
unknown's avatar
unknown committed
409 410 411 412
    {
      DBUG_PRINT("error",("Found wrong lock"));
    }
  }
413
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
414 415 416
}

#else /* EXTRA_DEBUG */
417
#define check_locks(A,B,C,D)
unknown's avatar
unknown committed
418 419 420 421 422 423 424 425 426
#endif


	/* Initialize a lock */

void thr_lock_init(THR_LOCK *lock)
{
  DBUG_ENTER("thr_lock_init");
  bzero((char*) lock,sizeof(*lock));
Marc Alff's avatar
Marc Alff committed
427
  mysql_mutex_init(key_THR_LOCK_mutex, &lock->mutex, MY_MUTEX_INIT_FAST);
unknown's avatar
unknown committed
428 429 430 431 432
  lock->read.last= &lock->read.data;
  lock->read_wait.last= &lock->read_wait.data;
  lock->write_wait.last= &lock->write_wait.data;
  lock->write.last= &lock->write.data;

Marc Alff's avatar
Marc Alff committed
433
  mysql_mutex_lock(&THR_LOCK_lock);              /* Add to locks in use */
unknown's avatar
unknown committed
434
  lock->list.data=(void*) lock;
unknown's avatar
unknown committed
435
  thr_lock_thread_list=list_add(thr_lock_thread_list,&lock->list);
Marc Alff's avatar
Marc Alff committed
436
  mysql_mutex_unlock(&THR_LOCK_lock);
unknown's avatar
unknown committed
437 438 439 440 441 442 443
  DBUG_VOID_RETURN;
}


void thr_lock_delete(THR_LOCK *lock)
{
  DBUG_ENTER("thr_lock_delete");
Marc Alff's avatar
Marc Alff committed
444
  mysql_mutex_lock(&THR_LOCK_lock);
unknown's avatar
unknown committed
445
  thr_lock_thread_list=list_delete(thr_lock_thread_list,&lock->list);
Marc Alff's avatar
Marc Alff committed
446 447
  mysql_mutex_unlock(&THR_LOCK_lock);
  mysql_mutex_destroy(&lock->mutex);
unknown's avatar
unknown committed
448 449 450
  DBUG_VOID_RETURN;
}

451 452 453

void thr_lock_info_init(THR_LOCK_INFO *info)
{
unknown's avatar
unknown committed
454 455 456
  struct st_my_thread_var *tmp= my_thread_var;
  info->thread=    tmp->pthread_self;
  info->thread_id= tmp->id;
457 458
}

unknown's avatar
unknown committed
459 460 461 462 463 464
	/* Initialize a lock instance */

void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param)
{
  data->lock=lock;
  data->type=TL_UNLOCK;
465
  data->owner= 0;                               /* no owner yet */
unknown's avatar
unknown committed
466
  data->status_param=param;
unknown's avatar
unknown committed
467
  data->cond=0;
468
  data->priority= 0;
469
  data->debug_print_param= 0;
unknown's avatar
unknown committed
470 471 472
}


473
static inline my_bool
474
has_old_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner)
unknown's avatar
unknown committed
475 476 477
{
  for ( ; data ; data=data->next)
  {
478
    if (thr_lock_owner_equal(data->owner, owner))
unknown's avatar
unknown committed
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
      return 1;					/* Already locked by thread */
  }
  return 0;
}

static inline my_bool have_specific_lock(THR_LOCK_DATA *data,
					 enum thr_lock_type type)
{
  for ( ; data ; data=data->next)
  {
    if (data->type == type)
      return 1;
  }
  return 0;
}


496 497 498
static void wake_up_waiters(THR_LOCK *lock);


499 500
static enum enum_thr_lock_result
wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
501
              my_bool in_wait_list, ulong lock_wait_timeout)
unknown's avatar
unknown committed
502
{
503
  struct st_my_thread_var *thread_var= my_thread_var;
Marc Alff's avatar
Marc Alff committed
504
  mysql_cond_t *cond= &thread_var->suspend;
505 506
  struct timespec wait_timeout;
  enum enum_thr_lock_result result= THR_LOCK_ABORTED;
Konstantin Osipov's avatar
Konstantin Osipov committed
507
  const char *old_proc_info;
508
  my_bool use_wait_callbacks= FALSE;
509
  DBUG_ENTER("wait_for_lock");
unknown's avatar
unknown committed
510

511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
  /*
    One can use this to signal when a thread is going to wait for a lock.
    See debug_sync.cc.

    Beware of waiting for a signal here. The lock has aquired its mutex.
    While waiting on a signal here, the locking thread could not aquire
    the mutex to release the lock. One could lock up the table
    completely.

    In detail it works so: When thr_lock() tries to acquire a table
    lock, it locks the lock->mutex, checks if it can have the lock, and
    if not, it calls wait_for_lock(). Here it unlocks the table lock
    while waiting on a condition. The sync point is located before this
    wait for condition. If we have a waiting action here, we hold the
    the table locks mutex all the time. Any attempt to look at the table
    lock by another thread blocks it immediately on lock->mutex. This
    can easily become an unexpected and unobvious blockage. So be
    warned: Do not request a WAIT_FOR action for the 'wait_for_lock'
    sync point unless you really know what you do.
  */
  DEBUG_SYNC_C("wait_for_lock");

unknown's avatar
unknown committed
533 534 535 536 537 538 539
  if (!in_wait_list)
  {
    (*wait->last)=data;				/* Wait for lock */
    data->prev= wait->last;
    wait->last= &data->next;
  }

540 541
  statistic_increment(locks_waited, &THR_LOCK_lock);

unknown's avatar
unknown committed
542 543 544
  /* Set up control struct to allow others to abort locks */
  thread_var->current_mutex= &data->lock->mutex;
  thread_var->current_cond=  cond;
545
  data->cond= cond;
unknown's avatar
unknown committed
546

547
  old_proc_info= proc_info_hook(NULL, "Waiting for table level lock",
Sergei Golubchik's avatar
Sergei Golubchik committed
548
                               __func__, __FILE__, __LINE__);
Konstantin Osipov's avatar
Konstantin Osipov committed
549

550 551 552 553 554 555 556 557 558 559 560
  /*
    Since before_lock_wait potentially can create more threads to
    scheduler work for, we don't want to call the before_lock_wait
    callback unless it will really start to wait.

    For similar reasons, we do not want to call before_lock_wait and
    after_lock_wait for each lap around the loop, so we restrict
    ourselves to call it before_lock_wait once before starting to wait
    and once after the thread has exited the wait loop.
   */
  if ((!thread_var->abort || in_wait_list) && before_lock_wait)
561 562
  {
    use_wait_callbacks= TRUE;
563
    (*before_lock_wait)();
564
  }
565

566
  set_timespec(wait_timeout, lock_wait_timeout);
unknown's avatar
unknown committed
567
  while (!thread_var->abort || in_wait_list)
unknown's avatar
unknown committed
568
  {
569
    int rc= mysql_cond_timedwait(cond, &data->lock->mutex, &wait_timeout);
570 571 572 573 574 575 576 577 578 579 580 581 582 583
    /*
      We must break the wait if one of the following occurs:
      - the connection has been aborted (!thread_var->abort), but
        this is not a delayed insert thread (in_wait_list). For a delayed
        insert thread the proper action at shutdown is, apparently, to
        acquire the lock and complete the insert.
      - the lock has been granted (data->cond is set to NULL by the granter),
        or the waiting has been aborted (additionally data->type is set to
        TL_UNLOCK).
      - the wait has timed out (rc == ETIMEDOUT)
      Order of checks below is important to not report about timeout
      if the predicate is true.
    */
    if (data->cond == 0)
584 585
    {
      DBUG_PRINT("thr_lock", ("lock granted/aborted"));
586
      break;
587
    }
unknown's avatar
unknown committed
588
    if (rc == ETIMEDOUT || rc == ETIME)
589
    {
590 591
      /* purecov: begin inspected */
      DBUG_PRINT("thr_lock", ("lock timed out"));
592
      result= THR_LOCK_WAIT_TIMEOUT;
unknown's avatar
unknown committed
593
      break;
594
      /* purecov: end */
595
    }
unknown's avatar
unknown committed
596
  }
597 598 599 600 601

  /*
    We call the after_lock_wait callback once the wait loop has
    finished.
   */
602
  if (after_lock_wait && use_wait_callbacks)
603 604
    (*after_lock_wait)();

605 606
  DBUG_PRINT("thr_lock", ("aborted: %d  in_wait_list: %d",
                          thread_var->abort, in_wait_list));
unknown's avatar
unknown committed
607 608 609

  if (data->cond || data->type == TL_UNLOCK)
  {
610
    if (data->cond)                             /* aborted or timed out */
unknown's avatar
unknown committed
611 612 613 614 615
    {
      if (((*data->prev)=data->next))		/* remove from wait-list */
	data->next->prev= data->prev;
      else
	wait->last=data->prev;
616
      data->type= TL_UNLOCK;                    /* No lock */
617 618
      check_locks(data->lock, "killed or timed out wait_for_lock", data->type,
                  1);
619 620 621 622
      wake_up_waiters(data->lock);
    }
    else
    {
623
      DBUG_PRINT("thr_lock", ("lock aborted"));
624
      check_locks(data->lock, "aborted wait_for_lock", data->type, 0);
unknown's avatar
unknown committed
625 626 627 628
    }
  }
  else
  {
629
    result= THR_LOCK_SUCCESS;
unknown's avatar
unknown committed
630
    if (data->lock->get_status)
631 632
      (*data->lock->get_status)(data->status_param,
                                data->type == TL_WRITE_CONCURRENT_INSERT);
633
    check_locks(data->lock,"got wait_for_lock", data->type, 0);
unknown's avatar
unknown committed
634
  }
Marc Alff's avatar
Marc Alff committed
635
  mysql_mutex_unlock(&data->lock->mutex);
unknown's avatar
unknown committed
636 637

  /* The following must be done after unlock of lock->mutex */
Marc Alff's avatar
Marc Alff committed
638
  mysql_mutex_lock(&thread_var->mutex);
unknown's avatar
unknown committed
639 640
  thread_var->current_mutex= 0;
  thread_var->current_cond=  0;
Marc Alff's avatar
Marc Alff committed
641
  mysql_mutex_unlock(&thread_var->mutex);
Konstantin Osipov's avatar
Konstantin Osipov committed
642 643 644

  proc_info_hook(NULL, old_proc_info, __func__, __FILE__, __LINE__);

645
  DBUG_RETURN(result);
unknown's avatar
unknown committed
646 647 648
}


649
static enum enum_thr_lock_result
Sergei Golubchik's avatar
Sergei Golubchik committed
650
thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, ulong lock_wait_timeout)
unknown's avatar
unknown committed
651 652
{
  THR_LOCK *lock=data->lock;
653 654
  enum enum_thr_lock_result result= THR_LOCK_SUCCESS;
  struct st_lock_list *wait_queue;
655
  enum thr_lock_type lock_type= data->type;
unknown's avatar
unknown committed
656 657 658
  DBUG_ENTER("thr_lock");

  data->next=0;
unknown's avatar
unknown committed
659
  data->cond=0;					/* safety */
660
  data->owner= owner;                           /* Must be reset ! */
661
  data->priority&= ~THR_LOCK_LATE_PRIV;
Marc Alff's avatar
Marc Alff committed
662
  mysql_mutex_lock(&lock->mutex);
663
  DBUG_PRINT("lock",("data: 0x%lx  thread: 0x%lx  lock: 0x%lx  type: %d",
664
                     (long) data, data->owner->thread_id,
unknown's avatar
unknown committed
665
                     (long) lock, (int) lock_type));
unknown's avatar
unknown committed
666
  check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ?
667
	      "enter read_lock" : "enter write_lock", lock_type, 0);
unknown's avatar
unknown committed
668 669 670 671 672
  if ((int) lock_type <= (int) TL_READ_NO_INSERT)
  {
    /* Request for READ lock */
    if (lock->write.data)
    {
673 674 675 676 677 678 679 680 681
      /*
        We can allow a read lock even if there is already a
        write lock on the table if they are owned by the same
        thread or if they satisfy the following lock
        compatibility matrix:

           Request
          /-------
         H|++++  WRITE_ALLOW_WRITE
682 683 684
         e|+++-  WRITE_CONCURRENT_INSERT
         l|++++  WRITE_DELAYED
         d ||||
685 686 687 688
           |||\= READ_NO_INSERT
           ||\ = READ_HIGH_PRIORITY
           |\  = READ_WITH_SHARED_LOCKS
           \   = READ
689
          
690 691 692 693 694 695 696 697

        + = Request can be satisified.
        - = Request cannot be satisified.

        READ_NO_INSERT and WRITE_ALLOW_WRITE should in principle
        be incompatible. However this will cause starvation of
        LOCK TABLE READ in InnoDB under high write load.
        See Bug#42147 for more information.
unknown's avatar
unknown committed
698 699
      */

700
      DBUG_PRINT("lock",("write locked 1 by thread: 0x%lx",
701
			 lock->write.data->owner->thread_id));
702
      if (thr_lock_owner_equal(data->owner, lock->write.data->owner) ||
unknown's avatar
unknown committed
703 704
	  (lock->write.data->type <= TL_WRITE_DELAYED &&
	   (((int) lock_type <= (int) TL_READ_HIGH_PRIORITY) ||
705
	    (lock->write.data->type != TL_WRITE_CONCURRENT_INSERT))))
unknown's avatar
unknown committed
706 707 708 709
      {						/* Already got a write lock */
	(*lock->read.last)=data;		/* Add to running FIFO */
	data->prev=lock->read.last;
	lock->read.last= &data->next;
710
	if (lock_type == TL_READ_NO_INSERT)
unknown's avatar
unknown committed
711
	  lock->read_no_write_count++;
712
	check_locks(lock,"read lock with old write lock", lock_type, 0);
unknown's avatar
unknown committed
713
	if (lock->get_status)
714
	  (*lock->get_status)(data->status_param, 0);
715
	statistic_increment(locks_immediate,&THR_LOCK_lock);
unknown's avatar
unknown committed
716 717 718 719 720 721
	goto end;
      }
      if (lock->write.data->type == TL_WRITE_ONLY)
      {
	/* We are not allowed to get a READ lock in this case */
	data->type=TL_UNLOCK;
722
        result= THR_LOCK_ABORTED;               /* Can't wait for this one */
unknown's avatar
unknown committed
723 724 725 726 727 728
	goto end;
      }
    }
    else if (!lock->write_wait.data ||
	     lock->write_wait.data->type <= TL_WRITE_LOW_PRIORITY ||
	     lock_type == TL_READ_HIGH_PRIORITY ||
729
	     has_old_lock(lock->read.data, data->owner)) /* Has old read lock */
unknown's avatar
unknown committed
730 731 732 733
    {						/* No important write-locks */
      (*lock->read.last)=data;			/* Add to running FIFO */
      data->prev=lock->read.last;
      lock->read.last= &data->next;
734
      if (lock_type == TL_READ_NO_INSERT)
unknown's avatar
unknown committed
735
	lock->read_no_write_count++;
736
      check_locks(lock,"read lock with no write locks", lock_type, 0);
unknown's avatar
unknown committed
737 738
      if (lock->get_status)
	(*lock->get_status)(data->status_param, 0);
739
      statistic_increment(locks_immediate,&THR_LOCK_lock);
unknown's avatar
unknown committed
740 741
      goto end;
    }
742
    /*
743
      We're here if there is an active write lock or no write
744 745 746 747
      lock but a high priority write waiting in the write_wait queue.
      In the latter case we should yield the lock to the writer.
    */
    wait_queue= &lock->read_wait;
unknown's avatar
unknown committed
748 749 750 751 752 753 754 755
  }
  else						/* Request for WRITE lock */
  {
    if (lock_type == TL_WRITE_DELAYED)
    {
      if (lock->write.data && lock->write.data->type == TL_WRITE_ONLY)
      {
	data->type=TL_UNLOCK;
756
        result= THR_LOCK_ABORTED;               /* Can't wait for this one */
unknown's avatar
unknown committed
757 758
	goto end;
      }
759
      if (lock->write.data || lock->read.data)
unknown's avatar
unknown committed
760 761 762 763 764 765
      {
	/* Add delayed write lock to write_wait queue, and return at once */
	(*lock->write_wait.last)=data;
	data->prev=lock->write_wait.last;
	lock->write_wait.last= &data->next;
	data->cond=get_cond();
766 767 768 769
        /*
          We don't have to do get_status here as we will do it when we change
          the delayed lock to a real write lock
        */
770
	statistic_increment(locks_immediate,&THR_LOCK_lock);
unknown's avatar
unknown committed
771 772 773 774
	goto end;
      }
    }
    else if (lock_type == TL_WRITE_CONCURRENT_INSERT && ! lock->check_status)
775
      data->type=lock_type= thr_upgraded_concurrent_insert_lock;
unknown's avatar
unknown committed
776 777 778 779 780

    if (lock->write.data)			/* If there is a write lock */
    {
      if (lock->write.data->type == TL_WRITE_ONLY)
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
781
        /* purecov: begin tested */
782 783 784 785 786 787 788 789
        /* Allow lock owner to bypass TL_WRITE_ONLY. */
        if (!thr_lock_owner_equal(data->owner, lock->write.data->owner))
        {
          /* We are not allowed to get a lock in this case */
          data->type=TL_UNLOCK;
          result= THR_LOCK_ABORTED;               /* Can't wait for this one */
          goto end;
        }
Konstantin Osipov's avatar
Konstantin Osipov committed
790
        /* purecov: end */
unknown's avatar
unknown committed
791 792 793
      }

      /*
794 795
        The idea is to allow us to get a lock at once if we already have
        a write lock or if there is no pending write locks and if all
unknown's avatar
unknown committed
796 797
        write locks are of the same type and are either
        TL_WRITE_ALLOW_WRITE or TL_WRITE_CONCURRENT_INSERT
798 799

        Note that, since lock requests for the same table are sorted in
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
        such way that requests with higher thr_lock_type value come first
        (with one exception (*)), lock being requested usually (**) has
        equal or "weaker" type than one which thread might have already
        acquired.
        *)  The only exception to this rule is case when type of old lock
            is TL_WRITE_LOW_PRIORITY and type of new lock is changed inside
            of thr_lock() from TL_WRITE_CONCURRENT_INSERT to TL_WRITE since
            engine turns out to be not supporting concurrent inserts.
            Note that since TL_WRITE has the same compatibility rules as
            TL_WRITE_LOW_PRIORITY (their only difference is priority),
            it is OK to grant new lock without additional checks in such
            situation.
        **) The exceptions are situations when:
            - when old lock type is TL_WRITE_DELAYED
            But these should never happen within MySQL.
815 816 817 818 819 820
        Therefore it is OK to allow acquiring write lock on the table if
        this thread already holds some write lock on it.

        (INSERT INTO t1 VALUES (f1()), where f1() is stored function which
        tries to update t1, is an example of statement which requests two
        different types of write lock on the same table).
unknown's avatar
unknown committed
821
      */
822
      DBUG_ASSERT(! has_old_lock(lock->write.data, data->owner) ||
823 824 825
                  ((lock_type <= lock->write.data->type ||
                    (lock_type == TL_WRITE &&
                     lock->write.data->type == TL_WRITE_LOW_PRIORITY)) &&
826
                   lock->write.data->type != TL_WRITE_DELAYED));
827

Sergei Golubchik's avatar
Sergei Golubchik committed
828 829 830
      if (((lock_type == TL_WRITE_ALLOW_WRITE ||
           (lock_type == TL_WRITE_CONCURRENT_INSERT &&
             lock->allow_multiple_concurrent_insert)) &&
831
           ! lock->write_wait.data &&
Sergei Golubchik's avatar
Sergei Golubchik committed
832
           lock->write.data->type == lock_type) ||
833
          has_old_lock(lock->write.data, data->owner))
unknown's avatar
unknown committed
834
      {
unknown's avatar
unknown committed
835 836 837 838
        DBUG_PRINT("info", ("write_wait.data: 0x%lx  old_type: %d",
                            (ulong) lock->write_wait.data,
                            lock->write.data->type));

unknown's avatar
unknown committed
839 840 841
	(*lock->write.last)=data;	/* Add to running fifo */
	data->prev=lock->write.last;
	lock->write.last= &data->next;
842
	check_locks(lock,"second write lock", lock_type, 0);
unknown's avatar
unknown committed
843 844 845
	if (lock->get_status)
	  (*lock->get_status)(data->status_param,
                              lock_type == TL_WRITE_CONCURRENT_INSERT);
846
	statistic_increment(locks_immediate,&THR_LOCK_lock);
unknown's avatar
unknown committed
847 848
	goto end;
      }
849
      DBUG_PRINT("lock",("write locked 2 by thread: 0x%lx",
850
			 lock->write.data->owner->thread_id));
unknown's avatar
unknown committed
851 852 853
    }
    else
    {
unknown's avatar
unknown committed
854 855
      DBUG_PRINT("info", ("write_wait.data: 0x%lx",
                          (ulong) lock->write_wait.data));
unknown's avatar
unknown committed
856 857
      if (!lock->write_wait.data)
      {						/* no scheduled write locks */
858 859 860 861 862 863 864 865 866 867
        my_bool concurrent_insert= 0;
	if (lock_type == TL_WRITE_CONCURRENT_INSERT)
        {
          concurrent_insert= 1;
          if ((*lock->check_status)(data->status_param))
          {
            concurrent_insert= 0;
            data->type=lock_type= thr_upgraded_concurrent_insert_lock;
          }
        }
unknown's avatar
unknown committed
868 869 870 871 872 873 874 875 876 877

	if (!lock->read.data ||
	    (lock_type <= TL_WRITE_DELAYED &&
	     ((lock_type != TL_WRITE_CONCURRENT_INSERT &&
	       lock_type != TL_WRITE_ALLOW_WRITE) ||
	      !lock->read_no_write_count)))
	{
	  (*lock->write.last)=data;		/* Add as current write lock */
	  data->prev=lock->write.last;
	  lock->write.last= &data->next;
unknown's avatar
unknown committed
878 879
	  if (lock->get_status)
	    (*lock->get_status)(data->status_param, concurrent_insert);
880
	  check_locks(lock,"only write lock", lock_type, 0);
881
	  statistic_increment(locks_immediate,&THR_LOCK_lock);
unknown's avatar
unknown committed
882 883 884
	  goto end;
	}
      }
885
      DBUG_PRINT("lock",("write locked 3 by thread: 0x%lx  type: %d",
886
			 lock->read.data->owner->thread_id, data->type));
unknown's avatar
unknown committed
887
    }
888
    wait_queue= &lock->write_wait;
unknown's avatar
unknown committed
889
  }
890
  /* Can't get lock yet;  Wait for it */
891
  DBUG_RETURN(wait_for_lock(wait_queue, data, 0, lock_wait_timeout));
unknown's avatar
unknown committed
892
end:
Marc Alff's avatar
Marc Alff committed
893
  mysql_mutex_unlock(&lock->mutex);
unknown's avatar
unknown committed
894 895 896 897 898
  DBUG_RETURN(result);
}


static inline void free_all_read_locks(THR_LOCK *lock,
899
				       my_bool using_concurrent_insert)
unknown's avatar
unknown committed
900 901 902
{
  THR_LOCK_DATA *data=lock->read_wait.data;

903
  check_locks(lock,"before freeing read locks", TL_UNLOCK, 1);
unknown's avatar
unknown committed
904 905 906 907 908 909 910 911 912 913 914

  /* move all locks from read_wait list to read list */
  (*lock->read.last)=data;
  data->prev=lock->read.last;
  lock->read.last=lock->read_wait.last;

  /* Clear read_wait list */
  lock->read_wait.last= &lock->read_wait.data;

  do
  {
Marc Alff's avatar
Marc Alff committed
915
    mysql_cond_t *cond= data->cond;
unknown's avatar
unknown committed
916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
    if ((int) data->type == (int) TL_READ_NO_INSERT)
    {
      if (using_concurrent_insert)
      {
	/*
	  We can't free this lock; 
	  Link lock away from read chain back into read_wait chain
	*/
	if (((*data->prev)=data->next))
	  data->next->prev=data->prev;
	else
	  lock->read.last=data->prev;
	*lock->read_wait.last= data;
	data->prev= lock->read_wait.last;
	lock->read_wait.last= &data->next;
	continue;
      }
      lock->read_no_write_count++;
    }      
unknown's avatar
unknown committed
935
    /* purecov: begin inspected */
936
    DBUG_PRINT("lock",("giving read lock to thread: 0x%lx",
937
		       data->owner->thread_id));
unknown's avatar
unknown committed
938
    /* purecov: end */
unknown's avatar
unknown committed
939
    data->cond=0;				/* Mark thread free */
Marc Alff's avatar
Marc Alff committed
940
    mysql_cond_signal(cond);
unknown's avatar
unknown committed
941 942 943 944
  } while ((data=data->next));
  *lock->read_wait.last=0;
  if (!lock->read_wait.data)
    lock->write_lock_count=0;
945
  check_locks(lock,"after giving read locks", TL_UNLOCK, 0);
unknown's avatar
unknown committed
946 947 948 949
}

	/* Unlock lock and free next thread on same lock */

950
void thr_unlock(THR_LOCK_DATA *data, uint unlock_flags)
unknown's avatar
unknown committed
951 952 953 954
{
  THR_LOCK *lock=data->lock;
  enum thr_lock_type lock_type=data->type;
  DBUG_ENTER("thr_unlock");
955
  DBUG_PRINT("lock",("data: 0x%lx  thread: 0x%lx  lock: 0x%lx",
956
                     (long) data, data->owner->thread_id, (long) lock));
Marc Alff's avatar
Marc Alff committed
957
  mysql_mutex_lock(&lock->mutex);
958
  check_locks(lock,"start of release lock", lock_type, 0);
unknown's avatar
unknown committed
959 960 961 962 963 964 965

  if (((*data->prev)=data->next))		/* remove from lock-list */
    data->next->prev= data->prev;
  else if (lock_type <= TL_READ_NO_INSERT)
    lock->read.last=data->prev;
  else if (lock_type == TL_WRITE_DELAYED && data->cond)
  {
unknown's avatar
unknown committed
966 967 968 969
    /*
      This only happens in extreme circumstances when a 
      write delayed lock that is waiting for a lock
    */
unknown's avatar
unknown committed
970 971 972 973
    lock->write_wait.last=data->prev;		/* Put it on wait queue */
  }
  else
    lock->write.last=data->prev;
974 975
 
  if (unlock_flags & THR_UNLOCK_UPDATE_STATUS)
976
  {
977 978 979 980 981 982 983 984 985 986 987
    /* External lock was not called; Update or restore status */
    if (lock_type >= TL_WRITE_CONCURRENT_INSERT)
    {
      if (lock->update_status)
        (*lock->update_status)(data->status_param);
    }
    else
    {
      if (lock->restore_status)
        (*lock->restore_status)(data->status_param);
    }
988
  }
unknown's avatar
unknown committed
989 990 991
  if (lock_type == TL_READ_NO_INSERT)
    lock->read_no_write_count--;
  data->type=TL_UNLOCK;				/* Mark unlocked */
992
  wake_up_waiters(lock);
Marc Alff's avatar
Marc Alff committed
993
  mysql_mutex_unlock(&lock->mutex);
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
  DBUG_VOID_RETURN;
}


/**
  @brief  Wake up all threads which pending requests for the lock
          can be satisfied.

  @param  lock  Lock for which threads should be woken up

*/

static void wake_up_waiters(THR_LOCK *lock)
{
  THR_LOCK_DATA *data;
  enum thr_lock_type lock_type;
  DBUG_ENTER("wake_up_waiters");
unknown's avatar
unknown committed
1011

1012
  check_locks(lock, "before waking up waiters", TL_UNLOCK, 1);
1013
  if (!lock->write.data)			/* If no active write locks */
unknown's avatar
unknown committed
1014 1015 1016 1017 1018 1019 1020
  {
    data=lock->write_wait.data;
    if (!lock->read.data)			/* If no more locks in use */
    {
      /* Release write-locks with TL_WRITE or TL_WRITE_ONLY priority first */
      if (data &&
	  (data->type != TL_WRITE_LOW_PRIORITY || !lock->read_wait.data ||
unknown's avatar
unknown committed
1021
	   lock->read_wait.data->type < TL_READ_HIGH_PRIORITY))
unknown's avatar
unknown committed
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
      {
	if (lock->write_lock_count++ > max_write_lock_count)
	{
	  /* Too many write locks in a row;  Release all waiting read locks */
	  lock->write_lock_count=0;
	  if (lock->read_wait.data)
	  {
	    DBUG_PRINT("info",("Freeing all read_locks because of max_write_lock_count"));
	    free_all_read_locks(lock,0);
	    goto end;
	  }
	}
	for (;;)
	{
	  if (((*data->prev)=data->next))	/* remove from wait-list */
	    data->next->prev= data->prev;
	  else
	    lock->write_wait.last=data->prev;
	  (*lock->write.last)=data;		/* Put in execute list */
	  data->prev=lock->write.last;
	  data->next=0;
	  lock->write.last= &data->next;
	  if (data->type == TL_WRITE_CONCURRENT_INSERT &&
	      (*lock->check_status)(data->status_param))
	    data->type=TL_WRITE;			/* Upgrade lock */
unknown's avatar
unknown committed
1047
          /* purecov: begin inspected */
1048
	  DBUG_PRINT("lock",("giving write lock of type %d to thread: 0x%lx",
1049
			     data->type, data->owner->thread_id));
unknown's avatar
unknown committed
1050
          /* purecov: end */
unknown's avatar
unknown committed
1051
	  {
Marc Alff's avatar
Marc Alff committed
1052
            mysql_cond_t *cond= data->cond;
unknown's avatar
unknown committed
1053
	    data->cond=0;				/* Mark thread free */
Marc Alff's avatar
Marc Alff committed
1054
            mysql_cond_signal(cond);                    /* Start waiting thread */
unknown's avatar
unknown committed
1055 1056 1057 1058 1059 1060 1061 1062
	  }
	  if (data->type != TL_WRITE_ALLOW_WRITE ||
	      !lock->write_wait.data ||
	      lock->write_wait.data->type != TL_WRITE_ALLOW_WRITE)
	    break;
	  data=lock->write_wait.data;		/* Free this too */
	}
	if (data->type >= TL_WRITE_LOW_PRIORITY)
1063
          goto end;
unknown's avatar
unknown committed
1064 1065 1066 1067 1068 1069 1070 1071 1072
	/* Release possible read locks together with the write lock */
      }
      if (lock->read_wait.data)
	free_all_read_locks(lock,
			    data &&
			    (data->type == TL_WRITE_CONCURRENT_INSERT ||
			     data->type == TL_WRITE_ALLOW_WRITE));
      else
      {
unknown's avatar
unknown committed
1073
	DBUG_PRINT("lock",("No waiting read locks to free"));
unknown's avatar
unknown committed
1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
      }
    }
    else if (data &&
	     (lock_type=data->type) <= TL_WRITE_DELAYED &&
	     ((lock_type != TL_WRITE_CONCURRENT_INSERT &&
	       lock_type != TL_WRITE_ALLOW_WRITE) ||
	      !lock->read_no_write_count))
    {
      /*
	For DELAYED, ALLOW_READ, WRITE_ALLOW_WRITE or CONCURRENT_INSERT locks
	start WRITE locks together with the READ locks
      */
      if (lock_type == TL_WRITE_CONCURRENT_INSERT &&
	  (*lock->check_status)(data->status_param))
      {
	data->type=TL_WRITE;			/* Upgrade lock */
	if (lock->read_wait.data)
	  free_all_read_locks(lock,0);
	goto end;
      }
      do {
Marc Alff's avatar
Marc Alff committed
1095
        mysql_cond_t *cond= data->cond;
unknown's avatar
unknown committed
1096 1097 1098 1099
	if (((*data->prev)=data->next))		/* remove from wait-list */
	  data->next->prev= data->prev;
	else
	  lock->write_wait.last=data->prev;
1100
	(*lock->write.last)=data;		/* Put in execute list */
unknown's avatar
unknown committed
1101 1102 1103 1104
	data->prev=lock->write.last;
	lock->write.last= &data->next;
	data->next=0;				/* Only one write lock */
	data->cond=0;				/* Mark thread free */
Marc Alff's avatar
Marc Alff committed
1105
        mysql_cond_signal(cond);                /* Start waiting thread */
unknown's avatar
unknown committed
1106 1107 1108 1109 1110 1111 1112 1113
      } while (lock_type == TL_WRITE_ALLOW_WRITE &&
	       (data=lock->write_wait.data) &&
	       data->type == TL_WRITE_ALLOW_WRITE);
      if (lock->read_wait.data)
	free_all_read_locks(lock,
			    (lock_type == TL_WRITE_CONCURRENT_INSERT ||
			     lock_type == TL_WRITE_ALLOW_WRITE));
    }
1114
    else if (!data && lock->read_wait.data)
unknown's avatar
unknown committed
1115 1116 1117
      free_all_read_locks(lock,0);
  }
end:
1118
  check_locks(lock, "after waking up waiters", TL_UNLOCK, 0);
unknown's avatar
unknown committed
1119 1120 1121 1122 1123
  DBUG_VOID_RETURN;
}


/*
1124 1125 1126 1127
  Get all locks in a specific order to avoid dead-locks
  Sort acording to lock position and put write_locks before read_locks if
  lock on same lock. Locks on MERGE tables has lower priority than other
  locks of the same type. See comment for lock_priority.
unknown's avatar
unknown committed
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
*/

static void sort_locks(THR_LOCK_DATA **data,uint count)
{
  THR_LOCK_DATA **pos,**end,**prev,*tmp;

  /* Sort locks with insertion sort (fast because almost always few locks) */

  for (pos=data+1,end=data+count; pos < end ; pos++)
  {
    tmp= *pos;
    if (LOCK_CMP(tmp,pos[-1]))
    {
      prev=pos;
      do {
	prev[0]=prev[-1];
      } while (--prev != data && LOCK_CMP(tmp,prev[-1]));
      prev[0]=tmp;
    }
  }
}


1151
enum enum_thr_lock_result
1152
thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_INFO *owner,
1153
               ulong lock_wait_timeout)
unknown's avatar
unknown committed
1154
{
1155
  THR_LOCK_DATA **pos, **end, **first_lock;
unknown's avatar
unknown committed
1156
  DBUG_ENTER("thr_multi_lock");
unknown's avatar
unknown committed
1157
  DBUG_PRINT("lock",("data: 0x%lx  count: %d", (long) data, count));
1158

unknown's avatar
unknown committed
1159 1160
  if (count > 1)
    sort_locks(data,count);
1161 1162 1163
  else if (count == 0)
    DBUG_RETURN(THR_LOCK_SUCCESS);

unknown's avatar
unknown committed
1164 1165 1166
  /* lock everything */
  for (pos=data,end=data+count; pos < end ; pos++)
  {
Sergei Golubchik's avatar
Sergei Golubchik committed
1167
    enum enum_thr_lock_result result= thr_lock(*pos, owner, lock_wait_timeout);
1168
    if (result != THR_LOCK_SUCCESS)
unknown's avatar
unknown committed
1169
    {						/* Aborted */
1170
      thr_multi_unlock(data,(uint) (pos-data), 0);
1171
      DBUG_RETURN(result);
unknown's avatar
unknown committed
1172
    }
1173
    DEBUG_SYNC_C("thr_multi_lock_after_thr_lock");
unknown's avatar
unknown committed
1174
#ifdef MAIN
1175
    printf("Thread: %s  Got lock: 0x%lx  type: %d\n",my_thread_name(),
unknown's avatar
unknown committed
1176 1177 1178
	   (long) pos[0]->lock, pos[0]->type); fflush(stdout);
#endif
  }
1179

unknown's avatar
unknown committed
1180
  /*
1181
    Call start_trans for all locks.
unknown's avatar
unknown committed
1182
    If we lock the same table multiple times, we must use the same
1183 1184
    status_param; We ensure this by calling copy_status() for all
    copies of the same tables.
unknown's avatar
unknown committed
1185
  */
1186 1187 1188
  if ((*data)->lock->start_trans)
    ((*data)->lock->start_trans)((*data)->status_param);
  for (first_lock=data, pos= data+1 ; pos < end ; pos++)
unknown's avatar
unknown committed
1189
  {
1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201
    /* Get the current status (row count, checksum, trid etc) */
    if ((*pos)->lock->start_trans)
      (*(*pos)->lock->start_trans)((*pos)->status_param);
    /*
      If same table as previous table use pointer to previous status
      information to ensure that all read/write tables shares same
      state.
    */
    if (pos[0]->lock == pos[-1]->lock && pos[0]->lock->copy_status)
      (pos[0]->lock->copy_status)((*pos)->status_param,
                                  (*first_lock)->status_param);
    else
unknown's avatar
unknown committed
1202
    {
1203 1204 1205
      /* Different lock, use this as base for next lock */
      first_lock= pos;
    }
unknown's avatar
unknown committed
1206
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
1207 1208 1209 1210 1211
  DBUG_RETURN(THR_LOCK_SUCCESS);
}


/**
1212 1213 1214 1215 1216 1217 1218 1219 1220
  Merge two sets of locks.

  @param data       All locks. First old locks, then new locks.
  @param old_count  Original number of locks. These are first in 'data'.
  @param new_count  How many new locks

  The merge is needed if the new locks contains same tables as the old
  locks, in which case we have to ensure that same tables shares the
  same status (as after a thr_multi_lock()).
Konstantin Osipov's avatar
Konstantin Osipov committed
1221 1222
*/

1223
void thr_merge_locks(THR_LOCK_DATA **data, uint old_count, uint new_count)
Konstantin Osipov's avatar
Konstantin Osipov committed
1224
{
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238
  THR_LOCK_DATA **pos, **end, **first_lock= 0;
  DBUG_ENTER("thr_merge_lock");

  /* Remove marks on old locks to make them sort before new ones */
  for (pos=data, end= pos + old_count; pos < end ; pos++)
    (*pos)->priority&= ~THR_LOCK_LATE_PRIV;

  /* Mark new locks with LATE_PRIV to make them sort after org ones */
  for (pos=data + old_count, end= pos + new_count; pos < end ; pos++)
    (*pos)->priority|= THR_LOCK_LATE_PRIV;

  sort_locks(data, old_count + new_count);

  for (pos=data ; pos < end ; pos++)
unknown's avatar
unknown committed
1239
  {
1240 1241
    /* Check if lock was unlocked before */
    if (pos[0]->type == TL_UNLOCK || ! pos[0]->lock->fix_status)
unknown's avatar
unknown committed
1242
    {
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262
      DBUG_PRINT("info", ("lock skipped.  unlocked: %d  fix_status: %d",
                          pos[0]->type == TL_UNLOCK,
                          pos[0]->lock->fix_status == 0));
      continue;
    }

    /*
      If same table as previous table use pointer to previous status
      information to ensure that all read/write tables shares same
      state.
    */
    if (first_lock && pos[0]->lock == first_lock[0]->lock)
      (pos[0]->lock->fix_status)((*first_lock)->status_param,
                                 (*pos)->status_param);
    else
    {
      /* Different lock, use this as base for next lock */
      first_lock= pos;
      (pos[0]->lock->fix_status)((*first_lock)->status_param, 0);
    }
unknown's avatar
unknown committed
1263
  }
1264
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1265 1266 1267
}


1268 1269 1270
/* Unlock all locks */

void thr_multi_unlock(THR_LOCK_DATA **data,uint count, uint unlock_flags)
unknown's avatar
unknown committed
1271 1272 1273
{
  THR_LOCK_DATA **pos,**end;
  DBUG_ENTER("thr_multi_unlock");
1274 1275
  DBUG_PRINT("lock",("data: 0x%lx  count: %d  flags: %u", (long) data, count,
                     unlock_flags));
unknown's avatar
unknown committed
1276 1277 1278 1279

  for (pos=data,end=data+count; pos < end ; pos++)
  {
#ifdef MAIN
1280
    printf("Thread: %s  Rel lock: 0x%lx  type: %d\n",
unknown's avatar
unknown committed
1281 1282 1283 1284
	   my_thread_name(), (long) pos[0]->lock, pos[0]->type);
    fflush(stdout);
#endif
    if ((*pos)->type != TL_UNLOCK)
1285
      thr_unlock(*pos, unlock_flags);
unknown's avatar
unknown committed
1286 1287
    else
    {
unknown's avatar
unknown committed
1288
      DBUG_PRINT("lock",("Free lock: data: 0x%lx  thread: 0x%lx  lock: 0x%lx",
1289
                         (long) *pos, (*pos)->owner->thread_id,
unknown's avatar
unknown committed
1290
                         (long) (*pos)->lock));
unknown's avatar
unknown committed
1291 1292 1293 1294 1295
    }
  }
  DBUG_VOID_RETURN;
}

1296 1297
/*
  Abort all threads waiting for a lock. The lock will be upgraded to
unknown's avatar
unknown committed
1298 1299 1300
  TL_WRITE_ONLY to abort any new accesses to the lock
*/

1301
void thr_abort_locks(THR_LOCK *lock, my_bool upgrade_lock)
unknown's avatar
unknown committed
1302 1303 1304
{
  THR_LOCK_DATA *data;
  DBUG_ENTER("thr_abort_locks");
Marc Alff's avatar
Marc Alff committed
1305
  mysql_mutex_lock(&lock->mutex);
unknown's avatar
unknown committed
1306 1307 1308 1309

  for (data=lock->read_wait.data; data ; data=data->next)
  {
    data->type=TL_UNLOCK;			/* Mark killed */
1310
    /* It's safe to signal the cond first: we're still holding the mutex. */
Marc Alff's avatar
Marc Alff committed
1311
    mysql_cond_signal(data->cond);
unknown's avatar
unknown committed
1312 1313 1314 1315 1316
    data->cond=0;				/* Removed from list */
  }
  for (data=lock->write_wait.data; data ; data=data->next)
  {
    data->type=TL_UNLOCK;
Marc Alff's avatar
Marc Alff committed
1317
    mysql_cond_signal(data->cond);
unknown's avatar
unknown committed
1318 1319 1320 1321 1322
    data->cond=0;
  }
  lock->read_wait.last= &lock->read_wait.data;
  lock->write_wait.last= &lock->write_wait.data;
  lock->read_wait.data=lock->write_wait.data=0;
unknown's avatar
unknown committed
1323
  if (upgrade_lock && lock->write.data)
unknown's avatar
unknown committed
1324
    lock->write.data->type=TL_WRITE_ONLY;
Marc Alff's avatar
Marc Alff committed
1325
  mysql_mutex_unlock(&lock->mutex);
unknown's avatar
unknown committed
1326 1327 1328 1329
  DBUG_VOID_RETURN;
}


1330 1331 1332 1333 1334 1335
/*
  Abort all locks for specific table/thread combination

  This is used to abort all locks for a specific thread
*/

unknown's avatar
unknown committed
1336
my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread_id)
1337 1338
{
  THR_LOCK_DATA *data;
unknown's avatar
unknown committed
1339
  my_bool found= FALSE;
1340 1341
  DBUG_ENTER("thr_abort_locks_for_thread");

Marc Alff's avatar
Marc Alff committed
1342
  mysql_mutex_lock(&lock->mutex);
1343 1344
  for (data= lock->read_wait.data; data ; data= data->next)
  {
1345
    if (data->owner->thread_id == thread_id)    /* purecov: tested */
1346 1347 1348
    {
      DBUG_PRINT("info",("Aborting read-wait lock"));
      data->type= TL_UNLOCK;			/* Mark killed */
1349
      /* It's safe to signal the cond first: we're still holding the mutex. */
unknown's avatar
unknown committed
1350
      found= TRUE;
Marc Alff's avatar
Marc Alff committed
1351
      mysql_cond_signal(data->cond);
1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
      data->cond= 0;				/* Removed from list */

      if (((*data->prev)= data->next))
	data->next->prev= data->prev;
      else
	lock->read_wait.last= data->prev;
    }
  }
  for (data= lock->write_wait.data; data ; data= data->next)
  {
1362
    if (data->owner->thread_id == thread_id) /* purecov: tested */
1363 1364 1365
    {
      DBUG_PRINT("info",("Aborting write-wait lock"));
      data->type= TL_UNLOCK;
unknown's avatar
unknown committed
1366
      found= TRUE;
Marc Alff's avatar
Marc Alff committed
1367
      mysql_cond_signal(data->cond);
1368 1369 1370 1371 1372 1373 1374 1375
      data->cond= 0;

      if (((*data->prev)= data->next))
	data->next->prev= data->prev;
      else
	lock->write_wait.last= data->prev;
    }
  }
1376
  wake_up_waiters(lock);
Marc Alff's avatar
Marc Alff committed
1377
  mysql_mutex_unlock(&lock->mutex);
unknown's avatar
unknown committed
1378
  DBUG_RETURN(found);
1379 1380 1381
}


unknown's avatar
unknown committed
1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394
/*
  Downgrade a WRITE_* to a lower WRITE level
  SYNOPSIS
    thr_downgrade_write_lock()
    in_data                   Lock data of thread downgrading its lock
    new_lock_type             New write lock type
  RETURN VALUE
    NONE
  DESCRIPTION
    This can be used to downgrade a lock already owned. When the downgrade
    occurs also other waiters, both readers and writers can be allowed to
    start.
    The previous lock is often TL_WRITE_ONLY but can also be
1395
    TL_WRITE. The normal downgrade variants are:
unknown's avatar
unknown committed
1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
    TL_WRITE_ONLY => TL_WRITE after a short exclusive lock while holding a
    write table lock
    TL_WRITE_ONLY => TL_WRITE_ALLOW_WRITE After a short exclusive lock after
    already earlier having dongraded lock to TL_WRITE_ALLOW_WRITE
    The implementation is conservative and rather don't start rather than
    go on unknown paths to start, the common cases are handled.

    NOTE:
    In its current implementation it is only allowed to downgrade from
    TL_WRITE_ONLY. In this case there are no waiters. Thus no wake up
    logic is required.
*/

void thr_downgrade_write_lock(THR_LOCK_DATA *in_data,
                              enum thr_lock_type new_lock_type)
{
  THR_LOCK *lock=in_data->lock;
unknown's avatar
unknown committed
1413
#ifndef DBUG_OFF
unknown's avatar
unknown committed
1414
  enum thr_lock_type old_lock_type= in_data->type;
unknown's avatar
unknown committed
1415
#endif
unknown's avatar
unknown committed
1416 1417
  DBUG_ENTER("thr_downgrade_write_only_lock");

Marc Alff's avatar
Marc Alff committed
1418
  mysql_mutex_lock(&lock->mutex);
unknown's avatar
unknown committed
1419 1420 1421
  DBUG_ASSERT(old_lock_type == TL_WRITE_ONLY);
  DBUG_ASSERT(old_lock_type > new_lock_type);
  in_data->type= new_lock_type;
1422
  check_locks(lock,"after downgrading lock", old_lock_type, 0);
unknown's avatar
unknown committed
1423

Marc Alff's avatar
Marc Alff committed
1424
  mysql_mutex_unlock(&lock->mutex);
unknown's avatar
unknown committed
1425 1426
  DBUG_VOID_RETURN;
}
1427

unknown's avatar
unknown committed
1428 1429
/* Upgrade a WRITE_DELAY lock to a WRITE_LOCK */

1430
my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data,
1431 1432
                                     enum thr_lock_type new_lock_type,
                                     ulong lock_wait_timeout)
unknown's avatar
unknown committed
1433 1434
{
  THR_LOCK *lock=data->lock;
1435
  enum enum_thr_lock_result res;
unknown's avatar
unknown committed
1436 1437
  DBUG_ENTER("thr_upgrade_write_delay_lock");

Marc Alff's avatar
Marc Alff committed
1438
  mysql_mutex_lock(&lock->mutex);
1439
  if (data->type == TL_UNLOCK || data->type >= TL_WRITE_LOW_PRIORITY)
unknown's avatar
unknown committed
1440
  {
Marc Alff's avatar
Marc Alff committed
1441
    mysql_mutex_unlock(&lock->mutex);
1442
    DBUG_RETURN(data->type == TL_UNLOCK);	/* Test if Aborted */
unknown's avatar
unknown committed
1443
  }
1444
  check_locks(lock,"before upgrading lock", data->type, 0);
unknown's avatar
unknown committed
1445
  /* TODO:  Upgrade to TL_WRITE_CONCURRENT_INSERT in some cases */
1446
  data->type= new_lock_type;                    /* Upgrade lock */
unknown's avatar
unknown committed
1447 1448 1449 1450 1451 1452 1453

  /* Check if someone has given us the lock */
  if (!data->cond)
  {
    if (!lock->read.data)			/* No read locks */
    {						/* We have the lock */
      if (data->lock->get_status)
1454
	(*data->lock->get_status)(data->status_param, 0);
Marc Alff's avatar
Marc Alff committed
1455
      mysql_mutex_unlock(&lock->mutex);
1456 1457
      if (lock->start_trans)
	(*lock->start_trans)(data->status_param);
unknown's avatar
unknown committed
1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471
      DBUG_RETURN(0);
    }

    if (((*data->prev)=data->next))		/* remove from lock-list */
      data->next->prev= data->prev;
    else
      lock->write.last=data->prev;

    if ((data->next=lock->write_wait.data))	/* Put first in lock_list */
      data->next->prev= &data->next;
    else
      lock->write_wait.last= &data->next;
    data->prev= &lock->write_wait.data;
    lock->write_wait.data=data;
1472
    check_locks(lock,"upgrading lock", new_lock_type, 0);
unknown's avatar
unknown committed
1473
  }
unknown's avatar
unknown committed
1474 1475
  else
  {
1476
    check_locks(lock,"waiting for lock", new_lock_type, 0);
unknown's avatar
unknown committed
1477
  }
Sergei Golubchik's avatar
Sergei Golubchik committed
1478
  res= wait_for_lock(&lock->write_wait, data, 1, lock_wait_timeout);
1479 1480 1481
  if (res == THR_LOCK_SUCCESS && lock->start_trans)
    DBUG_RETURN((*lock->start_trans)(data->status_param));
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1482 1483 1484 1485 1486
}


/* downgrade a WRITE lock to a WRITE_DELAY lock if there is pending locks */

1487 1488
my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data,
                                  ulong lock_wait_timeout)
unknown's avatar
unknown committed
1489 1490
{
  THR_LOCK *lock=data->lock;
1491
  enum thr_lock_type write_lock_type;
unknown's avatar
unknown committed
1492 1493
  DBUG_ENTER("thr_reschedule_write_lock");

Marc Alff's avatar
Marc Alff committed
1494
  mysql_mutex_lock(&lock->mutex);
unknown's avatar
unknown committed
1495 1496
  if (!lock->read_wait.data)			/* No waiting read locks */
  {
Marc Alff's avatar
Marc Alff committed
1497
    mysql_mutex_unlock(&lock->mutex);
unknown's avatar
unknown committed
1498 1499 1500
    DBUG_RETURN(0);
  }

1501
  write_lock_type= data->type;
unknown's avatar
unknown committed
1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518
  data->type=TL_WRITE_DELAYED;
  if (lock->update_status)
    (*lock->update_status)(data->status_param);
  if (((*data->prev)=data->next))		/* remove from lock-list */
    data->next->prev= data->prev;
  else
    lock->write.last=data->prev;

  if ((data->next=lock->write_wait.data))	/* Put first in lock_list */
    data->next->prev= &data->next;
  else
    lock->write_wait.last= &data->next;
  data->prev= &lock->write_wait.data;
  data->cond=get_cond();			/* This was zero */
  lock->write_wait.data=data;
  free_all_read_locks(lock,0);

Marc Alff's avatar
Marc Alff committed
1519
  mysql_mutex_unlock(&lock->mutex);
1520 1521
  DBUG_RETURN(thr_upgrade_write_delay_lock(data, write_lock_type,
                                           lock_wait_timeout));
unknown's avatar
unknown committed
1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537
}


#include <my_sys.h>

static void thr_print_lock(const char* name,struct st_lock_list *list)
{
  THR_LOCK_DATA *data,**prev;
  uint count=0;

  if (list->data)
  {
    printf("%-10s: ",name);
    prev= &list->data;
    for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next)
    {
1538
      printf("0x%lx (%lu:%d); ", (ulong) data, data->owner->thread_id,
1539
             (int) data->type);
unknown's avatar
unknown committed
1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554
      if (data->prev != prev)
	printf("\nWarning: prev didn't point at previous lock\n");
      prev= &data->next;
    }
    puts("");
    if (prev != list->last)
      printf("Warning: last didn't point at last lock\n");
  }
}

void thr_print_locks(void)
{
  LIST *list;
  uint count=0;

Marc Alff's avatar
Marc Alff committed
1555
  mysql_mutex_lock(&THR_LOCK_lock);
unknown's avatar
unknown committed
1556
  puts("Current locks:");
1557 1558
  for (list= thr_lock_thread_list; list && count++ < MAX_THREADS;
       list= list_rest(list))
unknown's avatar
unknown committed
1559 1560
  {
    THR_LOCK *lock=(THR_LOCK*) list->data;
Marc Alff's avatar
Marc Alff committed
1561
    mysql_mutex_lock(&lock->mutex);
1562
    printf("lock: 0x%lx:",(ulong) lock);
unknown's avatar
unknown committed
1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578
    if ((lock->write_wait.data || lock->read_wait.data) &&
	(! lock->read.data && ! lock->write.data))
      printf(" WARNING: ");
    if (lock->write.data)
      printf(" write");
    if (lock->write_wait.data)
      printf(" write_wait");
    if (lock->read.data)
      printf(" read");
    if (lock->read_wait.data)
      printf(" read_wait");
    puts("");
    thr_print_lock("write",&lock->write);
    thr_print_lock("write_wait",&lock->write_wait);
    thr_print_lock("read",&lock->read);
    thr_print_lock("read_wait",&lock->read_wait);
Marc Alff's avatar
Marc Alff committed
1579
    mysql_mutex_unlock(&lock->mutex);
unknown's avatar
unknown committed
1580 1581 1582
    puts("");
  }
  fflush(stdout);
Marc Alff's avatar
Marc Alff committed
1583
  mysql_mutex_unlock(&THR_LOCK_lock);
unknown's avatar
unknown committed
1584 1585
}

unknown's avatar
unknown committed
1586 1587 1588 1589 1590

/*****************************************************************************
** Test of thread locks
****************************************************************************/

unknown's avatar
unknown committed
1591 1592 1593 1594 1595 1596 1597
#ifdef MAIN

struct st_test {
  uint lock_nr;
  enum thr_lock_type lock_type;
};

unknown's avatar
unknown committed
1598
THR_LOCK locks[6];			/* Number of locks +1 */
unknown's avatar
unknown committed
1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611

struct st_test test_0[] = {{0,TL_READ}};	/* One lock */
struct st_test test_1[] = {{0,TL_READ},{0,TL_WRITE}}; /* Read and write lock of lock 0 */
struct st_test test_2[] = {{1,TL_WRITE},{0,TL_READ},{2,TL_READ}};
struct st_test test_3[] = {{2,TL_WRITE},{1,TL_READ},{0,TL_READ}}; /* Deadlock with test_2 ? */
struct st_test test_4[] = {{0,TL_WRITE},{0,TL_READ},{0,TL_WRITE},{0,TL_READ}};
struct st_test test_5[] = {{0,TL_READ},{1,TL_READ},{2,TL_READ},{3,TL_READ}}; /* Many reads */
struct st_test test_6[] = {{0,TL_WRITE},{1,TL_WRITE},{2,TL_WRITE},{3,TL_WRITE}}; /* Many writes */
struct st_test test_7[] = {{3,TL_READ}};
struct st_test test_8[] = {{1,TL_READ_NO_INSERT},{2,TL_READ_NO_INSERT},{3,TL_READ_NO_INSERT}};	/* Should be quick */
struct st_test test_9[] = {{4,TL_READ_HIGH_PRIORITY}};
struct st_test test_10[] ={{4,TL_WRITE}};
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 */
1612 1613 1614 1615
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}};
struct st_test test_13[] = {{0,TL_WRITE_CONCURRENT_INSERT},{1,TL_READ}};
struct st_test test_14[] = {{0,TL_WRITE_ALLOW_WRITE},{1,TL_READ}};
struct st_test test_15[] = {{0,TL_WRITE_ALLOW_WRITE},{1,TL_WRITE_ALLOW_WRITE}};
unknown's avatar
unknown committed
1616 1617 1618

struct st_test *tests[] = {test_0,test_1,test_2,test_3,test_4,test_5,test_6,
			   test_7,test_8,test_9,test_10,test_11,test_12,
1619
			   test_13,test_14,test_15};
unknown's avatar
unknown committed
1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634
int lock_counts[]= {sizeof(test_0)/sizeof(struct st_test),
		    sizeof(test_1)/sizeof(struct st_test),
		    sizeof(test_2)/sizeof(struct st_test),
		    sizeof(test_3)/sizeof(struct st_test),
		    sizeof(test_4)/sizeof(struct st_test),
		    sizeof(test_5)/sizeof(struct st_test),
		    sizeof(test_6)/sizeof(struct st_test),
		    sizeof(test_7)/sizeof(struct st_test),
		    sizeof(test_8)/sizeof(struct st_test),
		    sizeof(test_9)/sizeof(struct st_test),
		    sizeof(test_10)/sizeof(struct st_test),
		    sizeof(test_11)/sizeof(struct st_test),
		    sizeof(test_12)/sizeof(struct st_test),
		    sizeof(test_13)/sizeof(struct st_test),
		    sizeof(test_14)/sizeof(struct st_test),
1635
		    sizeof(test_15)/sizeof(struct st_test)
unknown's avatar
unknown committed
1636 1637 1638
};


Marc Alff's avatar
Marc Alff committed
1639 1640
static mysql_cond_t COND_thread_count;
static mysql_mutex_t LOCK_thread_count;
unknown's avatar
unknown committed
1641 1642 1643 1644
static uint thread_count;
static ulong sum=0;

#define MAX_LOCK_COUNT 8
1645
#define TEST_TIMEOUT 100000
unknown's avatar
unknown committed
1646 1647 1648

/* The following functions is for WRITE_CONCURRENT_INSERT */

1649
static void test_get_status(void* param __attribute__((unused)),
Sergei Golubchik's avatar
Sergei Golubchik committed
1650
                            my_bool concurrent_insert __attribute__((unused)))
unknown's avatar
unknown committed
1651 1652 1653
{
}

unknown's avatar
unknown committed
1654 1655 1656 1657
static void test_update_status(void* param __attribute__((unused)))
{
}

unknown's avatar
unknown committed
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672
static void test_copy_status(void* to __attribute__((unused)) ,
			     void *from __attribute__((unused)))
{
}

static my_bool test_check_status(void* param __attribute__((unused)))
{
  return 0;
}


static void *test_thread(void *arg)
{
  int i,j,param=*((int*) arg);
  THR_LOCK_DATA data[MAX_LOCK_COUNT];
1673
  THR_LOCK_INFO lock_info;
unknown's avatar
unknown committed
1674 1675 1676 1677 1678
  THR_LOCK_DATA *multi_locks[MAX_LOCK_COUNT];
  my_thread_init();

  printf("Thread %s (%d) started\n",my_thread_name(),param); fflush(stdout);

1679
  thr_lock_info_init(&lock_info);
unknown's avatar
unknown committed
1680 1681 1682 1683 1684 1685 1686 1687 1688
  for (i=0; i < lock_counts[param] ; i++)
    thr_lock_data_init(locks+tests[param][i].lock_nr,data+i,NULL);
  for (j=1 ; j < 10 ; j++)		/* try locking 10 times */
  {
    for (i=0; i < lock_counts[param] ; i++)
    {					/* Init multi locks */
      multi_locks[i]= &data[i];
      data[i].type= tests[param][i].lock_type;
    }
1689
    thr_multi_lock(multi_locks, lock_counts[param], &lock_info, TEST_TIMEOUT);
Marc Alff's avatar
Marc Alff committed
1690
    mysql_mutex_lock(&LOCK_thread_count);
unknown's avatar
unknown committed
1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703
    {
      int tmp=rand() & 7;			/* Do something from 0-2 sec */
      if (tmp == 0)
	sleep(1);
      else if (tmp == 1)
	sleep(2);
      else
      {
	ulong k;
	for (k=0 ; k < (ulong) (tmp-2)*100000L ; k++)
	  sum+=k;
      }
    }
Marc Alff's avatar
Marc Alff committed
1704
    mysql_mutex_unlock(&LOCK_thread_count);
1705
    thr_multi_unlock(multi_locks,lock_counts[param], THR_UNLOCK_UPDATE_STATUS);
unknown's avatar
unknown committed
1706 1707
  }

1708
  printf("Thread %s (%d) ended\n",my_thread_name(),param); fflush(stdout);
unknown's avatar
unknown committed
1709
  thr_print_locks();
Marc Alff's avatar
Marc Alff committed
1710
  mysql_mutex_lock(&LOCK_thread_count);
unknown's avatar
unknown committed
1711
  thread_count--;
Marc Alff's avatar
Marc Alff committed
1712 1713
  mysql_cond_signal(&COND_thread_count); /* Tell main we are ready */
  mysql_mutex_unlock(&LOCK_thread_count);
1714
  free((uchar*) arg);
unknown's avatar
unknown committed
1715 1716 1717 1718 1719 1720 1721 1722
  return 0;
}


int main(int argc __attribute__((unused)),char **argv __attribute__((unused)))
{
  pthread_t tid;
  pthread_attr_t thr_attr;
unknown's avatar
unknown committed
1723 1724
  int *param,error;
  uint i;
unknown's avatar
unknown committed
1725 1726 1727 1728 1729 1730
  MY_INIT(argv[0]);
  if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '#')
    DBUG_PUSH(argv[1]+2);

  printf("Main thread: %s\n",my_thread_name());

Marc Alff's avatar
Marc Alff committed
1731
  if ((error= mysql_cond_init(0, &COND_thread_count, NULL)))
unknown's avatar
unknown committed
1732
  {
Marc Alff's avatar
Marc Alff committed
1733
    fprintf(stderr, "Got error: %d from mysql_cond_init (errno: %d)",
unknown's avatar
unknown committed
1734 1735 1736
	    error,errno);
    exit(1);
  }
Marc Alff's avatar
Marc Alff committed
1737
  if ((error= mysql_mutex_init(0, &LOCK_thread_count, MY_MUTEX_INIT_FAST)))
unknown's avatar
unknown committed
1738
  {
Marc Alff's avatar
Marc Alff committed
1739
    fprintf(stderr, "Got error: %d from mysql_cond_init (errno: %d)",
unknown's avatar
unknown committed
1740 1741 1742 1743
	    error,errno);
    exit(1);
  }

unknown's avatar
unknown committed
1744
  for (i=0 ; i < array_elements(locks) ; i++)
unknown's avatar
unknown committed
1745 1746 1747
  {
    thr_lock_init(locks+i);
    locks[i].check_status= test_check_status;
unknown's avatar
unknown committed
1748
    locks[i].update_status=test_update_status;
unknown's avatar
unknown committed
1749 1750
    locks[i].copy_status=  test_copy_status;
    locks[i].get_status=   test_get_status;
unknown's avatar
unknown committed
1751
    locks[i].allow_multiple_concurrent_insert= 1;
unknown's avatar
unknown committed
1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774
  }
  if ((error=pthread_attr_init(&thr_attr)))
  {
    fprintf(stderr,"Got error: %d from pthread_attr_init (errno: %d)",
	    error,errno);
    exit(1);
  }
  if ((error=pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED)))
  {
    fprintf(stderr,
	    "Got error: %d from pthread_attr_setdetachstate (errno: %d)",
	    error,errno);
    exit(1);
  }
#ifndef pthread_attr_setstacksize		/* void return value */
  if ((error=pthread_attr_setstacksize(&thr_attr,65536L)))
  {
    fprintf(stderr,"Got error: %d from pthread_attr_setstacksize (errno: %d)",
	    error,errno);
    exit(1);
  }
#endif
#ifdef HAVE_THR_SETCONCURRENCY
Konstantin Osipov's avatar
Konstantin Osipov committed
1775
  (void) thr_setconcurrency(2);
unknown's avatar
unknown committed
1776
#endif
unknown's avatar
unknown committed
1777
  for (i=0 ; i < array_elements(lock_counts) ; i++)
unknown's avatar
unknown committed
1778 1779 1780 1781
  {
    param=(int*) malloc(sizeof(int));
    *param=i;

Marc Alff's avatar
Marc Alff committed
1782
    if ((error= mysql_mutex_lock(&LOCK_thread_count)))
unknown's avatar
unknown committed
1783
    {
Marc Alff's avatar
Marc Alff committed
1784 1785
      fprintf(stderr, "Got error: %d from mysql_mutex_lock (errno: %d)",
              error, errno);
unknown's avatar
unknown committed
1786 1787
      exit(1);
    }
Marc Alff's avatar
Marc Alff committed
1788 1789 1790
    if ((error= mysql_thread_create(0,
                                    &tid, &thr_attr, test_thread,
                                    (void*) param)))
unknown's avatar
unknown committed
1791
    {
Marc Alff's avatar
Marc Alff committed
1792 1793 1794
      fprintf(stderr, "Got error: %d from mysql_thread_create (errno: %d)\n",
              error, errno);
      mysql_mutex_unlock(&LOCK_thread_count);
unknown's avatar
unknown committed
1795 1796 1797
      exit(1);
    }
    thread_count++;
Marc Alff's avatar
Marc Alff committed
1798
    mysql_mutex_unlock(&LOCK_thread_count);
unknown's avatar
unknown committed
1799 1800 1801
  }

  pthread_attr_destroy(&thr_attr);
Marc Alff's avatar
Marc Alff committed
1802 1803
  if ((error= mysql_mutex_lock(&LOCK_thread_count)))
    fprintf(stderr, "Got error: %d from mysql_mutex_lock\n", error);
unknown's avatar
unknown committed
1804 1805
  while (thread_count)
  {
Marc Alff's avatar
Marc Alff committed
1806 1807
    if ((error= mysql_cond_wait(&COND_thread_count, &LOCK_thread_count)))
      fprintf(stderr, "Got error: %d from mysql_cond_wait\n", error);
unknown's avatar
unknown committed
1808
  }
Marc Alff's avatar
Marc Alff committed
1809 1810
  if ((error= mysql_mutex_unlock(&LOCK_thread_count)))
    fprintf(stderr, "Got error: %d from mysql_mutex_unlock\n", error);
unknown's avatar
unknown committed
1811
  for (i=0 ; i < array_elements(locks) ; i++)
unknown's avatar
unknown committed
1812 1813 1814 1815 1816 1817 1818 1819 1820 1821
    thr_lock_delete(locks+i);
#ifdef EXTRA_DEBUG
  if (found_errors)
    printf("Got %d warnings\n",found_errors);
  else
#endif
    printf("Test succeeded\n");
  return 0;
}

unknown's avatar
unknown committed
1822
#endif /* MAIN */