-
Notifications
You must be signed in to change notification settings - Fork 250
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This data structure uses an AVL tree to maintain a time-sorted set of alarm objects. It supports the use of circular clocks of an bit width. Issue #5138
- Loading branch information
Showing
11 changed files
with
559 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Scenario that tests 'Genode::Alarm_registry' |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
_/src/init | ||
_/src/test-alarm |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
2024-03-13 c282e266e79dc12297300db0f9eddbfff3d83d1c |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<runtime ram="32M" caps="1000" binary="test-alarm"> | ||
|
||
<fail after_seconds="20"/> | ||
<succeed> | ||
[init] in range [1...3]: a1 | ||
[init] in range [1...3]: a2 | ||
[init] in range [1...3]: a3 | ||
[init] in range [3...1]: a3 | ||
[init] in range [3...1]: a4 | ||
[init] in range [3...1]: a0 | ||
[init] in range [3...1]: a1* | ||
[init] soonest(5) -> 0* | ||
[init] Test succeeded. | ||
</succeed> | ||
|
||
<content> | ||
<rom label="ld.lib.so"/> | ||
<rom label="test-alarm"/> | ||
</content> | ||
|
||
<config/> | ||
|
||
</runtime> |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
SRC_DIR = src/test/alarm src/include/base/internal | ||
include $(GENODE_DIR)/repos/base/recipes/src/content.inc |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
2024-03-13 89f82089c7e23c62627f04af81fdc19ee17bfad3 |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
base |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,281 @@ | ||
/* | ||
* \brief Registry of time-sorted alarms | ||
* \author Norman Feske | ||
* \date 2024-03-06 | ||
*/ | ||
|
||
/* | ||
* Copyright (C) 2024 Genode Labs GmbH | ||
* | ||
* This file is part of the Genode OS framework, which is distributed | ||
* under the terms of the GNU Affero General Public License version 3. | ||
*/ | ||
|
||
#ifndef _INCLUDE__BASE__INTERNAL__ALARM_REGISTRY_H_ | ||
#define _INCLUDE__BASE__INTERNAL__ALARM_REGISTRY_H_ | ||
|
||
/* Genode includes */ | ||
#include <base/log.h> | ||
|
||
namespace Genode { template <typename, typename> class Alarm_registry; } | ||
|
||
|
||
/** | ||
* Registry of schedules alarm objects | ||
* | ||
* \param T alarm type, must be derived from 'Alarm_registry::Element' | ||
* \param CLOCK type representing a circular clock | ||
* | ||
* The registry represents a set of scheduled alarms. An alarm object is | ||
* scheduled upon creation and de-scheduled on destruction. | ||
* | ||
* The 'CLOCK' type must be constructible with an '<unsigned>' numeric value | ||
* where '<unsigned>' can be an unsigned integer of any byte width. | ||
* The clock provides the following interface: | ||
* | ||
* ! static constexpr <unsigned> MASK = <lsb-bitmask>; | ||
* ! <unsigned> value() const; | ||
* ! void print(Output &) const; | ||
* | ||
* 'MASK' defines the limit of the circular clock. | ||
* The 'value()' method returns a number between 0 and MASK. | ||
* The 'print' method is needed only when using 'Alarm_registry::print'. | ||
* In this case, the alarm type must also provide a 'print' method. | ||
*/ | ||
template <typename T, typename CLOCK> | ||
class Genode::Alarm_registry : Noncopyable | ||
{ | ||
private: | ||
|
||
using Clock = CLOCK; | ||
|
||
struct Range | ||
{ | ||
Clock start, end; /* range [start,end] where 'start' >= 'end' */ | ||
|
||
void with_intersection(Range const other, auto const &fn) const | ||
{ | ||
auto const f = max(start.value(), other.start.value()); | ||
auto const t = min(end.value(), other.end.value()); | ||
|
||
if (f <= t) | ||
fn(Range { Clock { f }, Clock { t } }); | ||
} | ||
|
||
bool contains(Clock const time) const | ||
{ | ||
return (time.value() >= start.value()) | ||
&& (time.value() <= end.value()); | ||
} | ||
|
||
void print(Output &out) const | ||
{ | ||
Genode::print(out, "[", start.value(), "...", end.value(), "]"); | ||
} | ||
}; | ||
|
||
public: | ||
|
||
struct None { }; | ||
using Soonest_result = Attempt<Clock, None>; | ||
|
||
class Element : Avl_node<Element> | ||
{ | ||
private: | ||
|
||
Alarm_registry &_registry; | ||
|
||
T &_obj; | ||
|
||
friend class Alarm_registry; | ||
friend class Avl_node<Element>; | ||
friend class Avl_tree<Element>; | ||
|
||
public: | ||
|
||
Clock time; | ||
|
||
Element(Alarm_registry ®istry, T &obj, Clock time) | ||
: | ||
_registry(registry), _obj(obj), time(time) | ||
{ | ||
_registry._elements.insert(this); | ||
} | ||
|
||
~Element() | ||
{ | ||
_registry._elements.remove(this); | ||
} | ||
|
||
/** | ||
* Avl_node ordering, allow duplicated keys | ||
*/ | ||
bool higher(Element const * const other) const | ||
{ | ||
return time.value() <= other->time.value(); | ||
} | ||
|
||
void print(Output &out) const | ||
{ | ||
Genode::print(out, _obj, ": time=", time); | ||
} | ||
|
||
private: | ||
|
||
static void _with_child(auto &element, bool side, auto const &fn) | ||
{ | ||
if (element.child(side)) | ||
fn(*element.child(side)); | ||
} | ||
|
||
void _for_each(Range const range, auto const &fn) const | ||
{ | ||
_with_child(*this, this->LEFT, [&] (Element const &child) { | ||
range.with_intersection({ Clock { }, time }, [&] (Range l_range) { | ||
child._for_each(l_range, fn); }); }); | ||
|
||
if (range.contains(time)) | ||
fn(_obj); | ||
|
||
_with_child(*this, this->RIGHT, [&] (Element const &child) { | ||
range.with_intersection({ time, Clock { Clock::MASK }}, [&] (Range r_range) { | ||
child._for_each(r_range, fn); }); }); | ||
} | ||
|
||
Element *_find_any(Range const range) | ||
{ | ||
if (range.contains(time)) | ||
return this; | ||
|
||
Element *result = nullptr; | ||
|
||
_with_child(*this, this->LEFT, [&] (Element &child) { | ||
range.with_intersection({ Clock { }, time }, [&] (Range l_range) { | ||
result = child._find_any(l_range); }); }); | ||
|
||
if (result) | ||
return result; | ||
|
||
_with_child(*this, this->RIGHT, [&] (Element &child) { | ||
range.with_intersection({ time, Clock { Clock::MASK }}, [&] (Range r_range) { | ||
result = child._find_any(r_range); }); }); | ||
|
||
return result; | ||
} | ||
|
||
Soonest_result _soonest(Clock const now) const | ||
{ | ||
Soonest_result result = None { }; | ||
|
||
if (time.value() >= now.value()) { | ||
result = time; | ||
_with_child(*this, this->LEFT, [&] (Element const &child) { | ||
child._soonest(now).with_result( | ||
[&] (Clock left_soonest) { | ||
if (time.value() > left_soonest.value()) | ||
result = left_soonest; }, | ||
[&] (None) { }); }); | ||
} else { | ||
_with_child(*this, this->RIGHT, [&] (Element const &child) { | ||
result = child._soonest(now); }); | ||
} | ||
|
||
return result; | ||
} | ||
}; | ||
|
||
private: | ||
|
||
Avl_tree<Element> _elements { }; | ||
|
||
static void _with_first(auto ®istry, auto const &fn) | ||
{ | ||
if (registry._elements.first()) | ||
fn(*registry._elements.first()); | ||
} | ||
|
||
/* | ||
* Call 'fn' up to two times, with the first element and a search range | ||
* as argument. | ||
*/ | ||
static void _for_each_search_range(auto ®istry, Clock start, Clock end, | ||
auto const &fn) | ||
{ | ||
_with_first(registry, [&] (auto &first) { | ||
|
||
if (start.value() <= end.value()) { | ||
fn(first, Range { start, end }); | ||
|
||
} else if (start.value() > end.value()) { | ||
fn(first, Range { start, Clock { Clock::MASK } }); | ||
fn(first, Range { Clock { }, end }); | ||
} | ||
}); | ||
} | ||
|
||
public: | ||
|
||
/** | ||
* Return soonest alarm time from 'now' | ||
*/ | ||
Soonest_result soonest(Clock const now) const | ||
{ | ||
Soonest_result result = None { }; | ||
|
||
_with_first(*this, [&] (auto &first) { | ||
first._soonest(now).with_result( | ||
[&] (Clock soonest) { | ||
result = soonest; }, | ||
[&] (None) { /* clock wrapped, search from beginning */ | ||
result = first._soonest(Clock { }); }); }); | ||
|
||
return result; | ||
} | ||
|
||
/** | ||
* Call 'fn' for each alarm scheduled between 'start' and 'end' | ||
* | ||
* The 'start' and 'end' values may wrap. | ||
*/ | ||
void for_each_in_range(Clock const start, Clock const end, auto const &fn) const | ||
{ | ||
_for_each_search_range(*this, start, end, [&] (Element const &e, Range range) { | ||
e._for_each(range, fn); }); | ||
} | ||
|
||
/** | ||
* Call 'fn' with any alarm scheduled between 'start' and 'end' | ||
* | ||
* \return true if 'fn' was called | ||
* | ||
* The found alarm is passed to 'fn' as non-const reference, which | ||
* allows the caller to modify or destroy it. | ||
* | ||
* The return value is handy for calling 'with_any_in_range' as a | ||
* condition of a while loop, purging all alarms within the time | ||
* window. | ||
*/ | ||
bool with_any_in_range(Clock const start, Clock const end, auto const &fn) | ||
{ | ||
Element *found_ptr = nullptr; | ||
_for_each_search_range(*this, start, end, [&] (Element &e, Range range) { | ||
if (!found_ptr) | ||
found_ptr = e._find_any(range); }); | ||
|
||
if (found_ptr) | ||
fn(found_ptr->_obj); | ||
|
||
return found_ptr != nullptr; | ||
} | ||
|
||
void print(Output &out) const | ||
{ | ||
bool first = true; | ||
for_each_in_range(Clock { }, Clock { Clock::MASK }, [&] (Element const &e) { | ||
Genode::print(out, first ? "" : "\n", e); | ||
first = false; | ||
}); | ||
} | ||
}; | ||
|
||
#endif /* _INCLUDE__BASE__INTERNAL__ALARM_REGISTRY_H_ */ |
Oops, something went wrong.