summaryrefslogtreecommitdiffstats
path: root/mysys/wqueue.c
diff options
context:
space:
mode:
Diffstat (limited to 'mysys/wqueue.c')
-rw-r--r--mysys/wqueue.c242
1 files changed, 242 insertions, 0 deletions
diff --git a/mysys/wqueue.c b/mysys/wqueue.c
new file mode 100644
index 00000000..84fac01f
--- /dev/null
+++ b/mysys/wqueue.c
@@ -0,0 +1,242 @@
+/*
+ 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;
+}