~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_NO_INSERT	# Read without concurrent inserts
30
TL_WRITE_ALLOW_WRITE	# Write lock that allows other writers
31
TL_WRITE_ALLOW_READ	# Write lock, but allow reading
32
TL_WRITE_CONCURRENT_INSERT
33
			# Insert that can be mixed when selects
34
TL_WRITE		# High priority write
35
TL_WRITE_ONLY		# High priority write
36
			# Abort all new lock request with an error
37
38
Locks are prioritized according to:
39
40
WRITE_ALLOW_WRITE, WRITE_ALLOW_READ, WRITE_CONCURRENT_INSERT, WRITE_DELAYED,
41
WRITE_LOW_PRIORITY, READ, WRITE, READ_HIGH_PRIORITY and WRITE_ONLY
42
43
Locks in the same privilege level are scheduled in first-in-first-out order.
44
45
To allow concurrent read/writes locks, with 'WRITE_CONCURRENT_INSERT' one
46
should put a pointer to the following functions in the lock structure:
47
(If the pointer is zero (default), the function is not called)
48
49
check_status:
50
	 Before giving a lock of type TL_WRITE_CONCURRENT_INSERT,
51
         we check if this function exists and returns 0.
52
	 If not, then the lock is upgraded to TL_WRITE_LOCK
53
	 In MyISAM this is a simple check if the insert can be done
54
	 at the end of the datafile.
55
update_status:
56
	Before a write lock is released, this function is called.
57
	In MyISAM this functions updates the count and length of the datafile
58
get_status:
59
	When one gets a lock this functions is called.
60
	In MyISAM this stores the number of rows and size of the datafile
61
	for concurrent reads.
