Make mlist a separate component.
Mlist is now made available as a separate component instead of being a part of skygw_utils.
This commit is contained in:
421
server/core/mlist.c
Normal file
421
server/core/mlist.c
Normal file
@ -0,0 +1,421 @@
|
||||
/*
|
||||
* This file is distributed as part of the MariaDB Corporation MaxScale. It 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.
|
||||
*
|
||||
* 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-1301 USA.
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2014
|
||||
*/
|
||||
|
||||
#include <mlist.h>
|
||||
|
||||
static void mlist_free_memory(mlist_t* ml, char* name);
|
||||
static mlist_node_t* mlist_node_init(void* data, mlist_cursor_t* cursor);
|
||||
//static mlist_node_t* mlist_node_get_next(mlist_node_t* curr_node);
|
||||
//static mlist_node_t* mlist_get_first(mlist_t* list);
|
||||
//static mlist_cursor_t* mlist_get_cursor(mlist_t* list);
|
||||
|
||||
|
||||
/**
|
||||
* @node Cut off nodes of the list.
|
||||
*
|
||||
* Parameters:
|
||||
* @param ml - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @return Pointer to the first of the detached nodes.
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
*
|
||||
*/
|
||||
mlist_node_t* mlist_detach_nodes(mlist_t* ml)
|
||||
{
|
||||
mlist_node_t* node;
|
||||
CHK_MLIST(ml);
|
||||
|
||||
node = ml->mlist_first;
|
||||
ml->mlist_first = NULL;
|
||||
ml->mlist_last = NULL;
|
||||
ml->mlist_nodecount = 0;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @node Create a list with rwlock and optional read-only cursor
|
||||
*
|
||||
* Parameters:
|
||||
* @param listp - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @param cursor - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @param name - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @return Address of mlist_t struct.
|
||||
*
|
||||
*
|
||||
* @details Cursor must protect its reads with read lock, and after
|
||||
* acquiring read lock reader must check whether the list is deleted
|
||||
* (mlist_deleted).
|
||||
*
|
||||
*/
|
||||
mlist_t* mlist_init(mlist_t* listp, mlist_cursor_t** cursor, char* name,
|
||||
void (*datadel)(void*), int maxnodes)
|
||||
{
|
||||
mlist_cursor_t* c;
|
||||
mlist_t* list;
|
||||
|
||||
if (cursor != NULL)
|
||||
{
|
||||
ss_dassert(*cursor == NULL);
|
||||
}
|
||||
/** listp is not NULL if caller wants flat list */
|
||||
if (listp == NULL)
|
||||
{
|
||||
list = (mlist_t*) calloc(1, sizeof (mlist_t));
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Caller wants list flat, memory won't be freed */
|
||||
list = listp;
|
||||
list->mlist_flat = true;
|
||||
}
|
||||
ss_dassert(list != NULL);
|
||||
|
||||
if (list == NULL)
|
||||
{
|
||||
fprintf(stderr, "* Allocating memory for mlist failed\n");
|
||||
mlist_free_memory(list, name);
|
||||
goto return_list;
|
||||
}
|
||||
list->mlist_chk_top = CHK_NUM_MLIST;
|
||||
list->mlist_chk_tail = CHK_NUM_MLIST;
|
||||
/** Set size limit for list. 0 means unlimited */
|
||||
list->mlist_nodecount_max = maxnodes;
|
||||
/** Set data deletion callback fun */
|
||||
list->mlist_datadel = datadel;
|
||||
|
||||
if (name != NULL)
|
||||
{
|
||||
list->mlist_name = name;
|
||||
}
|
||||
/** Create mutex, return NULL if fails. */
|
||||
if (simple_mutex_init(&list->mlist_mutex, "writebuf mutex") == NULL)
|
||||
{
|
||||
ss_dfprintf(stderr, "* Creating rwlock for mlist failed\n");
|
||||
mlist_free_memory(list, name);
|
||||
list = NULL;
|
||||
goto return_list;
|
||||
}
|
||||
|
||||
/** Create cursor for reading the list */
|
||||
if (cursor != NULL)
|
||||
{
|
||||
c = mlist_cursor_init(list);
|
||||
|
||||
if (c == NULL)
|
||||
{
|
||||
simple_mutex_done(&list->mlist_mutex);
|
||||
mlist_free_memory(list, name);
|
||||
list = NULL;
|
||||
goto return_list;
|
||||
}
|
||||
CHK_MLIST_CURSOR(c);
|
||||
*cursor = c;
|
||||
}
|
||||
list->mlist_versno = 2; /*< vresno != 0 means that list is initialized */
|
||||
CHK_MLIST(list);
|
||||
|
||||
return_list:
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @node Free mlist memory allocations. name must be explicitly
|
||||
* set if mlist has one.
|
||||
*
|
||||
* Parameters:
|
||||
* @param ml - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @param name - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
*
|
||||
*/
|
||||
static void mlist_free_memory(mlist_t* ml, char* name)
|
||||
{
|
||||
mlist_node_t* node;
|
||||
|
||||
/** name */
|
||||
if (name != NULL)
|
||||
{
|
||||
free(name);
|
||||
}
|
||||
if (ml != NULL)
|
||||
{
|
||||
/** list data */
|
||||
while (ml->mlist_first != NULL)
|
||||
{
|
||||
/** Scan list and free nodes and data inside nodes */
|
||||
node = ml->mlist_first->mlnode_next;
|
||||
mlist_node_done(ml->mlist_first);
|
||||
ml->mlist_first = node;
|
||||
}
|
||||
|
||||
/** list structure */
|
||||
if (!ml->mlist_flat)
|
||||
{
|
||||
free(ml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* mlist_node_get_data(mlist_node_t* node)
|
||||
{
|
||||
CHK_MLIST_NODE(node);
|
||||
return node->mlnode_data;
|
||||
}
|
||||
|
||||
void mlist_node_done(mlist_node_t* n)
|
||||
{
|
||||
CHK_MLIST_NODE(n);
|
||||
if (n->mlnode_data != NULL)
|
||||
{
|
||||
if (n->mlnode_list->mlist_datadel != NULL)
|
||||
{
|
||||
(n->mlnode_list->mlist_datadel(n->mlnode_data));
|
||||
}
|
||||
free(n->mlnode_data);
|
||||
}
|
||||
free(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @node Mark list as deleted and free the memory.
|
||||
*
|
||||
* Parameters:
|
||||
* @param list - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
*
|
||||
*/
|
||||
void mlist_done(mlist_t* list)
|
||||
{
|
||||
CHK_MLIST(list);
|
||||
simple_mutex_lock(&list->mlist_mutex, true);
|
||||
list->mlist_deleted = true;
|
||||
simple_mutex_unlock(&list->mlist_mutex);
|
||||
simple_mutex_done(&list->mlist_mutex);
|
||||
mlist_free_memory(list, list->mlist_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @node Adds data to list by allocating node for it. Checks list size limit.
|
||||
*
|
||||
* Parameters:
|
||||
* @param list - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @param data - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @return true, if succeed, false, if list had node limit and it is full.
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
*
|
||||
*/
|
||||
bool mlist_add_data_nomutex(mlist_t* list, void* data)
|
||||
{
|
||||
bool succp;
|
||||
|
||||
succp = mlist_add_node_nomutex(list, mlist_node_init(data, NULL));
|
||||
|
||||
return succp;
|
||||
}
|
||||
|
||||
static mlist_node_t* mlist_node_init(void* data, mlist_cursor_t* cursor)
|
||||
{
|
||||
mlist_node_t* node;
|
||||
|
||||
node = (mlist_node_t*) calloc(1, sizeof (mlist_node_t));
|
||||
node->mlnode_chk_top = CHK_NUM_MLIST_NODE;
|
||||
node->mlnode_chk_tail = CHK_NUM_MLIST_NODE;
|
||||
node->mlnode_data = data;
|
||||
CHK_MLIST_NODE(node);
|
||||
|
||||
if (cursor != NULL)
|
||||
{
|
||||
cursor->mlcursor_pos = node;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
mlist_node_t* mlist_detach_first(mlist_t* ml)
|
||||
{
|
||||
mlist_node_t* node;
|
||||
|
||||
CHK_MLIST(ml);
|
||||
node = ml->mlist_first;
|
||||
CHK_MLIST_NODE(node);
|
||||
ml->mlist_first = node->mlnode_next;
|
||||
node->mlnode_next = NULL;
|
||||
|
||||
ml->mlist_nodecount -= 1;
|
||||
if (ml->mlist_nodecount == 0)
|
||||
{
|
||||
ml->mlist_last = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
CHK_MLIST_NODE(ml->mlist_first);
|
||||
}
|
||||
CHK_MLIST(ml);
|
||||
|
||||
return (node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @node Add new node to end of list if there is space for it.
|
||||
*
|
||||
* Parameters:
|
||||
* @param list - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @param newnode - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @param add_last - <usage>
|
||||
* <description>
|
||||
*
|
||||
* @return true, if succeede, false, if list size limit was exceeded.
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
*
|
||||
*/
|
||||
bool mlist_add_node_nomutex(mlist_t* list, mlist_node_t* newnode)
|
||||
{
|
||||
bool succp = false;
|
||||
|
||||
CHK_MLIST(list);
|
||||
CHK_MLIST_NODE(newnode);
|
||||
ss_dassert(!list->mlist_deleted);
|
||||
|
||||
/** List is full already. */
|
||||
if (list->mlist_nodecount == list->mlist_nodecount_max)
|
||||
{
|
||||
goto return_succp;
|
||||
}
|
||||
/** Find location for new node */
|
||||
if (list->mlist_last != NULL)
|
||||
{
|
||||
ss_dassert(!list->mlist_last->mlnode_deleted);
|
||||
CHK_MLIST_NODE(list->mlist_last);
|
||||
CHK_MLIST_NODE(list->mlist_first);
|
||||
ss_dassert(list->mlist_last->mlnode_next == NULL);
|
||||
list->mlist_last->mlnode_next = newnode;
|
||||
}
|
||||
else
|
||||
{
|
||||
list->mlist_first = newnode;
|
||||
}
|
||||
list->mlist_last = newnode;
|
||||
newnode->mlnode_list = list;
|
||||
list->mlist_nodecount += 1;
|
||||
succp = true;
|
||||
return_succp:
|
||||
CHK_MLIST(list);
|
||||
return succp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mlist_cursor_t
|
||||
*/
|
||||
mlist_cursor_t* mlist_cursor_init(mlist_t* list)
|
||||
{
|
||||
CHK_MLIST(list);
|
||||
mlist_cursor_t* c;
|
||||
|
||||
/** acquire shared lock to the list */
|
||||
simple_mutex_lock(&list->mlist_mutex, true);
|
||||
|
||||
c = (mlist_cursor_t *) calloc(1, sizeof (mlist_cursor_t));
|
||||
|
||||
if (c == NULL)
|
||||
{
|
||||
simple_mutex_unlock(&list->mlist_mutex);
|
||||
goto return_cursor;
|
||||
}
|
||||
c->mlcursor_chk_top = CHK_NUM_MLIST_CURSOR;
|
||||
c->mlcursor_chk_tail = CHK_NUM_MLIST_CURSOR;
|
||||
c->mlcursor_list = list;
|
||||
|
||||
/** Set cursor position if list is not empty */
|
||||
if (list->mlist_first != NULL)
|
||||
{
|
||||
c->mlcursor_pos = list->mlist_first;
|
||||
}
|
||||
simple_mutex_unlock(&list->mlist_mutex);
|
||||
|
||||
CHK_MLIST_CURSOR(c);
|
||||
|
||||
return_cursor:
|
||||
return c;
|
||||
}
|
||||
|
||||
void* mlist_cursor_get_data_nomutex(mlist_cursor_t* mc)
|
||||
{
|
||||
CHK_MLIST_CURSOR(mc);
|
||||
return (mc->mlcursor_pos->mlnode_data);
|
||||
}
|
||||
|
||||
bool mlist_cursor_move_to_first(mlist_cursor_t* mc)
|
||||
{
|
||||
bool succp = false;
|
||||
mlist_t* list;
|
||||
|
||||
CHK_MLIST_CURSOR(mc);
|
||||
list = mc->mlcursor_list;
|
||||
CHK_MLIST(list);
|
||||
simple_mutex_lock(&list->mlist_mutex, true);
|
||||
|
||||
if (mc->mlcursor_list->mlist_deleted)
|
||||
{
|
||||
simple_mutex_unlock(&list->mlist_mutex);
|
||||
return false;
|
||||
}
|
||||
/** Set position point to first node */
|
||||
mc->mlcursor_pos = list->mlist_first;
|
||||
|
||||
if (mc->mlcursor_pos != NULL)
|
||||
{
|
||||
CHK_MLIST_NODE(mc->mlcursor_pos);
|
||||
succp = true;
|
||||
}
|
||||
simple_mutex_unlock(&list->mlist_mutex);
|
||||
return succp;
|
||||
}
|
Reference in New Issue
Block a user