Conflicts: client/maxadmin.c server/core/CMakeLists.txt server/core/dcb.c server/core/gateway.c server/core/poll.c server/core/test/CMakeLists.txt server/core/test/makefile server/include/poll.h server/modules/routing/debugcmd.c
		
			
				
	
	
		
			315 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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 2014
 | 
						|
 */
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <housekeeper.h>
 | 
						|
#include <thread.h>
 | 
						|
#include <spinlock.h>
 | 
						|
 | 
						|
/**
 | 
						|
 * @file housekeeper.c  Provide a mechanism to run periodic tasks
 | 
						|
 *
 | 
						|
 * The housekeeper provides a mechanism to allow for tasks, function
 | 
						|
 * calls basically, to be run on a tiem basis. A task may be run
 | 
						|
 * repeatedly, with a given frequency (in seconds), or may be a one
 | 
						|
 * shot task that will only be run once after a specified number of
 | 
						|
 * seconds.
 | 
						|
 *
 | 
						|
 * The housekeeper also maintains a global variable, hkheartbeat, that
 | 
						|
 * is incremented every 100ms.
 | 
						|
 *
 | 
						|
 * @verbatim
 | 
						|
 * Revision History
 | 
						|
 *
 | 
						|
 * Date		Who		Description
 | 
						|
 * 29/08/14	Mark Riddoch	Initial implementation
 | 
						|
 * 22/10/14	Mark Riddoch	Addition of one-shot tasks
 | 
						|
 *
 | 
						|
 * @endverbatim
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * List of all tasks that need to be run
 | 
						|
 */
 | 
						|
static HKTASK	*tasks = NULL;
 | 
						|
/**
 | 
						|
 * Spinlock to protect the tasks list
 | 
						|
 */
 | 
						|
static SPINLOCK	tasklock = SPINLOCK_INIT;
 | 
						|
 | 
						|
static int	do_shutdown = 0;
 | 
						|
unsigned long	hkheartbeat = 0;
 | 
						|
 | 
						|
static	void	hkthread(void *);
 | 
						|
 | 
						|
/**
 | 
						|
 * Initialise the housekeeper thread
 | 
						|
 */
 | 
						|
void
 | 
						|
