243 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			243 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2018 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: 2024-02-10
 | |
|  *
 | |
|  * 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/mainworker.hh>
 | |
| #include <signal.h>
 | |
| #include <maxscale/config.hh>
 | |
| 
 | |
| namespace
 | |
| {
 | |
| 
 | |
| static struct ThisUnit
 | |
| {
 | |
|     maxscale::MainWorker* pCurrent_main;
 | |
|     int64_t               clock_ticks;
 | |
| } this_unit;
 | |
| }
 | |
| 
 | |
| namespace maxscale
 | |
| {
 | |
| 
 | |
| MainWorker::MainWorker()
 | |
| {
 | |
|     mxb_assert(!this_unit.pCurrent_main);
 | |
| 
 | |
|     this_unit.pCurrent_main = this;
 | |
| 
 | |
|     delayed_call(100, &MainWorker::inc_ticks);
 | |
| }
 | |
| 
 | |
| MainWorker::~MainWorker()
 | |
| {
 | |
|     mxb_assert(this_unit.pCurrent_main);
 | |
| 
 | |
|     this_unit.pCurrent_main = nullptr;
 | |
| }
 | |
| 
 | |
| // static
 | |
| bool MainWorker::created()
 | |
| {
 | |
|     return this_unit.pCurrent_main ? true : false;
 | |
| }
 | |
| 
 | |
| // static
 | |
| MainWorker& MainWorker::get()
 | |
| {
 | |
|     mxb_assert(this_unit.pCurrent_main);
 | |
| 
 | |
|     return *this_unit.pCurrent_main;
 | |
| }
 | |
| 
 | |
| void MainWorker::add_task(const std::string& name, TASKFN func, void* pData, int frequency)
 | |
| {
 | |
|     execute([=]() {
 | |
|                 mxb_assert_message(m_tasks_by_name.find(name) == m_tasks_by_name.end(), "%s", name.c_str());
 | |
| 
 | |
|                 Task task(name.c_str(), func, pData, frequency);
 | |
| 
 | |
|                 auto p = m_tasks_by_name.insert(std::make_pair(name, task));
 | |
|                 Task& inserted_task = (*p.first).second;
 | |
| 
 | |
|                 inserted_task.id = delayed_call(frequency * 1000,
 | |
|                                                 &MainWorker::call_task,
 | |
|                                                 this,
 | |
|                                                 &inserted_task);
 | |
|             },
 | |
|             EXECUTE_AUTO);
 | |
| }
 | |
| 
 | |
| void MainWorker::remove_task(const std::string& name)
 | |
| {
 | |
| 
 | |
|     call([this, name]() {
 | |
|              auto it = m_tasks_by_name.find(name);
 | |
|              mxb_assert(it != m_tasks_by_name.end());
 | |
| 
 | |
|              if (it != m_tasks_by_name.end())
 | |
|              {
 | |
|                  MXB_AT_DEBUG(bool cancelled = ) cancel_delayed_call(it->second.id);
 | |
|                  mxb_assert(cancelled);
 | |
| 
 | |
|                  m_tasks_by_name.erase(it);
 | |
|              }
 | |
|          },
 | |
|          EXECUTE_AUTO);
 | |
| }
 | |
| 
 | |
| void MainWorker::show_tasks(DCB* pDcb) const
 | |
| {
 | |
|     // TODO: Make call() const.
 | |
|     MainWorker* pThis = const_cast<MainWorker*>(this);
 | |
|     pThis->call([this, pDcb]() {
 | |
|                     dcb_printf(pDcb, "%-25s | Frequency | Next Due\n", "Name");
 | |
|                     dcb_printf(pDcb, "--------------------------+-----------+-------------------------\n");
 | |
| 
 | |
|                     for (auto it = m_tasks_by_name.begin(); it != m_tasks_by_name.end(); ++it)
 | |
|                     {
 | |
|                         const Task& task = it->second;
 | |
| 
 | |
|                         struct tm tm;
 | |
|                         char buf[40];
 | |
|                         localtime_r(&task.nextdue, &tm);
 | |
|                         asctime_r(&tm, buf);
 | |
|                         dcb_printf(pDcb, "%-25s | %-9d | %s", task.name.c_str(), task.frequency, buf);
 | |
|                     }
 | |
|                 },
 | |
|                 EXECUTE_AUTO);
 | |
| }
 | |
| 
 | |
| json_t* MainWorker::tasks_to_json(const char* zHost) const
 | |
| {
 | |
|     json_t* pResult = json_array();
 | |
| 
 | |
|     // TODO: Make call() const.
 | |
|     MainWorker* pThis = const_cast<MainWorker*>(this);
 | |
|     pThis->call([this, zHost, pResult]() {
 | |
|                     for (auto it = m_tasks_by_name.begin(); it != m_tasks_by_name.end(); ++it)
 | |
|                     {
 | |
|                         const Task& task = it->second;
 | |
| 
 | |
|                         struct tm tm;
 | |
|                         char buf[40];
 | |
|                         localtime_r(&task.nextdue, &tm);
 | |
|                         asctime_r(&tm, buf);
 | |
|                         char* nl = strchr(buf, '\n');
 | |
|                         mxb_assert(nl);
 | |
|                         *nl = '\0';
 | |
| 
 | |
|                         json_t* pObject = json_object();
 | |
| 
 | |
|                         json_object_set_new(pObject, CN_ID, json_string(task.name.c_str()));
 | |
|                         json_object_set_new(pObject, CN_TYPE, json_string("tasks"));
 | |
| 
 | |
|                         json_t* pAttrs = json_object();
 | |
|                         json_object_set_new(pAttrs, "frequency", json_integer(task.frequency));
 | |
|                         json_object_set_new(pAttrs, "next_execution", json_string(buf));
 | |
| 
 | |
|                         json_object_set_new(pObject, CN_ATTRIBUTES, pAttrs);
 | |
|                         json_array_append_new(pResult, pObject);
 | |
|                     }
 | |
|                 },
 | |
|                 EXECUTE_AUTO);
 | |
| 
 | |
|     return pResult;
 | |
| }
 | |
| 
 | |
| // static
 | |
| int64_t MainWorker::ticks()
 | |
| {
 | |
|     return mxb::atomic::load(&this_unit.clock_ticks, mxb::atomic::RELAXED);
 | |
| }
 | |
| 
 | |
| bool MainWorker::pre_run()
 | |
| {
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| void MainWorker::post_run()
 | |
| {
 | |
| }
 | |
| 
 | |
| void MainWorker::epoll_tick()
 | |
| {
 | |
| }
 | |
| 
 | |
| bool MainWorker::call_task(Worker::Call::action_t action, MainWorker::Task* pTask)
 | |
| {
 | |
|     bool call_again = false;
 | |
| 
 | |
|     if (action == Worker::Call::EXECUTE)
 | |
|     {
 | |
|         mxb_assert(m_tasks_by_name.find(pTask->name) != m_tasks_by_name.end());
 | |
| 
 | |
|         call_again = pTask->func(pTask->pData);
 | |
| 
 | |
|         if (call_again)
 | |
|         {
 | |
|             pTask->nextdue = time(0) + pTask->frequency;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             auto it = m_tasks_by_name.find(pTask->name);
 | |
| 
 | |
|             if (it != m_tasks_by_name.end())    // Not found, if task function removes task.
 | |
|             {
 | |
|                 m_tasks_by_name.erase(it);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return call_again;
 | |
| }
 | |
| 
 | |
| // static
 | |
| bool MainWorker::inc_ticks(Worker::Call::action_t action)
 | |
| {
 | |
|     if (action == Worker::Call::EXECUTE)
 | |
|     {
 | |
|         mxb::atomic::add(&this_unit.clock_ticks, 1, mxb::atomic::RELAXED);
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| {
 | |
| 
 | |
| void hktask_add(const char* zName, TASKFN func, void* pData, int frequency)
 | |
| {
 | |
|     mxs::MainWorker::get().add_task(zName, func, pData, frequency);
 | |
| }
 | |
| 
 | |
| void hktask_remove(const char* zName)
 | |
| {
 | |
|     mxs::MainWorker::get().remove_task(zName);
 | |
| }
 | |
| 
 | |
| void hkshow_tasks(DCB* pDcb)
 | |
| {
 | |
|     mxs::MainWorker::get().show_tasks(pDcb);
 | |
| }
 | |
| 
 | |
| json_t* hk_tasks_json(const char* zHost)
 | |
| {
 | |
|     return mxs::MainWorker::get().tasks_to_json(zHost);
 | |
| }
 | |
| 
 | |
| int64_t mxs_clock()
 | |
| {
 | |
|     return mxs::MainWorker::ticks();
 | |
| }
 | |
| }
 | 
