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