Compare commits
10 Commits
d0f0955c47
...
c2fb269d40
Author | SHA1 | Date | |
---|---|---|---|
c2fb269d40 | |||
8a783c87a4 | |||
281f70adb4 | |||
a3043963da | |||
aa73fb50a5 | |||
782171268a | |||
1aa14f541e | |||
295bb7c5f0 | |||
32fc72824b | |||
e6d9d2205d |
@ -17,6 +17,7 @@ target_sources(nf7_core
|
||||
luajit/lambda.hh
|
||||
luajit/thread.hh
|
||||
clock.hh
|
||||
logger.hh
|
||||
version.hh
|
||||
)
|
||||
|
||||
|
29
core/logger.hh
Normal file
29
core/logger.hh
Normal file
@ -0,0 +1,29 @@
|
||||
// No copyright
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "iface/common/exception.hh"
|
||||
#include "iface/subsys/logger.hh"
|
||||
|
||||
|
||||
namespace nf7::core {
|
||||
|
||||
class NullLogger : public subsys::Logger {
|
||||
public:
|
||||
static const std::shared_ptr<NullLogger>& instance()
|
||||
try {
|
||||
static const auto kInstance = std::make_shared<NullLogger>();
|
||||
return kInstance;
|
||||
} catch (const std::bad_alloc&) {
|
||||
throw Exception {"memory shortage"};
|
||||
}
|
||||
|
||||
public:
|
||||
NullLogger() noexcept : subsys::Logger("nf7::core::NullLogger") { }
|
||||
|
||||
public:
|
||||
void Push(const Item&) noexcept override { }
|
||||
};
|
||||
|
||||
} // namespace nf7::core
|
@ -14,13 +14,10 @@
|
||||
|
||||
#include "iface/common/exception.hh"
|
||||
#include "iface/common/task.hh"
|
||||
#include "iface/subsys/clock.hh"
|
||||
#include "iface/subsys/concurrency.hh"
|
||||
#include "iface/subsys/parallelism.hh"
|
||||
#include "iface/env.hh"
|
||||
|
||||
#include "core/clock.hh"
|
||||
|
||||
namespace nf7::core::luajit::test {
|
||||
|
||||
class ContextFixture : public ::testing::TestWithParam<Context::Kind> {
|
||||
@ -97,7 +94,6 @@ class ContextFixture : public ::testing::TestWithParam<Context::Kind> {
|
||||
void SetUp() override {
|
||||
syncq_ = std::make_shared<SimpleTaskQueue<SyncTask>>();
|
||||
asyncq_ = std::make_shared<SimpleTaskQueue<AsyncTask>>();
|
||||
clock_ = std::make_shared<Clock>();
|
||||
|
||||
env_.emplace(SimpleEnv::FactoryMap {
|
||||
{
|
||||
@ -112,11 +108,6 @@ class ContextFixture : public ::testing::TestWithParam<Context::Kind> {
|
||||
WrappedTaskQueue<subsys::Parallelism>>(asyncq_);
|
||||
},
|
||||
},
|
||||
{
|
||||
typeid(subsys::Clock), [this](auto&) {
|
||||
return clock_;
|
||||
},
|
||||
},
|
||||
{
|
||||
typeid(Context), [this](auto& env) {
|
||||
return Context::Create(env, GetParam());
|
||||
@ -156,7 +147,6 @@ class ContextFixture : public ::testing::TestWithParam<Context::Kind> {
|
||||
protected:
|
||||
std::shared_ptr<SimpleTaskQueue<SyncTask>> syncq_;
|
||||
std::shared_ptr<SimpleTaskQueue<AsyncTask>> asyncq_;
|
||||
std::shared_ptr<Clock> clock_;
|
||||
std::optional<SimpleEnv> env_;
|
||||
|
||||
private:
|
||||
|
@ -30,6 +30,10 @@ class Lambda::Thread : public luajit::Thread {
|
||||
}
|
||||
void onAborted(TaskContext& lua) noexcept override {
|
||||
if (auto la = la_.lock()) {
|
||||
if (auto logger = la->logger_) {
|
||||
const auto msg = lua_tostring(*lua, -1);
|
||||
logger->Error(msg);
|
||||
}
|
||||
++la->abort_count_;
|
||||
TryResume(lua, la);
|
||||
}
|
||||
@ -138,9 +142,14 @@ void Lambda::PushLuaContextObject(TaskContext& lua) noexcept {
|
||||
|
||||
lua_pushcfunction(*lua, [](auto L) {
|
||||
const auto la = self(L);
|
||||
if (nullptr == la->clock_) {
|
||||
return luaL_error(L, "clock is not installed in the environment");
|
||||
}
|
||||
|
||||
const auto wla = std::weak_ptr<Lambda> {la};
|
||||
const auto dur = luaL_checkinteger(L, 2);
|
||||
const auto after = la->clock_->now() + dur * 1ms;
|
||||
|
||||
la->lua_->Push(Task {after, [wla](auto& lua) {
|
||||
if (auto la = wla.lock()) {
|
||||
assert(la->thread_);
|
||||
@ -150,6 +159,32 @@ void Lambda::PushLuaContextObject(TaskContext& lua) noexcept {
|
||||
return lua_yield(L, 0);
|
||||
});
|
||||
lua_setfield(*lua, -2, "sleep");
|
||||
|
||||
static const auto logFunc = []<subsys::Logger::Level lv>(auto L) {
|
||||
const auto la = self(L);
|
||||
const auto contents = luaL_checkstring(L, 2);
|
||||
la->logger_->Push(subsys::Logger::Item {lv, contents});
|
||||
return 0;
|
||||
};
|
||||
lua_pushcfunction(*lua, [](auto L) {
|
||||
return logFunc.operator()<subsys::Logger::kTrace>(L);
|
||||
});
|
||||
lua_setfield(*lua, -2, "trace");
|
||||
|
||||
lua_pushcfunction(*lua, [](auto L) {
|
||||
return logFunc.operator()<subsys::Logger::kInfo>(L);
|
||||
});
|
||||
lua_setfield(*lua, -2, "info");
|
||||
|
||||
lua_pushcfunction(*lua, [](auto L) {
|
||||
return logFunc.operator()<subsys::Logger::kWarn>(L);
|
||||
});
|
||||
lua_setfield(*lua, -2, "warn");
|
||||
|
||||
lua_pushcfunction(*lua, [](auto L) {
|
||||
return logFunc.operator()<subsys::Logger::kError>(L);
|
||||
});
|
||||
lua_setfield(*lua, -2, "error");
|
||||
}
|
||||
lua_setfield(*lua, -2, "__index");
|
||||
}
|
||||
|
@ -9,11 +9,13 @@
|
||||
|
||||
#include "iface/subsys/clock.hh"
|
||||
#include "iface/subsys/concurrency.hh"
|
||||
#include "iface/subsys/logger.hh"
|
||||
#include "iface/env.hh"
|
||||
#include "iface/lambda.hh"
|
||||
|
||||
#include "core/luajit/context.hh"
|
||||
#include "core/luajit/thread.hh"
|
||||
#include "core/logger.hh"
|
||||
|
||||
namespace nf7::core::luajit {
|
||||
|
||||
@ -23,8 +25,9 @@ class Lambda :
|
||||
public:
|
||||
explicit Lambda(nf7::Env& env, const std::shared_ptr<luajit::Value>& func)
|
||||
: LambdaBase(),
|
||||
clock_(env.Get<subsys::Clock>()),
|
||||
clock_(env.GetOr<subsys::Clock>()),
|
||||
concurrency_(env.Get<subsys::Concurrency>()),
|
||||
logger_(env.GetOr<subsys::Logger>(NullLogger::instance())),
|
||||
lua_(env.Get<luajit::Context>()),
|
||||
func_(func) { }
|
||||
|
||||
@ -43,6 +46,7 @@ class Lambda :
|
||||
private:
|
||||
const std::shared_ptr<subsys::Clock> clock_;
|
||||
const std::shared_ptr<subsys::Concurrency> concurrency_;
|
||||
const std::shared_ptr<subsys::Logger> logger_;
|
||||
|
||||
const std::shared_ptr<Context> lua_;
|
||||
const std::shared_ptr<Value> func_;
|
||||
|
@ -6,9 +6,13 @@
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "iface/subsys/clock.hh"
|
||||
|
||||
#include "core/luajit/context.hh"
|
||||
#include "core/clock.hh"
|
||||
|
||||
#include "iface/common/observer_test.hh"
|
||||
#include "iface/subsys/logger_test.hh"
|
||||
|
||||
#include "core/luajit/context_test.hh"
|
||||
|
||||
@ -36,10 +40,15 @@ class LuaJIT_Lambda : public nf7::core::luajit::test::ContextFixture {
|
||||
void Expect(const char* script,
|
||||
const std::vector<nf7::Value>& in,
|
||||
uint32_t expectExit = 1, uint32_t expectAbort = 0,
|
||||
const std::vector<nf7::Value>& out = {}) {
|
||||
const std::vector<nf7::Value>& out = {},
|
||||
nf7::Env* env = nullptr) {
|
||||
if (nullptr == env) {
|
||||
env = &*env_;
|
||||
}
|
||||
|
||||
auto func = Compile(script);
|
||||
|
||||
auto sut = std::make_shared<nf7::core::luajit::Lambda>(*env_, func);
|
||||
auto sut = std::make_shared<nf7::core::luajit::Lambda>(*env, func);
|
||||
for (const auto& v : in) {
|
||||
sut->taker()->Take(v);
|
||||
}
|
||||
@ -133,20 +142,71 @@ TEST_P(LuaJIT_Lambda, CtxMultiSend) {
|
||||
}
|
||||
|
||||
TEST_P(LuaJIT_Lambda, CtxSleep) {
|
||||
clock_->Tick();
|
||||
const auto begin = clock_->now();
|
||||
const auto clock = std::make_shared<nf7::core::Clock>();
|
||||
nf7::SimpleEnv env {{
|
||||
{typeid(nf7::subsys::Clock), [&](auto&) { return clock; }},
|
||||
}, *env_};
|
||||
|
||||
clock->Tick();
|
||||
const auto begin = clock->now();
|
||||
|
||||
Expect(
|
||||
"local ctx = ...\nctx:sleep(100)",
|
||||
{nf7::Value {}},
|
||||
1, 0);
|
||||
1, 0,
|
||||
{},
|
||||
&env);
|
||||
|
||||
clock_->Tick();
|
||||
const auto end = clock_->now();
|
||||
clock->Tick();
|
||||
const auto end = clock->now();
|
||||
|
||||
EXPECT_GE(end-begin, 100ms);
|
||||
}
|
||||
|
||||
TEST_P(LuaJIT_Lambda, CtxSleepWithoutClock) {
|
||||
Expect(
|
||||
"local ctx = ...\nctx:sleep(100)",
|
||||
{nf7::Value {}},
|
||||
0, 1);
|
||||
}
|
||||
|
||||
TEST_P(LuaJIT_Lambda, CtxLogging) {
|
||||
const auto logger = std::make_shared<nf7::subsys::test::LoggerMock>();
|
||||
|
||||
EXPECT_CALL(*logger, Push)
|
||||
.WillOnce([](auto& item) {
|
||||
EXPECT_EQ(item.level(), nf7::subsys::Logger::kTrace);
|
||||
EXPECT_EQ(item.contents(), "this is trace");
|
||||
})
|
||||
.WillOnce([](auto& item) {
|
||||
EXPECT_EQ(item.level(), nf7::subsys::Logger::kInfo);
|
||||
EXPECT_EQ(item.contents(), "this is info");
|
||||
})
|
||||
.WillOnce([](auto& item) {
|
||||
EXPECT_EQ(item.level(), nf7::subsys::Logger::kWarn);
|
||||
EXPECT_EQ(item.contents(), "this is warn");
|
||||
})
|
||||
.WillOnce([](auto& item) {
|
||||
EXPECT_EQ(item.level(), nf7::subsys::Logger::kError);
|
||||
EXPECT_EQ(item.contents(), "this is error");
|
||||
});
|
||||
|
||||
nf7::SimpleEnv env {{
|
||||
{typeid(nf7::subsys::Logger), [&](auto&) { return logger; }},
|
||||
}, *env_};
|
||||
|
||||
Expect(
|
||||
"local ctx = ...\n"
|
||||
"ctx:trace(\"this is trace\")\n"
|
||||
"ctx:info(\"this is info\")\n"
|
||||
"ctx:warn(\"this is warn\")\n"
|
||||
"ctx:error(\"this is error\")",
|
||||
{nf7::Value {}},
|
||||
1, 0,
|
||||
{},
|
||||
&env);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SyncOrAsync, LuaJIT_Lambda,
|
||||
testing::Values(
|
||||
|
@ -21,6 +21,7 @@ target_sources(nf7_iface
|
||||
data/interface.hh
|
||||
subsys/concurrency.hh
|
||||
subsys/interface.hh
|
||||
subsys/logger.hh
|
||||
subsys/parallelism.hh
|
||||
env.hh
|
||||
file.hh
|
||||
@ -40,6 +41,7 @@ target_sources(nf7_iface_test
|
||||
common/task_test.cc
|
||||
common/task_test.hh
|
||||
common/value_test.cc
|
||||
subsys/logger_test.hh
|
||||
lambda_test.cc
|
||||
lambda_test.hh
|
||||
)
|
||||
|
@ -64,6 +64,25 @@ class Container {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename I>
|
||||
class NullContainer : public Container<I> {
|
||||
public:
|
||||
static std::shared_ptr<NullContainer> instance()
|
||||
try {
|
||||
static const auto kInstance = std::make_shared<NullContainer>();
|
||||
return kInstance;
|
||||
} catch (const std::bad_alloc&) {
|
||||
throw Exception {"memory shortage"};
|
||||
}
|
||||
|
||||
public:
|
||||
NullContainer() = default;
|
||||
|
||||
public:
|
||||
std::shared_ptr<I> Get(std::type_index) override { return nullptr; }
|
||||
bool installed(std::type_index) const noexcept override { return false; }
|
||||
};
|
||||
|
||||
template <typename I>
|
||||
class SimpleContainer : public Container<I> {
|
||||
public:
|
||||
@ -95,8 +114,9 @@ class SimpleContainer : public Container<I> {
|
||||
}
|
||||
|
||||
SimpleContainer() = delete;
|
||||
explicit SimpleContainer(FactoryMap&& factories) noexcept
|
||||
: factories_(std::move(factories)) { }
|
||||
SimpleContainer(FactoryMap&& factories,
|
||||
Container<I>& fb = *NullContainer<I>::instance()) noexcept
|
||||
: fallback_(fb), factories_(std::move(factories)) { }
|
||||
|
||||
std::shared_ptr<I> Get(std::type_index idx) override {
|
||||
const auto obj_itr = objs_.find(idx);
|
||||
@ -122,11 +142,11 @@ class SimpleContainer : public Container<I> {
|
||||
throw Exception {"memory shortage"};
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return fallback_.Get(idx);
|
||||
}
|
||||
|
||||
bool installed(std::type_index idx) const noexcept override {
|
||||
return factories_.contains(idx);
|
||||
return factories_.contains(idx) || fallback_.installed(idx);
|
||||
}
|
||||
|
||||
using Container<I>::Get;
|
||||
@ -134,6 +154,8 @@ class SimpleContainer : public Container<I> {
|
||||
using Container<I>::installed;
|
||||
|
||||
private:
|
||||
Container<I>& fallback_;
|
||||
|
||||
FactoryMap factories_;
|
||||
ObjectMap objs_;
|
||||
|
||||
|
@ -66,6 +66,28 @@ TEST(SimpleContainer, CheckInstalled) {
|
||||
EXPECT_FALSE(sut.installed<IB>());
|
||||
}
|
||||
|
||||
TEST(SimpleContainer, FetchWithFallback) {
|
||||
SUT fb {{
|
||||
SUT::MakePair<IA, A>(),
|
||||
}};
|
||||
SUT sut {{}, fb};
|
||||
auto ptr = sut.Get<IA>();
|
||||
EXPECT_TRUE(std::dynamic_pointer_cast<A>(ptr));
|
||||
}
|
||||
TEST(SimpleContainer, FetchUnknownWithFallback) {
|
||||
SUT fb {{}};
|
||||
SUT sut {{}, fb};
|
||||
EXPECT_THROW(sut.Get<IA>(), nf7::Exception);
|
||||
}
|
||||
TEST(SimpleContainer, CheckInstalledWithFallback) {
|
||||
SUT fb {{
|
||||
SUT::MakePair<IA, A>(),
|
||||
}};
|
||||
SUT sut {{}, fb};
|
||||
EXPECT_TRUE(sut.installed<IA>());
|
||||
EXPECT_FALSE(sut.installed<IB>());
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
TEST(SimpleContainer, DeathByFetchRecursive) {
|
||||
SUT sut {{
|
||||
|
80
iface/subsys/logger.hh
Normal file
80
iface/subsys/logger.hh
Normal file
@ -0,0 +1,80 @@
|
||||
// No copyright
|
||||
#pragma once
|
||||
|
||||
#include <source_location>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "iface/subsys/interface.hh"
|
||||
|
||||
|
||||
namespace nf7::subsys {
|
||||
|
||||
class Logger : public Interface {
|
||||
public:
|
||||
using SrcLoc = std::source_location;
|
||||
|
||||
enum Level {
|
||||
kTrace,
|
||||
kInfo,
|
||||
kWarn,
|
||||
kError,
|
||||
};
|
||||
|
||||
class Item final {
|
||||
public:
|
||||
Item() = delete;
|
||||
Item(Level level,
|
||||
std::string_view contents,
|
||||
std::source_location srcloc = std::source_location::current()) noexcept
|
||||
: level_(level), contents_(contents), srcloc_(srcloc) { }
|
||||
|
||||
Item(const Item&) = default;
|
||||
Item(Item&&) = default;
|
||||
Item& operator=(const Item&) = default;
|
||||
Item& operator=(Item&&) = default;
|
||||
|
||||
Level level() const noexcept { return level_; }
|
||||
const std::string& contents() const noexcept { return contents_; }
|
||||
const std::source_location& srcloc() const noexcept { return srcloc_; }
|
||||
|
||||
private:
|
||||
Level level_;
|
||||
std::string contents_;
|
||||
std::source_location srcloc_;
|
||||
};
|
||||
|
||||
public:
|
||||
using Interface::Interface;
|
||||
|
||||
public:
|
||||
// THREAD-SAFE
|
||||
virtual void Push(const Item&) noexcept = 0;
|
||||
|
||||
// THREAD-SAFE
|
||||
void Trace(std::string_view contents,
|
||||
SrcLoc srcloc = SrcLoc::current()) noexcept {
|
||||
Push(Item {kTrace, contents, srcloc});
|
||||
}
|
||||
|
||||
// THREAD-SAFE
|
||||
void Info(std::string_view contents,
|
||||
SrcLoc srcloc = SrcLoc::current()) noexcept {
|
||||
Push(Item {kInfo, contents, srcloc});
|
||||
}
|
||||
|
||||
// THREAD-SAFE
|
||||
void Warn(std::string_view contents,
|
||||
SrcLoc srcloc = SrcLoc::current()) noexcept {
|
||||
Push(Item {kWarn, contents, srcloc});
|
||||
}
|
||||
|
||||
// THREAD-SAFE
|
||||
void Error(std::string_view contents,
|
||||
SrcLoc srcloc = SrcLoc::current()) noexcept {
|
||||
Push(Item {kError, contents, srcloc});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace nf7::subsys
|
19
iface/subsys/logger_test.hh
Normal file
19
iface/subsys/logger_test.hh
Normal file
@ -0,0 +1,19 @@
|
||||
// No copyright
|
||||
#pragma once
|
||||
|
||||
#include "iface/subsys/logger.hh"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
|
||||
namespace nf7::subsys::test {
|
||||
|
||||
class LoggerMock : public Logger {
|
||||
public:
|
||||
explicit LoggerMock(const char* name = "LoggerMock") noexcept
|
||||
: Logger(name) {}
|
||||
|
||||
MOCK_METHOD(void, Push, (const Item&), (noexcept));
|
||||
};
|
||||
|
||||
} // namespace nf7::subsys::test
|
Loading…
x
Reference in New Issue
Block a user