Timer "connection"

parent 36ae62f2
......@@ -23,5 +23,6 @@ add_subdirectory(irc)
add_subdirectory(lists)
add_subdirectory(posix)
add_subdirectory(script)
add_subdirectory(timer)
add_subdirectory(web)
add_subdirectory(xonotic)
......@@ -39,7 +39,7 @@ namespace posix {
/**
* \brief Acts as a network connection to handle standard input (or any file)
*/
class StdinConnection : public network::Connection
class StdinConnection : public network::SingleUnitConnection
{
public:
static std::unique_ptr<StdinConnection> create(
......@@ -49,7 +49,7 @@ public:
}
StdinConnection(const Settings& settings, const std::string& name)
: Connection(name),input{io_service}
: SingleUnitConnection(name),input{io_service}
{
std::string filename = settings.get("file","");
int file = open_file(filename);
......@@ -89,7 +89,6 @@ public:
std::string protocol() const override { return "stdin"; }
std::string name() const override { return "stdin"; }
network::Server server() const override { return {"stdin",0}; }
std::string description() const override { return "stdin"; }
......@@ -109,18 +108,8 @@ public:
void connect() override {}
void disconnect(const std::string&) override {}
void reconnect(const std::string&) override {}
bool channel_mask(const std::vector<std::string>&, const std::string&) const override { return true; }
bool user_auth(const std::string&, const std::string&) const override { return true; }
void update_user(const std::string&, const Properties& ) override {}
void update_user(const std::string&, const user::User& ) override {}
user::User get_user(const std::string&) const override { return {}; }
std::vector<user::User> get_users( const std::string& ) const override { return {}; }
bool add_to_group(const std::string&, const std::string&) override { return false; }
bool remove_from_group(const std::string&, const std::string& ) override { return false; }
std::vector<user::User> users_in_group(const std::string&) const override { return {}; }
std::vector<user::User> real_users_in_group(const std::string& group) const override { return {}; }
void command (network::Command) override {}
user::UserCounter count_users(const std::string& channel = {}) const override { return {}; }
// maybe could be given some actual functionality:
Properties pretty_properties() const override { return Properties{}; }
......
# Copyright (C) 2015 Mattia Basaglia
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
melanomodule(timer "Timer service" ON)
set(SOURCES
timer.cpp
)
add_library(${MODULE_NAME} MODULE ${SOURCES})
/**
* \file
* \author Mattia Basaglia
* \copyright Copyright 2015 Mattia Basaglia
* \license
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MELANOBOT_MODULES_TIMER_CONNECTION
#define MELANOBOT_MODULES_TIMER_CONNECTION
#include "network/connection.hpp"
#include "network/time.hpp"
#include "functional.hpp"
#include "concurrency/concurrency.hpp"
namespace timer {
using namespace network;
class TimerItem
{
public:
Time timeout;
std::string message;
/**
* \brief Comparator used to create the heap
*/
bool operator<(const TimerItem& rhs) const
{
return timeout > rhs.timeout;
}
};
class TimerConnection : public network::SingleUnitConnection
{
public:
static std::unique_ptr<TimerConnection> create(
const Settings& settings, const std::string& name)
{
return melanolib::New<TimerConnection>(name);
}
explicit TimerConnection(const std::string& name)
: SingleUnitConnection(name), timer{[this]{tick();}}
{}
~TimerConnection()
{
stop();
}
Server server() const override { return {}; }
std::string description() const override
{
return protocol();
}
void command ( Command cmd ) override
{
if ( cmd.parameters.empty() )
return;
push({cmd.timeout, cmd.parameters[0]});
}
void say ( const OutputMessage& message ) override
{
push({message.timeout, message.message.encode(formatter())});
}
Status status() const override { return CONNECTED; }
std::string protocol() const override { return "timer"; }
void connect() override {}
void disconnect(const std::string& message = {}) override {}
void reconnect(const std::string& quit_message = {}) {}
void stop() override
{
if ( thread.joinable() )
{
timer_status = Die;
condition.notify_one();
thread.join();
}
}
void start() override
{
if ( !thread.joinable() )
{
timer_status = Tick;
thread = std::thread([this]{run();});
}
}
string::Formatter* formatter() const override
{
return string::Formatter::formatter("config");
}
LockedProperties properties() override
{
static PropertyTree props;
return {nullptr, &props};
}
Properties pretty_properties() const override
{
return {};
}
private:
void run()
{
std::mutex condition_mutex;
while ( true )
{
bool empty = true;
Time timeout;
{
Lock lock_events(events_mutex);
empty = events.empty();
if ( !empty )
timeout = events[0].timeout;
}
std::unique_lock<std::mutex> lock(condition_mutex);
if ( !empty )
condition.wait_until(lock, timeout);
else
condition.wait(lock);
switch( timer_status )
{
case Noop:
condition.wait(lock);
case Tick:
tick();
case Die:
return;
}
}
}
void tick()
{
Lock lock(events_mutex);
if ( events.empty() )
return;
// Handle spurious wake ups
if ( events[0].timeout > Clock::now() )
return;
std::pop_heap(events.begin(), events.end());
TimerItem cmd = std::move(events.back());
events.pop_back();
lock.unlock();
/// \todo send message
}
void push(TimerItem&& item)
{
timer_status = Noop;
condition.notify_one();
Lock lock(events_mutex);
events.emplace_back(std::move(item));
std::push_heap(events.begin(), events.end());
lock.unlock();
timer_status = Tick;
condition.notify_one();
}
std::vector<TimerItem> events;
Timer timer;
std::condition_variable condition; ///< Wait condition
std::mutex events_mutex;
std::thread thread;
enum TimerStatus
{
Noop, Tick, Die
};
std::atomic<TimerStatus> timer_status;
};
} // namespace timer
#endif // MELANOBOT_MODULES_TIMER_CONNECTION
/**
* \file
* \author Mattia Basaglia
* \copyright Copyright 2015 Mattia Basaglia
* \section License
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "timer-connection.hpp"
#include "melanomodule.hpp"
/**
* \brief Initializes the timer module
*/
MELANOMODULE_ENTRY_POINT module::Melanomodule melanomodule_timer_metadata()
{
return {"timer", "Timer service"};
}
MELANOMODULE_ENTRY_POINT void melanomodule_timer_initialize(const Settings&)
{
module::register_connection<timer::TimerConnection>("timer");
}
......@@ -64,6 +64,7 @@ template<class Clock>
basic_timer(const basic_timer& rhs)
: timeout(rhs.timeout), repeating(rhs.repeating), action(rhs.action)
{}
basic_timer& operator=(const basic_timer& rhs)
{
timeout = rhs.timeout;
......@@ -101,9 +102,10 @@ template<class Clock>
{
if ( running() || !action )
return false;
if ( timeout == duration_type::zero() )
if ( timeout <= duration_type::zero() )
callback(action);
thread = std::move(std::thread([this]{run();}));
else
thread = std::move(std::thread([this]{run();}));
return running();
}
......@@ -138,7 +140,7 @@ template<class Clock>
try {
thread.join();
// catch in the case the thread joined early
}catch(const std::invalid_argument&) {}
} catch(const std::invalid_argument&) {}
}
}
......
......@@ -268,6 +268,29 @@ private:
std::string config_name_;
};
/**
* \brief Base class for connections with a single user and channel
*/
class SingleUnitConnection : public Connection
{
public:
using Connection::Connection;
bool channel_mask(const std::vector<std::string>&, const std::string&) const override { return true; }
bool user_auth(const std::string&, const std::string&) const override { return true; }
void update_user(const std::string&, const Properties& ) override {}
void update_user(const std::string&, const user::User& ) override {}
user::User get_user(const std::string&) const override { return {}; }
std::vector<user::User> get_users( const std::string& ) const override { return {}; }
bool add_to_group(const std::string&, const std::string&) override { return false; }
bool remove_from_group(const std::string&, const std::string& ) override { return false; }
std::vector<user::User> users_in_group(const std::string&) const override { return {}; }
std::vector<user::User> real_users_in_group(const std::string& group) const override { return {}; }
user::UserCounter count_users(const std::string& channel = {}) const override { return {}; }
std::string name() const override { return protocol(); }
};
/**
* \brief Creates connections from settings
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment