| | #include "catch2/catch.hpp"
|
| | #include <fastsignals/signal.h>
|
| | #include <array>
|
| | #include <mutex>
|
| | #include <random>
|
| | #include <vector>
|
| | #include <thread>
|
| |
|
| | using namespace fastsignals;
|
| |
|
| | namespace
|
| | {
|
| | using string_signal = signal<void(std::string)>;
|
| | using string_slot = string_signal::slot_type;
|
| | using void_signal = signal<void()>;
|
| | using void_slot = void_signal::slot_type;
|
| |
|
| | class named_entity
|
| | {
|
| | public:
|
| | std::string name() const
|
| | {
|
| | std::lock_guard lock(m_nameMutex);
|
| | return m_name;
|
| | }
|
| |
|
| | void fire_changed(std::string value)
|
| | {
|
| | bool fire = false;
|
| | {
|
| | std::lock_guard lock(m_nameMutex);
|
| | if (m_name != value)
|
| | {
|
| | m_name = std::move(value);
|
| | fire = true;
|
| | }
|
| | }
|
| | if (fire)
|
| | {
|
| | m_nameChanged(value);
|
| | }
|
| | }
|
| |
|
| | connection on_name_changed(string_slot slot)
|
| | {
|
| | return m_nameChanged.connect(std::move(slot));
|
| | }
|
| |
|
| | private:
|
| | mutable std::mutex m_nameMutex;
|
| | std::string m_name;
|
| | signal<void(std::string)> m_nameChanged;
|
| | };
|
| |
|
| | unsigned get_next_seed()
|
| | {
|
| | static std::minstd_rand seedEngine(777);
|
| | return seedEngine();
|
| | }
|
| |
|
| | size_t get_random_index(size_t size)
|
| | {
|
| | thread_local std::minstd_rand disconnectRandomEngine{ get_next_seed() };
|
| | std::uniform_int_distribution<size_t> disconnectIndexDistribution{ 0, size - 1 };
|
| |
|
| | return disconnectIndexDistribution(disconnectRandomEngine);
|
| | }
|
| | }
|
| |
|
| | TEST_CASE("Can work in a few threads", "[signal]")
|
| | {
|
| | constexpr unsigned fireThreadCount = 8;
|
| | constexpr unsigned signalsCount = 7;
|
| | constexpr unsigned fireCountPerThread = 100'000;
|
| | constexpr unsigned connectCallsCount = 80'000;
|
| | constexpr unsigned totalRunCount = 10;
|
| |
|
| | for (unsigned i = 0; i < totalRunCount; ++i)
|
| | {
|
| | std::array<void_signal, signalsCount> signals;
|
| |
|
| | std::mutex connectionsMutex;
|
| | std::vector<connection> connections;
|
| | connections.reserve(connectCallsCount);
|
| |
|
| | std::vector<std::thread> threads;
|
| |
|
| | auto slot = [&] {
|
| | std::lock_guard lock(connectionsMutex);
|
| | if (!connections.empty())
|
| | {
|
| | const size_t index = get_random_index(connections.size());
|
| | connections.at(index).disconnect();
|
| | }
|
| | };
|
| |
|
| | threads.emplace_back([&] {
|
| | for (unsigned cci = 0; cci < connectCallsCount; ++cci)
|
| | {
|
| | const size_t index = get_random_index(signalsCount);
|
| | connection conn = signals.at(index).connect(slot);
|
| | {
|
| | std::lock_guard lock(connectionsMutex);
|
| | connections.emplace_back(conn);
|
| | }
|
| | }
|
| | });
|
| |
|
| | for (unsigned fti = 0; fti < fireThreadCount; ++fti)
|
| | {
|
| | threads.emplace_back([&] {
|
| | for (unsigned fi = 0; fi < fireCountPerThread; ++fi)
|
| | {
|
| | const size_t index = get_random_index(signalsCount);
|
| | signals.at(index)();
|
| | }
|
| | });
|
| | }
|
| | for (auto& thread : threads)
|
| | {
|
| | thread.join();
|
| | }
|
| | }
|
| | }
|
| |
|