mirror of https://gitee.com/bigwinds/arangodb
562 lines
15 KiB
C++
562 lines
15 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief input-output scheduler
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
|
|
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// @author Dr. Frank Celler
|
|
/// @author Achim Brandt
|
|
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
|
|
/// @author Copyright 2008-2014, triAGENS GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef _WIN32
|
|
#include "Basics/win-utils.h"
|
|
#endif
|
|
|
|
#include "Scheduler.h"
|
|
|
|
#include "Basics/MutexLocker.h"
|
|
#include "Basics/StringUtils.h"
|
|
#include "Basics/Thread.h"
|
|
#include "Basics/json.h"
|
|
#include "Basics/JsonHelper.h"
|
|
#include "Basics/logging.h"
|
|
#include "Scheduler/SchedulerThread.h"
|
|
#include "Scheduler/Task.h"
|
|
|
|
using namespace std;
|
|
using namespace triagens::basics;
|
|
using namespace triagens::rest;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors and destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief constructor
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Scheduler::Scheduler (size_t nrThreads)
|
|
: nrThreads(nrThreads),
|
|
threads(0),
|
|
stopping(0),
|
|
nextLoop(0),
|
|
_active(true) {
|
|
|
|
// check for multi-threading scheduler
|
|
multiThreading = (nrThreads > 1);
|
|
|
|
if (! multiThreading) {
|
|
nrThreads = 1;
|
|
}
|
|
|
|
// report status
|
|
if (multiThreading) {
|
|
LOG_TRACE("scheduler is multi-threaded, number of threads: %d", (int) nrThreads);
|
|
}
|
|
else {
|
|
LOG_TRACE("scheduler is single-threaded");
|
|
}
|
|
|
|
// setup signal handlers
|
|
initialiseSignalHandlers();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief destructor
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Scheduler::~Scheduler () {
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief starts scheduler, keeps running
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Scheduler::start (ConditionVariable* cv) {
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
// start the schedulers threads
|
|
for (size_t i = 0; i < nrThreads; ++i) {
|
|
bool ok = threads[i]->start(cv);
|
|
|
|
if (! ok) {
|
|
LOG_FATAL_AND_EXIT("cannot start threads");
|
|
}
|
|
}
|
|
|
|
// and wait until the threads are started
|
|
bool waiting = true;
|
|
|
|
while (waiting) {
|
|
waiting = false;
|
|
|
|
usleep(1000);
|
|
|
|
for (size_t i = 0; i < nrThreads; ++i) {
|
|
if (! threads[i]->isRunning()) {
|
|
waiting = true;
|
|
LOG_TRACE("waiting for thread #%d to start", (int) i);
|
|
}
|
|
}
|
|
}
|
|
|
|
LOG_TRACE("all scheduler threads are up and running");
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief checks if the scheduler threads are up and running
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Scheduler::isStarted () {
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
for (size_t i = 0; i < nrThreads; ++i) {
|
|
if (! threads[i]->isStarted()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief opens the scheduler for business
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Scheduler::open () {
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
for (size_t i = 0; i < nrThreads; ++i) {
|
|
if (! threads[i]->open()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief checks if scheduler is still running
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Scheduler::isRunning () {
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
for (size_t i = 0; i < nrThreads; ++i) {
|
|
if (threads[i]->isRunning()) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief starts shutdown sequence
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Scheduler::beginShutdown () {
|
|
if (stopping != 0) {
|
|
return;
|
|
}
|
|
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
LOG_DEBUG("beginning shutdown sequence of scheduler");
|
|
|
|
for (size_t i = 0; i < nrThreads; ++i) {
|
|
threads[i]->beginShutdown();
|
|
}
|
|
|
|
// set the flag AFTER stopping the threads
|
|
stopping = 1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief checks if scheduler is shuting down
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Scheduler::isShutdownInProgress () {
|
|
return stopping != 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief shuts down the scheduler
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Scheduler::shutdown () {
|
|
for (auto& it : taskRegistered) {
|
|
LOG_DEBUG("forcefully removing task '%s'", it->name().c_str());
|
|
|
|
deleteTask(it);
|
|
}
|
|
|
|
taskRegistered.clear();
|
|
task2thread.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief list user tasks
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_json_t* Scheduler::getUserTasks () {
|
|
std::unique_ptr<TRI_json_t> json(TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE));
|
|
|
|
if (json == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
{
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
for (auto& it : task2thread) {
|
|
auto const* task = it.first;
|
|
|
|
if (task->isUserDefined()) {
|
|
TRI_json_t* obj = task->toJson();
|
|
|
|
if (obj != nullptr) {
|
|
TRI_PushBack3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json.get(), obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return json.release();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief get a single user task
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_json_t* Scheduler::getUserTask (string const& id) {
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
for (auto& it : task2thread) {
|
|
auto const* task = it.first;
|
|
|
|
if (task->isUserDefined() && task->id() == id) {
|
|
return task->toJson();
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief unregister and delete a user task by id
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int Scheduler::unregisterUserTask (string const& id) {
|
|
if (id.empty()) {
|
|
return TRI_ERROR_TASK_INVALID_ID;
|
|
}
|
|
|
|
Task* task = nullptr;
|
|
|
|
{
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
for (auto& it : task2thread) {
|
|
auto const* t = it.first;
|
|
|
|
if (t->id() == id) {
|
|
// found the sought task
|
|
if (! t->isUserDefined()) {
|
|
return TRI_ERROR_TASK_NOT_FOUND;
|
|
}
|
|
|
|
task = const_cast<Task*>(t);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (task == nullptr) {
|
|
// not found
|
|
return TRI_ERROR_TASK_NOT_FOUND;
|
|
}
|
|
|
|
return destroyTask(task);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief unregister all user tasks
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int Scheduler::unregisterUserTasks () {
|
|
while (true) {
|
|
Task* task = nullptr;
|
|
|
|
{
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
for (auto& it : task2thread) {
|
|
auto const* t = it.first;
|
|
|
|
if (t->isUserDefined()) {
|
|
task = const_cast<Task*>(t);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (task == nullptr) {
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
destroyTask(task);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief registers a new task
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int Scheduler::registerTask (Task* task) {
|
|
return registerTask(task, nullptr, -1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief registers a new task and returns the chosen threads number
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int Scheduler::registerTask (Task* task, ssize_t* tn) {
|
|
*tn = -1;
|
|
return registerTask(task, tn, -1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief registers a new task with a pre-selected thread number
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int Scheduler::registerTaskInThread (Task* task, ssize_t tn) {
|
|
return registerTask(task, nullptr, tn);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief unregisters a task
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int Scheduler::unregisterTask (Task* task) {
|
|
SchedulerThread* thread = nullptr;
|
|
|
|
{
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
auto it = task2thread.find(task);
|
|
|
|
if (it == task2thread.end()) {
|
|
LOG_WARNING("unregisterTask called for an unknown task %p (%s)", (void*) task, task->name().c_str());
|
|
|
|
return TRI_ERROR_TASK_NOT_FOUND;
|
|
}
|
|
|
|
LOG_TRACE("unregisterTask for task %p (%s)", (void*) task, task->name().c_str());
|
|
|
|
thread = (*it).second;
|
|
|
|
taskRegistered.erase(task);
|
|
task2thread.erase(it);
|
|
}
|
|
|
|
thread->unregisterTask(task);
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief destroys task
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int Scheduler::destroyTask (Task* task) {
|
|
SchedulerThread* thread = nullptr;
|
|
|
|
{
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
auto it = task2thread.find(task);
|
|
|
|
if (it == task2thread.end()) {
|
|
LOG_WARNING("destroyTask called for an unknown task %p (%s)", (void*) task, task->name().c_str());
|
|
|
|
return TRI_ERROR_TASK_NOT_FOUND;
|
|
}
|
|
|
|
LOG_TRACE("destroyTask for task %p (%s)", (void*) task, task->name().c_str());
|
|
|
|
thread = (*it).second;
|
|
|
|
taskRegistered.erase(task);
|
|
task2thread.erase(it);
|
|
}
|
|
|
|
thread->destroyTask(task);
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief called to display current status
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Scheduler::reportStatus () {
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief sets the process affinity
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Scheduler::setProcessorAffinity (size_t i, size_t c) {
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
threads[i]->setProcessorAffinity(c);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief registers a new task
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int Scheduler::registerTask (Task* task, ssize_t* got, ssize_t want) {
|
|
if (task->isUserDefined() && task->id().empty()) {
|
|
// user-defined task without id is invalid
|
|
return TRI_ERROR_TASK_INVALID_ID;
|
|
}
|
|
|
|
std::string const& name = task->name();
|
|
LOG_TRACE("registerTask for task %p (%s)", (void*) task, name.c_str());
|
|
|
|
// determine thread
|
|
SchedulerThread* thread = nullptr;
|
|
size_t n = 0;
|
|
if (0 <= want) {
|
|
n = want;
|
|
|
|
if (nrThreads <= n) {
|
|
return TRI_ERROR_INTERNAL;
|
|
}
|
|
}
|
|
|
|
{
|
|
MUTEX_LOCKER(schedulerLock);
|
|
|
|
int res = checkInsertTask(task);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
return res;
|
|
}
|
|
|
|
if (0 > want) {
|
|
if (multiThreading && ! task->needsMainEventLoop()) {
|
|
n = (++nextLoop) % nrThreads;
|
|
}
|
|
}
|
|
|
|
thread = threads[n];
|
|
|
|
task2thread[task] = thread;
|
|
taskRegistered.emplace(task);
|
|
}
|
|
|
|
if (nullptr != got) {
|
|
*got = static_cast<ssize_t>(n);
|
|
}
|
|
|
|
if (! thread->registerTask(this, task)) {
|
|
return TRI_ERROR_INTERNAL;
|
|
}
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief check whether a task can be inserted
|
|
/// the caller must ensure the schedulerLock is held
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int Scheduler::checkInsertTask (Task const* task) {
|
|
if (task->isUserDefined()) {
|
|
// this is a user-defined task
|
|
|
|
// now check if there is an id conflict
|
|
std::string const& id = task->id();
|
|
|
|
for (auto& it : task2thread) {
|
|
auto const* t = it.first;
|
|
|
|
if (t->isUserDefined() && t->id() == id) {
|
|
return TRI_ERROR_TASK_DUPLICATE_ID;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- static private methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief inialises the signal handlers for the scheduler
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Scheduler::initialiseSignalHandlers () {
|
|
|
|
#ifdef _WIN32
|
|
// Windows does not support POSIX signal handling
|
|
#else
|
|
struct sigaction action;
|
|
memset(&action, 0, sizeof(action));
|
|
sigfillset(&action.sa_mask);
|
|
|
|
// ignore broken pipes
|
|
action.sa_handler = SIG_IGN;
|
|
|
|
int res = sigaction(SIGPIPE, &action, 0);
|
|
|
|
if (res < 0) {
|
|
LOG_ERROR("cannot initialise signal handlers for pipe");
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- END-OF-FILE
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Local Variables:
|
|
// mode: outline-minor
|
|
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
|
|
// End:
|