62
63
The lock algorithm allows one to have one TL_WRITE_ALLOW_READ,
1008.1.1 by Brian Aker
Remove dead lock code around delayed INSERT.
64
TL_WRITE_CONCURRENT_INSERT lock at the same time as multiple read locks.
1 by brian
clean slate
65
66
*/
67
994.2.4 by Monty Taylor
Blast. Fixed some make distcheck issues.
68
#include "mysys/mysys_priv.h"
1 by brian
clean slate
69
70
#include "thr_lock.h"
212.5.18 by Monty Taylor
Moved m_ctype, m_string and my_bitmap. Removed t_ctype.
71
#include <mystrings/m_string.h>
1 by brian
clean slate
72
#include <errno.h>
916.1.9 by Padraig O'Sullivan
Removing an instance of the custom list structure from thr_lock.
73
#include <list>
1 by brian
clean slate
74
481.1.15 by Monty Taylor
Removed time.h and sys/time.h from global.h.
75
#if TIME_WITH_SYS_TIME
76
# include <sys/time.h>
77
# include <time.h>
78
#else
79
# if HAVE_SYS_TIME_H
80
#  include <sys/time.h>
81
# else
82
#  include <time.h>
83
# endif
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
84
#endif
481.1.15 by Monty Taylor
Removed time.h and sys/time.h from global.h.
85
492.1.7 by Monty Taylor
Moved test() to its own file.
86
#include <drizzled/util/test.h>
481.1.15 by Monty Taylor
Removed time.h and sys/time.h from global.h.
87
916.1.9 by Padraig O'Sullivan
Removing an instance of the custom list structure from thr_lock.
88
using namespace std;
89
1054.1.4 by Brian Aker
Formatting.
90
bool thr_lock_inited= false;
298 by Brian Aker
ulong conversion.
91
uint32_t locks_immediate = 0L, locks_waited = 0L;
622.1.1 by Brian Aker
32bit fixes around vars
92
uint64_t table_lock_wait_timeout;
1165.1.90 by Stewart Smith
make thr_upgraded_concurrent_insert_lock static to mysys/thr_lock.cc
93
static enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE;
1 by brian
clean slate
94
95
916.1.16 by Padraig O'Sullivan
Make the thr_lock_thread_list static as it is not used anywhere outside the
96
static list<THR_LOCK *> thr_lock_thread_list;          /* List of threads in use */
916.1.9 by Padraig O'Sullivan
Removing an instance of the custom list structure from thr_lock.
97
622.1.1 by Brian Aker
32bit fixes around vars
98
uint64_t max_write_lock_count= ~(uint64_t) 0L;
1 by brian
clean slate
99
100
static inline pthread_cond_t *get_cond(void)
101
{
102
  return &my_thread_var->suspend;
103
}
104
105
/*
106
** For the future (now the thread specific cond is alloced by my_pthread.c)
107
*/
108
146 by Brian Aker
my_bool cleanup.
109
bool init_thr_lock()
1 by brian
clean slate
110
{
1054.1.4 by Brian Aker
Formatting.
111
  thr_lock_inited= true;
112
113
  return false;
1 by brian
clean slate
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
123
	/* Initialize a lock */
124
125
void thr_lock_init(THR_LOCK *lock)
126
{
212.6.14 by Mats Kindahl
Removing redundant use of casts in mysys for memcmp(), memcpy(), memset(), and memmove().
127
  memset(lock, 0, sizeof(*lock));
398.1.10 by Monty Taylor
Actually removed VOID() this time.
128
  pthread_mutex_init(&lock->mutex,MY_MUTEX_INIT_FAST);
1 by brian
clean slate
129
  lock->read.last= &lock->read.data;
130
  lock->read_wait.last= &lock->read_wait.data;
131
  lock->write_wait.last= &lock->write_wait.data;
132
  lock->write.last= &lock->write.data;
133
134
  pthread_mutex_lock(&THR_LOCK_lock);		/* Add to locks in use */
916.1.9 by Padraig O'Sullivan
Removing an instance of the custom list structure from thr_lock.
135
  thr_lock_thread_list.push_front(lock);
1 by brian
clean slate
136
  pthread_mutex_unlock(&THR_LOCK_lock);
137
}
138
139
140
void thr_lock_delete(THR_LOCK *lock)
141
{
398.1.10 by Monty Taylor
Actually removed VOID() this time.
142
  pthread_mutex_destroy(&lock->mutex);
1 by brian
clean slate
143
  pthread_mutex_lock(&THR_LOCK_lock);
916.1.9 by Padraig O'Sullivan
Removing an instance of the custom list structure from thr_lock.
144
  thr_lock_thread_list.remove(lock);
1 by brian
clean slate
145
  pthread_mutex_unlock(&THR_LOCK_lock);
146
}
147
148
149
void thr_lock_info_init(THR_LOCK_INFO *info)
150
{
151
  struct st_my_thread_var *tmp= my_thread_var;
1054.1.4 by Brian Aker
Formatting.
152
  info->thread= tmp->pthread_self;
1 by brian
clean slate
153
  info->thread_id= tmp->id;
154
  info->n_cursors= 0;
155
}
156
157
	/* Initialize a lock instance */
158
159
void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param)
160
{
1054.1.4 by Brian Aker
Formatting.
161
  data->lock= lock;
162
  data->type= TL_UNLOCK;
163
  data->owner= NULL;                               /* no owner yet */
164
  data->status_param= param;
165
  data->cond= NULL;
1 by brian
clean slate
166
}
167
168
146 by Brian Aker
my_bool cleanup.
169
static inline bool
1 by brian
clean slate
170
have_old_read_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner)
171
{
172
  for ( ; data ; data=data->next)
173
  {
174
    if (thr_lock_owner_equal(data->owner, owner))
1054.1.4 by Brian Aker
Formatting.
175
      return true;					/* Already locked by thread */
1 by brian
clean slate
176
  }
1054.1.4 by Brian Aker
Formatting.
177
  return false;
1 by brian
clean slate
178
}
179
180
static void wake_up_waiters(THR_LOCK *lock);
181
182
183
static enum enum_thr_lock_result
184
wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
146 by Brian Aker
my_bool cleanup.
185
              bool in_wait_list)
