Skip to content

Commit c331732

Browse files
committed
io_,task_scheduler: Add scheduleOnceNoAlloc.
1 parent 3c22a9e commit c331732

5 files changed

Lines changed: 42 additions & 5 deletions

File tree

software/src/modules/io_scheduler/io_scheduler.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ void IoScheduler::drive(RoundStateBase *state)
9696

9797
state->in_flight = true;
9898

99-
this->scheduleOnce([state]() {
99+
this->scheduleOnceNoAlloc(&state->during_io_task_buf, [state]() {
100100
state->run_during_io();
101101

102-
task_scheduler.scheduleOnce([state]() {
102+
task_scheduler.scheduleOnceNoAlloc(&state->after_io_task_buf, [state]() {
103103
state->run_after_io();
104104
state->in_flight = false;
105105
}, 0_ms, state->src_location);

software/src/modules/io_scheduler/io_scheduler.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ class IoScheduler final : public IModule
5252
return scheduler.scheduleOnce(std::move(fn), delay_ms, src_location);
5353
}
5454

55+
uint64_t scheduleOnceNoAlloc(aligned_storage<Task> *task_buf, std::function<void(void)> &&fn, millis_t delay_ms = 0_ms, const std::source_location &src_location = std::source_location::current())
56+
{
57+
return scheduler.scheduleOnceNoAlloc(task_buf, std::move(fn), delay_ms, src_location);
58+
}
59+
5560
// Registers an uncancelable driven round.
5661
// before_io and after_io run on the main task, during_io runs on the IO task.
5762
// Pass nullptr for before_io/after_io if unused.
@@ -124,6 +129,8 @@ class IoScheduler final : public IModule
124129
[[noreturn]] static void task_fn(void *arg);
125130

126131
struct RoundStateBase {
132+
aligned_storage<Task> during_io_task_buf;
133+
aligned_storage<Task> after_io_task_buf;
127134
const std::source_location src_location;
128135
bool in_flight = false; // Only accessed on the main task.
129136

software/src/modules/task_scheduler/task_scheduler.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,14 +248,21 @@ bool TaskScheduler::nextTaskReady()
248248
return next_deadline <= now;
249249
}
250250

251-
uint64_t TaskScheduler::scheduleOnce(std::function<void(void)> &&fn, millis_t delay_ms, const std::source_location &src_location)
251+
uint64_t TaskScheduler::scheduleOnceNoAlloc(aligned_storage<Task> *task_buffer, std::function<void(void)> &&fn, millis_t delay_ms, const std::source_location &src_location, bool transfer_task_ownership)
252252
{
253253
std::lock_guard<std::mutex> lock{this->task_mutex};
254254
uint64_t task_id = ++last_task_id;
255-
tasks.emplace(new Task(std::move(fn), task_id, delay_ms, 0_us, src_location.file_name(), src_location.line(), true));
255+
Task *ptr = new(task_buffer) Task(std::move(fn), task_id, delay_ms, 0_us, src_location.file_name(), src_location.line(), true);
256+
ptr->owned = transfer_task_ownership;
257+
tasks.emplace(ptr);
256258
return task_id;
257259
}
258260

261+
uint64_t TaskScheduler::scheduleOnce(std::function<void(void)> &&fn, millis_t delay_ms, const std::source_location &src_location)
262+
{
263+
return this->scheduleOnceNoAlloc(new aligned_storage<Task>, std::move(fn), delay_ms, src_location, true);
264+
}
265+
259266
uint64_t TaskScheduler::scheduleWithFixedDelay(std::function<void(void)> &&fn, millis_t first_delay_ms, millis_t delay_ms, const std::source_location &src_location)
260267
{
261268
std::lock_guard<std::mutex> lock{this->task_mutex};
@@ -268,7 +275,9 @@ uint64_t TaskScheduler::scheduleUncancelable(std::function<void(void)> &&fn, mil
268275
{
269276
std::lock_guard<std::mutex> lock{this->task_mutex};
270277
uint64_t task_id = ++last_task_id | (1ull << 62ull);
271-
tasks.emplace(perm_new<Task>(DRAM, std::move(fn), task_id, first_delay_ms, delay_ms, src_location.file_name(), src_location.line(), false));
278+
auto *task = perm_new<Task>(DRAM, std::move(fn), task_id, first_delay_ms, delay_ms, src_location.file_name(), src_location.line(), false);
279+
task->owned = false;
280+
tasks.emplace(task);
272281
return task_id;
273282
}
274283

software/src/modules/task_scheduler/task_scheduler.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,22 @@ struct Task {
4646
// Task is currently running while another thread (or fn itself) called rescheduleNow with this task's ID.
4747
// Will be executed as next task again.
4848
bool immediate_reschedule;
49+
// Task was allocated by the task scheduler.
50+
bool owned = true;
4951

5052
Task(std::function<void(void)> &&fn, uint64_t task_id, micros_t first_run_delay, micros_t delay, const char *file, uint_least32_t line, bool once);
5153
};
5254

55+
// https://stackoverflow.com/a/50957671
56+
// This specializes the default deleter used by std::unique_ptr to only delete a Task if it is owned.
57+
template <>
58+
struct std::default_delete<Task> {
59+
default_delete() = default;
60+
template <class U>
61+
constexpr default_delete(default_delete<U>) noexcept {}
62+
void operator()(Task* p) const noexcept { if (p->owned) delete p; }
63+
};
64+
5365
#define IS_WALL_CLOCK_TASK_ID(task_id) (task_id & (1ull << 63))
5466
#define IS_UNCANCELABLE_TASK_ID(task_id) (task_id & (1ull << 62))
5567

@@ -123,6 +135,7 @@ class TaskScheduler final : public IModule
123135
bool nextTaskReady();
124136

125137
uint64_t scheduleOnce(std::function<void(void)> &&fn, millis_t delay_ms = 0_ms, const std::source_location &src_location = std::source_location::current());
138+
uint64_t scheduleOnceNoAlloc(aligned_storage<Task> *task_buffer, std::function<void(void)> &&fn, millis_t delay_ms = 0_ms, const std::source_location &src_location = std::source_location::current(), bool transfer_task_ownership = false);
126139

127140
[[nodiscard("Use scheduleUncancelable if you don't need the returned task ID to cancel this task later. Cast to void if you intend to write a self-canceling task that will use task_scheduler.currentTaskId()")]]
128141
inline uint64_t scheduleWithFixedDelay(std::function<void(void)> &&fn, millis_t delay_ms, const std::source_location &src_location = std::source_location::current()) {return this->scheduleWithFixedDelay(std::move(fn), 0_ms, delay_ms, src_location);}

software/src/tools.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,11 @@ struct PointerWithBits {
297297
[[gnu::always_inline]]
298298
T *get_ptr() const { return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(this->ptr) & (~BITMASK)); }
299299
};
300+
301+
// std::aligned_storage is deprecated in C++23.
302+
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1413r3.pdf
303+
// Use this as an replacement.
304+
template <typename T>
305+
struct aligned_storage {
306+
alignas(T) std::byte buf[sizeof(T)];
307+
};

0 commit comments

Comments
 (0)