Skip to content

Commit

Permalink
refactor: dispatch system (#1685)
Browse files Browse the repository at this point in the history
Dispatch has been refactored to be centralized in just one thread.
Previously, each task was sent to the threads that were free, which
could cause bottlenecks because they were stuck with mutexes, causing
queues depending on how many threads the cpu had.

small illustration of a possible problem:

![image](https://github.com/opentibiabr/canary/assets/2267386/9cfba304-c8aa-4590-9538-48edb95a972a)

Apart from what was mentioned above,, the scheduling class has been
removed and we now have new methods in dispatch, addEvent_async,
scheduleEvent and cycleEvent.

**Note**
- addEvent_async(func, context_id)
- With this method it is now possible to create an asynchronous event
that will be executed before all synchronous events.

**Questions**
- Why Scoped Object
- there is a big cost in allocating(new) and deallocating(delete) an
object from memory and since we don't need to manage the lifetime of the
object or share it, it doesn't make sense to use smart pointer.

**Benchmark**
- Scoped Object vs Smart Pointer

![image](https://github.com/opentibiabr/canary/assets/2267386/7c514967-2087-4d46-ad0b-19820a31e46a)
<details>
  
```c++

struct Teste {
	int i;
};

Benchmark bm;
for (uint_fast32_t i = 0; i < 999; ++i) {
	std::vector<Teste> list;
	for (uint_fast32_t d = 0; d < 99999; ++d) {
		list.emplace_back(d);
	}
	list.clear();
}

g_logger().info("Scoped Object {}ms", bm.duration());

bm.reset();
bm.start();

for (uint_fast32_t i = 0; i < 999; ++i) {
	std::vector<std::unique_ptr<Teste>> list;
	for (uint_fast32_t d = 0; d < 99999; ++d) {
		list.emplace_back(std::make_unique<Teste>(d));
	}
	list.clear();
}

g_logger().info("Smart Pointer {}ms", bm.duration());
```</details>

---------

Co-authored-by: Luan Santos <[email protected]>
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 18, 2023
1 parent 7ce2982 commit fb5f3ca
Show file tree
Hide file tree
Showing 42 changed files with 680 additions and 348 deletions.
5 changes: 4 additions & 1 deletion src/canary_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@ CanaryServer::CanaryServer(
std::set_new_handler(badAllocationHandler);
srand(static_cast<unsigned int>(OTSYS_TIME()));

g_dispatcher().init();

#ifdef _WIN32
SetConsoleTitleA(STATUS_SERVER_NAME);
#endif
}

int CanaryServer::run() {
g_dispatcher().addTask(
g_dispatcher().addEvent(
[this] {
try {
loadConfigLua();
Expand Down Expand Up @@ -365,4 +367,5 @@ void CanaryServer::modulesLoadHelper(bool loaded, std::string moduleName) {

void CanaryServer::shutdown() {
inject<ThreadPool>().shutdown();
g_dispatcher().shutdown();
}
2 changes: 1 addition & 1 deletion src/creatures/combat/condition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2040,7 +2040,7 @@ bool ConditionFeared::executeCondition(std::shared_ptr<Creature> creature, int32
}

if (getFleePath(creature, currentPos, listDir)) {
g_dispatcher().addTask(std::bind(&Game::forcePlayerAutoWalk, &g_game(), creature->getID(), listDir), "ConditionFeared::executeCondition");
g_dispatcher().addEvent(std::bind(&Game::forcePlayerAutoWalk, &g_game(), creature->getID(), listDir), "ConditionFeared::executeCondition");
g_logger().debug("[ConditionFeared::executeCondition] Walking Scheduled");
}
}
Expand Down
15 changes: 7 additions & 8 deletions src/creatures/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include "game/scheduling/dispatcher.hpp"
#include "game/game.hpp"
#include "creatures/monsters/monster.hpp"
#include "game/scheduling/scheduler.hpp"
#include "game/zones/zone.hpp"
#include "map/spectators.hpp"

Expand Down Expand Up @@ -260,15 +259,15 @@ void Creature::addEventWalk(bool firstStep) {
g_game().checkCreatureWalk(getID());
}

eventWalk = g_scheduler().addEvent(
eventWalk = g_dispatcher().scheduleEvent(
static_cast<uint32_t>(ticks), std::bind(&Game::checkCreatureWalk, &g_game(), getID()),
"Creature::checkCreatureWalk"
);
}

void Creature::stopEventWalk() {
if (eventWalk != 0) {
g_scheduler().stopEvent(eventWalk);
g_dispatcher().stopEvent(eventWalk);
eventWalk = 0;
}
}
Expand Down Expand Up @@ -598,7 +597,7 @@ void Creature::onCreatureMove(std::shared_ptr<Creature> creature, std::shared_pt
if (followCreature && (creature == getCreature() || creature == followCreature)) {
if (hasFollowPath) {
isUpdatingPath = true;
g_dispatcher().addTask(std::bind(&Game::updateCreatureWalk, &g_game(), getID()), "Game::updateCreatureWalk");
g_dispatcher().addEvent(std::bind(&Game::updateCreatureWalk, &g_game(), getID()), "Game::updateCreatureWalk");
}

if (newPos.z != oldPos.z || !canSee(followCreature->getPosition())) {
Expand All @@ -613,7 +612,7 @@ void Creature::onCreatureMove(std::shared_ptr<Creature> creature, std::shared_pt
} else {
if (hasExtraSwing()) {
// our target is moving lets see if we can get in hit
g_dispatcher().addTask(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack");
g_dispatcher().addEvent(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack");
}

if (newTile->getZoneType() != oldTile->getZoneType()) {
Expand Down Expand Up @@ -754,7 +753,7 @@ bool Creature::dropCorpse(std::shared_ptr<Creature> lastHitCreature, std::shared

if (player->checkAutoLoot()) {
int32_t pos = tile->getStackposOfItem(player, corpse);
g_dispatcher().addTask(
g_dispatcher().addEvent(
std::bind(&Game::playerQuickLoot, &g_game(), mostDamageCreature->getID(), this->getPosition(), corpse->getID(), pos - 1, nullptr, false, true),
"Game::playerQuickLoot"
);
Expand Down Expand Up @@ -801,7 +800,7 @@ void Creature::changeHealth(int32_t healthChange, bool sendHealthChange /* = tru
g_game().addCreatureHealth(static_self_cast<Creature>());
}
if (health <= 0) {
g_dispatcher().addTask(std::bind(&Game::executeDeath, &g_game(), getID()), "Game::executeDeath");
g_dispatcher().addEvent(std::bind(&Game::executeDeath, &g_game(), getID()), "Game::executeDeath");
}
}

Expand Down Expand Up @@ -1309,7 +1308,7 @@ void Creature::removeCondition(ConditionType_t conditionType, ConditionId_t cond
if (!force && conditionType == CONDITION_PARALYZE) {
int32_t walkDelay = getWalkDelay();
if (walkDelay > 0) {
g_scheduler().addEvent(
g_dispatcher().scheduleEvent(
walkDelay,
std::bind(&Game::forceRemoveCondition, &g_game(), getID(), conditionType, conditionId),
"Game::forceRemoveCondition"
Expand Down
4 changes: 2 additions & 2 deletions src/creatures/interactions/chat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include "creatures/interactions/chat.hpp"
#include "game/game.hpp"
#include "utils/pugicast.hpp"
#include "game/scheduling/scheduler.hpp"
#include "game/scheduling/dispatcher.hpp"

bool PrivateChatChannel::isInvited(uint32_t guid) const {
if (guid == getOwner()) {
Expand Down Expand Up @@ -81,7 +81,7 @@ bool ChatChannel::addUser(const std::shared_ptr<Player> &player) {
if (id == CHANNEL_GUILD) {
const auto guild = player->getGuild();
if (guild && !guild->getMotd().empty()) {
g_scheduler().addEvent(150, std::bind(&Game::sendGuildMotd, &g_game(), player->getID()), "Game::sendGuildMotd");
g_dispatcher().scheduleEvent(150, std::bind(&Game::sendGuildMotd, &g_game(), player->getID()), "Game::sendGuildMotd");
}
}

Expand Down
21 changes: 15 additions & 6 deletions src/creatures/monsters/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,12 +340,21 @@ void Monster::updateTargetList() {

auto targetIterator = targetIDList.begin();
while (targetIterator != targetIDList.end()) {
auto creature = targetListMap[*targetIterator].lock();
if (!creature || creature->getHealth() <= 0 || !canSee(creature->getPosition())) {
targetIterator = targetIDList.erase(targetIterator);
targetListMap.erase(*targetIterator);
const uint32_t targetId = *targetIterator;

auto itTLM = targetListMap.find(targetId);
const bool existTarget = itTLM != targetListMap.end();

if (existTarget) {
const auto &creature = itTLM->second.lock();
if (!creature || creature->getHealth() <= 0 || !canSee(creature->getPosition())) {
targetIterator = targetIDList.erase(targetIterator);
targetListMap.erase(itTLM);
} else {
++targetIterator;
}
} else {
++targetIterator;
targetIterator = targetIDList.erase(targetIterator);
}
}

Expand Down Expand Up @@ -662,7 +671,7 @@ bool Monster::selectTarget(std::shared_ptr<Creature> creature) {

if (isHostile() || isSummon()) {
if (setAttackedCreature(creature)) {
g_dispatcher().addTask(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack");
g_dispatcher().addEvent(std::bind(&Game::checkCreatureAttack, &g_game(), getID()), "Game::checkCreatureAttack");
}
}
return setFollowCreature(creature);
Expand Down
10 changes: 5 additions & 5 deletions src/creatures/monsters/spawns/spawn_monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include "creatures/monsters/spawns/spawn_monster.hpp"
#include "game/game.hpp"
#include "creatures/monsters/monster.hpp"
#include "game/scheduling/scheduler.hpp"
#include "game/scheduling/dispatcher.hpp"
#include "game/scheduling/events_scheduler.hpp"
#include "lua/creature/events.hpp"
#include "lua/callbacks/event_callback.hpp"
Expand Down Expand Up @@ -144,7 +144,7 @@ bool SpawnsMonster::isInZone(const Position &centerPos, int32_t radius, const Po

void SpawnMonster::startSpawnMonsterCheck() {
if (checkSpawnMonsterEvent == 0) {
checkSpawnMonsterEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this), "SpawnMonster::checkSpawnMonster");
checkSpawnMonsterEvent = g_dispatcher().scheduleEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this), "SpawnMonster::checkSpawnMonster");
}
}

Expand Down Expand Up @@ -239,7 +239,7 @@ void SpawnMonster::checkSpawnMonster() {
}

if (spawnedMonsterMap.size() < spawnMonsterMap.size()) {
checkSpawnMonsterEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this), "SpawnMonster::checkSpawnMonster");
checkSpawnMonsterEvent = g_dispatcher().scheduleEvent(getInterval(), std::bind(&SpawnMonster::checkSpawnMonster, this), "SpawnMonster::checkSpawnMonster");
}
}

Expand All @@ -248,7 +248,7 @@ void SpawnMonster::scheduleSpawn(uint32_t spawnMonsterId, spawnBlock_t &sb, uint
spawnMonster(spawnMonsterId, sb.monsterType, sb.pos, sb.direction);
} else {
g_game().addMagicEffect(sb.pos, CONST_ME_TELEPORT);
g_scheduler().addEvent(1400, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL), "SpawnMonster::scheduleSpawn");
g_dispatcher().scheduleEvent(1400, std::bind(&SpawnMonster::scheduleSpawn, this, spawnMonsterId, sb, interval - NONBLOCKABLE_SPAWN_MONSTER_INTERVAL), "SpawnMonster::scheduleSpawn");
}
}

Expand Down Expand Up @@ -298,7 +298,7 @@ void SpawnMonster::removeMonster(std::shared_ptr<Monster> monster) {

void SpawnMonster::stopEvent() {
if (checkSpawnMonsterEvent != 0) {
g_scheduler().stopEvent(checkSpawnMonsterEvent);
g_dispatcher().stopEvent(checkSpawnMonsterEvent);
checkSpawnMonsterEvent = 0;
}
}
5 changes: 2 additions & 3 deletions src/creatures/npcs/npc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include "game/game.hpp"
#include "lua/callbacks/creaturecallback.hpp"
#include "game/scheduling/dispatcher.hpp"
#include "game/scheduling/scheduler.hpp"
#include "map/spectators.hpp"

int32_t Npc::despawnRange;
Expand Down Expand Up @@ -350,7 +349,7 @@ void Npc::onPlayerSellAllLoot(uint32_t playerId, uint16_t itemId, bool ignore, u
return;
}
if (hasMore) {
g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, totalPrice), __FUNCTION__);
g_dispatcher().scheduleEvent(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, totalPrice), __FUNCTION__);
return;
}
ss << "You sold all of the items from your loot pouch for ";
Expand All @@ -365,7 +364,7 @@ void Npc::onPlayerSellItem(std::shared_ptr<Player> player, uint16_t itemId, uint
return;
}
if (itemId == ITEM_GOLD_POUCH) {
g_scheduler().addEvent(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, 0), __FUNCTION__);
g_dispatcher().scheduleEvent(SCHEDULER_MINTICKS, std::bind(&Npc::onPlayerSellAllLoot, this, player->getID(), itemId, ignore, 0), __FUNCTION__);
return;
}

Expand Down
10 changes: 5 additions & 5 deletions src/creatures/npcs/spawns/spawn_npc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include "creatures/npcs/spawns/spawn_npc.hpp"
#include "creatures/npcs/npc.hpp"
#include "game/game.hpp"
#include "game/scheduling/scheduler.hpp"
#include "game/scheduling/dispatcher.hpp"
#include "lua/creature/events.hpp"
#include "lua/callbacks/event_callback.hpp"
#include "lua/callbacks/events_callbacks.hpp"
Expand Down Expand Up @@ -131,7 +131,7 @@ bool SpawnsNpc::isInZone(const Position &centerPos, int32_t radius, const Positi

void SpawnNpc::startSpawnNpcCheck() {
if (checkSpawnNpcEvent == 0) {
checkSpawnNpcEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this), "SpawnNpc::checkSpawnNpc");
checkSpawnNpcEvent = g_dispatcher().scheduleEvent(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this), "SpawnNpc::checkSpawnNpc");
}
}

Expand Down Expand Up @@ -216,7 +216,7 @@ void SpawnNpc::checkSpawnNpc() {
}

if (spawnedNpcMap.size() < spawnNpcMap.size()) {
checkSpawnNpcEvent = g_scheduler().addEvent(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this), __FUNCTION__);
checkSpawnNpcEvent = g_dispatcher().scheduleEvent(getInterval(), std::bind(&SpawnNpc::checkSpawnNpc, this), __FUNCTION__);
}
}

Expand All @@ -225,7 +225,7 @@ void SpawnNpc::scheduleSpawnNpc(uint32_t spawnId, spawnBlockNpc_t &sb, uint16_t
spawnNpc(spawnId, sb.npcType, sb.pos, sb.direction);
} else {
g_game().addMagicEffect(sb.pos, CONST_ME_TELEPORT);
g_scheduler().addEvent(1400, std::bind(&SpawnNpc::scheduleSpawnNpc, this, spawnId, sb, interval - NONBLOCKABLE_SPAWN_NPC_INTERVAL), __FUNCTION__);
g_dispatcher().scheduleEvent(1400, std::bind(&SpawnNpc::scheduleSpawnNpc, this, spawnId, sb, interval - NONBLOCKABLE_SPAWN_NPC_INTERVAL), __FUNCTION__);
}
}

Expand Down Expand Up @@ -275,7 +275,7 @@ void SpawnNpc::removeNpc(std::shared_ptr<Npc> npc) {

void SpawnNpc::stopEvent() {
if (checkSpawnNpcEvent != 0) {
g_scheduler().stopEvent(checkSpawnNpcEvent);
g_dispatcher().stopEvent(checkSpawnNpcEvent);
checkSpawnNpcEvent = 0;
}
}
Loading

0 comments on commit fb5f3ca

Please sign in to comment.