~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
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
88
#endif
481.1.15 by Monty Taylor
Removed time.h and sys/time.h from global.h.
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;
622.1.1 by Brian Aker
32bit fixes around vars
94
uint64_t table_lock_wait_timeout;
1 by brian
clean slate
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 */
622.1.1 by Brian Aker
32bit fixes around vars
99
uint64_t max_write_lock_count= ~(uint64_t) 0L;
1 by brian
clean slate
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
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 */
135
  lock->list.data=(void*) lock;
136
  thr_lock_thread_list=list_add(thr_lock_thread_list,&lock->list);
137
  pthread_mutex_unlock(&THR_LOCK_lock);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
138
  return;
1 by brian
clean slate
139
}
140
141
142
void thr_lock_delete(THR_LOCK *lock)
143
{
398.1.10 by Monty Taylor
Actually removed VOID() this time.
144
  pthread_mutex_destroy(&lock->mutex);
1 by brian
clean slate
145
  pthread_mutex_lock(&THR_LOCK_lock);
146
  thr_lock_thread_list=list_delete(thr_lock_thread_list,&lock->list);
147
  pthread_mutex_unlock(&THR_LOCK_lock);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
148
  return;
1 by brian
clean slate
149
}
150
151
152
void thr_lock_info_init(THR_LOCK_INFO *info)
153
{
154
  struct st_my_thread_var *tmp= my_thread_var;
155
  info->thread=    tmp->pthread_self;
156
  info->thread_id= tmp->id;
157
  info->n_cursors= 0;
158
}
159
160
	/* Initialize a lock instance */
161
162
void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param)
163
{
164
  data->lock=lock;
165
  data->type=TL_UNLOCK;
166
  data->owner= 0;                               /* no owner yet */
167
  data->status_param=param;
168
  data->cond=0;
169
}
170
171
146 by Brian Aker
my_bool cleanup.
172
static inline bool
1 by brian
clean slate
173
have_old_read_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner)
174
{
175
  for ( ; data ; data=data->next)
176
  {
177
    if (thr_lock_owner_equal(data->owner, owner))
178
      return 1;					/* Already locked by thread */
179
  }
180
  return 0;
181
}
182
146 by Brian Aker
my_bool cleanup.
183
static inline bool have_specific_lock(THR_LOCK_DATA *data,
1 by brian
clean slate
184
					 enum thr_lock_type type)
185
{
186
  for ( ; data ; data=data->next)
187
  {
188
    if (data->type == type)
189
      return 1;
190
  }
191
  return 0;
192
}
193
194
195
static void wake_up_waiters(THR_LOCK *lock);
196
197
198
static enum enum_thr_lock_result
199
wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
146 by Brian Aker
my_bool cleanup.
200
              bool in_wait_list)
