
It is now possible to specify the thread stack size to be used, when a new thread is created. This will subsequently be used for allowing the stack size to be specified for worker threads.
335 lines
7.9 KiB
C++
335 lines
7.9 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/bsl11.
|
|
*
|
|
* Change Date: 2020-01-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/housekeeper.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <maxscale/alloc.h>
|
|
#include <maxscale/atomic.h>
|
|
#include <maxscale/config.h>
|
|
#include <maxscale/semaphore.h>
|
|
#include <maxscale/spinlock.h>
|
|
#include <maxscale/thread.h>
|
|
#include <maxscale/json_api.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 time 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.
|
|
*/
|
|
|
|
/**
|
|
* 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 bool do_shutdown = 0;
|
|
|
|
int64_t hkheartbeat = 0; /*< One heartbeat is 100 milliseconds */
|
|
static THREAD hk_thr_handle;
|
|
|
|
static void hkthread(void *);
|
|
|
|
bool hkinit()
|
|
{
|
|
bool inited = false;
|
|
|
|
if (thread_start(&hk_thr_handle, hkthread, NULL, 0) != NULL)
|
|
{
|
|
inited = true;
|
|
}
|
|
else
|
|
{
|
|
MXS_ALERT("Failed to start housekeeper thread.");
|
|
}
|
|
|
|
return inited;
|
|
}
|
|
|
|
int hktask_add(const char *name, void (*taskfn)(void *), void *data, int frequency)
|
|
{
|
|
HKTASK *task, *ptr;
|
|
|
|
if ((task = (HKTASK *)MXS_MALLOC(sizeof(HKTASK))) == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
if ((task->name = MXS_STRDUP(name)) == NULL)
|
|
{
|
|
MXS_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);
|
|
MXS_FREE(task->name);
|
|
MXS_FREE(task);
|
|
return 0;
|
|
}
|
|
ptr = ptr->next;
|
|
}
|
|
if (ptr)
|
|
{
|
|
if (strcmp(ptr->name, name) == 0)
|
|
{
|
|
spinlock_release(&tasklock);
|
|
MXS_FREE(task->name);
|
|
MXS_FREE(task);
|
|
return 0;
|
|
}
|
|
ptr->next = task;
|
|
}
|
|
else
|
|
{
|
|
tasks = task;
|
|
}
|
|
spinlock_release(&tasklock);
|
|
|
|
return task->nextdue;
|
|
}
|
|
|
|
int hktask_oneshot(const char *name, void (*taskfn)(void *), void *data, int when)
|
|
{
|
|
HKTASK *task, *ptr;
|
|
|
|
if ((task = (HKTASK *)MXS_MALLOC(sizeof(HKTASK))) == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
if ((task->name = MXS_STRDUP(name)) == NULL)
|
|
{
|
|
MXS_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)
|
|
{
|
|
ptr = ptr->next;
|
|
}
|
|
if (ptr)
|
|
{
|
|
ptr->next = task;
|
|
}
|
|
else
|
|
{
|
|
tasks = task;
|
|
}
|
|
spinlock_release(&tasklock);
|
|
|
|
return task->nextdue;
|
|
}
|
|
|
|
int hktask_remove(const 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)
|
|
{
|
|
MXS_FREE(ptr->name);
|
|
MXS_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;
|
|
|
|
while (!do_shutdown)
|
|
{
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
thread_millisleep(100);
|
|
atomic_add_int64(&hkheartbeat, 1);
|
|
}
|
|
now = time(0);
|
|
spinlock_acquire(&tasklock);
|
|
ptr = tasks;
|
|
while (!do_shutdown && ptr)
|
|
{
|
|
if (ptr->nextdue <= now)
|
|
{
|
|
ptr->nextdue = now + ptr->frequency;
|
|
taskfn = ptr->task;
|
|
taskdata = ptr->data;
|
|
// We need to copy type and name, in case hktask_remove is called from
|
|
// the callback. Otherwise we will access freed data.
|
|
HKTASK_TYPE type = ptr->type;
|
|
char name[strlen(ptr->name) + 1];
|
|
strcpy(name, ptr->name);
|
|
spinlock_release(&tasklock);
|
|
(*taskfn)(taskdata);
|
|
if (type == HK_ONESHOT)
|
|
{
|
|
hktask_remove(name);
|
|
}
|
|
spinlock_acquire(&tasklock);
|
|
ptr = tasks;
|
|
}
|
|
else
|
|
{
|
|
ptr = ptr->next;
|
|
}
|
|
}
|
|
spinlock_release(&tasklock);
|
|
}
|
|
|
|
MXS_NOTICE("Housekeeper shutting down.");
|
|
}
|
|
|
|
void hkshutdown()
|
|
{
|
|
do_shutdown = true;
|
|
atomic_synchronize();
|
|
}
|
|
|
|
void hkfinish()
|
|
{
|
|
ss_dassert(do_shutdown);
|
|
|
|
MXS_NOTICE("Waiting for housekeeper to shut down.");
|
|
thread_wait(hk_thr_handle);
|
|
do_shutdown = false;
|
|
MXS_NOTICE("Housekeeper has shut down.");
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
json_t* hk_tasks_json(const char* host)
|
|
{
|
|
json_t* arr = json_array();
|
|
|
|
spinlock_acquire(&tasklock);
|
|
|
|
for (HKTASK* ptr = tasks; ptr; ptr = ptr->next)
|
|
{
|
|
struct tm tm;
|
|
char buf[40];
|
|
localtime_r(&ptr->nextdue, &tm);
|
|
asctime_r(&tm, buf);
|
|
char* nl = strchr(buf, '\n');
|
|
ss_dassert(nl);
|
|
*nl = '\0';
|
|
|
|
const char* task_type = ptr->type == HK_REPEATED ? "Repeated" : "One-Shot";
|
|
|
|
json_t* obj = json_object();
|
|
|
|
json_object_set_new(obj, CN_ID, json_string(ptr->name));
|
|
json_object_set_new(obj, CN_TYPE, json_string("tasks"));
|
|
|
|
json_t* attr = json_object();
|
|
json_object_set_new(attr, "task_type", json_string(task_type));
|
|
json_object_set_new(attr, "frequency", json_integer(ptr->frequency));
|
|
json_object_set_new(attr, "next_execution", json_string(buf));
|
|
|
|
json_object_set_new(obj, CN_ATTRIBUTES, attr);
|
|
json_array_append_new(arr, obj);
|
|
}
|
|
|
|
spinlock_release(&tasklock);
|
|
|
|
return mxs_json_resource(host, MXS_JSON_API_TASKS, arr);
|
|
}
|