hkinit()
 | 
						|
{
 | 
						|
	thread_start(hkthread, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Add a new task to the housekeepers lists of tasks that should be
 | 
						|
 * run periodically.
 | 
						|
 *
 | 
						|
 * The task will be first run frequency seconds after this call is
 | 
						|
 * made and will the be executed repeatedly every frequency seconds
 | 
						|
 * until the task is removed.
 | 
						|
 *
 | 
						|
 * Task names must be unique.
 | 
						|
 *
 | 
						|
 * @param name		The unique name for this housekeeper task
 | 
						|
 * @param taskfn	The function to call for the task
 | 
						|
 * @param data		Data to pass to the task function
 | 
						|
 * @param frequency	How often to run the task, expressed in seconds
 | 
						|
 * @return		Return the time in seconds when the task will be first run if the task was added, otherwise 0
 | 
						|
 */
 | 
						|
int
 | 
						|
hktask_add(char *name, void (*taskfn)(void *), void *data, int frequency)
 | 
						|
{
 | 
						|
HKTASK	*task, *ptr;
 | 
						|
 | 
						|
	if ((task = (HKTASK *)malloc(sizeof(HKTASK))) == NULL)
 | 
						|
	{
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	if ((task->name = strdup(name)) == NULL)
 | 
						|
	{
 | 
						|
		free(task);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	task->task = taskfn;
 | 
						|
	task->data = data;
 | 
						|
	task->frequency = frequency;
 | 
						|
	task->type = HK_REPEATED;
 | 
						|
	task->nextdue = time(0) + frequency;
 | 
						|
	task->next = NULL;
 | 
						|
	spinlock_acquire(&tasklock);
 | 
						|
	ptr = tasks;
 | 
						|
	while (ptr && ptr->next)
 | 
						|
	{
 | 
						|
		if (strcmp(ptr->name, name) == 0)
 | 
						|
		{
 | 
						|
			spinlock_release(&tasklock);
 | 
						|
			free(task->name);
 | 
						|
			free(task);
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
		ptr = ptr->next;
 | 
						|
	}
 | 
						|
	if (ptr)
 | 
						|
		ptr->next = task;
 | 
						|
	else
 | 
						|
		tasks = task;
 | 
						|
	spinlock_release(&tasklock);
 | 
						|
 | 
						|
	return task->nextdue;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Add a one-shot task to the housekeeper task list
 | 
						|
 *
 | 
						|
 * Task names must be unique.
 | 
						|
 *
 | 
						|
 * @param name		The unique name for this housekeeper task
 | 
						|
 * @param taskfn	The function to call for the task
 | 
						|
 * @param data		Data to pass to the task function
 | 
						|
 * @param when		How many second until the task is executed
 | 
						|
 * @return		Return the time in seconds when the task will be first run if the task was added, otherwise 0
 | 
						|
 *
 | 
						|
 */
 | 
						|
int
 | 
						|
hktask_oneshot(char *name, void (*taskfn)(void *), void *data, int when)
 | 
						|
{
 | 
						|
HKTASK	*task, *ptr;
 | 
						|
 | 
						|
	if ((task = (HKTASK *)malloc(sizeof(HKTASK))) == NULL)
 | 
						|
	{
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	if ((task->name = strdup(name)) == NULL)
 | 
						|
	{
 | 
						|
		free(task);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	task->task = taskfn;
 | 
						|
	task->data = data;
 | 
						|
	task->frequency = 0;
 | 
						|
	task->type = HK_ONESHOT;
 | 
						|
	task->nextdue = time(0) + when;
 | 
						|
	task->next = NULL;
 | 
						|
	spinlock_acquire(&tasklock);
 | 
						|
	ptr = tasks;
 | 
						|
	while (ptr && ptr->next)
 | 
						|
	{
 | 
						|
		if (strcmp(ptr->name, name) == 0)
 | 
						|
		{
 | 
						|
			spinlock_release(&tasklock);
 | 
						|
			free(task->name);
 | 
						|
			free(task);
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
		ptr = ptr->next;
 | 
						|
	}
 | 
						|
	if (ptr)
 | 
						|
		ptr->next = task;
 | 
						|
	else
 | 
						|
		tasks = task;
 | 
						|
	spinlock_release(&tasklock);
 | 
						|
 | 
						|
	return task->nextdue;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Remove a named task from the housekeepers task list
 | 
						|
 *
 | 
						|
 * @param name		The task name to remove
 | 
						|
 * @return		Returns 0 if the task could not be removed
 | 
						|
 */
 | 
						|
int
 | 
						|
hktask_remove(char *name)
 | 
						|
{
 | 
						|
HKTASK	*ptr, *lptr = NULL;
 | 
						|
 | 
						|
	spinlock_acquire(&tasklock);
 | 
						|
	ptr = tasks;
 | 
						|
	while (ptr && strcmp(ptr->name, name) != 0)
 | 
						|
	{
 | 
						|
		lptr = ptr;
 | 
						|
		ptr = ptr->next;
 | 
						|
	}
 | 
						|
	if (ptr && lptr)
 | 
						|
		lptr->next = ptr->next;
 | 
						|
	else if (ptr)
 | 
						|
		tasks = ptr->next;
 | 
						|
	spinlock_release(&tasklock);
 | 
						|
 | 
						|
	if (ptr)
 | 
						|
	{
 | 
						|
		free(ptr->name);
 | 
						|
		free(ptr);
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * The housekeeper thread implementation.
 | 
						|
 *
 | 
						|
 * This function is responsible for executing the housekeeper tasks.
 | 
						|
 *
 | 
						|
 * The implementation of the callng of the task functions is such that
 | 
						|
 * the tasks are called without the tasklock spinlock being held. This
 | 
						|
 * allows manipulation of the housekeeper task list during execution of
 | 
						|
 * one of the tasks. The resutl is that upon completion of a task the
 | 
						|
 * search for tasks to run must restart from the start of the queue.
 | 
						|
 * It is vital that the task->nextdue tiem is updated before the task
 | 
						|
 * is run.
 | 
						|
 *
 | 
						|
 * @param	data		Unused, here to satisfy the thread system
 | 
						|
 */
 | 
						|
void
 | 
						|
hkthread(void *data)
 | 
						|
{
 | 
						|
HKTASK	*ptr;
 | 
						|
time_t	now;
 | 
						|
void	(*taskfn)(void *);
 | 
						|
void	*taskdata;
 | 
						|
int	i;
 | 
						|
 | 
						|
	for (;;)
 | 
						|
	{
 | 
						|
		for (i = 0; i < 10; i++)
 | 
						|
		{
 | 
						|
			if (do_shutdown)
 | 
						|
				return;
 | 
						|
			thread_millisleep(100);
 | 
						|
			hkheartbeat++;
 | 
						|
		}
 | 
						|
		now = time(0);
 | 
						|
		spinlock_acquire(&tasklock);
 | 
						|
		ptr = tasks;
 | 
						|
		while (ptr)
 | 
						|
		{
 | 
						|
			if (ptr->nextdue <= now)
 | 
						|
			{
 | 
						|
				ptr->nextdue = now + ptr->frequency;
 | 
						|
				taskfn = ptr->task;
 | 
						|
				taskdata = ptr->data;
 | 
						|
				spinlock_release(&tasklock);
 | 
						|
				(*taskfn)(taskdata);
 | 
						|
				if (ptr->type == HK_ONESHOT)
 | 
						|
					hktask_remove(ptr->name);
 | 
						|
				spinlock_acquire(&tasklock);
 | 
						|
				ptr = tasks;
 | 
						|
			}
 | 
						|
			else
 | 
						|
				ptr = ptr->next;
 | 
						|
		}
 | 
						|
		spinlock_release(&tasklock);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Called to shutdown the housekeeper
 | 
						|
 *
 | 
						|
 */
 | 
						|
void
 | 
						|
hkshutdown()
 | 
						|
{
 | 
						|
	do_shutdown = 1;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Show the tasks that are scheduled for the house keeper
 | 
						|
 *
 | 
						|
 * @param pdcb		The DCB to send to output
 | 
						|
 */
 | 
						|
void
 | 
						|
hkshow_tasks(DCB *pdcb)
 | 
						|
{
 | 
						|
HKTASK		*ptr;
 | 
						|
struct tm	tm;
 | 
						|
char		buf[40];
 | 
						|
 | 
						|
	dcb_printf(pdcb, "%-25s | Type     | Frequency | Next Due\n", "Name");
 | 
						|
	dcb_printf(pdcb, "--------------------------+----------+-----------+-------------------------\n");
 | 
						|
	spinlock_acquire(&tasklock);
 | 
						|
	ptr = tasks;
 | 
						|
	while (ptr)
 | 
						|
	{
 | 
						|
		localtime_r(&ptr->nextdue, &tm);
 | 
						|
		asctime_r(&tm, buf);
 | 
						|
		dcb_printf(pdcb, "%-25s | %-8s | %-9d | %s",
 | 
						|
			ptr->name,
 | 
						|
			ptr->type == HK_REPEATED ? "Repeated" : "One-Shot",
 | 
						|
			ptr->frequency,
 | 
						|
			buf);
 | 
						|
		ptr = ptr->next;
 | 
						|
	}
 | 
						|
	spinlock_release(&tasklock);
 | 
						|
}
 |