1 by brian
clean slate
201
{
202
  struct st_my_thread_var *thread_var= my_thread_var;
203
  pthread_cond_t *cond= &thread_var->suspend;
204
  struct timespec wait_timeout;
205
  enum enum_thr_lock_result result= THR_LOCK_ABORTED;
146 by Brian Aker
my_bool cleanup.
206
  bool can_deadlock= test(data->owner->info->n_cursors);
1 by brian
clean slate
207
208
  if (!in_wait_list)
209
  {
210
    (*wait->last)=data;				/* Wait for lock */
211
    data->prev= wait->last;
212
    wait->last= &data->next;
213
  }
214
215
  statistic_increment(locks_waited, &THR_LOCK_lock);
216
217
  /* Set up control struct to allow others to abort locks */
218
  thread_var->current_mutex= &data->lock->mutex;
219
  thread_var->current_cond=  cond;
220
  data->cond= cond;
221
222
  if (can_deadlock)
223
    set_timespec(wait_timeout, table_lock_wait_timeout);
224
  while (!thread_var->abort || in_wait_list)
225
  {
226
    int rc= (can_deadlock ?
227
             pthread_cond_timedwait(cond, &data->lock->mutex,
228
                                    &wait_timeout) :
229
             pthread_cond_wait(cond, &data->lock->mutex));
230
    /*
231
      We must break the wait if one of the following occurs:
232
      - the connection has been aborted (!thread_var->abort), but
233
        this is not a delayed insert thread (in_wait_list). For a delayed
234
        insert thread the proper action at shutdown is, apparently, to
235
        acquire the lock and complete the insert.
236
      - the lock has been granted (data->cond is set to NULL by the granter),
237
        or the waiting has been aborted (additionally data->type is set to
238
        TL_UNLOCK).
239
      - the wait has timed out (rc == ETIMEDOUT)
240
      Order of checks below is important to not report about timeout
241
      if the predicate is true.
242
    */
243
    if (data->cond == 0)
244
    {
245
      break;
246
    }
247
    if (rc == ETIMEDOUT || rc == ETIME)
248
    {
249
      /* purecov: begin inspected */
250
      result= THR_LOCK_WAIT_TIMEOUT;
251
      break;
252
      /* purecov: end */
253
    }
254
  }
255
  if (data->cond || data->type == TL_UNLOCK)
256
  {
257
    if (data->cond)                             /* aborted or timed out */
258
    {
259
      if (((*data->prev)=data->next))		/* remove from wait-list */
260
	data->next->prev= data->prev;
261
      else
262
	wait->last=data->prev;
263
      data->type= TL_UNLOCK;                    /* No lock */
264
      wake_up_waiters(data->lock);
265
    }
266
  }
267
  else
268
  {
269
    result= THR_LOCK_SUCCESS;
270
    if (data->lock->get_status)
271
      (*data->lock->get_status)(data->status_param, 0);
272
  }
273
  pthread_mutex_unlock(&data->lock->mutex);
274
275
  /* The following must be done after unlock of lock->mutex */
276
  pthread_mutex_lock(&thread_var->mutex);
277
  thread_var->current_mutex= 0;
278
  thread_var->current_cond=  0;
279
  pthread_mutex_unlock(&thread_var->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
280
  return(result);
1 by brian
clean slate
281
}
282
283
284
enum enum_thr_lock_result
285
thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
286
         enum thr_lock_type lock_type)
