From 0390ca3c0172ee05c68218446e948db0bcca9ef1 Mon Sep 17 00:00:00 2001
From: Hoop77 <p.badenhoop@gmx.de>
Date: Sun, 21 Jan 2018 23:24:03 +0100
Subject: [PATCH] added Busyable to Timer.h

---
 NetworkingLib/CMakeLists.txt          |  3 +-
 NetworkingLib/include/Busyable.h      | 34 +++++------------
 NetworkingLib/include/Closeable.h     | 25 ++++++++++++-
 NetworkingLib/include/ServiceClient.h |  2 +-
 NetworkingLib/include/Timer.h         | 21 +++++++++--
 NetworkingLib/src/Busyable.cpp        | 54 +++++++++++++++++++++++++++
 NetworkingLib/src/Timer.cpp           | 52 ++++++++++++++++----------
 7 files changed, 138 insertions(+), 53 deletions(-)
 create mode 100644 NetworkingLib/src/Busyable.cpp

diff --git a/NetworkingLib/CMakeLists.txt b/NetworkingLib/CMakeLists.txt
index 71c9cbf9..6bb23b2e 100644
--- a/NetworkingLib/CMakeLists.txt
+++ b/NetworkingLib/CMakeLists.txt
@@ -46,7 +46,8 @@ set(SOURCE_FILES
         src/Time.cpp
         src/Networking.cpp
         src/Socket.cpp
-        src/Stream.cpp)
+        src/Stream.cpp
+        src/Busyable.cpp)
 add_library(NetworkingLib STATIC ${SOURCE_FILES})
 
 set(PUBLIC_HEADER_FILES
diff --git a/NetworkingLib/include/Busyable.h b/NetworkingLib/include/Busyable.h
index 043e499e..df60b123 100644
--- a/NetworkingLib/include/Busyable.h
+++ b/NetworkingLib/include/Busyable.h
@@ -23,8 +23,7 @@ public:
      * @attention Returning false does not guarantee that a subsequent
      *             BusyLock instantiation with this object won't throw!
      */
-    bool isBusy() const noexcept
-    { return busy; }
+    bool isBusy() const noexcept;
 
 private:
     std::mutex busyMutex;
@@ -34,37 +33,22 @@ private:
 class BusyLock
 {
 public:
-    BusyLock(Busyable & busyable)
-        : busyable(busyable)
-    {
-        if (!busyable.busyMutex.try_lock())
-            throw error::Busy{};
+    BusyLock(Busyable & busyable);
 
-        owns = true;
-        busyable.busy = true;
-    }
-
-    ~BusyLock()
-    {
-        unlock();
-    }
+    ~BusyLock();
 
     BusyLock(const BusyLock &) = delete;
 
     BusyLock & operator=(const BusyLock &) = delete;
 
-    void unlock()
-    {
-        if (owns)
-        {
-            busyable.busy = false;
-            busyable.busyMutex.unlock();
-            owns = false;
-        }
-    }
+    BusyLock(BusyLock && other) noexcept;
+
+    BusyLock & operator=(BusyLock && other) noexcept;
+
+    void unlock();
 
 private:
-    Busyable & busyable;
+    Busyable * busyable{nullptr};
     std::atomic<bool> owns{false};
 };
 
diff --git a/NetworkingLib/include/Closeable.h b/NetworkingLib/include/Closeable.h
index b3cd704b..92e9497f 100644
--- a/NetworkingLib/include/Closeable.h
+++ b/NetworkingLib/include/Closeable.h
@@ -18,12 +18,32 @@ template<typename Closeable>
 class Closer
 {
 public:
-    Closer(Closeable & closeable) : closeable(closeable)
+    Closer(Closeable & closeable)
+        : closeable(closeable)
     {}
 
     ~Closer()
     {
-        close(closeable);
+        if (alive)
+            close(closeable);
+    }
+
+    Closer(const Closer &) = delete;
+
+    Closer & operator=(const Closer &) = delete;
+
+    Closer(Closer && other)
+        : closeable(other.closeable)
+    {
+        other.alive = false;
+    }
+
+    Closer & operator=(Closer && other)
+    {
+        closeable = other.closeable;
+        alive = other.alive;
+        other.alive = false;
+        return *this;
     }
 
     static void close(Closeable & closeable)
@@ -34,6 +54,7 @@ public:
 
 private:
     Closeable & closeable;
+    bool alive{true};
 };
 
 template<typename Closeable>
diff --git a/NetworkingLib/include/ServiceClient.h b/NetworkingLib/include/ServiceClient.h
index d87bb16d..a6af95c6 100644
--- a/NetworkingLib/include/ServiceClient.h
+++ b/NetworkingLib/include/ServiceClient.h
@@ -182,7 +182,7 @@ private:
         time::Duration timeout;
         time::TimePoint startTime;
         boost::asio::streambuf buffer;
-        closeable::Closer <Socket> closer;
+        closeable::Closer<Socket> closer;
     };
 };
 
diff --git a/NetworkingLib/include/Timer.h b/NetworkingLib/include/Timer.h
index d1ae35b9..25eb9978 100644
--- a/NetworkingLib/include/Timer.h
+++ b/NetworkingLib/include/Timer.h
@@ -9,13 +9,16 @@
 #include <boost/asio.hpp>
 #include <boost/asio/steady_timer.hpp>
 #include "Time.h"
