Mlist is only used by log_manager so for the time being it is moved under server/core/maxscale. Eventually it may be included in log_manager itself.
		
			
				
	
	
		
			418 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			418 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2016 MariaDB Corporation Ab
 | 
						|
 *
 | 
						|
 * Use of this software is governed by the Business Source License included
 | 
						|
 * in the LICENSE.TXT file and at www.mariadb.com/bsl.
 | 
						|
 *
 | 
						|
 * Change Date: 2019-07-01
 | 
						|
 *
 | 
						|
 * On the date above, in accordance with the Business Source License, use
 | 
						|
 * of this software will be governed by version 2 or later of the General
 | 
						|
 * Public License.
 | 
						|
 */
 | 
						|
 | 
						|
#include "maxscale/mlist.h"
 | 
						|
#include <maxscale/alloc.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*) MXS_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)
 | 
						|
    {
 | 
						|
        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)
 | 
						|
    {
 | 
						|
        MXS_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)
 | 
						|
        {
 | 
						|
            MXS_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));
 | 
						|
        }
 | 
						|
        MXS_FREE(n->mlnode_data);
 | 
						|
    }
 | 
						|
    MXS_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*) MXS_CALLOC(1, sizeof (mlist_node_t));
 | 
						|
    MXS_ABORT_IF_NULL(node);
 | 
						|
    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 *) MXS_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;
 | 
						|
}
 |