287
{
288
  THR_LOCK *lock=data->lock;
289
  enum enum_thr_lock_result result= THR_LOCK_SUCCESS;
290
  struct st_lock_list *wait_queue;
291
  THR_LOCK_DATA *lock_owner;
292
293
  data->next=0;
294
  data->cond=0;					/* safety */
295
  data->type=lock_type;
296
  data->owner= owner;                           /* Must be reset ! */
398.1.10 by Monty Taylor
Actually removed VOID() this time.
297
  pthread_mutex_lock(&lock->mutex);
1 by brian
clean slate
298
  if ((int) lock_type <= (int) TL_READ_NO_INSERT)
299
  {
300
    /* Request for READ lock */
301
    if (lock->write.data)
302
    {
303
      /* We can allow a read lock even if there is already a write lock
304
	 on the table in one the following cases:
305
	 - This thread alread have a write lock on the table
306
	 - The write lock is TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED
307
           and the read lock is TL_READ_HIGH_PRIORITY or TL_READ
308
         - The write lock is TL_WRITE_CONCURRENT_INSERT or TL_WRITE_ALLOW_WRITE
309
	   and the read lock is not TL_READ_NO_INSERT
310
      */
311
312
      if (thr_lock_owner_equal(data->owner, lock->write.data->owner) ||
313
	  (lock->write.data->type <= TL_WRITE_DELAYED &&
314
	   (((int) lock_type <= (int) TL_READ_HIGH_PRIORITY) ||
315
	    (lock->write.data->type != TL_WRITE_CONCURRENT_INSERT &&
316
	     lock->write.data->type != TL_WRITE_ALLOW_READ))))
317
      {						/* Already got a write lock */
318
	(*lock->read.last)=data;		/* Add to running FIFO */
319
	data->prev=lock->read.last;
320
	lock->read.last= &data->next;
321
	if (lock_type == TL_READ_NO_INSERT)
322
	  lock->read_no_write_count++;
323
	if (lock->get_status)
324
	  (*lock->get_status)(data->status_param, 0);
325
	statistic_increment(locks_immediate,&THR_LOCK_lock);
326
	goto end;
327
      }
328
      if (lock->write.data->type == TL_WRITE_ONLY)
329
      {
330
	/* We are not allowed to get a READ lock in this case */
331
	data->type=TL_UNLOCK;
332
        result= THR_LOCK_ABORTED;               /* Can't wait for this one */
333
	goto end;
334
      }
335
    }
336
    else if (!lock->write_wait.data ||
337
	     lock->write_wait.data->type <= TL_WRITE_LOW_PRIORITY ||
338
	     lock_type == TL_READ_HIGH_PRIORITY ||
339
	     have_old_read_lock(lock->read.data, data->owner))
340
    {						/* No important write-locks */
341
      (*lock->read.last)=data;			/* Add to running FIFO */
342
      data->prev=lock->read.last;
343
      lock->read.last= &data->next;
344
      if (lock->get_status)
345
	(*lock->get_status)(data->status_param, 0);
346
      if (lock_type == TL_READ_NO_INSERT)
347
	lock->read_no_write_count++;
348
      statistic_increment(locks_immediate,&THR_LOCK_lock);
349
      goto end;
350
    }
351
    /*
352
      We're here if there is an active write lock or no write
353
      lock but a high priority write waiting in the write_wait queue.
354
      In the latter case we should yield the lock to the writer.
355
    */
356
    wait_queue= &lock->read_wait;
357
  }
358
  else						/* Request for WRITE lock */
359
  {
360
    if (lock_type == TL_WRITE_DELAYED)
361
    {
362
      if (lock->write.data && lock->write.data->type == TL_WRITE_ONLY)
363
      {
364
	data->type=TL_UNLOCK;
365
        result= THR_LOCK_ABORTED;               /* Can't wait for this one */
366
	goto end;
367
      }
368
      /*
369
	if there is a TL_WRITE_ALLOW_READ lock, we have to wait for a lock
370
	(TL_WRITE_ALLOW_READ is used for ALTER TABLE in MySQL)
371
      */
372
      if ((!lock->write.data ||
373
	   lock->write.data->type != TL_WRITE_ALLOW_READ) &&
374
	  !have_specific_lock(lock->write_wait.data,TL_WRITE_ALLOW_READ) &&
375
	  (lock->write.data || lock->read.data))
376
      {
377
	/* Add delayed write lock to write_wait queue, and return at once */
378
	(*lock->write_wait.last)=data;
379
	data->prev=lock->write_wait.last;
380
	lock->write_wait.last= &data->next;
381
	data->cond=get_cond();
382
        /*
383
          We don't have to do get_status here as we will do it when we change
384
          the delayed lock to a real write lock
385
        */
386
	statistic_increment(locks_immediate,&THR_LOCK_lock);
387
	goto end;
388
      }
389
    }
390
    else if (lock_type == TL_WRITE_CONCURRENT_INSERT && ! lock->check_status)
391
      data->type=lock_type= thr_upgraded_concurrent_insert_lock;
392
393
    if (lock->write.data)			/* If there is a write lock */
394
    {
395
      if (lock->write.data->type == TL_WRITE_ONLY)
396
      {
397
        /* Allow lock owner to bypass TL_WRITE_ONLY. */
398
        if (!thr_lock_owner_equal(data->owner, lock->write.data->owner))
399
        {
400
          /* We are not allowed to get a lock in this case */
401
          data->type=TL_UNLOCK;
402
          result= THR_LOCK_ABORTED;               /* Can't wait for this one */
403
          goto end;
404
        }
405
      }
406
407
      /*
408
	The following test will not work if the old lock was a
409
	TL_WRITE_ALLOW_WRITE, TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED in
410
	the same thread, but this will never happen within MySQL.
411
      */
412
      if (thr_lock_owner_equal(data->owner, lock->write.data->owner) ||
413
	  (lock_type == TL_WRITE_ALLOW_WRITE &&
414
	   !lock->write_wait.data &&
415
	   lock->write.data->type == TL_WRITE_ALLOW_WRITE))
416
      {
417
	/*
418
          We have already got a write lock or all locks are
419
          TL_WRITE_ALLOW_WRITE
420
        */
421
422
	(*lock->write.last)=data;	/* Add to running fifo */
423
	data->prev=lock->write.last;
424
	lock->write.last= &data->next;
425
	if (data->lock->get_status)
426
	  (*data->lock->get_status)(data->status_param, 0);
427
	statistic_increment(locks_immediate,&THR_LOCK_lock);
428
	goto end;
429
      }
430
    }
431
    else
432
    {
433
      if (!lock->write_wait.data)
434
      {						/* no scheduled write locks */
146 by Brian Aker
my_bool cleanup.
435
        bool concurrent_insert= 0;
1 by brian
clean slate
436
	if (lock_type == TL_WRITE_CONCURRENT_INSERT)
437
        {
438
          concurrent_insert= 1;
439
          if ((*lock->check_status)(data->status_param))
440
          {
441
            concurrent_insert= 0;
442
            data->type=lock_type= thr_upgraded_concurrent_insert_lock;
443
          }
444
        }
445
446
	if (!lock->read.data ||
447
	    (lock_type <= TL_WRITE_DELAYED &&
448
	     ((lock_type != TL_WRITE_CONCURRENT_INSERT &&
449
	       lock_type != TL_WRITE_ALLOW_WRITE) ||
450
	      !lock->read_no_write_count)))
451
	{
452
	  (*lock->write.last)=data;		/* Add as current write lock */
453
	  data->prev=lock->write.last;
454
	  lock->write.last= &data->next;
455
	  if (data->lock->get_status)
456
	    (*data->lock->get_status)(data->status_param, concurrent_insert);
457
	  statistic_increment(locks_immediate,&THR_LOCK_lock);
458
	  goto end;
459
	}
460
      }
461
    }
462
    wait_queue= &lock->write_wait;
463
  }
464
  /*
465
    Try to detect a trivial deadlock when using cursors: attempt to
466
    lock a table that is already locked by an open cursor within the
467
    same connection. lock_owner can be zero if we succumbed to a high
468
    priority writer in the write_wait queue.
469
  */
470
  lock_owner= lock->read.data ? lock->read.data : lock->write.data;
471
  if (lock_owner && lock_owner->owner->info == owner->info)
472
  {
473
    result= THR_LOCK_DEADLOCK;
474
    goto end;
475
  }
476
  /* Can't get lock yet;  Wait for it */
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
477
  return(wait_for_lock(wait_queue, data, 0));
1 by brian
clean slate
478
end:
479
  pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
480
  return(result);
1 by brian
clean slate
481
}
482
483
484
static inline void free_all_read_locks(THR_LOCK *lock,
146 by Brian Aker
my_bool cleanup.
485
				       bool using_concurrent_insert)
1 by brian
clean slate
486
{
487
  THR_LOCK_DATA *data=lock->read_wait.data;
488
489
  /* move all locks from read_wait list to read list */
490
  (*lock->read.last)=data;
491
  data->prev=lock->read.last;
492
  lock->read.last=lock->read_wait.last;
493
494
  /* Clear read_wait list */
495
  lock->read_wait.last= &lock->read_wait.data;
496
497
  do
498
  {
499
    pthread_cond_t *cond=data->cond;
500
    if ((int) data->type == (int) TL_READ_NO_INSERT)
501
    {
502
      if (using_concurrent_insert)
503
      {
504
	/*
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
505
	  We can't free this lock;
1 by brian
clean slate
506
	  Link lock away from read chain back into read_wait chain
507
	*/
508
	if (((*data->prev)=data->next))
509
	  data->next->prev=data->prev;
510
	else
511
	  lock->read.last=data->prev;
512
	*lock->read_wait.last= data;
513
	data->prev= lock->read_wait.last;
514
	lock->read_wait.last= &data->next;
515
	continue;
516
      }
517
      lock->read_no_write_count++;
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
518
    }
1 by brian
clean slate
519
    data->cond=0;				/* Mark thread free */
398.1.10 by Monty Taylor
Actually removed VOID() this time.
520
    pthread_cond_signal(cond);
1 by brian
clean slate
521
  } while ((data=data->next));
522
  *lock->read_wait.last=0;
523
  if (!lock->read_wait.data)
524
    lock->write_lock_count=0;
525
}
526
527
	/* Unlock lock and free next thread on same lock */
