-
Notifications
You must be signed in to change notification settings - Fork 624
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use Task System to schedule Conquest tasks #4623
Conversation
e16cfb4
to
e2cb738
Compare
e2cb738
to
9f8862c
Compare
{ | ||
submit([this]() | ||
{ sql = std::make_unique<SqlConnection>(); }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think task_system is going to work for your use case here if you have to submit a task to configure it - I think it's too simple of a building block to achieve what you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What might work here, is if you make IMessageHandler also inherit from task_system, giving you access to the guts and the construction of everything. This would allow you to lazily set up the sql handle upon submitting the first task, or on construction, or whatever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and just because this was the review point that made me remember the term I've been trying to remember for WEEKS: https://en.wikipedia.org/wiki/Convention_over_configuration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So been thinking about this. I wouldn't need to do this if it wasn't for the warning that pops up when checking if sql query is executed in the same thread it's instantiated.
Because all queries are going to happen in the same thread (the task system thread), it's okay that we instantiate sql in a different thread. Ideally, the check would only happen if two queries are called from different threads. Would you be okay with that change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I do see that sqlconnection fires a query on instantiation, which makes this change harder
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely not. I don't play up my 'trap card' to be cute or funny, I do it because it's a legitimate problem that was very annoying to diagnose and fix.
Line 30 in b61c149
// NOTE: Access to any of the MySQL resources is NOT thread-safe. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The warning triggers in the exact scenario that results in UB, most often seen as the sql handle getting wiped out - so don't ignore it
class IMessageHandler | ||
{ | ||
public: | ||
IMessageHandler() | ||
{ | ||
ts = new ts::task_system(1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::make_unique
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But as I mentioned above, I'd look into making this class: class IMessageHandler : public ts::task_system
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also make it inherit from SqlConnection
👀
virtual ~IMessageHandler() | ||
{ | ||
delete ts; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you use smart pointers, there's no need for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will use a smart pointer. I was following the Async
example. Thought there was a reason for not using a smart pointer
You can also edit Async to look like this: #pragma once
#include "sql.h"
#include <task_system.hpp>
#include <functional>
#include <string>
class Async : public ts::task_system
{
public:
~Async() = default;
static Async* getInstance();
void query(std::string const& query);
void query(std::function<void(SqlConnection*)> const& func);
void submit(std::function<void()> const& func);
private:
Async();
}; #include "async.h"
#include "sql.h"
#include <task_system.hpp>
namespace
{
// Allocated on and owned the main thread
std::unique_ptr<Async> _instance = nullptr;
// Allocated on and owned by the task_system worker thread
std::unique_ptr<SqlConnection> _sql = nullptr;
} // namespace
Async::Async()
: ts::task_system(1)
{
}
Async* Async::getInstance()
{
if (_instance == nullptr)
{
_instance = std::make_unique<Async>();
}
return _instance.get();
}
void Async::query(std::string const& query)
{
// clang-format off
this->schedule([query]()
{
TracySetThreadName("Async Worker Thread");
if (_sql == nullptr)
{
_sql = std::make_unique<SqlConnection>();
}
_sql->Query(query.c_str());
});
// clang-format on
}
// NOTE: Be _very_ careful when defining your sql argument in the passed-in function.
// : If you define your arg as _sql, but then call sql, it will use the global
// : SQLConnection, which is on the main thread.
// : Remember that SQLConnection is NOT THREAD-SAFE!
void Async::query(std::function<void(SqlConnection*)> const& func)
{
// clang-format off
this->schedule([func]()
{
TracySetThreadName("Async Worker Thread");
if (_sql == nullptr)
{
_sql = std::make_unique<SqlConnection>();
}
func(_sql.get());
});
// clang-format on
}
void Async::submit(std::function<void()> const& func)
{
// clang-format off
this->schedule([func]()
{
TracySetThreadName("Async Worker Thread");
func();
});
// clang-format on
} And then use |
But you'd want to make ts::task_system inheritance private, so you don't leak it to consumers |
Think ill use the modified async version. The only downside is that single thread for all systems, but in theory that is okay with our current usage. If it does become a problem, couldn't we just create non singleton versions of |
hmm I do see that currently chat audits go through this thread. It may be a better idea to have separate Async instances per system |
Chat audits are on the map server and this targets world server no? |
oooh good point. We can do this then. |
I don't think it's worth worrying too much about performance until it becomes an actual problem, everything is lock less and on a different thread, this will hold the workload easily |
Compiler is not liking the private Async ctor when calling: std::make_unique(). Make this is why it wasn't a unique ptr before? |
Use the regular boilerplate for a singleton for Async, now that we're going to use it as a singleton: #include <iostream>
#include <string>
#include <memory>
// async.h
class Async
{
public:
static Async* getInstance();
// other methods here
private:
Async() = default;
~Async() = default;
Async(const Async&) = delete;
Async& operator=(const Async&) = delete;
};
// async.cpp
Async* Async::getInstance()
{
static Async instance;
return &instance;
}
int main()
{
return 0;
}
|
oh... I don't know why I was using std pointer ... mb |
I'm not sure we can just rely on ->query() to run queries. How would you then do something like |
I've done the refactoring to make Async a singleton, and it'll be scalable to multiple threads if the workload ever demands it: |
At this point, just abandon multi-threading and revert everything to lockstep ticks on the world server - being fed messages from the ZMQ queue, and pushing messages back onto the ZMQ queue. Once besieged is "done" we can come back and look at whether or not it's worth breaking things out onto individual threads. |
Use this PR to fix this: If that requires mutexes, so be it. I'll vet it as it happens |
I've done a different version of this refactoring here: #4632 This should be sufficient for allowing you to fix those other conquest bugs (if this was needed for that), and for your work on besieged. If things need to be re-threaded later, we'll deal with it later - but I think it's unlikely. |
works for me. will integrate it to ASB once merged |
I affirm:
What does this pull request do?
Steps to test these changes
Run conquest updates. Make sure conquest map is consistent. Check all zones and make sure there are no crashes.