+#include "Busyable.h"
 
 namespace networking
 {
 namespace time
 {
 
-class Timer : public std::enable_shared_from_this<Timer>
+class Timer
+    : public std::enable_shared_from_this<Timer>
+      , private Busyable
 {
 private:
     struct PrivateTag
@@ -31,17 +34,27 @@ public:
 
     static Ptr create(Networking & net);
 
-    void startTimeout(const time::Duration & duration, TimeoutHandler handler);
+    void startTimeout(const time::Duration & duration, const TimeoutHandler & handler);
 
-    void startPeriodicTimeout(const time::Duration & interval, TimeoutHandler handler);
+    void startPeriodicTimeout(const time::Duration & interval, const TimeoutHandler & handler);
 
     void stop();
 
 private:
+    struct AsyncState
+    {
+        AsyncState(Ptr self, const TimeoutHandler & handler, const time::Duration & duration);
+
+        BusyLock busyLock;
+        Ptr self;
+        TimeoutHandler handler;
+        time::Duration duration;
+    };
+
     boost::asio::basic_waitable_timer<time::Clock> timer;
     std::atomic<bool> enabled{true};
 
-    void nextPeriod(const time::Duration & interval, TimeoutHandler handler);
+    void nextPeriod(std::shared_ptr<AsyncState> & state);
 };
 
 }
diff --git a/NetworkingLib/src/Busyable.cpp b/NetworkingLib/src/Busyable.cpp
new file mode 100644
index 00000000..fc994b30
--- /dev/null
+++ b/NetworkingLib/src/Busyable.cpp
@@ -0,0 +1,54 @@
+//
+// Created by philipp on 21.01.18.
+//
+
+#include "../include/Busyable.h"
+
+namespace networking
+{
+
+bool Busyable::isBusy() const noexcept
+{
+    return busy;
+}
+
+BusyLock::BusyLock(Busyable & busyable) : busyable(&busyable)
+{
+    if (!busyable.busyMutex.try_lock())
+        throw error::Busy{};
+
+    owns = true;
+    busyable.busy = true;
+}
+
+BusyLock::~BusyLock()
+{
+    if (owns)
+        unlock();
+}
+
+BusyLock::BusyLock(BusyLock && other) noexcept
+    : busyable(other.busyable)
+      , owns(other.owns.load())
+{
+    other.busyable = nullptr;
+    other.owns = false;
+}
+
+BusyLock & BusyLock::operator=(BusyLock && other) noexcept
+{
+    busyable = other.busyable;
+    owns = other.owns.load();
+
+    other.busyable = nullptr;
+    other.owns = false;
+}
+
+void BusyLock::unlock()
+{
+    busyable->busy = false;
+    busyable->busyMutex.unlock();
+    owns = false;
+}
+
+}
\ No newline at end of file
diff --git a/NetworkingLib/src/Timer.cpp b/NetworkingLib/src/Timer.cpp
index 8e1a10b3..5af7265f 100644
--- a/NetworkingLib/src/Timer.cpp
+++ b/NetworkingLib/src/Timer.cpp
@@ -19,36 +19,41 @@ Timer::Ptr Timer::create(Networking & net)
     return std::make_shared<Timer>(PrivateTag{}, net);
 }
 
-void Timer::startTimeout(const time::Duration & duration, TimeoutHandler handler)
+void Timer::startTimeout(const time::Duration & duration, const TimeoutHandler & handler)
 {
+    auto self = shared_from_this();
+    auto state = std::make_shared<AsyncState>(self, handler, duration);
+
     enabled = true;
 
-    auto self = shared_from_this();
     timer.expires_from_now(duration);
     timer.async_wait(
-        [self, handler](const boost::system::error_code & error)
+        [state](const boost::system::error_code & error) mutable
         {
-            if (error || !self->enabled)
+            if (error || !state->self->enabled)
                 return;
 
-            handler();
+            state->busyLock.unlock();
+            state->handler();
         });
 }
 
-void Timer::startPeriodicTimeout(const time::Duration & interval, Timer::TimeoutHandler handler)
+void Timer::startPeriodicTimeout(const time::Duration & interval, const TimeoutHandler & handler)
 {
+    auto self = shared_from_this();
+    auto state = std::make_shared<AsyncState>(self, handler, interval);
+
     enabled = true;
 
-    auto self = shared_from_this();
     timer.expires_from_now(interval);
     timer.async_wait(
-        [self, interval, handler](const boost::system::error_code & error)
+        [state = std::move(state)](const boost::system::error_code & error) mutable
         {
-            if (error || !self->enabled)
+            if (error || !state->self->enabled)
                 return;
 
-            handler();
-            self->nextPeriod(interval, handler);
+            state->handler();
+            state->self->nextPeriod(state);
         });
 }
 
@@ -62,23 +67,30 @@ void Timer::stop()
     timer.cancel(ignoredError);
 }
 
-void Timer::nextPeriod(const time::Duration & interval, Timer::TimeoutHandler handler)
+void Timer::nextPeriod(std::shared_ptr<AsyncState> & state)
 {
-    if (!enabled)
+    if (!state->self->enabled)
         return;
 
-    auto self = shared_from_this();
-    timer.expires_at(timer.expires_at() + interval);
-    timer.async_wait(
-        [self, interval, handler](const boost::system::error_code & error)
+    state->self->timer.expires_at(state->self->timer.expires_at() + state->duration);
+    state->self->timer.async_wait(
+        [state](const boost::system::error_code & error) mutable
         {
-            if (error || !self->enabled)
+            if (error || !state->self->enabled)
                 return;
 
-            handler();
-            self->nextPeriod(interval, handler);
+            state->handler();
+            state->self->nextPeriod(state);
         });
 }
 
+Timer::AsyncState::AsyncState(Timer::Ptr self, const Timer::TimeoutHandler & handler, const time::Duration & duration)
+    : busyLock(*self)
+      , self(self)
+      , handler(handler)
+      , duration(duration)
+{
+}
+
 }
 }
\ No newline at end of file
-- 
GitLab