528
529
void thr_unlock(THR_LOCK_DATA *data)
530
{
531
  THR_LOCK *lock=data->lock;
532
  enum thr_lock_type lock_type=data->type;
533
  pthread_mutex_lock(&lock->mutex);
534
535
  if (((*data->prev)=data->next))		/* remove from lock-list */
536
    data->next->prev= data->prev;
537
  else if (lock_type <= TL_READ_NO_INSERT)
538
    lock->read.last=data->prev;
539
  else if (lock_type == TL_WRITE_DELAYED && data->cond)
540
  {
541
    /*
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
542
      This only happens in extreme circumstances when a
1 by brian
clean slate
543
      write delayed lock that is waiting for a lock
544
    */
545
    lock->write_wait.last=data->prev;		/* Put it on wait queue */
546
  }
547
  else
548
    lock->write.last=data->prev;
549
  if (lock_type >= TL_WRITE_CONCURRENT_INSERT)
550
  {
551
    if (lock->update_status)
552
      (*lock->update_status)(data->status_param);
553
  }
554
  else
555
  {
556
    if (lock->restore_status)
557
      (*lock->restore_status)(data->status_param);
558
  }
559
  if (lock_type == TL_READ_NO_INSERT)
560
    lock->read_no_write_count--;
561
  data->type=TL_UNLOCK;				/* Mark unlocked */
562
  wake_up_waiters(lock);
563
  pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
564
  return;
1 by brian
clean slate
565
}
566
567
568
/**
569
  @brief  Wake up all threads which pending requests for the lock
570
          can be satisfied.
571
572
  @param  lock  Lock for which threads should be woken up
573
574
*/
575
576
static void wake_up_waiters(THR_LOCK *lock)
577
{
578
  THR_LOCK_DATA *data;
579
  enum thr_lock_type lock_type;
580
581
  if (!lock->write.data)			/* If no active write locks */
582
  {
583
    data=lock->write_wait.data;
584
    if (!lock->read.data)			/* If no more locks in use */
585
    {
586
      /* Release write-locks with TL_WRITE or TL_WRITE_ONLY priority first */
587
      if (data &&
588
	  (data->type != TL_WRITE_LOW_PRIORITY || !lock->read_wait.data ||
589
	   lock->read_wait.data->type < TL_READ_HIGH_PRIORITY))
590
      {
591
	if (lock->write_lock_count++ > max_write_lock_count)
592
	{
593
	  /* Too many write locks in a row;  Release all waiting read locks */
594
	  lock->write_lock_count=0;
595
	  if (lock->read_wait.data)
596
	  {
597
	    free_all_read_locks(lock,0);
598
	    goto end;
599
	  }
600
	}
601
	for (;;)
602
	{
603
	  if (((*data->prev)=data->next))	/* remove from wait-list */
604
	    data->next->prev= data->prev;
605
	  else
606
	    lock->write_wait.last=data->prev;
607
	  (*lock->write.last)=data;		/* Put in execute list */
608
	  data->prev=lock->write.last;
609
	  data->next=0;
610
	  lock->write.last= &data->next;
611
	  if (data->type == TL_WRITE_CONCURRENT_INSERT &&
612
	      (*lock->check_status)(data->status_param))
613
	    data->type=TL_WRITE;			/* Upgrade lock */
614
	  {
615
	    pthread_cond_t *cond=data->cond;
616
	    data->cond=0;				/* Mark thread free */
398.1.10 by Monty Taylor
Actually removed VOID() this time.
617
	    pthread_cond_signal(cond);	/* Start waiting thread */
1 by brian
clean slate
618
	  }
619
	  if (data->type != TL_WRITE_ALLOW_WRITE ||
620
	      !lock->write_wait.data ||
621
	      lock->write_wait.data->type != TL_WRITE_ALLOW_WRITE)
622
	    break;
623
	  data=lock->write_wait.data;		/* Free this too */
624
	}
625
	if (data->type >= TL_WRITE_LOW_PRIORITY)
626
          goto end;
627
	/* Release possible read locks together with the write lock */
628
      }
629
      if (lock->read_wait.data)
630
	free_all_read_locks(lock,
631
			    data &&
632
			    (data->type == TL_WRITE_CONCURRENT_INSERT ||
633
			     data->type == TL_WRITE_ALLOW_WRITE));
634
    }
635
    else if (data &&
636
	     (lock_type=data->type) <= TL_WRITE_DELAYED &&
637
	     ((lock_type != TL_WRITE_CONCURRENT_INSERT &&
638
	       lock_type != TL_WRITE_ALLOW_WRITE) ||
639
	      !lock->read_no_write_count))
640
    {
641
      /*
642
	For DELAYED, ALLOW_READ, WRITE_ALLOW_WRITE or CONCURRENT_INSERT locks
643
	start WRITE locks together with the READ locks
644
      */
645
      if (lock_type == TL_WRITE_CONCURRENT_INSERT &&
646
	  (*lock->check_status)(data->status_param))
647
      {
648
	data->type=TL_WRITE;			/* Upgrade lock */
649
	if (lock->read_wait.data)
650
	  free_all_read_locks(lock,0);
651
	goto end;
652
      }
653
      do {
654
	pthread_cond_t *cond=data->cond;
655
	if (((*data->prev)=data->next))		/* remove from wait-list */
656
	  data->next->prev= data->prev;
657
	else
658
	  lock->write_wait.last=data->prev;
659
	(*lock->write.last)=data;		/* Put in execute list */
660
	data->prev=lock->write.last;
661
	lock->write.last= &data->next;
662
	data->next=0;				/* Only one write lock */
663
	data->cond=0;				/* Mark thread free */
398.1.10 by Monty Taylor
Actually removed VOID() this time.
664
	pthread_cond_signal(cond);	/* Start waiting thread */
1 by brian
clean slate
665
      } while (lock_type == TL_WRITE_ALLOW_WRITE &&
666
	       (data=lock->write_wait.data) &&
667
	       data->type == TL_WRITE_ALLOW_WRITE);
668
      if (lock->read_wait.data)
669
	free_all_read_locks(lock,
670
			    (lock_type == TL_WRITE_CONCURRENT_INSERT ||
671
			     lock_type == TL_WRITE_ALLOW_WRITE));
672
    }
673
    else if (!data && lock->read_wait.data)
674
      free_all_read_locks(lock,0);
675
  }
676
end:
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
677
  return;
1 by brian
clean slate
678
}
679
680
681
/*
682
** Get all locks in a specific order to avoid dead-locks
683
** Sort acording to lock position and put write_locks before read_locks if
684
** lock on same lock.
685
*/
686
687
895 by Brian Aker
Completion (?) of uint conversion.
688
#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
689
482 by Brian Aker
Remove uint.
690
static void sort_locks(THR_LOCK_DATA **data,uint32_t count)
1 by brian
clean slate
691
{
692
  THR_LOCK_DATA **pos,**end,**prev,*tmp;
693
694
  /* Sort locks with insertion sort (fast because almost always few locks) */
695
696
  for (pos=data+1,end=data+count; pos < end ; pos++)
697
  {
698
    tmp= *pos;
699
    if (LOCK_CMP(tmp,pos[-1]))
700
    {
701
      prev=pos;
702
      do {
703
	prev[0]=prev[-1];
704
      } while (--prev != data && LOCK_CMP(tmp,prev[-1]));
705
      prev[0]=tmp;
706
    }
707
  }
708
}
709
710
711
enum enum_thr_lock_result
482 by Brian Aker
Remove uint.
712
thr_multi_lock(THR_LOCK_DATA **data, uint32_t count, THR_LOCK_OWNER *owner)
1 by brian
clean slate
713
{
714
  THR_LOCK_DATA **pos,**end;
715
  if (count > 1)
716
    sort_locks(data,count);
717
  /* lock everything */
718
  for (pos=data,end=data+count; pos < end ; pos++)
719
  {
720
    enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type);
721
    if (result != THR_LOCK_SUCCESS)
722
    {						/* Aborted */
895 by Brian Aker
Completion (?) of uint conversion.
723
      thr_multi_unlock(data,(uint32_t) (pos-data));
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
724
      return(result);
1 by brian
clean slate
725
    }
726
  }
