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