-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: Coroutine based webserver (#1699)
Code of new coroutine-based web server. The new server is not connected to Clio and not ready to use yet. For #919.
- Loading branch information
Showing
58 changed files
with
5,581 additions
and
488 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
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
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
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,76 @@ | ||
//------------------------------------------------------------------------------ | ||
/* | ||
This file is part of clio: https://github.com/XRPLF/clio | ||
Copyright (c) 2024, the clio developers. | ||
Permission to use, copy, modify, and distribute this software for any | ||
purpose with or without fee is hereby granted, provided that the above | ||
copyright notice and this permission notice appear in all copies. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | ||
//============================================================================== | ||
|
||
#include "util/CoroutineGroup.hpp" | ||
|
||
#include "util/Assert.hpp" | ||
|
||
#include <boost/asio/spawn.hpp> | ||
#include <boost/asio/steady_timer.hpp> | ||
|
||
#include <cstddef> | ||
#include <functional> | ||
#include <optional> | ||
#include <utility> | ||
|
||
namespace util { | ||
|
||
CoroutineGroup::CoroutineGroup(boost::asio::yield_context yield, std::optional<int> maxChildren) | ||
: timer_{yield.get_executor(), boost::asio::steady_timer::duration::max()}, maxChildren_{maxChildren} | ||
{ | ||
} | ||
|
||
CoroutineGroup::~CoroutineGroup() | ||
{ | ||
ASSERT(childrenCounter_ == 0, "CoroutineGroup is destroyed without waiting for child coroutines to finish"); | ||
} | ||
|
||
bool | ||
CoroutineGroup::spawn(boost::asio::yield_context yield, std::function<void(boost::asio::yield_context)> fn) | ||
{ | ||
if (maxChildren_.has_value() && childrenCounter_ >= *maxChildren_) | ||
return false; | ||
|
||
++childrenCounter_; | ||
boost::asio::spawn(yield, [this, fn = std::move(fn)](boost::asio::yield_context yield) { | ||
fn(yield); | ||
--childrenCounter_; | ||
if (childrenCounter_ == 0) | ||
timer_.cancel(); | ||
}); | ||
return true; | ||
} | ||
|
||
void | ||
CoroutineGroup::asyncWait(boost::asio::yield_context yield) | ||
{ | ||
if (childrenCounter_ == 0) | ||
return; | ||
|
||
boost::system::error_code error; | ||
timer_.async_wait(yield[error]); | ||
} | ||
|
||
size_t | ||
CoroutineGroup::size() const | ||
{ | ||
return childrenCounter_; | ||
} | ||
|
||
} // namespace util |
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,88 @@ | ||
//------------------------------------------------------------------------------ | ||
/* | ||
This file is part of clio: https://github.com/XRPLF/clio | ||
Copyright (c) 2024, the clio developers. | ||
Permission to use, copy, modify, and distribute this software for any | ||
purpose with or without fee is hereby granted, provided that the above | ||
copyright notice and this permission notice appear in all copies. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | ||
//============================================================================== | ||
|
||
#pragma once | ||
|
||
#include <boost/asio/spawn.hpp> | ||
#include <boost/asio/steady_timer.hpp> | ||
|
||
#include <cstddef> | ||
#include <functional> | ||
#include <optional> | ||
|
||
namespace util { | ||
|
||
/** | ||
* @brief CoroutineGroup is a helper class to manage a group of coroutines. It allows to spawn multiple coroutines and | ||
* wait for all of them to finish. | ||
*/ | ||
class CoroutineGroup { | ||
boost::asio::steady_timer timer_; | ||
std::optional<int> maxChildren_; | ||
int childrenCounter_{0}; | ||
|
||
public: | ||
/** | ||
* @brief Construct a new Coroutine Group object | ||
* | ||
* @param yield The yield context to use for the internal timer | ||
* @param maxChildren The maximum number of coroutines that can be spawned at the same time. If not provided, there | ||
* is no limit | ||
*/ | ||
CoroutineGroup(boost::asio::yield_context yield, std::optional<int> maxChildren = std::nullopt); | ||
|
||
/** | ||
* @brief Destroy the Coroutine Group object | ||
* | ||
* @note asyncWait() must be called before the object is destroyed | ||
*/ | ||
~CoroutineGroup(); | ||
|
||
/** | ||
* @brief Spawn a new coroutine in the group | ||
* | ||
* @param yield The yield context to use for the coroutine (it should be the same as the one used in the | ||
* constructor) | ||
* @param fn The function to execute | ||
* @return true If the coroutine was spawned successfully. false if the maximum number of coroutines has been | ||
* reached | ||
*/ | ||
bool | ||
spawn(boost::asio::yield_context yield, std::function<void(boost::asio::yield_context)> fn); | ||
|
||
/** | ||
* @brief Wait for all the coroutines in the group to finish | ||
* | ||
* @note This method must be called before the object is destroyed | ||
* | ||
* @param yield The yield context to use for the internal timer | ||
*/ | ||
void | ||
asyncWait(boost::asio::yield_context yield); | ||
|
||
/** | ||
* @brief Get the number of coroutines in the group | ||
* | ||
* @return size_t The number of coroutines in the group | ||
*/ | ||
size_t | ||
size() const; | ||
}; | ||
|
||
} // namespace util |
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,71 @@ | ||
//------------------------------------------------------------------------------ | ||
/* | ||
This file is part of clio: https://github.com/XRPLF/clio | ||
Copyright (c) 2024, the clio developers. | ||
Permission to use, copy, modify, and distribute this software for any | ||
purpose with or without fee is hereby granted, provided that the above | ||
copyright notice and this permission notice appear in all copies. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | ||
//============================================================================== | ||
|
||
#pragma once | ||
|
||
#include <boost/asio/associated_executor.hpp> | ||
#include <boost/asio/bind_cancellation_slot.hpp> | ||
#include <boost/asio/cancellation_signal.hpp> | ||
#include <boost/asio/cancellation_type.hpp> | ||
#include <boost/asio/spawn.hpp> | ||
#include <boost/asio/steady_timer.hpp> | ||
#include <boost/system/detail/error_code.hpp> | ||
#include <boost/system/errc.hpp> | ||
|
||
#include <chrono> | ||
#include <ctime> | ||
#include <memory> | ||
|
||
namespace util { | ||
|
||
/** | ||
* @brief Perform a coroutine operation with a timeout. | ||
* | ||
* @tparam Operation The operation type to perform. Must be a callable accepting yield context with bound cancellation | ||
* token. | ||
* @param operation The operation to perform. | ||
* @param yield The yield context. | ||
* @param timeout The timeout duration. | ||
* @return The error code of the operation. | ||
*/ | ||
template <typename Operation> | ||
boost::system::error_code | ||
withTimeout(Operation&& operation, boost::asio::yield_context yield, std::chrono::steady_clock::duration timeout) | ||
{ | ||
boost::system::error_code error; | ||
auto operationCompleted = std::make_shared<bool>(false); | ||
boost::asio::cancellation_signal cancellationSignal; | ||
auto cyield = boost::asio::bind_cancellation_slot(cancellationSignal.slot(), yield[error]); | ||
|
||
boost::asio::steady_timer timer{boost::asio::get_associated_executor(cyield), timeout}; | ||
timer.async_wait([&cancellationSignal, operationCompleted](boost::system::error_code errorCode) { | ||
if (!errorCode and !*operationCompleted) | ||
cancellationSignal.emit(boost::asio::cancellation_type::terminal); | ||
}); | ||
operation(cyield); | ||
*operationCompleted = true; | ||
|
||
// Map error code to timeout | ||
if (error == boost::system::errc::operation_canceled) { | ||
return boost::system::errc::make_error_code(boost::system::errc::timed_out); | ||
} | ||
return error; | ||
} | ||
|
||
} // namespace util |
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
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
Oops, something went wrong.