727
  /*
728
    Ensure that all get_locks() have the same status
729
    If we lock the same table multiple times, we must use the same
730
    status_param!
731
  */
732
#if !defined(DONT_USE_RW_LOCKS)
733
  if (count > 1)
734
  {
735
    THR_LOCK_DATA *last_lock= end[-1];
736
    pos=end-1;
737
    do
738
    {
739
      pos--;
740
      if (last_lock->lock == (*pos)->lock &&
741
	  last_lock->lock->copy_status)
742
      {
743
	if (last_lock->type <= TL_READ_NO_INSERT)
744
	{
745
	  THR_LOCK_DATA **read_lock;
746
	  /*
747
	    If we are locking the same table with read locks we must ensure
748
	    that all tables share the status of the last write lock or
749
	    the same read lock.
750
	  */
751
	  for (;
752
	       (*pos)->type <= TL_READ_NO_INSERT &&
753
		 pos != data &&
754
		 pos[-1]->lock == (*pos)->lock ;
755
	       pos--) ;
756
757
	  read_lock = pos+1;
758
	  do
759
	  {
760
	    (last_lock->lock->copy_status)((*read_lock)->status_param,
761
					   (*pos)->status_param);
762
	  } while (*(read_lock++) != last_lock);
763
	  last_lock= (*pos);			/* Point at last write lock */
764
	}
765
	else
766
	  (*last_lock->lock->copy_status)((*pos)->status_param,
767
					  last_lock->status_param);
768
      }
769
      else
770
	last_lock=(*pos);
771
    } while (pos != data);
772
  }
773
#endif
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
774
  return(THR_LOCK_SUCCESS);
1 by brian
clean slate
775
}
776
777
  /* free all locks */
