Canceling asio timers has bad semantics, and causes loads of trouble. Therefore, cancelled timers are simply marked as such, and the asio callback is silently ignored. This fixes several crashes, if players were removed from a game due to an error, because some timers were not cancelled. *sigh* This one took a lot of nerve.

This commit is contained in:
lotodore
2009-06-07 23:25:13 +00:00
parent a6146399d4
commit c8594e8e7b
10 changed files with 56 additions and 44 deletions
+18 -19
View File
@@ -35,31 +35,30 @@ TimerManager::RegisterTimer(unsigned timeoutMsec, boost::function<void()> timerH
boost::recursive_mutex::scoped_lock lock(m_timerMutex);
// Use a unique id for each timer.
unsigned id = GetNextTimerId();
TimerData data;
data.timer.reset(
boost::shared_ptr<TimerData> data(new TimerData);
data->timer.reset(
new boost::asio::deadline_timer(*m_ioService, boost::posix_time::milliseconds(timeoutMsec)));
data.userHandler = timerHandler;
data.durationMsec = timeoutMsec;
data.autoRestart = autoRestart;
data.timer->async_wait(boost::bind(&TimerManager::Handler, boost::asio::placeholders::error, data));
data->userHandler = timerHandler;
data->durationMsec = timeoutMsec;
data->autoRestart = autoRestart;
data->cancelled = false;
data->timer->async_wait(boost::bind(&TimerManager::Handler, boost::asio::placeholders::error, data));
m_timerMap.insert(TimerMap::value_type(id, data));
return id;
}
bool
TimerManager::RestartTimer(unsigned timerId, unsigned timeoutMsec, boost::function<void()> timerHandler)
TimerManager::AddTimer(unsigned timerId, unsigned timeoutMsec, boost::function<void()> timerHandler)
{
boost::recursive_mutex::scoped_lock lock(m_timerMutex);
bool restarted = false;
TimerMap::iterator pos = m_timerMap.find(timerId);
if (pos != m_timerMap.end())
{
pos->second.userHandler = timerHandler;
boost::shared_ptr<boost::asio::deadline_timer> timer(pos->second.timer);
timer->cancel();
timer->expires_from_now(boost::posix_time::milliseconds(timeoutMsec));
timer->async_wait(boost::bind(&TimerManager::Handler, boost::asio::placeholders::error, pos->second));
pos->second->userHandler = timerHandler;
pos->second->timer->expires_from_now(boost::posix_time::milliseconds(timeoutMsec));
pos->second->timer->async_wait(boost::bind(&TimerManager::Handler, boost::asio::placeholders::error, pos->second));
restarted = true;
}
return restarted;
@@ -74,7 +73,7 @@ TimerManager::UnregisterTimer(unsigned timerId)
TimerMap::iterator pos = m_timerMap.find(timerId);
if (pos != m_timerMap.end())
{
pos->second.timer->cancel();
pos->second->cancelled = true;
m_timerMap.erase(pos);
unregistered = true;
}
@@ -82,15 +81,15 @@ TimerManager::UnregisterTimer(unsigned timerId)
}
void
TimerManager::Handler(boost::system::error_code ec, TimerManager::TimerData data)
TimerManager::Handler(const boost::system::error_code &ec, boost::shared_ptr<TimerManager::TimerData> data)
{
if (!ec)
if (!ec && data && !data->cancelled)
{
data.userHandler();
if (data.autoRestart)
data->userHandler();
if (data->autoRestart)
{
data.timer->expires_from_now(boost::posix_time::milliseconds(data.durationMsec));
data.timer->async_wait(boost::bind(&TimerManager::Handler, boost::asio::placeholders::error, data));
data->timer->expires_from_now(boost::posix_time::milliseconds(data->durationMsec));
data->timer->async_wait(boost::bind(&TimerManager::Handler, boost::asio::placeholders::error, data));
}
}
}
+4 -3
View File
@@ -32,7 +32,7 @@ public:
TimerManager(boost::shared_ptr<boost::asio::io_service> ioService);
unsigned RegisterTimer(unsigned timeoutMsec, boost::function<void()> timerHandler, bool autoRestart = false);
bool RestartTimer(unsigned timerId, unsigned timeoutMsec, boost::function<void()> timerHandler);
bool AddTimer(unsigned timerId, unsigned timeoutMsec, boost::function<void()> timerHandler);
bool UnregisterTimer(unsigned timerId);
protected:
@@ -43,10 +43,11 @@ protected:
boost::function<void()> userHandler;
unsigned durationMsec;
bool autoRestart;
bool cancelled;
};
static void Handler(boost::system::error_code ec, TimerData data);
typedef std::map<unsigned, TimerData> TimerMap;
static void Handler(const boost::system::error_code &ec, boost::shared_ptr<TimerData> data);
typedef std::map<unsigned, boost::shared_ptr<TimerData> > TimerMap;
unsigned GetNextTimerId();
+2 -2
View File
@@ -48,7 +48,7 @@ class SendDataManager : public boost::enable_shared_from_this<SendDataManager>
{
}
void HandleWrite(const boost::system::error_code& error);
void HandleWrite(const boost::system::error_code &error);
void AsyncSendNextPacket(bool handlerMode = false);
@@ -61,7 +61,7 @@ class SendDataManager : public boost::enable_shared_from_this<SendDataManager>
void
SendDataManager::HandleWrite(const boost::system::error_code& error)
SendDataManager::HandleWrite(const boost::system::error_code &error)
{
// TODO error handling
AsyncSendNextPacket(true);
+2 -2
View File
@@ -24,7 +24,7 @@
#include <core/loghelper.h>
#define NET_SERVER_LISTEN_BACKLOG 5
#define NET_SERVER_LISTEN_BACKLOG 20
using namespace std;
using boost::asio::ip::tcp;
@@ -96,7 +96,7 @@ ServerAcceptHelper::InternalListen(unsigned serverPort, bool ipv6, bool sctp)
void
ServerAcceptHelper::HandleAccept(boost::shared_ptr<boost::asio::ip::tcp::socket> acceptedSocket,
const boost::system::error_code& error)
const boost::system::error_code &error)
{
if (!error)
{
+11 -3
View File
@@ -46,7 +46,16 @@ ServerGame::ServerGame(ServerLobbyThread &lobbyThread, u_int32_t id, const strin
LOG_VERBOSE("Game object " << GetId() << " created.");
m_receiver.reset(new ReceiverHelper);
}
ServerGame::~ServerGame()
{
LOG_VERBOSE("Game object " << GetId() << " destructed.");
}
void
ServerGame::Init()
{
m_voteKickTimerId = GetLobbyThread().GetTimerManager().RegisterTimer(
SERVER_CHECK_VOTE_KICK_INTERVAL_MSEC,
boost::bind(&ServerGame::TimerVoteKick, this),
@@ -55,12 +64,11 @@ ServerGame::ServerGame(ServerLobbyThread &lobbyThread, u_int32_t id, const strin
SetState(SERVER_INITIAL_STATE::Instance());
}
ServerGame::~ServerGame()
void
ServerGame::Exit()
{
GetLobbyThread().GetTimerManager().UnregisterTimer(m_voteKickTimerId);
GetLobbyThread().GetTimerManager().UnregisterTimer(m_stateTimerId);
LOG_VERBOSE("Game object " << GetId() << " destructed.");
}
u_int32_t
+8 -8
View File
@@ -400,7 +400,7 @@ ServerGameStateInit::TimerAdminWarning(ServerGame &server)
server.GetLobbyThread().GetSender().Send(session.sessionData, warning);
}
// Start timeout timer.
server.GetLobbyThread().GetTimerManager().RestartTimer(
server.GetLobbyThread().GetTimerManager().AddTimer(
server.GetStateTimerId(),
SERVER_GAME_ADMIN_WARNING_REMAINING_SEC * 1000,
boost::bind(&ServerGameStateInit::TimerAdminTimeout, this, boost::ref(server)));
@@ -701,7 +701,7 @@ ServerGameStateHand::TimerLoop(ServerGame &server)
server.SendToAllPlayers(allIn, SessionData::Game);
curGame.getCurrentHand()->setCardsShown(true);
server.GetLobbyThread().GetTimerManager().RestartTimer(
server.GetLobbyThread().GetTimerManager().AddTimer(
server.GetStateTimerId(),
SERVER_SHOW_CARDS_DELAY_SEC * 1000,
boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)));
@@ -710,7 +710,7 @@ ServerGameStateHand::TimerLoop(ServerGame &server)
{
SendNewRoundCards(server, curGame, newRound);
server.GetLobbyThread().GetTimerManager().RestartTimer(
server.GetLobbyThread().GetTimerManager().AddTimer(
server.GetStateTimerId(),
GetDealCardsDelaySec(server) * 1000,
boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)));
@@ -741,7 +741,7 @@ ServerGameStateHand::TimerLoop(ServerGame &server)
// If the player is computer controlled, let the engine act.
if (curPlayer->getMyType() == PLAYER_TYPE_COMPUTER)
{
server.GetLobbyThread().GetTimerManager().RestartTimer(
server.GetLobbyThread().GetTimerManager().AddTimer(
server.GetStateTimerId(),
SERVER_COMPUTER_ACTION_DELAY_SEC * 1000,
boost::bind(&ServerGameStateHand::TimerComputerAction, this, boost::ref(server)));
@@ -750,7 +750,7 @@ ServerGameStateHand::TimerLoop(ServerGame &server)
else if (!server.GetSessionManager().IsPlayerConnected(curPlayer->getMyName()))
{
PerformPlayerAction(server, curPlayer, PLAYER_ACTION_FOLD, 0);
server.GetLobbyThread().GetTimerManager().RestartTimer(
server.GetLobbyThread().GetTimerManager().AddTimer(
server.GetStateTimerId(),
SERVER_LOOP_DELAY_MSEC,
boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)));
@@ -833,14 +833,14 @@ ServerGameStateHand::TimerLoop(ServerGame &server)
else if (playersWithCash.size() == 1)
{
// View a dialog for a new game - delayed.
server.GetLobbyThread().GetTimerManager().RestartTimer(
server.GetLobbyThread().GetTimerManager().AddTimer(
server.GetStateTimerId(),
SERVER_DELAY_NEXT_GAME_SEC * 1000,
boost::bind(&ServerGameStateHand::TimerNextGame, this, boost::ref(server)));
}
else
{
server.GetLobbyThread().GetTimerManager().RestartTimer(
server.GetLobbyThread().GetTimerManager().AddTimer(
server.GetStateTimerId(),
SERVER_DELAY_NEXT_HAND_SEC * 1000,
boost::bind(&ServerGameStateHand::TimerNextHand, this, boost::ref(server)));
@@ -855,7 +855,7 @@ ServerGameStateHand::TimerShowCards(ServerGame &server)
Game &curGame = server.GetGame();
SendNewRoundCards(server, curGame, curGame.getCurrentHand()->getCurrentRound());
server.GetLobbyThread().GetTimerManager().RestartTimer(
server.GetLobbyThread().GetTimerManager().AddTimer(
server.GetStateTimerId(),
GetDealCardsDelaySec(server) * 1000,
boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)));
+6 -5
View File
@@ -563,7 +563,7 @@ ServerLobbyThread::RegisterTimers()
}
void
ServerLobbyThread::HandleRead(SessionId sessionId, const boost::system::error_code& error, size_t bytesRead)
ServerLobbyThread::HandleRead(SessionId sessionId, const boost::system::error_code &error, size_t bytesRead)
{
// Find the session.
SessionWrapper session = m_sessionManager.GetSessionById(sessionId);
@@ -949,11 +949,11 @@ ServerLobbyThread::HandleNetPacketCreateGame(SessionWrapper session, const NetPa
session.playerData->GetUniqueId(),
GetGui(),
m_playerConfig));
MoveSessionToGame(*game, session);
game->Init();
// Add game to list of games.
InternalAddGame(game);
MoveSessionToGame(*game, session);
}
void
@@ -1043,7 +1043,7 @@ ServerLobbyThread::TimerRemoveGame()
++next;
boost::shared_ptr<ServerGame> tmpGame = i->second;
if (!tmpGame->GetSessionManager().HasSessions())
InternalRemoveGame(tmpGame); // This will delete the entry from the map.
InternalRemoveGame(tmpGame); // This will delete the game.
i = next;
}
}
@@ -1154,6 +1154,7 @@ ServerLobbyThread::InternalRemoveGame(boost::shared_ptr<ServerGame> game)
// Remove all sessions left in the game.
game->ResetComputerPlayerList();
game->RemoveAllSessions();
game->Exit();
// Notify all players.
boost::shared_ptr<NetPacket> packet = CreateNetPacketGameListUpdate(game->GetId(), GAME_MODE_CLOSED);
m_sessionManager.SendLobbyMsgToAllSessions(GetSender(), packet, SessionData::Established);
+1 -1
View File
@@ -43,7 +43,7 @@ protected:
void InternalListen(unsigned serverPort, bool ipv6, bool sctp);
void HandleAccept(boost::shared_ptr<boost::asio::ip::tcp::socket> acceptedSocket,
const boost::system::error_code& error);
const boost::system::error_code &error);
ServerCallback &GetCallback();
+3
View File
@@ -43,6 +43,9 @@ public:
ServerLobbyThread &lobbyThread, u_int32_t id, const std::string &name, const std::string &pwd, const GameData &gameData, unsigned adminPlayerId, GuiInterface &gui, ConfigFile *playerConfig);
virtual ~ServerGame();
void Init();
void Exit();
u_int32_t GetId() const;
const std::string &GetName() const;
+1 -1
View File
@@ -114,7 +114,7 @@ protected:
virtual void Main();
void RegisterTimers();
void HandleRead(SessionId sessionId, const boost::system::error_code& error, size_t bytesRead);
void HandleRead(SessionId sessionId, const boost::system::error_code &error, size_t bytesRead);
void HandlePacket(SessionWrapper session, boost::shared_ptr<NetPacket> packet);
void HandleNetPacketInit(SessionWrapper session, const NetPacketInit &tmpPacket);
void HandleNetPacketAvatarHeader(SessionWrapper session, const NetPacketAvatarHeader &tmpPacket);