Compare commits
2 Commits
7a18929409
...
76595be62a
Author | SHA1 | Date | |
---|---|---|---|
|
76595be62a | ||
|
923eba7037 |
105
CMakeLists.txt
105
CMakeLists.txt
@ -9,7 +9,7 @@ if (NOT tile_VERSION_BUILD)
|
||||
endif()
|
||||
project(
|
||||
tile
|
||||
VERSION ${tile_VERSION_MAJOR}.${tile_VERSION_MINOR}.${tile_VERSION_PATCH}.${tile_VERSION_BUILD}
|
||||
VERSION "${tile_VERSION_MAJOR}.${tile_VERSION_MINOR}.${tile_VERSION_PATCH}"
|
||||
LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
@ -24,9 +24,6 @@ option(TILE_WITH_OPENSSL "Build with openssl" OFF)
|
||||
option(TILE_BUILD_SHARED "Build shared library" ON)
|
||||
option(TILE_BUILD_STATIC "Build static library" ON)
|
||||
|
||||
# set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "time")
|
||||
# set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "time")
|
||||
|
||||
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(TILE_BUILD_TESTS ON)
|
||||
set(TILE_BUILD_BENCHMARKS ON)
|
||||
@ -210,9 +207,9 @@ set(TILE_SRCS
|
||||
"tile/rpc/protocol/http/buffer_io.cc"
|
||||
"tile/rpc/protocol/message.cc"
|
||||
# "tile/rpc/server.cc"
|
||||
"tile/util/config.cc"
|
||||
"tile/util/ini_file_config.cc"
|
||||
"tile/util/layered_config.cc"
|
||||
"tile/base/config/config.cc"
|
||||
"tile/base/config/ini_file_config.cc"
|
||||
"tile/base/config/layered_config.cc"
|
||||
)
|
||||
|
||||
if((NOT TILE_HAVE_GETIFADDRS) OR (NOT TILE_HAVE_FREEIFADDRS))
|
||||
@ -295,82 +292,30 @@ if(TILE_BUILD_TESTS)
|
||||
add_test(NAME ${test_name} COMMAND ${test_name})
|
||||
endmacro()
|
||||
|
||||
tile_add_test(io_util_rate_limiter_test "tile/io/util/rate_limiter_test.cc")
|
||||
tile_add_test(base_exposed_var_test "tile/base/exposed_var_test.cc")
|
||||
tile_add_test(base_byte_order_test "tile/base/byte_order_test.cc")
|
||||
# tile_add_test(fiber_detail_scheduler_test "tile/fiber/detail/scheduler_test.cc")
|
||||
tile_add_test(base_internal_meta_test "tile/base/internal/meta_test.cc")
|
||||
# tile_add_test(net_internal_http_engine_test
|
||||
# "tile/net/internal/http_engine_test.cc")
|
||||
tile_add_test(net_internal_http_task_test
|
||||
"tile/net/internal/http_task_test.cc")
|
||||
tile_add_test(net_http_http_client_test "tile/net/http/http_client_test.cc")
|
||||
tile_add_test(net_http_http_request_test "tile/net/http/http_reqeust_test.cc")
|
||||
tile_add_test(net_http_http_response_test
|
||||
"tile/net/http/http_response_test.cc")
|
||||
tile_add_test(base_compression_util_test "tile/base/compression/util_test.cc")
|
||||
tile_add_test(rpc_protocol_http_buffer_io_test
|
||||
"tile/rpc/protocol/http/buffer_io_test.cc")
|
||||
function(add_test_group prefix group_name)
|
||||
file(GLOB_RECURSE TEST_FILES "${prefix}/**/*_test.cc")
|
||||
foreach(SRC_FILE ${TEST_FILES})
|
||||
if(IS_DIRECTORY ${SRC_FILE})
|
||||
continue()
|
||||
endif()
|
||||
# convert to relative path message(STATUS "${prefix} -> ${TEST_FILE}")
|
||||
file(RELATIVE_PATH TEST_NAME "${prefix}" "${SRC_FILE}")
|
||||
string(REPLACE "/" "_" TEST_NAME "${TEST_NAME}")
|
||||
# if group_name is not empty, add suffix _
|
||||
if (NOT group_name STREQUAL "")
|
||||
set(TEST_NAME "${group_name}_${TEST_NAME}")
|
||||
endif()
|
||||
|
||||
tile_add_test(base_compression_test "tile/base/compression_test.cc")
|
||||
tile_add_test(base_casting_test "tile/base/casting_test.cc")
|
||||
tile_add_test(base_future_future_test "tile/base/future/future_test.cc")
|
||||
tile_add_test(base_future_boxed_test "tile/base/future/boxed_test.cc")
|
||||
tile_add_test(base_option_option_service_test
|
||||
"tile/base/option/option_service_test.cc")
|
||||
tile_add_test(base_ref_ptr_test "tile/base/ref_ptr_test.cc")
|
||||
tile_add_test(base_object_pool_disabled_test
|
||||
"tile/base/object_pool/disabled_test.cc")
|
||||
tile_add_test(base_object_pool_types_test
|
||||
"tile/base/object_pool/types_test.cc")
|
||||
tile_add_test(base_string_test "tile/base/string_test.cc")
|
||||
tile_add_test(base_deferred_test "tile/base/deferred_test.cc")
|
||||
tile_add_test(base_internal_singly_linked_list_test
|
||||
"tile/base/internal/singly_linked_list_test.cc")
|
||||
tile_add_test(base_internal_move_on_copy_test
|
||||
"tile/base/internal/move_on_copy_test.cc")
|
||||
tile_add_test(base_internal_thread_pool_test
|
||||
"tile/base/internal/thread_pool_test.cc")
|
||||
tile_add_test(base_internal_format_test "tile/base/internal/format_test.cc")
|
||||
tile_add_test(base_internal_background_task_host_test
|
||||
"tile/base/internal/background_task_host_test.cc")
|
||||
tile_add_test(base_down_cast_test "tile/base/down_cast_test.cc")
|
||||
tile_add_test(base_encoding_hex_test "tile/base/encoding/hex_test.cc")
|
||||
tile_add_test(base_encoding_percent_test "tile/base/encoding/percent_test.cc")
|
||||
tile_add_test(base_encoding_base64_test "tile/base/encoding/base64_test.cc")
|
||||
tile_add_test(base_internal_case_insensitive_hash_map_test
|
||||
"tile/base/internal/case_insensitive_hash_map_test.cc")
|
||||
tile_add_test(net_http_http_headers_test "tile/net/http/http_headers_test.cc")
|
||||
tile_add_test(base_demangle_test "tile/base/demangle_test.cc")
|
||||
tile_add_test(base_option_json_parser_test
|
||||
"tile/base/option/json_parser_test.cc")
|
||||
tile_add_test(base_option_key_test "tile/base/option/key_test.cc")
|
||||
tile_add_test(base_dependency_registry_test
|
||||
"tile/base/dependency_registry_test.cc")
|
||||
tile_add_test(base_maybe_owning_test "tile/base/maybe_owning_test.cc")
|
||||
tile_add_test(base_status_test "tile/base/status_test.cc")
|
||||
tile_add_test(base_net_endpoint_test "tile/base/net/endpoint_test.cc")
|
||||
tile_add_test(base_handle_test "tile/base/handle_test.cc")
|
||||
tile_add_test(base_thread_scoped_lock_test
|
||||
"tile/base/thread/scoped_lock_test.cc")
|
||||
tile_add_test(base_thread_spinlock_test "tile/base/thread/spinlock_test.cc")
|
||||
tile_add_test(base_thread_unique_lock_test
|
||||
"tile/base/thread/unique_lock_test.cc")
|
||||
tile_add_test(base_thread_cond_var_test "tile/base/thread/cond_var_test.cc")
|
||||
tile_add_test(base_thread_latch_test "tile/base/thread/latch_test.cc")
|
||||
# tile_add_test(fiber_ucontext_test "tile/fiber/ucontext_test.cc")
|
||||
tile_add_test(${TEST_NAME} ${SRC_FILE})
|
||||
endforeach()
|
||||
endfunction(add_test_group)
|
||||
|
||||
tile_add_test(init_on_init_test "tile/init/on_init_test.cc")
|
||||
tile_add_test(base_buffer_test "tile/base/buffer_test.cc")
|
||||
tile_add_test(base_object_pool_thread_local_test
|
||||
"tile/base/object_pool/thread_local_test.cc")
|
||||
tile_add_test(base_internal_logging_test "tile/base/internal/logging_test.cc")
|
||||
tile_add_test(base_chrono_test "tile/base/chrono_test.cc")
|
||||
tile_add_test(init_override_flag_test "tile/init/override_flag_test.cc")
|
||||
add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/base base)
|
||||
add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/init init)
|
||||
add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/io io)
|
||||
add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/net net)
|
||||
add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/rpc rpc)
|
||||
tile_add_custom_test("custom_http_client_test" "tests/http_client_test.cc")
|
||||
# tile_add_test(base_internal_time_keeper_test
|
||||
# "tile/base/internal/time_keeper_test.cc")
|
||||
tile_add_test(tile_util_ini_file_config_test "tile/util/ini_file_config_test.cc")
|
||||
endif(TILE_BUILD_TESTS)
|
||||
|
||||
if(TILE_BUILD_BENCHMARKS)
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "tile/util/config.h"
|
||||
#include "tile/base/config/config.h"
|
||||
#include "tile/base/thread/unique_lock.h"
|
||||
|
||||
namespace tile {
|
@ -1,8 +1,7 @@
|
||||
#ifndef TILE_UTIL_CONFIG_H
|
||||
#define TILE_UTIL_CONFIG_H
|
||||
#ifndef TILE_BASE_CONFIG_CONFIG_H
|
||||
#define TILE_BASE_CONFIG_CONFIG_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tile/base/optional.h"
|
||||
#include "tile/base/ref_ptr.h"
|
||||
#include "tile/base/slice.h"
|
||||
@ -125,4 +124,4 @@ private:
|
||||
} // namespace util
|
||||
} // namespace tile
|
||||
|
||||
#endif // TILE_UTIL_CONFIG_H
|
||||
#endif // TILE_BASE_CONFIG_CONFIG_H
|
19
tile/base/config/configurable.h
Normal file
19
tile/base/config/configurable.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef TILE_BASE_CONFIG_CONFIGURABLE_H
|
||||
#define TILE_BASE_CONFIG_CONFIGURABLE_H
|
||||
|
||||
#pragma once
|
||||
#include "tile/base/string.h"
|
||||
|
||||
namespace tile {
|
||||
namespace util {
|
||||
class Configurable {
|
||||
Configurable();
|
||||
virtual ~Configurable() = default;
|
||||
virtual void SetProperty(const Slice &name, const Slice &value) = 0;
|
||||
virtual std::string GetProperty(const Slice &name) const = 0;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
} // namespace tile
|
||||
|
||||
#endif // TILE_BASE_CONFIG_CONFIGURABLE_H
|
@ -1,4 +1,4 @@
|
||||
#include "tile/util/ini_file_config.h"
|
||||
#include "tile/base/config/ini_file_config.h"
|
||||
#include "tile/base/thread/scoped_lock.h"
|
||||
|
||||
namespace tile {
|
@ -1,9 +1,9 @@
|
||||
#ifndef TILE_UTIL_INI_FILE_CONFIG_H
|
||||
#define TILE_UTIL_INI_FILE_CONFIG_H
|
||||
#ifndef TILE_BASE_CONFIG_INI_FILE_CONFIG_H
|
||||
#define TILE_BASE_CONFIG_INI_FILE_CONFIG_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tile/util/config.h"
|
||||
#include "tile/base/config/config.h"
|
||||
#include <map>
|
||||
|
||||
namespace tile {
|
||||
@ -37,4 +37,4 @@ private:
|
||||
} // namespace util
|
||||
} // namespace tile
|
||||
|
||||
#endif // TILE_UTIL_INI_FILE_CONFIG_H
|
||||
#endif // TILE_BASE_CONFIG_INI_FILE_CONFIG_H
|
@ -1,4 +1,4 @@
|
||||
#include "tile/util/ini_file_config.h"
|
||||
#include "tile/base/config/ini_file_config.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
const char *kIniFileConfig = R"(
|
@ -1,4 +1,4 @@
|
||||
#include "tile/util/layered_config.h"
|
||||
#include "tile/base/config/layered_config.h"
|
||||
|
||||
namespace tile {
|
||||
namespace util {
|
@ -1,9 +1,9 @@
|
||||
#ifndef TILE_UTIL_LAYERD_CONFIG_H
|
||||
#define TILE_UTIL_LAYERD_CONFIG_H
|
||||
#ifndef TILE_BASE_CONFIG_LAYERED_CONFIG_H
|
||||
#define TILE_BASE_CONFIG_LAYERED_CONFIG_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tile/util/config.h"
|
||||
#include "tile/base/config/config.h"
|
||||
#include <list>
|
||||
|
||||
namespace tile {
|
||||
@ -54,4 +54,4 @@ private:
|
||||
} // namespace util
|
||||
} // namespace tile
|
||||
|
||||
#endif // TILE_UTIL_LAYERD_CONFIG_H
|
||||
#endif // TILE_BASE_CONFIG_LAYERED_CONFIG_H
|
@ -1,178 +0,0 @@
|
||||
#include "tile/fiber/detail/fiber.h"
|
||||
|
||||
#include "tile/base/align.h"
|
||||
#include "tile/base/internal/index_alloc.h"
|
||||
#include "tile/base/internal/move_on_copy.h"
|
||||
#include "tile/base/logging.h"
|
||||
#include "tile/base/make_unique.h"
|
||||
#include "tile/base/object_pool.h"
|
||||
#include "tile/base/string.h"
|
||||
|
||||
#include "nova/context/fcontext.h"
|
||||
|
||||
namespace tile {
|
||||
namespace fiber {
|
||||
namespace detail {
|
||||
|
||||
std::string ToString(const FiberState &state) noexcept {
|
||||
switch (state) {
|
||||
case FiberState::Runnable:
|
||||
return "Runnable";
|
||||
case FiberState::Running:
|
||||
return "Running";
|
||||
case FiberState::Waiting:
|
||||
return "Waiting";
|
||||
case FiberState::Terminated:
|
||||
return "Terminated";
|
||||
default:
|
||||
TILE_UNEXPECTED("");
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
std::ostream &operator<<(std::ostream &os, const FiberState &state) noexcept {
|
||||
return os << ToString(state);
|
||||
}
|
||||
|
||||
static thread_local Fiber *tls_current_fiber = nullptr;
|
||||
static thread_local Fiber *tls_master_fiber = nullptr;
|
||||
|
||||
constexpr std::size_t kStackSize = 128 * 1024; // 128k
|
||||
constexpr std::size_t kAlignSize = 16;
|
||||
|
||||
struct alignas(hardware_destructive_interference_size) Fiber::FiberContext {
|
||||
std::aligned_storage<kStackSize, kAlignSize>::type stack;
|
||||
std::function<void()> proc;
|
||||
};
|
||||
|
||||
void FiberEntry(fcontext_transfer_t t) {
|
||||
std::function<void()> *fn = static_cast<std::function<void()> *>(t.data);
|
||||
TILE_CHECK(t.data != nullptr);
|
||||
TILE_CHECK(t.fctx != nullptr);
|
||||
Fiber *self = nullptr;
|
||||
|
||||
try {
|
||||
// From Resume()
|
||||
t = jump_fcontext(t.fctx, nullptr);
|
||||
Fiber *caller = reinterpret_cast<Fiber *>(t.data);
|
||||
|
||||
self = GetCurrentFiber();
|
||||
if (caller) {
|
||||
caller->ctx_ = t.fctx;
|
||||
} else {
|
||||
TILE_CHECK_EQ(self, GetMasterFiber());
|
||||
}
|
||||
|
||||
(*fn)();
|
||||
} catch (const std::exception &e) {
|
||||
TILE_LOG_ERROR("Exception caught in fiber: {}", e.what());
|
||||
}
|
||||
self->state_ = FiberState::Terminated;
|
||||
|
||||
if (GetMasterFiber() == self) {
|
||||
// master fiber end
|
||||
jump_fcontext(t.fctx, GetMasterFiber());
|
||||
} else {
|
||||
TILE_CHECK(GetMasterFiber() != nullptr);
|
||||
TILE_CHECK_NE(GetMasterFiber()->state(), FiberState::Terminated);
|
||||
GetMasterFiber()->Resume();
|
||||
}
|
||||
}
|
||||
|
||||
fcontext_transfer_t FiberOnTop(fcontext_transfer_t t) {}
|
||||
|
||||
fcontext_t CreateFiber(void *stack, std::size_t stack_size,
|
||||
std::function<void()> *fn) {
|
||||
void *stack_top = static_cast<char *>(stack) + stack_size;
|
||||
const fcontext_t fctx = make_fcontext(stack_top, stack_size, FiberEntry);
|
||||
TILE_CHECK(fctx != nullptr);
|
||||
|
||||
return jump_fcontext(fctx, fn).fctx;
|
||||
}
|
||||
|
||||
Fiber *GetCurrentFiber() noexcept { return tls_current_fiber; }
|
||||
void SetUpCurrentFiber(Fiber *fiber) noexcept { tls_current_fiber = fiber; }
|
||||
|
||||
Fiber *GetMasterFiber() noexcept { return tls_master_fiber; }
|
||||
void SetUpMasterFiber(Fiber *fiber) noexcept { tls_master_fiber = fiber; }
|
||||
|
||||
std::unique_ptr<Fiber> Fiber::Create(std::function<void()> proc) noexcept {
|
||||
return std::unique_ptr<Fiber>(new Fiber(std::move(proc)));
|
||||
}
|
||||
|
||||
Fiber::Fiber(std::function<void()> proc)
|
||||
: id_(internal::IndexAlloc::For<Fiber>()->Next()),
|
||||
data_(object_pool::Get<FiberContext>().Leak()) {
|
||||
TILE_CHECK(proc);
|
||||
|
||||
data_->proc = std::move(proc);
|
||||
ctx_ = CreateFiber(&data_->stack, kStackSize, &data_->proc);
|
||||
}
|
||||
|
||||
Fiber::~Fiber() {
|
||||
if (data_) {
|
||||
object_pool::Put<FiberContext>(data_.release());
|
||||
}
|
||||
internal::IndexAlloc::For<Fiber>()->Free(id_);
|
||||
}
|
||||
|
||||
void Fiber::Resume() {
|
||||
auto caller = GetCurrentFiber();
|
||||
TILE_CHECK_NE(caller, this, "Calling `Resume()`, on self is undefined.");
|
||||
|
||||
SetUpCurrentFiber(this);
|
||||
|
||||
auto t = jump_fcontext(internal::Exchange(ctx_, nullptr), caller);
|
||||
|
||||
if (auto from = reinterpret_cast<Fiber *>(t.data)) {
|
||||
from->ctx_ = t.fctx;
|
||||
}
|
||||
|
||||
SetUpCurrentFiber(caller);
|
||||
}
|
||||
|
||||
void Fiber::ResumeOn(std::function<void()> &&cb) noexcept {
|
||||
auto caller = GetCurrentFiber();
|
||||
TILE_CHECK_NE(caller, this, "Calling `ResumeOn()`, on self is undefined.");
|
||||
SetUpCurrentFiber(this);
|
||||
auto t = ontop_fcontext(ctx_, this, FiberOnTop);
|
||||
caller = reinterpret_cast<Fiber *>(t.data);
|
||||
SetUpCurrentFiber(caller);
|
||||
}
|
||||
|
||||
std::string ToString(const Fiber &fiber) noexcept { return ToString(&fiber); }
|
||||
std::string ToString(const std::unique_ptr<Fiber> &fiber) noexcept {
|
||||
return ToString(fiber.get());
|
||||
}
|
||||
std::string ToString(Fiber const *const fiber) noexcept {
|
||||
if (TILE_UNLIKELY(fiber == nullptr)) {
|
||||
return "Fiber(nullptr)";
|
||||
}
|
||||
return Format("Fiber({})[{}]", fiber->id(), fmt::ptr(fiber));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace fiber
|
||||
|
||||
template <> struct PoolTraits<tile::fiber::detail::Fiber::FiberContext> {
|
||||
static constexpr auto kType = PoolType::MemoryNodeShared;
|
||||
static constexpr std::size_t kLowWaterMark = 128;
|
||||
static constexpr std::size_t kHighWaterMark =
|
||||
std::numeric_limits<std::size_t>::max();
|
||||
static constexpr std::chrono::seconds kMaxIdle = std::chrono::seconds(10);
|
||||
static constexpr std::size_t kMinimumThreadCacheSize = 64;
|
||||
static constexpr std::size_t kTransferBatchSize = 16;
|
||||
};
|
||||
|
||||
constexpr PoolType PoolTraits<fiber::detail::Fiber::FiberContext>::kType;
|
||||
constexpr std::size_t
|
||||
PoolTraits<fiber::detail::Fiber::FiberContext>::kLowWaterMark;
|
||||
constexpr std::size_t
|
||||
PoolTraits<fiber::detail::Fiber::FiberContext>::kHighWaterMark;
|
||||
constexpr std::chrono::seconds
|
||||
PoolTraits<fiber::detail::Fiber::FiberContext>::kMaxIdle;
|
||||
constexpr std::size_t
|
||||
PoolTraits<fiber::detail::Fiber::FiberContext>::kMinimumThreadCacheSize;
|
||||
constexpr std::size_t
|
||||
PoolTraits<fiber::detail::Fiber::FiberContext>::kTransferBatchSize;
|
||||
|
||||
} // namespace tile
|
@ -1,93 +0,0 @@
|
||||
#ifndef TILE_FIBER_DETAIL_FIBER_H
|
||||
#define TILE_FIBER_DETAIL_FIBER_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tile/base/align.h"
|
||||
#include "tile/base/internal/test_prod.h"
|
||||
#include "tile/base/object_pool.h"
|
||||
#include "tile/base/ref_ptr.h"
|
||||
#include <memory>
|
||||
|
||||
struct tile_ucontext_t;
|
||||
// struct fcontext_transfer;
|
||||
typedef struct fcontext_transfer fcontext_transfer_t;
|
||||
|
||||
namespace tile {
|
||||
namespace fiber {
|
||||
namespace detail {
|
||||
|
||||
class Scheduler;
|
||||
|
||||
enum class FiberState {
|
||||
Runnable,
|
||||
Running,
|
||||
Waiting,
|
||||
Terminated,
|
||||
};
|
||||
std::string ToString(const FiberState &state) noexcept;
|
||||
std::ostream &operator<<(std::ostream &os, const FiberState &state) noexcept;
|
||||
|
||||
class Scheduler;
|
||||
|
||||
class alignas(hardware_destructive_interference_size) Fiber {
|
||||
public:
|
||||
using Id = std::uint64_t;
|
||||
|
||||
static std::unique_ptr<Fiber>
|
||||
Create(std::function<void()> proc = nullptr) noexcept;
|
||||
|
||||
~Fiber();
|
||||
|
||||
Fiber(const Fiber &) = delete;
|
||||
Fiber &operator=(const Fiber &) = delete;
|
||||
Fiber(Fiber &&other) noexcept = default;
|
||||
Fiber &operator=(Fiber &&other) noexcept = default;
|
||||
|
||||
void Resume();
|
||||
void ResumeOn(std::function<void()> &&cb) noexcept;
|
||||
void Yield();
|
||||
Id id() const noexcept { return id_; }
|
||||
FiberState state() const noexcept { return state_; }
|
||||
|
||||
Scheduler *scheduler() const { return scheduler_; }
|
||||
|
||||
private:
|
||||
TILE_FRIEND_TEST(Fiber, Base);
|
||||
friend Scheduler;
|
||||
struct FiberContext;
|
||||
friend class ::tile::PoolTraits<FiberContext>;
|
||||
|
||||
friend void FiberEntry(fcontext_transfer_t);
|
||||
friend fcontext_transfer_t FiberOnTop(fcontext_transfer_t);
|
||||
|
||||
Fiber(std::function<void()> proc = nullptr);
|
||||
|
||||
private:
|
||||
Id id_;
|
||||
|
||||
std::unique_ptr<FiberContext> data_;
|
||||
FiberState state_{FiberState::Runnable};
|
||||
|
||||
void *ctx_{nullptr};
|
||||
|
||||
// for scheduler
|
||||
Scheduler *scheduler_{nullptr};
|
||||
};
|
||||
Fiber *GetCurrentFiber() noexcept;
|
||||
Fiber *GetMasterFiber() noexcept;
|
||||
|
||||
void SetUpCurrentFiber(Fiber *fiber) noexcept;
|
||||
void SetUpMasterFiber(Fiber *fiber) noexcept;
|
||||
|
||||
inline bool IsFiberContext() noexcept { return GetCurrentFiber() != nullptr; }
|
||||
|
||||
std::string ToString(Fiber const *const fiber) noexcept;
|
||||
std::string ToString(const std::unique_ptr<Fiber> &fiber) noexcept;
|
||||
std::string ToString(const Fiber &fiber) noexcept;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace fiber
|
||||
} // namespace tile
|
||||
|
||||
#endif // TILE_FIBER_DETAIL_FIBER_H
|
@ -1,64 +0,0 @@
|
||||
#include "tile/base/chrono.h"
|
||||
#include "tile/base/random.h"
|
||||
#include "tile/fiber/detail/fiber.h"
|
||||
|
||||
#include "benchmark/benchmark.h"
|
||||
namespace tile {
|
||||
namespace fiber {
|
||||
namespace detail {
|
||||
|
||||
void Benchmark_FiberSwitch(benchmark::State &state) {
|
||||
constexpr int kFiberCount = 2 * 10000;
|
||||
std::unique_ptr<Fiber> master;
|
||||
std::unique_ptr<Fiber> worker[kFiberCount];
|
||||
|
||||
int cnt = 0;
|
||||
|
||||
master = Fiber::Create([&]() {
|
||||
while (state.KeepRunning()) {
|
||||
++cnt;
|
||||
auto &w1 = worker[Random(kFiberCount - 1)];
|
||||
auto &w2 = worker[Random(kFiberCount - 1)];
|
||||
auto start = ReadSteadyClock();
|
||||
w1->Resume();
|
||||
w2->Resume();
|
||||
w1->Resume();
|
||||
w2->Resume();
|
||||
w1->Resume();
|
||||
w2->Resume();
|
||||
w1->Resume();
|
||||
w2->Resume();
|
||||
auto end = ReadSteadyClock();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||
end - start) /
|
||||
8;
|
||||
state.SetIterationTime(duration.count());
|
||||
}
|
||||
|
||||
cnt = -1;
|
||||
for (int i = 0; i != kFiberCount; ++i) {
|
||||
worker[i]->Resume();
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i != kFiberCount; ++i) {
|
||||
worker[i] = Fiber::Create([&, i]() {
|
||||
while (cnt != -1) {
|
||||
master->Resume();
|
||||
master->Resume();
|
||||
master->Resume();
|
||||
master->Resume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SetUpMasterFiber(master.get());
|
||||
SetUpCurrentFiber(nullptr);
|
||||
|
||||
master->Resume();
|
||||
}
|
||||
|
||||
BENCHMARK(Benchmark_FiberSwitch)->UseManualTime();
|
||||
} // namespace detail
|
||||
} // namespace fiber
|
||||
} // namespace tile
|
@ -1,44 +0,0 @@
|
||||
#include "tile/fiber/detail/scheduler.h"
|
||||
|
||||
namespace tile {
|
||||
namespace fiber {
|
||||
namespace detail {
|
||||
static thread_local Scheduler *current_scheduler = nullptr;
|
||||
|
||||
void Scheduler::SwitchTo(Fiber *self, Fiber *to) {
|
||||
TILE_CHECK_EQ(self, GetCurrentFiber());
|
||||
|
||||
TILE_CHECK(to->state_ == FiberState::Terminated,
|
||||
"Fiber `to` is not in Runnable.");
|
||||
TILE_CHECK_NE(self, to, "Switch to yourself result in U.B.");
|
||||
|
||||
to->Resume();
|
||||
|
||||
TILE_CHECK_EQ(self, GetCurrentFiber());
|
||||
}
|
||||
|
||||
void Scheduler::Yield(Fiber *self) {
|
||||
auto master = GetMasterFiber();
|
||||
|
||||
TILE_CHECK(self, "self fiber is nullptr.");
|
||||
TILE_CHECK(master, "Master fiber is not set.");
|
||||
master->state_ = FiberState::Runnable;
|
||||
self->state_ = FiberState::Running;
|
||||
SwitchTo(self, master);
|
||||
}
|
||||
|
||||
void Scheduler::Halt(Fiber *self) {
|
||||
TILE_CHECK_EQ(self, GetCurrentFiber(), "Fiber is not current fiber.");
|
||||
TILE_CHECK(self->state() == FiberState::Running, "Fiber is not running.");
|
||||
|
||||
auto master = GetMasterFiber();
|
||||
self->state_ = FiberState::Waiting;
|
||||
|
||||
master->Resume();
|
||||
|
||||
TILE_CHECK_EQ(self, GetCurrentFiber());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace fiber
|
||||
} // namespace tile
|
@ -1,24 +0,0 @@
|
||||
#ifndef TILE_FIBER_DETAIL_SCHEDULER_H
|
||||
#define TILE_FIBER_DETAIL_SCHEDULER_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tile/fiber/detail/fiber.h"
|
||||
|
||||
namespace tile {
|
||||
namespace fiber {
|
||||
namespace detail {
|
||||
|
||||
class Scheduler {
|
||||
public:
|
||||
static Scheduler *Current() noexcept;
|
||||
|
||||
void SwitchTo(Fiber *self, Fiber *to);
|
||||
void Yield(Fiber *self);
|
||||
void Halt(Fiber *self);
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace fiber
|
||||
} // namespace tile
|
||||
|
||||
#endif // TILE_FIBER_DETAIL_SCHEDULER_H
|
@ -1,55 +0,0 @@
|
||||
#include "tile/fiber/detail/scheduler.h"
|
||||
|
||||
#include "tile/base/deferred.h"
|
||||
|
||||
#include "tile/base/random.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace tile {
|
||||
namespace fiber {
|
||||
namespace detail {
|
||||
|
||||
TEST(Fiber, Resume) {
|
||||
constexpr int kFiberCount = 1000;
|
||||
constexpr int kSwitchCount = 1000 * 1000;
|
||||
|
||||
std::unique_ptr<Fiber> master;
|
||||
std::unique_ptr<Fiber> worker[kFiberCount];
|
||||
|
||||
int cnt = 0;
|
||||
auto end_check = tile::Deferred([&]() { ASSERT_EQ(cnt, kSwitchCount); });
|
||||
|
||||
master = Fiber::Create([&]() {
|
||||
while (cnt < kSwitchCount) {
|
||||
ASSERT_EQ(GetCurrentFiber(), master.get());
|
||||
worker[Random(kFiberCount - 1)]->Resume();
|
||||
ASSERT_EQ(GetCurrentFiber(), master.get());
|
||||
}
|
||||
|
||||
for (int i = 0; i != kFiberCount; ++i) {
|
||||
worker[i]->Resume();
|
||||
}
|
||||
});
|
||||
for (int i = 0; i != kFiberCount; ++i) {
|
||||
worker[i] = Fiber::Create([&, i]() {
|
||||
while (cnt < kSwitchCount) {
|
||||
++cnt;
|
||||
ASSERT_EQ(GetCurrentFiber(), worker[i].get());
|
||||
master->Resume();
|
||||
ASSERT_EQ(GetCurrentFiber(), worker[i].get());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SetUpMasterFiber(master.get());
|
||||
SetUpCurrentFiber(nullptr);
|
||||
master->Resume();
|
||||
ASSERT_EQ(cnt, kSwitchCount);
|
||||
for (int i = 0; i != kFiberCount; ++i) {
|
||||
ASSERT_EQ(worker[i]->state(), FiberState::Terminated);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace fiber
|
||||
} // namespace tile
|
@ -1,42 +0,0 @@
|
||||
#include "tile/fiber/detail/waitable.h"
|
||||
#include "tile/base/thread/scoped_lock.h"
|
||||
|
||||
namespace tile {
|
||||
namespace fiber {
|
||||
namespace detail {
|
||||
WaitList::WaitList() {}
|
||||
WaitList::~WaitList() {}
|
||||
|
||||
bool WaitList::AddWaiter(WaitListNode *node) {
|
||||
ScopedLock _(lock_);
|
||||
TILE_CHECK(node->waiter);
|
||||
|
||||
waiters_.push_back(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
Fiber *WaitList::WakeOne() {
|
||||
ScopedLock _(lock_);
|
||||
while (true) {
|
||||
if (waiters_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WaitListNode *waiter = waiters_.front();
|
||||
waiters_.pop_front();
|
||||
if (!waiter) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (waiter->satisfied.exchange(true, std::memory_order_relaxed)) {
|
||||
continue;
|
||||
;
|
||||
}
|
||||
|
||||
return waiter->waiter;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace fiber
|
||||
} // namespace tile
|
@ -1,48 +0,0 @@
|
||||
#ifndef TILE_FIBER_DETAIL_WAITABLE_H
|
||||
#define TILE_FIBER_DETAIL_WAITABLE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tile/base/internal/singly_linked_list.h"
|
||||
#include "tile/base/thread/spinlock.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace tile {
|
||||
namespace fiber {
|
||||
namespace detail {
|
||||
|
||||
class Fiber;
|
||||
class Scheduler;
|
||||
|
||||
struct WaitListNode {
|
||||
Fiber *waiter = nullptr;
|
||||
tile::internal::SinglyLinkedListEntry chain;
|
||||
std::atomic<bool> satisfied{false};
|
||||
};
|
||||
|
||||
class WaitList {
|
||||
public:
|
||||
WaitList();
|
||||
~WaitList();
|
||||
|
||||
bool AddWaiter(WaitListNode *node);
|
||||
// Remove the waiter from the list.
|
||||
// return true if the waiter is removed, otherwise return nullptr.
|
||||
Fiber *WakeOne();
|
||||
|
||||
WaitList(const WaitList &) = delete;
|
||||
WaitList &operator=(const WaitList &) = delete;
|
||||
|
||||
private:
|
||||
Spinlock lock_;
|
||||
std::list<WaitListNode *> waiters_;
|
||||
// tile::internal::SinglyLinkedList<WaitListNode, &WaitListNode::chain>
|
||||
// waiters_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace fiber
|
||||
} // namespace tile
|
||||
|
||||
#endif // TILE_FIBER_DETAIL_WAITABLE_H
|
@ -1,49 +0,0 @@
|
||||
#include "tile/fiber/mutex.h"
|
||||
// #include "tile/base/thread/scoped_lock.h"
|
||||
#include "tile/base/thread/unique_lock.h"
|
||||
#include "tile/fiber/detail/os_fiber.h"
|
||||
|
||||
namespace tile {
|
||||
namespace fiber {
|
||||
void Mutex::Lock() {
|
||||
TILE_DCHECK(detail::IsFiberEnv());
|
||||
if (TILE_LIKELY(TryLock())) {
|
||||
return;
|
||||
}
|
||||
LockSlow();
|
||||
}
|
||||
bool Mutex::TryLock() {
|
||||
TILE_DCHECK(detail::IsFiberEnv());
|
||||
std::uint32_t expected = 0;
|
||||
return count_.compare_exchange_strong(expected, 1, std::memory_order_acquire);
|
||||
}
|
||||
void Mutex::Unlock() {
|
||||
TILE_CHECK(detail::IsFiberEnv());
|
||||
auto was = count_.fetch_sub(1, std::memory_order_release);
|
||||
if (was == 1) {
|
||||
// lock success.
|
||||
} else {
|
||||
TILE_CHECK_GT(was, 1);
|
||||
|
||||
UniqueLock<Spinlock> splk(slow_path_lock_);
|
||||
splk.Unlock();
|
||||
}
|
||||
}
|
||||
void Mutex::LockSlow() {
|
||||
TILE_DCHECK(detail::IsFiberEnv());
|
||||
if (TryLock()) {
|
||||
// lock success.
|
||||
return;
|
||||
}
|
||||
|
||||
UniqueLock<Spinlock> splk(slow_path_lock_);
|
||||
if (count_.fetch_add(1, std::memory_order_acquire) == 0) {
|
||||
// lock success.
|
||||
return;
|
||||
}
|
||||
|
||||
auto current = detail::OSFiber::Current();
|
||||
}
|
||||
|
||||
} // namespace fiber
|
||||
} // namespace tile
|
@ -1,24 +0,0 @@
|
||||
#include "tile/fiber/this_fiber.h"
|
||||
|
||||
namespace tile {
|
||||
namespace this_fiber {
|
||||
|
||||
void Yield() {
|
||||
auto self = fiber::detail::GetCurrentFiber();
|
||||
TILE_CHECK(self, "Yield() should be called in a fiber.");
|
||||
self->scheduler()->Yield(self);
|
||||
}
|
||||
|
||||
void SleepUntil(std::chrono::steady_clock::time_point expires_at) {}
|
||||
|
||||
void SleepFor(std::chrono::nanoseconds expires_in) {
|
||||
SleepUntil(ReadSteadyClock() + expires_in);
|
||||
}
|
||||
|
||||
Fiber::Id GetId() {
|
||||
auto self = fiber::detail::GetCurrentFiber();
|
||||
TILE_CHECK(self, "GetId() should be called in a fiber.");
|
||||
return self->id();
|
||||
}
|
||||
} // namespace this_fiber
|
||||
} // namespace tile
|
@ -1,29 +0,0 @@
|
||||
#include "tile/fiber/detail/fiber.h"
|
||||
#include "tile/fiber/detail/scheduler.h"
|
||||
|
||||
#include "tile/base/chrono.h"
|
||||
|
||||
namespace tile {
|
||||
namespace this_fiber {
|
||||
|
||||
using Fiber = fiber::detail::Fiber;
|
||||
|
||||
void Yield();
|
||||
|
||||
void SleepUntil(std::chrono::steady_clock::time_point expires_at);
|
||||
void SleepFor(std::chrono::nanoseconds expires_in);
|
||||
|
||||
template <typename Clock, typename Duration>
|
||||
void SleepFor(std::chrono::time_point<Clock, Duration> expires_at) {
|
||||
SleepUntil(ReadSteadyClock() + (expires_at - Clock::now()));
|
||||
}
|
||||
|
||||
template <typename Rep, typename Period>
|
||||
void SleepFor(std::chrono::duration<Rep, Period> expires_in) {
|
||||
return SleepFor(static_cast<std::chrono::nanoseconds>(expires_in));
|
||||
}
|
||||
|
||||
Fiber::Id GetId();
|
||||
|
||||
} // namespace this_fiber
|
||||
} // namespace tile
|
@ -1,54 +0,0 @@
|
||||
#define _XOPEN_SOURCE 600
|
||||
extern "C" {
|
||||
#include <ucontext.h>
|
||||
}
|
||||
#include "tile/base/logging.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
static int call_id = 0;
|
||||
char stack1[8192];
|
||||
char stack2[8192];
|
||||
|
||||
static void f1() {
|
||||
EXPECT_EQ(call_id, 0);
|
||||
call_id = 1;
|
||||
}
|
||||
static void f2() {
|
||||
EXPECT_EQ(call_id, 1);
|
||||
call_id = 2;
|
||||
}
|
||||
|
||||
// 0 -> main fiber
|
||||
// 1 -> f1
|
||||
// 2 -> f2
|
||||
static ucontext_t ctx[3];
|
||||
|
||||
void SetCtx1() {}
|
||||
|
||||
void SetCtx2() {}
|
||||
|
||||
TEST(Ucontext, Ucontext) {
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
getcontext(&ctx[0]);
|
||||
for (int i = 0; i != 1000; ++i) {
|
||||
|
||||
getcontext(&ctx[1]);
|
||||
ctx[1].uc_stack.ss_sp = stack1;
|
||||
ctx[1].uc_stack.ss_size = sizeof(stack1);
|
||||
ctx[1].uc_link = &ctx[0];
|
||||
makecontext(&ctx[1], f1, 0);
|
||||
|
||||
getcontext(&ctx[2]);
|
||||
ctx[2].uc_stack.ss_sp = stack2;
|
||||
ctx[2].uc_stack.ss_size = sizeof(stack2);
|
||||
ctx[2].uc_link = &ctx[0];
|
||||
makecontext(&ctx[2], f2, 0);
|
||||
|
||||
call_id = 0;
|
||||
swapcontext(&ctx[0], &ctx[1]);
|
||||
EXPECT_EQ(call_id, 1);
|
||||
swapcontext(&ctx[0], &ctx[2]);
|
||||
EXPECT_EQ(call_id, 2);
|
||||
}
|
||||
}
|
@ -75,6 +75,7 @@ public:
|
||||
void SetUp() override { site_url_ = "http://example.com/"; }
|
||||
std::string site_url_;
|
||||
};
|
||||
|
||||
TEST_F(HttpEngineTest, Basic) {
|
||||
HttpTask t;
|
||||
t.SetMethod(HttpMethod::Get);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "tile/base/casting.h"
|
||||
#include "tile/base/chrono.h"
|
||||
#include "tile/base/compression.h"
|
||||
#include "tile/base/config/ini_file_config.h"
|
||||
#include "tile/base/data.h"
|
||||
#include "tile/base/deferred.h"
|
||||
#include "tile/base/demangle.h"
|
||||
|
Loading…
Reference in New Issue
Block a user