778
482 by Brian Aker
Remove uint.
779
void thr_multi_unlock(THR_LOCK_DATA **data,uint32_t count)
1 by brian
clean slate
780
{
781
  THR_LOCK_DATA **pos,**end;
782
783
  for (pos=data,end=data+count; pos < end ; pos++)
784
  {
785
    if ((*pos)->type != TL_UNLOCK)
786
      thr_unlock(*pos);
787
  }
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
788
  return;
1 by brian
clean slate
789
}
790
791
/*
792
  Abort all threads waiting for a lock. The lock will be upgraded to
793
  TL_WRITE_ONLY to abort any new accesses to the lock
794
*/
795
146 by Brian Aker
my_bool cleanup.
796
void thr_abort_locks(THR_LOCK *lock, bool upgrade_lock)
1 by brian
clean slate
797
{
798
  THR_LOCK_DATA *data;
799
  pthread_mutex_lock(&lock->mutex);
800
801
  for (data=lock->read_wait.data; data ; data=data->next)
802
  {
803
    data->type=TL_UNLOCK;			/* Mark killed */
804
    /* It's safe to signal the cond first: we're still holding the mutex. */
805
    pthread_cond_signal(data->cond);
806
    data->cond=0;				/* Removed from list */
807
  }
808
  for (data=lock->write_wait.data; data ; data=data->next)
809
  {
810
    data->type=TL_UNLOCK;
811
    pthread_cond_signal(data->cond);
812
    data->cond=0;
813
  }
814
  lock->read_wait.last= &lock->read_wait.data;
815
  lock->write_wait.last= &lock->write_wait.data;
816
  lock->read_wait.data=lock->write_wait.data=0;
817
  if (upgrade_lock && lock->write.data)
818
    lock->write.data->type=TL_WRITE_ONLY;
819
  pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
820
  return;
1 by brian
clean slate
821
}
822
823
824
/*
825
  Abort all locks for specific table/thread combination
826
827
  This is used to abort all locks for a specific thread
828
*/
829
146 by Brian Aker
my_bool cleanup.
830
bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread_id)
1 by brian
clean slate
831
{
832
  THR_LOCK_DATA *data;
163 by Brian Aker
Merge Monty's code.
833
  bool found= false;
1 by brian
clean slate
834
835
  pthread_mutex_lock(&lock->mutex);
836
  for (data= lock->read_wait.data; data ; data= data->next)
837
  {
838
    if (data->owner->info->thread_id == thread_id)    /* purecov: tested */
839
    {
840
      data->type= TL_UNLOCK;			/* Mark killed */
841
      /* It's safe to signal the cond first: we're still holding the mutex. */
163 by Brian Aker
Merge Monty's code.
842
      found= true;
1 by brian
clean slate
843
      pthread_cond_signal(data->cond);
844
      data->cond= 0;				/* Removed from list */
845
846
      if (((*data->prev)= data->next))
847
	data->next->prev= data->prev;
848
      else
849
	lock->read_wait.last= data->prev;
850
    }
851
  }
852
  for (data= lock->write_wait.data; data ; data= data->next)
853
  {
854
    if (data->owner->info->thread_id == thread_id) /* purecov: tested */
855
    {
856
      data->type= TL_UNLOCK;
163 by Brian Aker
Merge Monty's code.
857
      found= true;
1 by brian
clean slate
858
      pthread_cond_signal(data->cond);
859
      data->cond= 0;
860
861
      if (((*data->prev)= data->next))
862
	data->next->prev= data->prev;
863
      else
864
	lock->write_wait.last= data->prev;
865
    }
866
  }
867
  wake_up_waiters(lock);
868
  pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
869
  return(found);
1 by brian
clean slate
870
}
871
872
873
/*
874
  Downgrade a WRITE_* to a lower WRITE level
875
  SYNOPSIS
876
    thr_downgrade_write_lock()
877
    in_data                   Lock data of thread downgrading its lock
878
    new_lock_type             New write lock type
879
  RETURN VALUE
880
    NONE
881
  DESCRIPTION
882
    This can be used to downgrade a lock already owned. When the downgrade
883
    occurs also other waiters, both readers and writers can be allowed to
884
    start.
885
    The previous lock is often TL_WRITE_ONLY but can also be
886
    TL_WRITE and TL_WRITE_ALLOW_READ. The normal downgrade variants are
887
    TL_WRITE_ONLY => TL_WRITE_ALLOW_READ After a short exclusive lock
888
    TL_WRITE_ALLOW_READ => TL_WRITE_ALLOW_WRITE After discovering that the
889
    operation didn't need such a high lock.
890
    TL_WRITE_ONLY => TL_WRITE after a short exclusive lock while holding a
891
    write table lock
892
    TL_WRITE_ONLY => TL_WRITE_ALLOW_WRITE After a short exclusive lock after
893
    already earlier having dongraded lock to TL_WRITE_ALLOW_WRITE
894
    The implementation is conservative and rather don't start rather than
895
    go on unknown paths to start, the common cases are handled.
896
897
    NOTE:
898
    In its current implementation it is only allowed to downgrade from
899
    TL_WRITE_ONLY. In this case there are no waiters. Thus no wake up
900
    logic is required.
901
*/
902
903
void thr_downgrade_write_lock(THR_LOCK_DATA *in_data,
904
                              enum thr_lock_type new_lock_type)
