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