1 by brian
clean slate
186
{
187
  struct st_my_thread_var *thread_var= my_thread_var;
188
  pthread_cond_t *cond= &thread_var->suspend;
189
  struct timespec wait_timeout;
190
  enum enum_thr_lock_result result= THR_LOCK_ABORTED;
146 by Brian Aker
my_bool cleanup.
191
  bool can_deadlock= test(data->owner->info->n_cursors);
1 by brian
clean slate
192
193
  if (!in_wait_list)
194
  {
195
    (*wait->last)=data;				/* Wait for lock */
196
    data->prev= wait->last;
197
    wait->last= &data->next;
198
  }
199
200
  statistic_increment(locks_waited, &THR_LOCK_lock);
201
202
  /* Set up control struct to allow others to abort locks */
203
  thread_var->current_mutex= &data->lock->mutex;
204
  thread_var->current_cond=  cond;
205
  data->cond= cond;
206
207
  if (can_deadlock)
208
    set_timespec(wait_timeout, table_lock_wait_timeout);
209
  while (!thread_var->abort || in_wait_list)
210
  {
211
    int rc= (can_deadlock ?
212
             pthread_cond_timedwait(cond, &data->lock->mutex,
213
                                    &wait_timeout) :
214
             pthread_cond_wait(cond, &data->lock->mutex));
215
    /*
216
      We must break the wait if one of the following occurs:
217
      - the connection has been aborted (!thread_var->abort), but
218
        this is not a delayed insert thread (in_wait_list). For a delayed
219
        insert thread the proper action at shutdown is, apparently, to
220
        acquire the lock and complete the insert.
221
      - the lock has been granted (data->cond is set to NULL by the granter),
222
        or the waiting has been aborted (additionally data->type is set to
223
        TL_UNLOCK).
224
      - the wait has timed out (rc == ETIMEDOUT)
225
      Order of checks below is important to not report about timeout
226
      if the predicate is true.
227
    */
228
    if (data->cond == 0)
229
    {
230
      break;
231
    }
232
    if (rc == ETIMEDOUT || rc == ETIME)
233
    {
234
      result= THR_LOCK_WAIT_TIMEOUT;
235
      break;
236
    }
237
  }
238
  if (data->cond || data->type == TL_UNLOCK)
239
  {
240
    if (data->cond)                             /* aborted or timed out */
241
    {
242
      if (((*data->prev)=data->next))		/* remove from wait-list */
243
	data->next->prev= data->prev;
244
      else
245
	wait->last=data->prev;
246
      data->type= TL_UNLOCK;                    /* No lock */
247
      wake_up_waiters(data->lock);
248
    }
249
  }
250
  else
251
  {
252
    result= THR_LOCK_SUCCESS;
253
    if (data->lock->get_status)
254
      (*data->lock->get_status)(data->status_param, 0);
255
  }
256
  pthread_mutex_unlock(&data->lock->mutex);
257
258
  /* The following must be done after unlock of lock->mutex */
259
  pthread_mutex_lock(&thread_var->mutex);
1054.1.4 by Brian Aker
Formatting.
260
  thread_var->current_mutex= NULL;
261
  thread_var->current_cond= NULL;
1 by brian
clean slate
262
  pthread_mutex_unlock(&thread_var->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
263
  return(result);
1 by brian
clean slate
264
}
265
266
1165.1.88 by Stewart Smith
make thr_lock() static to mysys/thr_lock.cc
267
static enum enum_thr_lock_result
1 by brian
clean slate
268
thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
269
         enum thr_lock_type lock_type)