905
{
906
  THR_LOCK *lock=in_data->lock;
907
908
  pthread_mutex_lock(&lock->mutex);
909
  in_data->type= new_lock_type;
910
911
  pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
912
  return;
1 by brian
clean slate
913
}
914
915
/* Upgrade a WRITE_DELAY lock to a WRITE_LOCK */
916
146 by Brian Aker
my_bool cleanup.
917
bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data)
1 by brian
clean slate
918
{
919
  THR_LOCK *lock=data->lock;
920
921
  pthread_mutex_lock(&lock->mutex);
922
  if (data->type == TL_UNLOCK || data->type >= TL_WRITE_LOW_PRIORITY)
923
  {
924
    pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
925
    return(data->type == TL_UNLOCK);	/* Test if Aborted */
1 by brian
clean slate
926
  }
927
  /* TODO:  Upgrade to TL_WRITE_CONCURRENT_INSERT in some cases */
928
  data->type=TL_WRITE;				/* Upgrade lock */
929
930
  /* Check if someone has given us the lock */
931
  if (!data->cond)
932
  {
933
    if (!lock->read.data)			/* No read locks */
934
    {						/* We have the lock */
935
      if (data->lock->get_status)
936
	(*data->lock->get_status)(data->status_param, 0);
937
      pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
938
      return(0);
1 by brian
clean slate
939
    }
940
941
    if (((*data->prev)=data->next))		/* remove from lock-list */
942
      data->next->prev= data->prev;
943
    else
944
      lock->write.last=data->prev;
945
946
    if ((data->next=lock->write_wait.data))	/* Put first in lock_list */
947
      data->next->prev= &data->next;
948
    else
949
      lock->write_wait.last= &data->next;
950
    data->prev= &lock->write_wait.data;
951
    lock->write_wait.data=data;
657 by Brian Aker
Random cleanup, removed dead mysys call for hw address creation.
952
  }
953
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
954
  return(wait_for_lock(&lock->write_wait,data,1));
1 by brian
clean slate
955
}
956
957
958
/* downgrade a WRITE lock to a WRITE_DELAY lock if there is pending locks */
959
146 by Brian Aker
my_bool cleanup.
960
bool thr_reschedule_write_lock(THR_LOCK_DATA *data)
1 by brian
clean slate
961
{
962
  THR_LOCK *lock=data->lock;
963
964
  pthread_mutex_lock(&lock->mutex);
965
  if (!lock->read_wait.data)			/* No waiting read locks */
966
  {
967
    pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
968
    return(0);
1 by brian
clean slate
969
  }
970
971
  data->type=TL_WRITE_DELAYED;
972
  if (lock->update_status)
973
    (*lock->update_status)(data->status_param);
974
  if (((*data->prev)=data->next))		/* remove from lock-list */
975
    data->next->prev= data->prev;
976
  else
977
    lock->write.last=data->prev;
978
979
  if ((data->next=lock->write_wait.data))	/* Put first in lock_list */
980
    data->next->prev= &data->next;
981
  else
982
    lock->write_wait.last= &data->next;
983
  data->prev= &lock->write_wait.data;
984
  data->cond=get_cond();			/* This was zero */
985
  lock->write_wait.data=data;
986
  free_all_read_locks(lock,0);
987
988
  pthread_mutex_unlock(&lock->mutex);
51.3.14 by Jay Pipes
Phase 2 removal of DBUG in mysys
989
  return(thr_upgrade_write_delay_lock(data));
1 by brian
clean slate
990
}