~drizzle-trunk/drizzle/development

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