270
{
271
  THR_LOCK *lock=data->lock;
272
  enum enum_thr_lock_result result= THR_LOCK_SUCCESS;
273
  struct st_lock_list *wait_queue;
274
  THR_LOCK_DATA *lock_owner;
275
276
  data->next=0;
277
  data->cond=0;					/* safety */
278
  data->type=lock_type;
279
  data->owner= owner;                           /* Must be reset ! */
398.1.10 by Monty Taylor
Actually removed VOID() this time.
280
  pthread_mutex_lock(&lock->mutex);
1 by brian
clean slate
281
  if ((int) lock_type <= (int) TL_READ_NO_INSERT)
282
  {
283
    /* Request for READ lock */
284
    if (lock->write.data)
285
    {
286
      /* We can allow a read lock even if there is already a write lock
287
	 on the table in one the following cases:
288
	 - This thread alread have a write lock on the table
289
	 - The write lock is TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED
290
           and the read lock is TL_READ_HIGH_PRIORITY or TL_READ
291
         - The write lock is TL_WRITE_CONCURRENT_INSERT or TL_WRITE_ALLOW_WRITE
292
	   and the read lock is not TL_READ_NO_INSERT
293
      */
294
295
      if (thr_lock_owner_equal(data->owner, lock->write.data->owner) ||
1008.1.1 by Brian Aker
Remove dead lock code around delayed INSERT.
296
	  (lock->write.data->type <= TL_WRITE_CONCURRENT_INSERT &&
1008.1.4 by Brian Aker
Removed TL_READ_HIGH_PRIORITY
297
	   (((int) lock_type <= (int) TL_READ_WITH_SHARED_LOCKS) ||
1 by brian
clean slate
298
	    (lock->write.data->type != TL_WRITE_CONCURRENT_INSERT &&
299
	     lock->write.data->type != TL_WRITE_ALLOW_READ))))
300
      {						/* Already got a write lock */
301
	(*lock->read.last)=data;		/* Add to running FIFO */
302
	data->prev=lock->read.last;
303
	lock->read.last= &data->next;
304
	if (lock_type == TL_READ_NO_INSERT)
305
	  lock->read_no_write_count++;
306
	if (lock->get_status)
307
	  (*lock->get_status)(data->status_param, 0);
308
	statistic_increment(locks_immediate,&THR_LOCK_lock);
309
	goto end;
310
      }
311
      if (lock->write.data->type == TL_WRITE_ONLY)
312
      {
313
	/* We are not allowed to get a READ lock in this case */
314
	data->type=TL_UNLOCK;
315
        result= THR_LOCK_ABORTED;               /* Can't wait for this one */
316
	goto end;
317
      }
318
    }
319
    else if (!lock->write_wait.data ||
1008.1.2 by Brian Aker
Removed old DELAYED keyword from parser (plus cleaned up tests). Also
320
	     lock->write_wait.data->type <= TL_WRITE_DEFAULT ||
1 by brian
clean slate
321
	     have_old_read_lock(lock->read.data, data->owner))
322
    {						/* No important write-locks */
323
      (*lock->read.last)=data;			/* Add to running FIFO */
324
      data->prev=lock->read.last;
325
      lock->read.last= &data->next;
326
      if (lock->get_status)
327
	(*lock->get_status)(data->status_param, 0);
328
      if (lock_type == TL_READ_NO_INSERT)
329
	lock->read_no_write_count++;
330
      statistic_increment(locks_immediate,&THR_LOCK_lock);
331
      goto end;
332
    }
333
    /*
334
      We're here if there is an active write lock or no write
335
      lock but a high priority write waiting in the write_wait queue.
336
      In the latter case we should yield the lock to the writer.
337
    */
338
    wait_queue= &lock->read_wait;
339
  }
340
  else						/* Request for WRITE lock */
341
  {
1008.1.1 by Brian Aker
Remove dead lock code around delayed INSERT.
342
    if (lock_type == TL_WRITE_CONCURRENT_INSERT && ! lock->check_status)
1 by brian
clean slate
343
      data->type=lock_type= thr_upgraded_concurrent_insert_lock;
344
345
    if (lock->write.data)			/* If there is a write lock */
346
    {
347
      if (lock->write.data->type == TL_WRITE_ONLY)
348
      {
349
        /* Allow lock owner to bypass TL_WRITE_ONLY. */
350
        if (!thr_lock_owner_equal(data->owner, lock->write.data->owner))
351
        {
352
          /* We are not allowed to get a lock in this case */
353
          data->type=TL_UNLOCK;
354
          result= THR_LOCK_ABORTED;               /* Can't wait for this one */
355
          goto end;
356
        }
357
      }
358
359
      /*
360
	The following test will not work if the old lock was a
361
	TL_WRITE_ALLOW_WRITE, TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED in
362
	the same thread, but this will never happen within MySQL.
363
      */
364
      if (thr_lock_owner_equal(data->owner, lock->write.data->owner) ||
365
	  (lock_type == TL_WRITE_ALLOW_WRITE &&
366
	   !lock->write_wait.data &&
367
	   lock->write.data->type == TL_WRITE_ALLOW_WRITE))
368
      {
369
	/*
370
          We have already got a write lock or all locks are
371
          TL_WRITE_ALLOW_WRITE
372
        */
373
374
	(*lock->write.last)=data;	/* Add to running fifo */
375
	data->prev=lock->write.last;
376
	lock->write.last= &data->next;
377
	if (data->lock->get_status)
378
	  (*data->lock->get_status)(data->status_param, 0);
379
	statistic_increment(locks_immediate,&THR_LOCK_lock);
380
	goto end;
381
      }
382
    }
383
    else
384
    {
385
      if (!lock->write_wait.data)
386
      {						/* no scheduled write locks */
146 by Brian Aker
my_bool cleanup.
387
        bool concurrent_insert= 0;
1 by brian
clean slate
388
	if (lock_type == TL_WRITE_CONCURRENT_INSERT)
389
        {
390
          concurrent_insert= 1;
391
          if ((*lock->check_status)(data->status_param))
392
          {
393
            concurrent_insert= 0;
394
            data->type=lock_type= thr_upgraded_concurrent_insert_lock;
395
          }
396
        }
397
398
	if (!lock->read.data ||
1008.1.1 by Brian Aker
Remove dead lock code around delayed INSERT.
399
	    (lock_type <= TL_WRITE_CONCURRENT_INSERT &&
1 by brian
clean slate
400
	     ((lock_type != TL_WRITE_CONCURRENT_INSERT &&
401
	       lock_type != TL_WRITE_ALLOW_WRITE) ||
402
	      !lock->read_no_write_count)))
403
	{
404
	  (*lock->write.last)=data;		/* Add as current write lock */
405
	  data->prev=lock->write.last;
406
	  lock->write.last= &data->next;
407
	  if (data->lock->get_status)
408
	    (*data->lock->get_status)(data->status_param, concurrent_insert);
409
	  statistic_increment(locks_immediate,&THR_LOCK_lock);
410
	  goto end;
411
	}
412
      }
413
    }
414
    wait_queue= &lock->write_wait;
415
  }
416
  /*
417
    Try to detect a trivial deadlock when using cursors: attempt to
418
    lock a table that is already locked by an open cursor within the
419
    same connection. lock_owner can be zero if we succumbed to a high
420
    priority writer in the write_wait queue.
421
  */
422
  lock_owner= lock->read.data ? lock->read.data : lock->write.data;
423
  if (lock_owner && lock_owner->owner->info == owner->info)
424
  {
425
    result= THR_LOCK_DEADLOCK;
426
    goto end;
427
  }
428
  /* Can't get lock yet;  Wait for it */
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
429
  return(wait_for_lock(wait_queue, data, 0));
1 by brian
clean slate
430
end:
431
  pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
432
  return(result);
1 by brian
clean slate
433
}
434
435
1022.2.29 by Monty Taylor
Fixed some no-inline warnings.
436
static void free_all_read_locks(THR_LOCK *lock, bool using_concurrent_insert)
1 by brian
clean slate
437
{
438
  THR_LOCK_DATA *data=lock->read_wait.data;
439
440
  /* move all locks from read_wait list to read list */
441
  (*lock->read.last)=data;
442
  data->prev=lock->read.last;
443
  lock->read.last=lock->read_wait.last;
444
445
  /* Clear read_wait list */
446
  lock->read_wait.last= &lock->read_wait.data;
447
448
  do
449
  {
450
    pthread_cond_t *cond=data->cond;
451
    if ((int) data->type == (int) TL_READ_NO_INSERT)
452
    {
453
      if (using_concurrent_insert)
454
      {
455
	/*
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
456
	  We can't free this lock;
1 by brian
clean slate
457
	  Link lock away from read chain back into read_wait chain
458
	*/
459
	if (((*data->prev)=data->next))
460
	  data->next->prev=data->prev;
461
	else
462
	  lock->read.last=data->prev;
463
	*lock->read_wait.last= data;
464
	data->prev= lock->read_wait.last;
465
	lock->read_wait.last= &data->next;
466
	continue;
467
      }
468
      lock->read_no_write_count++;
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
469
    }
1 by brian
clean slate
470
    data->cond=0;				/* Mark thread free */
398.1.10 by Monty Taylor
Actually removed VOID() this time.
471
    pthread_cond_signal(cond);
1 by brian
clean slate
472
  } while ((data=data->next));
473
  *lock->read_wait.last=0;
474
  if (!lock->read_wait.data)
475
    lock->write_lock_count=0;
476
}
477
478
	/* Unlock lock and free next thread on same lock */
