brpc/include/bthread/task_group.h
2022-12-14 19:05:52 +08:00

261 lines
9.3 KiB
C++

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
// bthread - A M:N threading library to make applications more concurrent.
// Date: Tue Jul 10 17:40:58 CST 2012
#ifndef BTHREAD_TASK_GROUP_H
#define BTHREAD_TASK_GROUP_H
#include "butil/time.h" // cpuwide_time_ns
#include "bthread/task_control.h"
#include "bthread/task_meta.h" // bthread_t, TaskMeta
#include "bthread/work_stealing_queue.h" // WorkStealingQueue
#include "bthread/remote_task_queue.h" // RemoteTaskQueue
#include "butil/resource_pool.h" // ResourceId
#include "bthread/parking_lot.h"
namespace bthread {
// For exiting a bthread.
class ExitException : public std::exception {
public:
explicit ExitException(void* value) : _value(value) {}
~ExitException() throw() {}
const char* what() const throw() override {
return "ExitException";
}
void* value() const {
return _value;
}
private:
void* _value;
};
// Thread-local group of tasks.
// Notice that most methods involving context switching are static otherwise
// pointer `this' may change after wakeup. The **pg parameters in following
// function are updated before returning.
class TaskGroup {
public:
// Create task `fn(arg)' with attributes `attr' in TaskGroup *pg and put
// the identifier into `tid'. Switch to the new task and schedule old task
// to run.
// Return 0 on success, errno otherwise.
static int start_foreground(TaskGroup** pg,
bthread_t* __restrict tid,
const bthread_attr_t* __restrict attr,
void * (*fn)(void*),
void* __restrict arg);
// Create task `fn(arg)' with attributes `attr' in this TaskGroup, put the
// identifier into `tid'. Schedule the new thread to run.
// Called from worker: start_background<false>
// Called from non-worker: start_background<true>
// Return 0 on success, errno otherwise.
template <bool REMOTE>
int start_background(bthread_t* __restrict tid,
const bthread_attr_t* __restrict attr,
void * (*fn)(void*),
void* __restrict arg);
// Suspend caller and run next bthread in TaskGroup *pg.
static void sched(TaskGroup** pg);
static void ending_sched(TaskGroup** pg);
// Suspend caller and run bthread `next_tid' in TaskGroup *pg.
// Purpose of this function is to avoid pushing `next_tid' to _rq and
// then being popped by sched(pg), which is not necessary.
static void sched_to(TaskGroup** pg, TaskMeta* next_meta);
static void sched_to(TaskGroup** pg, bthread_t next_tid);
static void exchange(TaskGroup** pg, bthread_t next_tid);
// The callback will be run in the beginning of next-run bthread.
// Can't be called by current bthread directly because it often needs
// the target to be suspended already.
typedef void (*RemainedFn)(void*);
void set_remained(RemainedFn cb, void* arg) {
_last_context_remained = cb;
_last_context_remained_arg = arg;
}
// Suspend caller for at least |timeout_us| microseconds.
// If |timeout_us| is 0, this function does nothing.
// If |group| is NULL or current thread is non-bthread, call usleep(3)
// instead. This function does not create thread-local TaskGroup.
// Returns: 0 on success, -1 otherwise and errno is set.
static int usleep(TaskGroup** pg, uint64_t timeout_us);
// Suspend caller and run another bthread. When the caller will resume
// is undefined.
static void yield(TaskGroup** pg);
// Suspend caller until bthread `tid' terminates.
static int join(bthread_t tid, void** return_value);
// Returns true iff the bthread `tid' still exists. Notice that it is
// just the result at this very moment which may change soon.
// Don't use this function unless you have to. Never write code like this:
// if (exists(tid)) {
// Wait for events of the thread. // Racy, may block indefinitely.
// }
static bool exists(bthread_t tid);
// Put attribute associated with `tid' into `*attr'.
// Returns 0 on success, -1 otherwise and errno is set.
static int get_attr(bthread_t tid, bthread_attr_t* attr);
// Get/set TaskMeta.stop of the tid.
static void set_stopped(bthread_t tid);
static bool is_stopped(bthread_t tid);
// The bthread running run_main_task();
bthread_t main_tid() const { return _main_tid; }
TaskStatistics main_stat() const;
// Routine of the main task which should be called from a dedicated pthread.
void run_main_task();
// current_task is a function in macOS 10.0+
#ifdef current_task
#undef current_task
#endif
// Meta/Identifier of current task in this group.
TaskMeta* current_task() const { return _cur_meta; }
bthread_t current_tid() const { return _cur_meta->tid; }
// Uptime of current task in nanoseconds.
int64_t current_uptime_ns() const
{ return butil::cpuwide_time_ns() - _cur_meta->cpuwide_start_ns; }
// True iff current task is the one running run_main_task()
bool is_current_main_task() const { return current_tid() == _main_tid; }
// True iff current task is in pthread-mode.
bool is_current_pthread_task() const
{ return _cur_meta->stack == _main_stack; }
// Active time in nanoseconds spent by this TaskGroup.
int64_t cumulated_cputime_ns() const { return _cumulated_cputime_ns; }
// Push a bthread into the runqueue
void ready_to_run(bthread_t tid, bool nosignal = false);
// Flush tasks pushed to rq but signalled.
void flush_nosignal_tasks();
// Push a bthread into the runqueue from another non-worker thread.
void ready_to_run_remote(bthread_t tid, bool nosignal = false);
void flush_nosignal_tasks_remote_locked(butil::Mutex& locked_mutex);
void flush_nosignal_tasks_remote();
// Automatically decide the caller is remote or local, and call
// the corresponding function.
void ready_to_run_general(bthread_t tid, bool nosignal = false);
void flush_nosignal_tasks_general();
// The TaskControl that this TaskGroup belongs to.
TaskControl* control() const { return _control; }
// Call this instead of delete.
void destroy_self();
// Wake up blocking ops in the thread.
// Returns 0 on success, errno otherwise.
static int interrupt(bthread_t tid, TaskControl* c);
// Get the meta associate with the task.
static TaskMeta* address_meta(bthread_t tid);
// Push a task into _rq, if _rq is full, retry after some time. This
// process make go on indefinitely.
void push_rq(bthread_t tid);
private:
friend class TaskControl;
// You shall use TaskControl::create_group to create new instance.
explicit TaskGroup(TaskControl*);
int init(size_t runqueue_capacity);
// You shall call destroy_self() instead of destructor because deletion
// of groups are postponed to avoid race.
~TaskGroup();
static void task_runner(intptr_t skip_remained);
// Callbacks for set_remained()
static void _release_last_context(void*);
static void _add_sleep_event(void*);
struct ReadyToRunArgs {
bthread_t tid;
bool nosignal;
};
static void ready_to_run_in_worker(void*);
static void ready_to_run_in_worker_ignoresignal(void*);
// Wait for a task to run.
// Returns true on success, false is treated as permanent error and the
// loop calling this function should end.
bool wait_task(bthread_t* tid);
bool steal_task(bthread_t* tid) {
if (_remote_rq.pop(tid)) {
return true;
}
#ifndef BTHREAD_DONT_SAVE_PARKING_STATE
_last_pl_state = _pl->get_state();
#endif
return _control->steal_task(tid, &_steal_seed, _steal_offset);
}
#ifndef NDEBUG
int _sched_recursive_guard;
#endif
TaskMeta* _cur_meta;
// the control that this group belongs to
TaskControl* _control;
int _num_nosignal;
int _nsignaled;
// last scheduling time
int64_t _last_run_ns;
int64_t _cumulated_cputime_ns;
size_t _nswitch;
RemainedFn _last_context_remained;
void* _last_context_remained_arg;
ParkingLot* _pl;
#ifndef BTHREAD_DONT_SAVE_PARKING_STATE
ParkingLot::State _last_pl_state;
#endif
size_t _steal_seed;
size_t _steal_offset;
ContextualStack* _main_stack;
bthread_t _main_tid;
WorkStealingQueue<bthread_t> _rq;
RemoteTaskQueue _remote_rq;
int _remote_num_nosignal;
int _remote_nsignaled;
};
} // namespace bthread
#include "task_group_inl.h"
#endif // BTHREAD_TASK_GROUP_H