Browse Source

* Started to implemented timer and delay

master
bergmann 4 years ago
parent
commit
277ee99b12
31 changed files with 507 additions and 28 deletions
  1. +1
    -1
      .vscode/launch.json
  2. +7
    -2
      .vscode/settings.json
  3. +1
    -1
      cmake/modules
  4. +1
    -7
      include/asyncpp.h
  5. +9
    -0
      include/asyncpp/core.h
  6. +0
    -0
      include/asyncpp/core/future.h
  7. +0
    -0
      include/asyncpp/core/future.inl
  8. +0
    -0
      include/asyncpp/core/future.pre.h
  9. +0
    -0
      include/asyncpp/core/future/and_then.h
  10. +0
    -0
      include/asyncpp/core/future/and_then.inl
  11. +0
    -0
      include/asyncpp/core/future/map.h
  12. +1
    -2
      include/asyncpp/core/future/map.inl
  13. +2
    -14
      include/asyncpp/core/result.h
  14. +0
    -0
      include/asyncpp/core/result.inl
  15. +30
    -0
      include/asyncpp/core/result.pre.h
  16. +0
    -0
      include/asyncpp/core/stream.h
  17. +0
    -0
      include/asyncpp/core/stream.inl
  18. +0
    -0
      include/asyncpp/core/stream.pre.h
  19. +7
    -0
      include/asyncpp/timer.h
  20. +52
    -0
      include/asyncpp/timer/delay.h
  21. +72
    -0
      include/asyncpp/timer/delay.inl
  22. +20
    -0
      include/asyncpp/timer/misc.h
  23. +13
    -0
      include/asyncpp/timer/misc.inl
  24. +103
    -0
      include/asyncpp/timer/timer.h
  25. +102
    -0
      include/asyncpp/timer/timer.inl
  26. +1
    -1
      test/CMakeLists.txt
  27. +0
    -0
      test/asyncpp/core/future_tests.cpp
  28. +0
    -0
      test/asyncpp/core/result_tests.cpp
  29. +0
    -0
      test/asyncpp/core/stream_tests.cpp
  30. +45
    -0
      test/asyncpp/timer/delay_tests.cpp
  31. +40
    -0
      test/helper/now_mock.h

+ 1
- 1
.vscode/launch.json View File

@@ -8,7 +8,7 @@
"name": "future tests",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/test/test-asyncpp-future-tests",
"program": "${workspaceFolder}/build/test/test-asyncpp-timer-delay-tests",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",


+ 7
- 2
.vscode/settings.json View File

@@ -1,4 +1,8 @@
{
"files.trimTrailingWhitespace": true,
"files.trimFinalNewlines": true,
"files.insertFinalNewline": true,
"files.eol": "\n",
"files.associations": {
"optional": "cpp",
"memory": "cpp",
@@ -48,6 +52,7 @@
"memory_resource": "cpp",
"random": "cpp",
"set": "cpp",
"string": "cpp"
"string": "cpp",
"chrono": "cpp"
}
}
}

+ 1
- 1
cmake/modules

@@ -1 +1 @@
Subproject commit 94b9877d65e46c9d8169ebc46f163d02e4d9dcf3
Subproject commit 165bf7c7702ec184b46b046889b62ef4a63afccf

+ 1
- 7
include/asyncpp.h View File

@@ -1,9 +1,3 @@
#pragma once

#include <asyncpp/future.h>
#include <asyncpp/result.h>
#include <asyncpp/stream.h>

#include <asyncpp/future.inl>
#include <asyncpp/result.inl>
#include <asyncpp/stream.inl>
#include <asyncpp/core.h>

+ 9
- 0
include/asyncpp/core.h View File

@@ -0,0 +1,9 @@
#pragma once

#include "core/future.h"
#include "core/result.h"
#include "core/stream.h"

#include "core/future.inl"
#include "core/result.inl"
#include "core/stream.inl"

include/asyncpp/future.h → include/asyncpp/core/future.h View File


include/asyncpp/future.inl → include/asyncpp/core/future.inl View File


include/asyncpp/future.pre.h → include/asyncpp/core/future.pre.h View File