479
1165.1.89 by Stewart Smith
make thr_unlock() static to mysys/thr_lock.cc
480
static void thr_unlock(THR_LOCK_DATA *data)
1 by brian
clean slate
481
{
482
  THR_LOCK *lock=data->lock;
483
  enum thr_lock_type lock_type=data->type;
484
  pthread_mutex_lock(&lock->mutex);
485
486
  if (((*data->prev)=data->next))		/* remove from lock-list */
487
    data->next->prev= data->prev;
488
  else if (lock_type <= TL_READ_NO_INSERT)
489
    lock->read.last=data->prev;
490
  else
491
    lock->write.last=data->prev;
492
  if (lock_type >= TL_WRITE_CONCURRENT_INSERT)
493
  {
494
    if (lock->update_status)
495
      (*lock->update_status)(data->status_param);
496
  }
497
  else
498
  {
499
    if (lock->restore_status)
500
      (*lock->restore_status)(data->status_param);
501
  }
502
  if (lock_type == TL_READ_NO_INSERT)
503
    lock->read_no_write_count--;
504
  data->type=TL_UNLOCK;				/* Mark unlocked */
505
  wake_up_waiters(lock);
506
  pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
507
  return;
1 by brian
clean slate
508
}
509
510
511
/**
512
  @brief  Wake up all threads which pending requests for the lock
513
          can be satisfied.
514
515
  @param  lock  Lock for which threads should be woken up
516
517
*/
518
519
static void wake_up_waiters(THR_LOCK *lock)
520
{
521
  THR_LOCK_DATA *data;
522
  enum thr_lock_type lock_type;
523
524
  if (!lock->write.data)			/* If no active write locks */
525
  {
526
    data=lock->write_wait.data;
527
    if (!lock->read.data)			/* If no more locks in use */
528
    {
529
      /* Release write-locks with TL_WRITE or TL_WRITE_ONLY priority first */
530
      if (data &&
1008.1.4 by Brian Aker
Removed TL_READ_HIGH_PRIORITY
531
	  (!lock->read_wait.data || lock->read_wait.data->type <= TL_READ_WITH_SHARED_LOCKS))
1 by brian
clean slate
532
      {
533
	if (lock->write_lock_count++ > max_write_lock_count)
534
	{
535
	  /* Too many write locks in a row;  Release all waiting read locks */
536
	  lock->write_lock_count=0;
537
	  if (lock->read_wait.data)
538
	  {
539
	    free_all_read_locks(lock,0);
540
	    goto end;
541
	  }
542
	}
543
	for (;;)
544
	{
545
	  if (((*data->prev)=data->next))	/* remove from wait-list */
546
	    data->next->prev= data->prev;
547
	  else
548
	    lock->write_wait.last=data->prev;
549
	  (*lock->write.last)=data;		/* Put in execute list */
550
	  data->prev=lock->write.last;
551
	  data->next=0;
552
	  lock->write.last= &data->next;
553
	  if (data->type == TL_WRITE_CONCURRENT_INSERT &&
554
	      (*lock->check_status)(data->status_param))
555
	    data->type=TL_WRITE;			/* Upgrade lock */
556
	  {
557
	    pthread_cond_t *cond=data->cond;
558
	    data->cond=0;				/* Mark thread free */
398.1.10 by Monty Taylor
Actually removed VOID() this time.
559
	    pthread_cond_signal(cond);	/* Start waiting thread */
1 by brian
clean slate
560
	  }
561
	  if (data->type != TL_WRITE_ALLOW_WRITE ||
562
	      !lock->write_wait.data ||
563
	      lock->write_wait.data->type != TL_WRITE_ALLOW_WRITE)
564
	    break;
565
	  data=lock->write_wait.data;		/* Free this too */
566
	}
1008.1.2 by Brian Aker
Removed old DELAYED keyword from parser (plus cleaned up tests). Also
567
	if (data->type >= TL_WRITE)
1 by brian
clean slate
568
          goto end;
569
	/* Release possible read locks together with the write lock */
570
      }
571
      if (lock->read_wait.data)
572
	free_all_read_locks(lock,
573
			    data &&
574
			    (data->type == TL_WRITE_CONCURRENT_INSERT ||
575
			     data->type == TL_WRITE_ALLOW_WRITE));
576
    }
577
    else if (data &&
1008.1.1 by Brian Aker
Remove dead lock code around delayed INSERT.
578
	     (lock_type=data->type) <= TL_WRITE_CONCURRENT_INSERT &&
1 by brian
clean slate
579
	     ((lock_type != TL_WRITE_CONCURRENT_INSERT &&
580
	       lock_type != TL_WRITE_ALLOW_WRITE) ||
581
	      !lock->read_no_write_count))
582
    {
583
      /*
584
	For DELAYED, ALLOW_READ, WRITE_ALLOW_WRITE or CONCURRENT_INSERT locks
585
	start WRITE locks together with the READ locks
586
      */
587
      if (lock_type == TL_WRITE_CONCURRENT_INSERT &&
588
	  (*lock->check_status)(data->status_param))
589
      {
590
	data->type=TL_WRITE;			/* Upgrade lock */
591
	if (lock->read_wait.data)
592
	  free_all_read_locks(lock,0);
593
	goto end;
594
      }
595
      do {
596
	pthread_cond_t *cond=data->cond;
597
	if (((*data->prev)=data->next))		/* remove from wait-list */
598
	  data->next->prev= data->prev;
599
	else
600
	  lock->write_wait.last=data->prev;
601
	(*lock->write.last)=data;		/* Put in execute list */
602
	data->prev=lock->write.last;
603
	lock->write.last= &data->next;
604
	data->next=0;				/* Only one write lock */
605
	data->cond=0;				/* Mark thread free */
398.1.10 by Monty Taylor
Actually removed VOID() this time.
606
	pthread_cond_signal(cond);	/* Start waiting thread */
1 by brian
clean slate
607
      } while (lock_type == TL_WRITE_ALLOW_WRITE &&
608
	       (data=lock->write_wait.data) &&
609
	       data->type == TL_WRITE_ALLOW_WRITE);
610
      if (lock->read_wait.data)
611
	free_all_read_locks(lock,
612
			    (lock_type == TL_WRITE_CONCURRENT_INSERT ||
613
			     lock_type == TL_WRITE_ALLOW_WRITE));
614
    }
615
    else if (!data && lock->read_wait.data)
616
      free_all_read_locks(lock,0);
617
  }
618
end:
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
619
  return;
1 by brian
clean slate
620
}
621
622
623
/*
624
** Get all locks in a specific order to avoid dead-locks
625
** Sort acording to lock position and put write_locks before read_locks if
626
** lock on same lock.
627
*/
628
629
895 by Brian Aker
Completion (?) of uint conversion.
630
#define LOCK_CMP(A,B) ((unsigned char*) (A->lock) - (uint32_t) ((A)->type) < (unsigned char*) (B->lock)- (uint32_t) ((B)->type))
1 by brian
clean slate
631
482 by Brian Aker
Remove uint.
632
static void sort_locks(THR_LOCK_DATA **data,uint32_t count)
1 by brian
clean slate
633
{
634
  THR_LOCK_DATA **pos,**end,**prev,*tmp;
635
636
  /* Sort locks with insertion sort (fast because almost always few locks) */
637
638
  for (pos=data+1,end=data+count; pos < end ; pos++)
639
  {
640
    tmp= *pos;
641
    if (LOCK_CMP(tmp,pos[-1]))
642
    {
643
      prev=pos;
644
      do {
645
	prev[0]=prev[-1];
646
      } while (--prev != data && LOCK_CMP(tmp,prev[-1]));
647
      prev[0]=tmp;
648
    }
649
  }
650
}
651
652
653
enum enum_thr_lock_result
482 by Brian Aker
Remove uint.
654
thr_multi_lock(THR_LOCK_DATA **data, uint32_t count, THR_LOCK_OWNER *owner)
1 by brian
clean slate
655
{
656
  THR_LOCK_DATA **pos,**end;
657
  if (count > 1)
658
    sort_locks(data,count);
659
  /* lock everything */
660
  for (pos=data,end=data+count; pos < end ; pos++)
661
  {
662
    enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type);
663
    if (result != THR_LOCK_SUCCESS)
664
    {						/* Aborted */
895 by Brian Aker
Completion (?) of uint conversion.
665
      thr_multi_unlock(data,(uint32_t) (pos-data));
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
666
      return(result);
1 by brian
clean slate
667
    }
668
  }
