 d3c8770852
			
		
	
	d3c8770852
	
	
	
		
			
			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);
 | |
| }
 |