include/asyncpp/future/and_then.h → include/asyncpp/core/future/and_then.h View File


include/asyncpp/future/and_then.inl → include/asyncpp/core/future/and_then.inl View File


include/asyncpp/future/map.h → include/asyncpp/core/future/map.h View File


include/asyncpp/future/map.inl → include/asyncpp/core/future/map.inl View File

@@ -17,12 +17,11 @@ namespace asyncpp
using inner_value_type = typename future_type::value_type;
using lambda_type = T_lambda;
using value_type = decltype(std::declval<lambda_type>()(std::declval<inner_value_type>()));
using result_type = future_result<value_type>;

template<typename X_future>
static inline auto poll(X_future& self)
{
using result_type = future_result<value_type>;

auto r = self.ref.future.poll();
return r
? result_type::ready(self.ref.lambda(*r))

include/asyncpp/result.h → include/asyncpp/core/result.h View File

@@ -2,17 +2,11 @@

#include <variant>

#include "result.pre.h"

namespace asyncpp
{

enum class result_status
{
unknown = 0,
not_ready,
ready,
done,
};

namespace __impl
{

@@ -127,10 +121,4 @@ namespace asyncpp

}

template<typename T_value>
using future_result = __impl::result<false, T_value>;

template<typename T_value>
using stream_result = __impl::result<true, T_value>;

}

include/asyncpp/result.inl → include/asyncpp/core/result.inl View File


+ 30
- 0
include/asyncpp/core/result.pre.h View File

@@ -0,0 +1,30 @@
#pragma once

#include <variant>

namespace asyncpp
{

enum class result_status
{
unknown = 0,
not_ready,
ready,
done,
};

namespace __impl
{

template<bool for_stream, typename T_value>
struct result;

}

template<typename T_value>
using future_result = __impl::result<false, T_value>;

template<typename T_value>
using stream_result = __impl::result<true, T_value>;

}

include/asyncpp/stream.h → include/asyncpp/core/stream.h View File


include/asyncpp/stream.inl → include/asyncpp/core/stream.inl View File


include/asyncpp/stream.pre.h → include/asyncpp/core/stream.pre.h View File


+ 7
- 0
include/asyncpp/timer.h View File

@@ -0,0 +1,7 @@
#pragma once

#include "timer/delay.h"
#include "timer/misc.h"

#include "timer/delay.inl"
#include "timer/misc.inl"

+ 52
- 0
include/asyncpp/timer/delay.h View File

@@ -0,0 +1,52 @@
#pragma once

#include "misc.h"
#include "timer.h"

namespace asyncpp {
namespace timer {

struct delay
{
public:
friend struct future_trait<delay, void>;

private:
time_point _timeout;
registration _registration;

public:
/**
* @brief Constructor. Create a delay at the given timeout.
*/
inline delay(const time_point& p_timeout);

/**
* @brief Constructor. Create a delay with the given duration.
*/
template<typename T_base, typename T_ratio>
inline delay(const duration<T_base, T_ratio>& p_duration);

/**
* @brief Destructor.
*/
inline ~delay();

/**
* @brief Get the current timeout of the delay.
*/
inline time_point timeout() const;

/**
* @brief Reset the delay to a new time point.
*/
inline void reset(const time_point& p_timeout);

/**
* @brief Reset the delay to the given duration.
*/
template<typename T_base, typename T_ratio>
inline void reset(const duration<T_base, T_ratio>& p_duration);
};

} }

+ 72
- 0
include/asyncpp/timer/delay.inl View File

@@ -0,0 +1,72 @@
#pragma once

#include <asyncpp/core/future.pre.h>
#include <asyncpp/core/result.pre.h>

#include "delay.h"

#include "timer.inl"

namespace asyncpp {
namespace timer {

/* delay */

delay::delay(const time_point& p_timeout)
: _timeout (p_timeout)
, _registration (*this)
{ }

template<typename T_base, typename T_ratio>
delay::delay(const duration<T_base, T_ratio>& p_duration)
: _timeout(clock::now() + p_duration)
{ }

inline delay::~delay()
{ timer::unregister_resource(_registration); }

time_point delay::timeout() const
{ return _timeout; }

void delay::reset(const time_point& p_timeout)
{
timer::unregister_resource(_registration);
_timeout = p_timeout;
}

template<typename T_base, typename T_ratio>
void delay::reset(const duration<T_base, T_ratio>& p_duration)
{
timer::unregister_resource(_registration);
_timeout = now() + p_duration;
}

} }

namespace asyncpp
{

/* future_impl for timer::delay */

template<>
struct future_trait<timer::delay, void>
: public future_base<future<timer::delay, void>>
{
using value_type = void;
using result_type = future_result<value_type>;

template<typename X_future>
static inline auto poll(X_future& self)
{
auto now = timer::now();

if (self.ref._timeout <= now)
return result_type::ready();

timer::timer::register_resource(self.ref._registration);

return result_type::not_ready();
}
};

}

+ 20
- 0
include/asyncpp/timer/misc.h View File

@@ -0,0 +1,20 @@
#pragma once

#include <chrono>

namespace asyncpp {
namespace timer {

using clock = std::chrono::steady_clock;

using time_point = clock::time_point;

template<typename T_base, typename T_ratio>
using duration = std::chrono::duration<T_base ,T_ratio>;

/**
* @brief Get the current time point.
*/
inline time_point now();

} }

+ 13
- 0
include/asyncpp/timer/misc.inl View File

@@ -0,0 +1,13 @@
#pragma once

#include "misc.h"

namespace asyncpp {
namespace timer {

#ifndef __asyncpp_has_impl_timer_now
time_point now()
{ return clock::now(); }
#endif

} }

+ 103
- 0
include/asyncpp/timer/timer.h View File

@@ -0,0 +1,103 @@
#pragma once

#include <set>
#include <memory>

#include "misc.h"

namespace asyncpp {
namespace timer {

struct delay;
struct timer;

struct registration
{
public:
struct inner
{
timer& owner;
time_point timeout;

/**
* @brief Constructor.
*/
inline inner(timer& p_owner, time_point p_timeout);
};

using inner_ptr_w = std::weak_ptr<inner>;

public:
delay& owner;
inner_ptr_w ptr;

/**
* @brief Constructor.
*/
inline registration(delay& p_owner);
};

struct timer
{
private:
using inner_ptr_s = std::shared_ptr<registration::inner>;

struct inner_less_compare
{ constexpr bool operator()(const inner_ptr_s& lhs, const inner_ptr_s& rhs) const; };

using inner_set = std::set<inner_ptr_s, inner_less_compare>;

private:
inner_set _registrations;

public:
/**
* @brief Get the number of resources assigned to this timer.
*/
inline size_t resource_count() const;

/**
* @brief Set the thread local current timer.
*/
inline void make_current();

/**
* @brief Set the thread local current timer.
*/
inline void clear_current();

public:
/**
* @brief Get the thread local timer instance.
*/
static inline timer* current();

/**
* @brief Register a new resource within this timer.
*/
static inline void register_resource(registration& p_value);

/**
* @brief Register a new resource within this timer.
*/
static inline void unregister_resource(registration& p_value);

private:
/**
* @brief Add a new resource to the timer.
*/
inline inner_ptr_s make_inner(registration& p_value);

private:
struct storage
{
timer* current { nullptr };
};

/**
* @brief Get the thread local storage.
*/
static inline storage& local_storage();
};

} }

+ 102
- 0
include/asyncpp/timer/timer.inl View File

@@ -0,0 +1,102 @@
#pragma once

#include "timer.h"
#include "delay.inl"

namespace asyncpp {
namespace timer {

/* registration::inner */

registration::inner::inner(timer& p_owner, time_point p_timeout)
: owner (p_owner)
, timeout (p_timeout)
{ }

/* registration */

registration::registration(delay& p_owner)
: owner(p_owner)
, ptr ()
{ }

/* timer::inner_less_compare */

constexpr bool timer::inner_less_compare::operator()(const inner_ptr_s& lhs, const inner_ptr_s& rhs) const
{
return (lhs->timeout < rhs->timeout)
? true
:
(lhs->timeout == rhs->timeout)
&& (lhs.get() < rhs.get())
? true
: false;
}

/* timer */

size_t timer::resource_count() const
{ return _registrations.size(); }

void timer::make_current()
{
auto& s = local_storage();
if (s.current)
throw std::runtime_error("Thread local timer instance is already assigned!");
s.current = this;
}

void timer::clear_current()
{
auto& s = local_storage();
if (s.current && s.current != this)
throw std::runtime_error("Thread local timer instance is not assigned to this instance!");
s.current = nullptr;
}

timer* timer::current()
{ return local_storage().current; }

void timer::register_resource(registration& p_value)
{
auto s = p_value.ptr.lock();

if (s && s->timeout != p_value.owner.timeout())
{
unregister_resource(p_value);
s.reset();
}

if (!s)
{
auto t = current();

if (!t)
throw std::runtime_error("Thread local timer instance is not assigned!");

p_value.ptr = t->make_inner(p_value);
}
}

void timer::unregister_resource(registration& p_value)
{
auto s = p_value.ptr.lock();
p_value.ptr.reset();
if (s)
s->owner._registrations.erase(s);
}

inline timer::inner_ptr_s timer::make_inner(registration& p_value)
{
auto s = std::make_shared<registration::inner>(*this, p_value.owner.timeout());
_registrations.insert(s);
return s;
}

timer::storage& timer::local_storage()
{
thread_local storage value;
return value;
}

} }

+ 1
- 1
test/CMakeLists.txt View File

@@ -38,7 +38,7 @@ ForEach ( FILE IN LISTS ASYNCPP_TEST_SOURCE_FILES )
Target_Link_Libraries ( ${TEST_NAME}
PUBLIC
asyncpp
GTest::Main )
GMock::Main )