669
  /*
670
    Ensure that all get_locks() have the same status
671
    If we lock the same table multiple times, we must use the same
672
    status_param!
673
  */
674
#if !defined(DONT_USE_RW_LOCKS)
675
  if (count > 1)
676
  {
677
    THR_LOCK_DATA *last_lock= end[-1];
678
    pos=end-1;
679
    do
680
    {
681
      pos--;
682
      if (last_lock->lock == (*pos)->lock &&
683
	  last_lock->lock->copy_status)
684
      {
685
	if (last_lock->type <= TL_READ_NO_INSERT)
686
	{
687
	  THR_LOCK_DATA **read_lock;
688
	  /*
689
	    If we are locking the same table with read locks we must ensure
690
	    that all tables share the status of the last write lock or
691
	    the same read lock.
692
	  */
693
	  for (;
694
	       (*pos)->type <= TL_READ_NO_INSERT &&
695
		 pos != data &&
696
		 pos[-1]->lock == (*pos)->lock ;
697
	       pos--) ;
698
699
	  read_lock = pos+1;
700
	  do
701
	  {
702
	    (last_lock->lock->copy_status)((*read_lock)->status_param,
703
					   (*pos)->status_param);
704
	  } while (*(read_lock++) != last_lock);
705
	  last_lock= (*pos);			/* Point at last write lock */
706
	}
707
	else
708
	  (*last_lock->lock->copy_status)((*pos)->status_param,
709
					  last_lock->status_param);
710
      }
711
      else
712
	last_lock=(*pos);
713
    } while (pos != data);
714
  }
