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