# Sanitizers
If ( Sanitizers_FOUND )


test/asyncpp/future_tests.cpp → test/asyncpp/core/future_tests.cpp View File


test/asyncpp/result_tests.cpp → test/asyncpp/core/result_tests.cpp View File


test/asyncpp/stream_tests.cpp → test/asyncpp/core/stream_tests.cpp View File


+ 45
- 0
test/asyncpp/timer/delay_tests.cpp View File

@@ -0,0 +1,45 @@
#include <gtest/gtest.h>

#include "../../helper/now_mock.h"

#include <asyncpp.h>
#include <asyncpp/timer.h>

using namespace ::testing;
using namespace ::asyncpp;
using namespace ::asyncpp::timer;

TEST(delay_tests, poll)
{
StrictMock<now_mock> m;

EXPECT_CALL(m, now)
.WillOnce(Return(time_point(std::chrono::seconds( 0))))
.WillOnce(Return(time_point(std::chrono::seconds( 999))))
.WillOnce(Return(time_point(std::chrono::seconds(1000))));

asyncpp::timer::timer t;
t.make_current();

{
delay d(time_point(std::chrono::seconds(1000)));
auto f = as_future(d);

EXPECT_EQ(0, t.resource_count());

auto r0 = f.poll();
ASSERT_FALSE(r0);

EXPECT_EQ(1, t.resource_count());

auto r1 = f.poll();
ASSERT_FALSE(r1);

EXPECT_EQ(1, t.resource_count());

auto r2 = f.poll();
ASSERT_TRUE (r2);
}

EXPECT_EQ(0, t.resource_count());
}

+ 40
- 0
test/helper/now_mock.h View File

@@ -0,0 +1,40 @@
#pragma once

#include <gtest/gtest.h>
#include <gmock/gmock.h>

#define __asyncpp_has_impl_timer_now

#include <asyncpp/timer/misc.h>

struct now_mock;

static now_mock * __now_mock_instance;

struct now_mock
{
public:
inline now_mock()
{ __now_mock_instance = this; }

inline ~now_mock()
{
if (__now_mock_instance == this)
__now_mock_instance = nullptr;
}

public:
MOCK_METHOD0(now, asyncpp::timer::time_point());
};

namespace asyncpp {
namespace timer {

time_point now()
{
return __now_mock_instance
? __now_mock_instance->now()
: std::chrono::steady_clock::now();
}

} }

Loading…
Cancel
Save