715
#endif
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
716
  return(THR_LOCK_SUCCESS);
1 by brian
clean slate
717
}
718
719
  /* free all locks */
720
482 by Brian Aker
Remove uint.
721
void thr_multi_unlock(THR_LOCK_DATA **data,uint32_t count)
1 by brian
clean slate
722
{
723
  THR_LOCK_DATA **pos,**end;
724
725
  for (pos=data,end=data+count; pos < end ; pos++)
726
  {
727
    if ((*pos)->type != TL_UNLOCK)
728
      thr_unlock(*pos);
729
  }
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
730
  return;
1 by brian
clean slate
731
}
732
733
/*
734
  Abort all threads waiting for a lock. The lock will be upgraded to
735
  TL_WRITE_ONLY to abort any new accesses to the lock
736
*/
737
1113.1.2 by Brian Aker
Refactor/kill some dead lock code.
738
void thr_abort_locks(THR_LOCK *lock)
1 by brian
clean slate
739
{
740
  THR_LOCK_DATA *data;
741
  pthread_mutex_lock(&lock->mutex);
742
743
  for (data=lock->read_wait.data; data ; data=data->next)
744
  {
1054.1.4 by Brian Aker
Formatting.
745
    data->type= TL_UNLOCK;			/* Mark killed */
1 by brian
clean slate
746
    /* It's safe to signal the cond first: we're still holding the mutex. */
747
    pthread_cond_signal(data->cond);
1054.1.4 by Brian Aker
Formatting.
748
    data->cond= NULL;				/* Removed from list */
1 by brian
clean slate
749
  }
750
  for (data=lock->write_wait.data; data ; data=data->next)
751
  {
752
    data->type=TL_UNLOCK;
753
    pthread_cond_signal(data->cond);
1054.1.4 by Brian Aker
Formatting.
754
    data->cond= NULL;
1 by brian
clean slate
755
  }
756
  lock->read_wait.last= &lock->read_wait.data;
757
  lock->write_wait.last= &lock->write_wait.data;
758
  lock->read_wait.data=lock->write_wait.data=0;
1113.1.2 by Brian Aker
Refactor/kill some dead lock code.
759
  if (lock->write.data)
1 by brian
clean slate
760
    lock->write.data->type=TL_WRITE_ONLY;
761
  pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
762
  return;
1 by brian
clean slate
763
}
764
765
766
/*
767
  Abort all locks for specific table/thread combination
768
769
  This is used to abort all locks for a specific thread
770
*/
771
146 by Brian Aker
my_bool cleanup.
772
bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread_id)
1 by brian
clean slate
773
{
774
  THR_LOCK_DATA *data;
163 by Brian Aker
Merge Monty's code.
775
  bool found= false;
1 by brian
clean slate
776
777
  pthread_mutex_lock(&lock->mutex);
778
  for (data= lock->read_wait.data; data ; data= data->next)
779
  {
971.6.11 by Eric Day
Removed purecov messages.
780
    if (data->owner->info->thread_id == thread_id)
1 by brian
clean slate
781
    {
782
      data->type= TL_UNLOCK;			/* Mark killed */
783
      /* It's safe to signal the cond first: we're still holding the mutex. */
163 by Brian Aker
Merge Monty's code.
784
      found= true;
1 by brian
clean slate
785
      pthread_cond_signal(data->cond);
786
      data->cond= 0;				/* Removed from list */
787
788
      if (((*data->prev)= data->next))
789
	data->next->prev= data->prev;
790
      else
791
	lock->read_wait.last= data->prev;
792
    }
793
  }
794
  for (data= lock->write_wait.data; data ; data= data->next)
795
  {
971.6.11 by Eric Day
Removed purecov messages.
796
    if (data->owner->info->thread_id == thread_id)
1 by brian
clean slate
797
    {
798
      data->type= TL_UNLOCK;
163 by Brian Aker
Merge Monty's code.
799
      found= true;
1 by brian
clean slate
800
      pthread_cond_signal(data->cond);
1054.1.4 by Brian Aker
Formatting.
801
      data->cond= NULL;
1 by brian
clean slate
802
803
      if (((*data->prev)= data->next))
804
	data->next->prev= data->prev;
805
      else
806
	lock->write_wait.last= data->prev;
807
    }
808
  }
809
  wake_up_waiters(lock);
810
  pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
811
  return(found);
1 by brian
clean slate
812
}