Last active
January 16, 2022 04:00
-
-
Save kirkshoop/9339e46b601126d5c1dad68c55a56200 to your computer and use it in GitHub Desktop.
interrupt_context
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2019-present Facebook, Inc. | |
* | |
* 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. | |
*/ | |
#include <Arduino.h> | |
#include "stdlibpatch.h" | |
#include "interrupt_context.hpp" | |
namespace unifex { | |
//static | |
interrupt_context* interrupt_context::self_ = nullptr; | |
interrupt_context::interrupt_context() | |
{ | |
assert(self_ == nullptr); | |
self_ = this; | |
} | |
interrupt_context::~interrupt_context() { | |
stop_ = true; | |
assert(head_ == nullptr); | |
self_ = nullptr; | |
} | |
void interrupt_context::enqueue(task_base* task) noexcept { | |
noInterrupts(); // disable all interrupts while interrupt parameters are changing | |
// Insert at the head of the queue. | |
task->next_ = head_; | |
task->prevNextPtr_ = &head_; | |
if (head_ != nullptr) { | |
head_->prevNextPtr_ = &task->next_; | |
} | |
head_ = task; | |
interrupts(); // enable all interrupts | |
} | |
// static | |
void interrupt_context::run() { | |
auto cursor_ = self_->head_; | |
while (!self_->stop_ && cursor_ != nullptr) { | |
auto task_ = cursor_; | |
cursor_ = cursor_->next_; | |
task_->execute(); | |
} | |
} | |
void _interrupt_context::cancel_callback::operator()() noexcept { | |
if (task_->prevNextPtr_ != nullptr) { | |
// Task is still in the queue, dequeue and requeue it. | |
noInterrupts(); // disable all interrupts while interrupt parameters are changing | |
// Remove from the queue. | |
*task_->prevNextPtr_ = task_->next_; | |
if (task_->next_ != nullptr) { | |
task_->next_->prevNextPtr_ = task_->prevNextPtr_; | |
} | |
task_->prevNextPtr_ = nullptr; | |
interrupts(); // enable all interrupts | |
} | |
} | |
void interrupt_context::attach(std::uint8_t pin, std::uint8_t mode) { | |
attachInterrupt(digitalPinToInterrupt(pin), run, mode); | |
} | |
} // namespace unifex |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2019-present Facebook, Inc. | |
* | |
* 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. | |
*/ | |
#pragma once | |
#include <cassert> | |
#include <type_traits> | |
#include <unifex/config.hpp> | |
#include <unifex/get_stop_token.hpp> | |
#include <unifex/manual_lifetime.hpp> | |
#include <unifex/receiver_concepts.hpp> | |
#include <unifex/scheduler_concepts.hpp> | |
#include <unifex/stop_token_concepts.hpp> | |
#include "sequence.hpp" | |
#include <unifex/detail/prologue.hpp> | |
namespace unifex { | |
class interrupt_context; | |
namespace _interrupt_context { | |
struct task_base { | |
using execute_fn = void(task_base*) noexcept; | |
explicit task_base(interrupt_context& context, execute_fn* execute) noexcept | |
: context_(&context), execute_(execute) {} | |
interrupt_context* const context_; | |
task_base* next_ = nullptr; | |
task_base** prevNextPtr_ = nullptr; | |
execute_fn* execute_; | |
std::ptrdiff_t index_ = 0; | |
void execute() noexcept { | |
execute_(this); | |
} | |
}; | |
class cancel_callback { | |
task_base* const task_; | |
public: | |
explicit cancel_callback(task_base* task) noexcept | |
: task_(task) {} | |
void operator()() noexcept; | |
}; | |
template <typename Receiver> | |
struct _interrupts_op { | |
class type; | |
class unwinder; | |
}; | |
template <typename Receiver> | |
using interrupts_operation = typename _interrupts_op<remove_cvref_t<Receiver>>::type; | |
template<typename Receiver> | |
struct _interrupts_op<Receiver>::unwinder { | |
using op_t = typename _interrupts_op<Receiver>::type; | |
op_t* op_; | |
Receiver& get_receiver() { | |
return op_->receiver_; | |
} | |
template <typename Cpo, typename... Vn> | |
friend void tag_invoke(unifex::tag_t<unifex::unwind>, unwinder& self, Cpo cpo, Vn&&... vn) noexcept { | |
unifex::unwound(self.get_receiver(), cpo, (Vn&&) vn...); | |
} | |
}; | |
template <typename Receiver> | |
class _interrupts_op<Receiver>::type final : task_base { | |
using unwinder_t = typename _interrupts_op<Receiver>::unwinder; | |
friend class _interrupts_op<Receiver>::unwinder; | |
static void execute_impl(task_base* p) noexcept { | |
auto& self = *static_cast<type*>(p); | |
self.cancelCallback_.destruct(); | |
if constexpr (is_stop_never_possible_v< | |
stop_token_type_t<Receiver&>>) { | |
unifex::set_index(static_cast<Receiver&&>(self.receiver_), p->index_++); | |
} else { | |
if (get_stop_token(self.receiver_).stop_requested()) { | |
unifex::set_done(static_cast<Receiver&&>(self.receiver_)); | |
} else { | |
unifex::set_index(static_cast<Receiver&&>(self.receiver_), p->index_++); | |
} | |
} | |
} | |
UNIFEX_NO_UNIQUE_ADDRESS Receiver receiver_; | |
UNIFEX_NO_UNIQUE_ADDRESS manual_lifetime<typename stop_token_type_t< | |
Receiver&>::template callback_type<cancel_callback>> | |
cancelCallback_; | |
public: | |
template <typename Receiver2> | |
explicit type( | |
interrupt_context& scheduler, | |
Receiver2&& receiver) | |
: task_base(scheduler, &type::execute_impl) | |
, receiver_((Receiver2 &&) receiver) { | |
} | |
friend unwinder_t tag_invoke(unifex::tag_t<unifex::get_unwinder>, const type& self) noexcept { | |
return unwinder_t{const_cast<type*>(&self)}; | |
} | |
friend void tag_invoke(unifex::tag_t<unifex::step>, type&) noexcept { | |
} | |
void start() noexcept; | |
}; | |
class interrupts_sender { | |
friend class unifex::interrupt_context; | |
explicit interrupts_sender( | |
const interrupt_context& context) | |
: context_(&context) {} | |
mutable interrupt_context* context_; | |
public: | |
template < | |
template <typename...> class Variant, | |
template <typename...> class Tuple> | |
using value_types = Variant<Tuple<std::ptrdiff_t>>; | |
template <template <typename...> class Variant> | |
using error_types = Variant<>; | |
static constexpr bool sends_done = true; | |
template <typename Receiver> | |
interrupts_operation<remove_cvref_t<Receiver>> connect(Receiver&& receiver) { | |
return interrupts_operation<remove_cvref_t<Receiver>>{ | |
*context_, (Receiver &&) receiver}; | |
} | |
}; | |
} // namespace _interrupt_context | |
class interrupt_context { | |
using task_base = _interrupt_context::task_base; | |
using cancel_callback = _interrupt_context::cancel_callback; | |
friend cancel_callback; | |
template <typename Receiver> | |
friend struct _interrupt_context::_interrupts_op; | |
void enqueue(task_base* task) noexcept; | |
// Head of a linked-list in ascending order of due-time. | |
task_base* head_ = nullptr; | |
bool stop_ = false; | |
static void run(); | |
static interrupt_context* self_; | |
public: | |
interrupt_context(); | |
~interrupt_context(); | |
void attach(std::uint8_t pin, std::uint8_t mode); | |
auto all_interrupts() const noexcept { | |
return _interrupt_context::interrupts_sender{*this}; | |
} | |
}; | |
namespace _interrupt_context { | |
template <typename Receiver> | |
inline void _interrupts_op<Receiver>::type::start() noexcept { | |
cancelCallback_.construct( | |
get_stop_token(receiver_), cancel_callback{this}); | |
this->context_->enqueue(this); | |
} | |
} // namespace _interrupt_context | |
} // namespace unifex | |
#include <unifex/detail/epilogue.hpp> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment