124c408e6SJim Walker/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
224c408e6SJim Walker/*
324c408e6SJim Walker *     Copyright 2016 Couchbase, Inc.
424c408e6SJim Walker *
524c408e6SJim Walker *   Licensed under the Apache License, Version 2.0 (the "License");
624c408e6SJim Walker *   you may not use this file except in compliance with the License.
724c408e6SJim Walker *   You may obtain a copy of the License at
824c408e6SJim Walker *
924c408e6SJim Walker *       http://www.apache.org/licenses/LICENSE-2.0
1024c408e6SJim Walker *
1124c408e6SJim Walker *   Unless required by applicable law or agreed to in writing, software
1224c408e6SJim Walker *   distributed under the License is distributed on an "AS IS" BASIS,
1324c408e6SJim Walker *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1424c408e6SJim Walker *   See the License for the specific language governing permissions and
1524c408e6SJim Walker *   limitations under the License.
1624c408e6SJim Walker */
1724c408e6SJim Walker
1824c408e6SJim Walker#pragma once
1924c408e6SJim Walker
2024c408e6SJim Walker#include "atomic.h"
2124c408e6SJim Walker#include "config.h"
224bf14710SJames Harrison#include "task_type.h"
2324c408e6SJim Walker
2474551d34SDave Rigby#include <platform/processclock.h>
2574551d34SDave Rigby#include <platform/sized_buffer.h>
2674551d34SDave Rigby#include <array>
2774551d34SDave Rigby
2824c408e6SJim Walkerenum task_state_t {
2924c408e6SJim Walker    TASK_RUNNING,
3024c408e6SJim Walker    TASK_SNOOZED,
3124c408e6SJim Walker    TASK_DEAD
3224c408e6SJim Walker};
3324c408e6SJim Walker
347dfe4a57SJames Harrisonstd::string to_string(task_state_t state);
357dfe4a57SJames Harrison
361adbeb80SJim Walkerenum class TaskId : int {
374bf14710SJames Harrison#define TASK(name, type, prio) name,
381adbeb80SJim Walker#include "tasks.def.h"
391adbeb80SJim Walker#undef TASK
401adbeb80SJim Walker    TASK_COUNT
411adbeb80SJim Walker};
421adbeb80SJim Walker
431adbeb80SJim Walkertypedef int queue_priority_t;
441adbeb80SJim Walker
451adbeb80SJim Walkerenum class TaskPriority : int {
464bf14710SJames Harrison#define TASK(name, type, prio) name = prio,
471adbeb80SJim Walker#include "tasks.def.h"
481adbeb80SJim Walker#undef TASK
491adbeb80SJim Walker    PRIORITY_COUNT
501adbeb80SJim Walker};
511adbeb80SJim Walker
5224c408e6SJim Walkerclass Taskable;
5324c408e6SJim Walkerclass EventuallyPersistentEngine;
5424c408e6SJim Walker
55f48fae53SJim Walkerclass GlobalTask {
56f48fae53SJim Walker    friend class CompareByDueDate;
57f48fae53SJim Walker    friend class CompareByPriority;
58f48fae53SJim Walker    friend class ExecutorPool;
59f48fae53SJim Walker    friend class ExecutorThread;
6024c408e6SJim Walkerpublic:
6124c408e6SJim Walker
621adbeb80SJim Walker    GlobalTask(Taskable& t,
631adbeb80SJim Walker               TaskId taskId,
641adbeb80SJim Walker               double sleeptime = 0,
651adbeb80SJim Walker               bool completeBeforeShutdown = true);
6624c408e6SJim Walker
671adbeb80SJim Walker    GlobalTask(EventuallyPersistentEngine *e,
681adbeb80SJim Walker               TaskId taskId,
691adbeb80SJim Walker               double sleeptime = 0,
701adbeb80SJim Walker               bool completeBeforeShutdown = true);
7124c408e6SJim Walker
7224c408e6SJim Walker    /* destructor */
7324c408e6SJim Walker    virtual ~GlobalTask(void) {
7424c408e6SJim Walker    }
7524c408e6SJim Walker
7624c408e6SJim Walker    /**
7724c408e6SJim Walker     * The invoked function when the task is executed.
7824c408e6SJim Walker     *
79e158d66eSDave Rigby     * If the task wants to run again, it should return true - it will be
80e158d66eSDave Rigby     * added back onto the ready queue and scheduled according to it's
81e158d66eSDave Rigby     * priority. To run again but at a later time, call snooze() specifying
82e158d66eSDave Rigby     * how long to sleep before it should be scheduled again.
83e158d66eSDave Rigby     * If the task is complete (and should never be run again), return false.
84e158d66eSDave Rigby     *
8524c408e6SJim Walker     * @return Whether or not this task should be rescheduled
8624c408e6SJim Walker     */
87e158d66eSDave Rigby    virtual bool run() = 0;
8824c408e6SJim Walker
8924c408e6SJim Walker    /**
9024c408e6SJim Walker     * Gives a description of this task.
9124c408e6SJim Walker     *
9224c408e6SJim Walker     * @return A description of this task
9324c408e6SJim Walker     */
9444a3fd5dSDave Rigby    virtual std::string getDescription() = 0;
9524c408e6SJim Walker
969f76e3c1SDave Rigby    /**
970ca3887cSDave Rigby     * The maximum expected duration of a single execution of this task - i.e.
980ca3887cSDave Rigby     * how long should run() take.
990ca3887cSDave Rigby     * Any task executions taking longer than this to run will be logged as
1000ca3887cSDave Rigby     * "slow".
1010ca3887cSDave Rigby     *
1020ca3887cSDave Rigby     * Exact values will vary significantly depending on the class of task;
1030ca3887cSDave Rigby     * however here are some general principles:
1040ca3887cSDave Rigby     *
1050ca3887cSDave Rigby     *   1. Our scheduler is non-preemptive, so a long-running task *cannot* be
1060ca3887cSDave Rigby     *      interrupted to allow another (possibly higher priority) task to run.
1070ca3887cSDave Rigby     *      As such, tasks in general should aim to only run for a brief
1080ca3887cSDave Rigby     *      duration at a time; for example at most 25ms (typical OS scheduler
1090ca3887cSDave Rigby     *      time-slice).
1100ca3887cSDave Rigby     *   2. Select a suitable limit for the given task - if a task is expected
1110ca3887cSDave Rigby     *      to complete in 1us; it isn't very useful to specify a limit of 25ms
1120ca3887cSDave Rigby     *      as we will fail to log any executions which are abnormally slow
1130ca3887cSDave Rigby     *      (even if they arn't causing scheduling issues for other tasks).
1140ca3887cSDave Rigby     *   3. Tasks which block other tasks while they run should aim to minimize
1150ca3887cSDave Rigby     *      their runtime - 25ms would be a significant delay to add to
1160ca3887cSDave Rigby     *      front-end operations if a particular task (e.g. HashTableResizer)
1170ca3887cSDave Rigby     *      blocks FE operations while running.
1180ca3887cSDave Rigby     *   4. One-off, startup tasks (e.g. Warmup) can take as long as necessary -
1190ca3887cSDave Rigby     *      given they must run before we can operate their duration isn't
1200ca3887cSDave Rigby     *      critical.
1219f76e3c1SDave Rigby     */
1220ca3887cSDave Rigby    virtual std::chrono::microseconds maxExpectedDuration() = 0;
12324c408e6SJim Walker
12424c408e6SJim Walker    /**
12524c408e6SJim Walker     * test if a task is dead
12624c408e6SJim Walker     */
12724c408e6SJim Walker     bool isdead(void) {
12824c408e6SJim Walker        return (state == TASK_DEAD);
12924c408e6SJim Walker     }
13024c408e6SJim Walker
13124c408e6SJim Walker
13224c408e6SJim Walker    /**
13324c408e6SJim Walker     * Cancels this task by marking it dead.
13424c408e6SJim Walker     */
13524c408e6SJim Walker    void cancel(void) {
13624c408e6SJim Walker        state = TASK_DEAD;
13724c408e6SJim Walker    }
13824c408e6SJim Walker
13924c408e6SJim Walker    /**
14024c408e6SJim Walker     * Puts the task to sleep for a given duration.
14124c408e6SJim Walker     */
14224c408e6SJim Walker    virtual void snooze(const double secs);
14324c408e6SJim Walker
144e158d66eSDave Rigby    /// Wake up a task, setting it to run as soon as possible.
145e158d66eSDave Rigby    void wakeUp();
146e158d66eSDave Rigby
14724c408e6SJim Walker    /**
14824c408e6SJim Walker     * Returns the id of this task.
14924c408e6SJim Walker     *
15024c408e6SJim Walker     * @return A unique task id number.
15124c408e6SJim Walker     */
1521adbeb80SJim Walker    size_t getId() const { return uid; }
15324c408e6SJim Walker
15424c408e6SJim Walker    /**
155482fe784SPaolo Cocchi     * Returns the id of this task.
15624c408e6SJim Walker     *
157482fe784SPaolo Cocchi     * @return The id of this task.
15824c408e6SJim Walker     */
159482fe784SPaolo Cocchi    TaskId getTaskId() {
160482fe784SPaolo Cocchi        return taskId;
161482fe784SPaolo Cocchi    }
16224c408e6SJim Walker
16324c408e6SJim Walker    /**
16424c408e6SJim Walker     * Gets the engine that this task was scheduled from
16524c408e6SJim Walker     *
16624c408e6SJim Walker     * @returns A handle to the engine
16724c408e6SJim Walker     */
16824c408e6SJim Walker    EventuallyPersistentEngine* getEngine() { return engine; }
16924c408e6SJim Walker
17024c408e6SJim Walker    task_state_t getState(void) {
17124c408e6SJim Walker        return state.load();
17224c408e6SJim Walker    }
17324c408e6SJim Walker
174fe6b9e03SDave Rigby    void setState(task_state_t tstate, task_state_t expected) {
175fe6b9e03SDave Rigby        state.compare_exchange_strong(expected, tstate);
17624c408e6SJim Walker    }
17724c408e6SJim Walker
17824c408e6SJim Walker    Taskable& getTaskable() const {
17924c408e6SJim Walker        return taskable;
18024c408e6SJim Walker    }
18124c408e6SJim Walker
1827f156521SDave Rigby    ProcessClock::time_point getWaketime() const {
1837f156521SDave Rigby        const auto waketime_chrono = std::chrono::nanoseconds(waketime);
1847f156521SDave Rigby        return ProcessClock::time_point(waketime_chrono);
18524c408e6SJim Walker    }
18624c408e6SJim Walker
1877dfe4a57SJames Harrison    void updateWaketime(ProcessClock::time_point tp) {
1887f156521SDave Rigby        waketime = to_ns_since_epoch(tp).count();
18924c408e6SJim Walker    }
19024c408e6SJim Walker
1917dfe4a57SJames Harrison    void updateWaketimeIfLessThan(ProcessClock::time_point tp) {
192d55c867eSSergey Avseyev        const int64_t tp_ns = to_ns_since_epoch(tp).count();
19317cbd824SDave Rigby        atomic_setIfBigger(waketime, tp_ns);
19424c408e6SJim Walker    }
19524c408e6SJim Walker
1967dfe4a57SJames Harrison    ProcessClock::time_point getLastStartTime() const {
1977dfe4a57SJames Harrison        const auto waketime_chrono = std::chrono::nanoseconds(lastStartTime);
1987dfe4a57SJames Harrison        return ProcessClock::time_point(waketime_chrono);
1997dfe4a57SJames Harrison    }
2007dfe4a57SJames Harrison
2017dfe4a57SJames Harrison    void updateLastStartTime(ProcessClock::time_point tp) {
2027dfe4a57SJames Harrison        lastStartTime = to_ns_since_epoch(tp).count();
2037dfe4a57SJames Harrison    }
2047dfe4a57SJames Harrison
2057dfe4a57SJames Harrison    ProcessClock::duration getTotalRuntime() const {
2067dfe4a57SJames Harrison        return std::chrono::nanoseconds(totalRuntime);
2077dfe4a57SJames Harrison    }
2087dfe4a57SJames Harrison
209da1ddf1eSJames Harrison    ProcessClock::duration getPrevRuntime() const {
210da1ddf1eSJames Harrison        return std::chrono::nanoseconds(previousRuntime);
211da1ddf1eSJames Harrison    }
212da1ddf1eSJames Harrison
2137dfe4a57SJames Harrison    void updateRuntime(ProcessClock::duration tp) {
214da1ddf1eSJames Harrison        int64_t nanoseconds =
215da1ddf1eSJames Harrison                std::chrono::duration_cast<std::chrono::nanoseconds>(tp)
216da1ddf1eSJames Harrison                        .count();
217da1ddf1eSJames Harrison        totalRuntime += nanoseconds;
218da1ddf1eSJames Harrison        previousRuntime = nanoseconds;
2197dfe4a57SJames Harrison    }
2207dfe4a57SJames Harrison
2211adbeb80SJim Walker    queue_priority_t getQueuePriority() const {
2221adbeb80SJim Walker        return static_cast<queue_priority_t>(priority);
2231adbeb80SJim Walker    }
2241adbeb80SJim Walker
2251adbeb80SJim Walker    /*
2261adbeb80SJim Walker     * Lookup the task name for TaskId id.
2271adbeb80SJim Walker     * The data used is generated from tasks.def.h
2281adbeb80SJim Walker     */
2291adbeb80SJim Walker    static const char* getTaskName(TaskId id);
2301adbeb80SJim Walker
2311adbeb80SJim Walker    /*
2321adbeb80SJim Walker     * Lookup the task priority for TaskId id.
2331adbeb80SJim Walker     * The data used is generated from tasks.def.h
2341adbeb80SJim Walker     */
2351adbeb80SJim Walker    static TaskPriority getTaskPriority(TaskId id);
2361adbeb80SJim Walker
2374bf14710SJames Harrison    /*
2384bf14710SJames Harrison     * Lookup the task type for TaskId id.
2394bf14710SJames Harrison     * The data used is generated from tasks.def.h
2404bf14710SJames Harrison     */
2414bf14710SJames Harrison    static task_type_t getTaskType(TaskId id);
2424bf14710SJames Harrison
2431adbeb80SJim Walker    /*
2441adbeb80SJim Walker     * A vector of all TaskId generated from tasks.def.h
2451adbeb80SJim Walker     */
2461adbeb80SJim Walker    static std::array<TaskId, static_cast<int>(TaskId::TASK_COUNT)> allTaskIds;
2471adbeb80SJim Walker
24824c408e6SJim Walkerprotected:
2497dfe4a57SJames Harrison    /**
2507dfe4a57SJames Harrison     * We are using a int64_t as opposed to ProcessTime::time_point because we
2517dfe4a57SJames Harrison     * want the access to be atomic without the use of a mutex. The reason for
2527dfe4a57SJames Harrison     * this is that we update these timepoints in locations which have been
2537dfe4a57SJames Harrison     * shown to be pretty hot (e.g. CompareByDueDate) and we want to avoid
2547dfe4a57SJames Harrison     * the overhead of acquiring mutexes.
2557dfe4a57SJames Harrison     */
2567dfe4a57SJames Harrison    using atomic_time_point = std::atomic<int64_t>;
2577dfe4a57SJames Harrison    using atomic_duration = std::atomic<int64_t>;
25824c408e6SJim Walker    bool blockShutdown;
25924c408e6SJim Walker    std::atomic<task_state_t> state;
2601adbeb80SJim Walker    const size_t uid;
261482fe784SPaolo Cocchi    const TaskId taskId;
2621adbeb80SJim Walker    TaskPriority priority;
26324c408e6SJim Walker    EventuallyPersistentEngine *engine;
26424c408e6SJim Walker    Taskable& taskable;
26524c408e6SJim Walker
26624c408e6SJim Walker    static std::atomic<size_t> task_id_counter;
26724c408e6SJim Walker    static size_t nextTaskId() { return task_id_counter.fetch_add(1); }
26824c408e6SJim Walker
2697dfe4a57SJames Harrison    atomic_duration totalRuntime;
270da1ddf1eSJames Harrison    atomic_duration previousRuntime;
2717dfe4a57SJames Harrison    atomic_time_point lastStartTime;
27224c408e6SJim Walker
27324c408e6SJim Walkerprivate:
2747dfe4a57SJames Harrison    atomic_time_point waketime; // used for priority_queue
27524c408e6SJim Walker};
27624c408e6SJim Walker
277f48fae53SJim Walkertypedef std::shared_ptr<GlobalTask> ExTask;
27824c408e6SJim Walker
27924c408e6SJim Walker/**
28024c408e6SJim Walker * Order tasks by their priority and taskId (try to ensure FIFO)
2811adbeb80SJim Walker * @return true if t2 should have priority over t1
28224c408e6SJim Walker */
28324c408e6SJim Walkerclass CompareByPriority {
28424c408e6SJim Walkerpublic:
28524c408e6SJim Walker    bool operator()(ExTask &t1, ExTask &t2) {
2861adbeb80SJim Walker        return (t1->getQueuePriority() == t2->getQueuePriority()) ?
2871adbeb80SJim Walker               (t1->uid > t2->uid) :
2881adbeb80SJim Walker               (t1->getQueuePriority() > t2->getQueuePriority());
28924c408e6SJim Walker    }
29024c408e6SJim Walker};
29124c408e6SJim Walker
29224c408e6SJim Walker/**
29324c408e6SJim Walker * Order tasks by their ready date.
2941adbeb80SJim Walker * @return true if t2 should have priority over t1
29524c408e6SJim Walker */
29624c408e6SJim Walkerclass CompareByDueDate {
29724c408e6SJim Walkerpublic:
29824c408e6SJim Walker    bool operator()(ExTask &t1, ExTask &t2) {
29924c408e6SJim Walker        return t2->waketime < t1->waketime;
30024c408e6SJim Walker    }
3011adbeb80SJim Walker};
302