mariadb/mysys/wqueue.c
2019-05-14 17:18:46 +03:00

242 lines
6.2 KiB
C

/*
Copyright (c) 2007, 2008, Sun Microsystems, Inc,
Copyright (c) 2011, 2012, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
#include <my_global.h>
#include <wqueue.h>
#define STRUCT_PTR(TYPE, MEMBER, a) \
(TYPE *) ((char *) (a) - offsetof(TYPE, MEMBER))
/*
Link a thread into double-linked queue of waiting threads.
SYNOPSIS
wqueue_link_into_queue()
wqueue pointer to the queue structure
thread pointer to the thread to be added to the queue
RETURN VALUE
none
NOTES.
Queue is represented by a circular list of the thread structures
The list is double-linked of the type (**prev,*next), accessed by
a pointer to the last element.
*/
void wqueue_link_into_queue(WQUEUE *wqueue, struct st_my_thread_var *thread)
{
struct st_my_thread_var *last;
if (!(last= wqueue->last_thread))
{
/* Queue is empty */
thread->next= thread;
thread->prev= &thread->next;
}
else
{
thread->prev= last->next->prev;
last->next->prev= &thread->next;
thread->next= last->next;
last->next= thread;
}
wqueue->last_thread= thread;
}
/*
Add a thread to single-linked queue of waiting threads
SYNOPSIS
wqueue_add_to_queue()
wqueue pointer to the queue structure
thread pointer to the thread to be added to the queue
RETURN VALUE
none
NOTES.
Queue is represented by a circular list of the thread structures
The list is single-linked of the type (*next), accessed by a pointer
to the last element.
*/
void wqueue_add_to_queue(WQUEUE *wqueue, struct st_my_thread_var *thread)
{
struct st_my_thread_var *last;
if (!(last= wqueue->last_thread))
thread->next= thread;
else
{
thread->next= last->next;
last->next= thread;
}
#ifndef DBUG_OFF
thread->prev= NULL; /* force segfault if used */
#endif
wqueue->last_thread= thread;
}
/*
Unlink a thread from double-linked queue of waiting threads
SYNOPSIS
wqueue_unlink_from_queue()
wqueue pointer to the queue structure
thread pointer to the thread to be removed from the queue
RETURN VALUE
none
NOTES.
See NOTES for link_into_queue
*/
void wqueue_unlink_from_queue(WQUEUE *wqueue, struct st_my_thread_var *thread)
{
if (thread->next == thread)
/* The queue contains only one member */
wqueue->last_thread= NULL;
else
{
thread->next->prev= thread->prev;
*thread->prev= thread->next;
if (wqueue->last_thread == thread)
wqueue->last_thread= STRUCT_PTR(struct st_my_thread_var, next,
thread->prev);
}
thread->next= NULL;
}
/*
Remove all threads from queue signaling them to proceed
SYNOPSIS
wqueue_realease_queue()
wqueue pointer to the queue structure
thread pointer to the thread to be added to the queue
RETURN VALUE
none
NOTES.
See notes for add_to_queue
When removed from the queue each thread is signaled via condition
variable thread->suspend.
*/
void wqueue_release_queue(WQUEUE *wqueue)
{
struct st_my_thread_var *last= wqueue->last_thread;
struct st_my_thread_var *next= last->next;
struct st_my_thread_var *thread;
do
{
thread= next;
mysql_cond_signal(&thread->suspend);
next= thread->next;
thread->next= NULL;
}
while (thread != last);
wqueue->last_thread= NULL;
}
/**
@brief Removes all threads waiting for read or first one waiting for write.
@param wqueue pointer to the queue structure
@param thread pointer to the thread to be added to the queue
@note This function is applicable only to single linked lists.
*/
void wqueue_release_one_locktype_from_queue(WQUEUE *wqueue)
{
struct st_my_thread_var *last= wqueue->last_thread;
struct st_my_thread_var *next= last->next;
struct st_my_thread_var *thread;
struct st_my_thread_var *new_list= NULL;
uint first_type= next->lock_type;
if (first_type == MY_PTHREAD_LOCK_WRITE)
{
/* release first waiting for write lock */
mysql_cond_signal(&next->suspend);
if (next == last)
wqueue->last_thread= NULL;
else
last->next= next->next;
next->next= NULL;
return;
}
do
{
thread= next;
next= thread->next;
if (thread->lock_type == MY_PTHREAD_LOCK_WRITE)
{
/* skip waiting for write lock */
if (new_list)
{
thread->next= new_list->next;
new_list= new_list->next= thread;
}
else
new_list= thread->next= thread;
}
else
{
/* release waiting for read lock */
mysql_cond_signal(&thread->suspend);
thread->next= NULL;
}
} while (thread != last);
wqueue->last_thread= new_list;
}
/*
Add thread and wait
SYNOPSIS
wqueue_add_and_wait()
wqueue queue to add to
thread thread which is waiting
lock mutex need for the operation
*/
void wqueue_add_and_wait(WQUEUE *wqueue,
struct st_my_thread_var *thread,
mysql_mutex_t *lock)
{
DBUG_ENTER("wqueue_add_and_wait");
DBUG_PRINT("enter",
("thread: %p cond: %p mutex: %p",
thread, &thread->suspend, lock));
wqueue_add_to_queue(wqueue, thread);
do
{
DBUG_PRINT("info", ("wait... cond: %p mutex: %p",
&thread->suspend, lock));
mysql_cond_wait(&thread->suspend, lock);
DBUG_PRINT("info", ("wait done cond: %p mutex: %p next: %p",
&thread->suspend, lock,
thread->next));
}
while (thread->next);
DBUG_VOID_RETURN;
}