Distributed tests
Summary: 1. added distributed tests, currently only one can be run (TODO) 2. usability changes: add Close() to Subscription 3. EventStream::Subscription -> Subscription 4. Add Subscription to ChainOnce 5. Added README.md conventions5 Reviewers: sasa.stanko Reviewed By: sasa.stanko Subscribers: pullbot, buda Differential Revision: https://phabricator.memgraph.io/D701
This commit is contained in:
parent
2954276ca8
commit
7f1c7a46cc
@ -3,22 +3,32 @@
|
|||||||
|
|
||||||
This subdirectory structure implements distributed infrastructure of Memgraph.
|
This subdirectory structure implements distributed infrastructure of Memgraph.
|
||||||
|
|
||||||
## terminology
|
## Terminology
|
||||||
|
|
||||||
* Memgraph Node Id (mnid): a machine (processs) that runs a (distributed) Memgraph program.
|
* Memgraph Node Id (mnid): a machine (processs) that runs a (distributed) Memgraph program.
|
||||||
* Node: a computer that performs (distributed) work.
|
* Node: a computer that performs (distributed) work.
|
||||||
* Vertex: an abstract graph concept.
|
* Vertex: an abstract graph concept.
|
||||||
* Reactor: a unit of concurrent execution, lives on its own thread.
|
* Reactor: a unit of concurrent execution, lives on its own thread.
|
||||||
* Channel: a (one-way) communication abstraction between Reactors. The reactors can be on the same machine or on different processes.
|
* Channel: a (one-way) communication abstraction between Reactors. The reactors can be on the same machine or on different processes.
|
||||||
|
* Message: gets sent through channels. Must be serializable if sent via network layer (library: cereal).
|
||||||
|
* Event: arrival of a (subclass of) Message. You can register callbacks. Register exact callbacks (not for derivated/subclasses).
|
||||||
* EventStream: read-end of a channel, is owned by exactly one Reactor/thread.
|
* EventStream: read-end of a channel, is owned by exactly one Reactor/thread.
|
||||||
* ChannelWriter: write-end of a channel, can be owned (wrote into) by multiple threads.
|
* ChannelWriter: write-end of a channel, can be owned (wrote into) by multiple threads.
|
||||||
|
|
||||||
## conventions
|
## Ownership:
|
||||||
|
|
||||||
1. Locked: A method having a Locked... prefix indicates that you
|
* System, Distributed are singletons. They should be always alive.
|
||||||
|
* ChannelWriter (write-end) should be lightweight and can be copied arbitrarily.
|
||||||
|
* EventStream (read-end) should never be written by anyone except the owner (the reactor that created it).
|
||||||
|
|
||||||
|
## Code Conventions
|
||||||
|
|
||||||
|
* Locked: A method having a Locked... prefix indicates that you
|
||||||
have to lock the appropriate mutex before calling this function.
|
have to lock the appropriate mutex before calling this function.
|
||||||
|
* ALWAYS close channels. You will memory leak if you don't.
|
||||||
|
Reactor::CloseChannel or Subscription::Close will do the trick.
|
||||||
|
|
||||||
## dependencies
|
## Dependencies
|
||||||
|
|
||||||
* cereal
|
* cereal
|
||||||
* <other memgraph dependencies>
|
* <other memgraph dependencies>
|
@ -95,6 +95,8 @@ class Network {
|
|||||||
/** Start a threadpool that dispatches the messages from the (outgoing) queue to the sockets */
|
/** Start a threadpool that dispatches the messages from the (outgoing) queue to the sockets */
|
||||||
void StartClient(int worker_count) {
|
void StartClient(int worker_count) {
|
||||||
LOG(INFO) << "Starting " << worker_count << " client workers";
|
LOG(INFO) << "Starting " << worker_count << " client workers";
|
||||||
|
client_run_ = true;
|
||||||
|
|
||||||
for (int i = 0; i < worker_count; ++i) {
|
for (int i = 0; i < worker_count; ++i) {
|
||||||
pool_.push_back(std::thread([worker_count, this]() {
|
pool_.push_back(std::thread([worker_count, this]() {
|
||||||
while (this->client_run_) {
|
while (this->client_run_) {
|
||||||
@ -129,6 +131,7 @@ class Network {
|
|||||||
for (size_t i = 0; i < pool_.size(); ++i) {
|
for (size_t i = 0; i < pool_.size(); ++i) {
|
||||||
pool_[i].join();
|
pool_[i].join();
|
||||||
}
|
}
|
||||||
|
pool_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
class RemoteChannelWriter : public ChannelWriter {
|
class RemoteChannelWriter : public ChannelWriter {
|
||||||
@ -209,6 +212,7 @@ class Network {
|
|||||||
if (server_ != nullptr) {
|
if (server_ != nullptr) {
|
||||||
server_->Shutdown();
|
server_->Shutdown();
|
||||||
thread_.join();
|
thread_.join();
|
||||||
|
server_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +221,7 @@ class Network {
|
|||||||
SpinLock mutex_;
|
SpinLock mutex_;
|
||||||
std::vector<std::thread> pool_;
|
std::vector<std::thread> pool_;
|
||||||
std::queue<NetworkMessage> queue_;
|
std::queue<NetworkMessage> queue_;
|
||||||
std::atomic<bool> client_run_{true};
|
std::atomic<bool> client_run_;
|
||||||
|
|
||||||
// server variables
|
// server variables
|
||||||
std::thread thread_;
|
std::thread thread_;
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
#include "reactors_local.hpp"
|
#include "reactors_local.hpp"
|
||||||
|
|
||||||
void EventStream::Subscription::unsubscribe() const {
|
void EventStream::Subscription::Unsubscribe() const {
|
||||||
event_queue_.RemoveCb(*this);
|
event_queue_.RemoveCb(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventStream::Subscription::Close() const {
|
||||||
|
event_queue_.Close();
|
||||||
|
}
|
||||||
|
|
||||||
thread_local Reactor* current_reactor_ = nullptr;
|
thread_local Reactor* current_reactor_ = nullptr;
|
||||||
|
|
||||||
std::string Channel::LocalChannelWriter::ReactorName() {
|
std::string Channel::LocalChannelWriter::ReactorName() {
|
||||||
@ -14,7 +18,9 @@ std::string Channel::LocalChannelWriter::Name() {
|
|||||||
return channel_name_;
|
return channel_name_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Channel::LocalEventStream::Close() {
|
void Channel::Close() {
|
||||||
|
// TODO(zuza): there will be major problems if a reactor tries to close a stream that isn't theirs
|
||||||
|
// luckily this should never happen if the framework is used as expected.
|
||||||
current_reactor_->CloseChannel(channel_name_);
|
current_reactor_->CloseChannel(channel_name_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,11 +65,7 @@ void Reactor::CloseChannel(const std::string &s) {
|
|||||||
auto it = channels_.find(s);
|
auto it = channels_.find(s);
|
||||||
assert(it != channels_.end());
|
assert(it != channels_.end());
|
||||||
channels_.erase(it);
|
channels_.erase(it);
|
||||||
}
|
cvar_->notify_all();
|
||||||
|
|
||||||
void Reactor::CloseAllChannels() {
|
|
||||||
std::unique_lock<std::mutex> lock(*mutex_);
|
|
||||||
channels_.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reactor::RunEventLoop() {
|
void Reactor::RunEventLoop() {
|
||||||
@ -76,6 +78,9 @@ void Reactor::RunEventLoop() {
|
|||||||
std::unique_lock<std::mutex> lock(*mutex_);
|
std::unique_lock<std::mutex> lock(*mutex_);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
// Not fair because was taken earlier, talk to lion.
|
||||||
|
msg_and_cb = LockedGetPendingMessages();
|
||||||
|
if (msg_and_cb.first != nullptr) break;
|
||||||
|
|
||||||
// Exit the loop if there are no more Channels.
|
// Exit the loop if there are no more Channels.
|
||||||
if (channels_.empty()) {
|
if (channels_.empty()) {
|
||||||
@ -83,10 +88,6 @@ void Reactor::RunEventLoop() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not fair because was taken earlier, talk to lion.
|
|
||||||
msg_and_cb = LockedGetPendingMessages();
|
|
||||||
if (msg_and_cb.first != nullptr) break;
|
|
||||||
|
|
||||||
cvar_->wait(lock);
|
cvar_->wait(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,13 +113,13 @@ auto Reactor::LockedGetPendingMessages() -> MsgAndCbInfo {
|
|||||||
if (msg_ptr == nullptr) continue;
|
if (msg_ptr == nullptr) continue;
|
||||||
std::type_index tidx = msg_ptr->GetTypeIndex();
|
std::type_index tidx = msg_ptr->GetTypeIndex();
|
||||||
|
|
||||||
std::vector<std::pair<EventStream::Callback, EventStream::Subscription> > cb_info;
|
std::vector<std::pair<EventStream::Callback, Subscription> > cb_info;
|
||||||
auto msg_type_cb_iter = event_queue.callbacks_.find(tidx);
|
auto msg_type_cb_iter = event_queue.callbacks_.find(tidx);
|
||||||
if (msg_type_cb_iter != event_queue.callbacks_.end()) { // There is a callback for this type.
|
if (msg_type_cb_iter != event_queue.callbacks_.end()) { // There is a callback for this type.
|
||||||
for (auto &tidx_cb_key_value : msg_type_cb_iter->second) {
|
for (auto &tidx_cb_key_value : msg_type_cb_iter->second) {
|
||||||
uint64_t uid = tidx_cb_key_value.first;
|
uint64_t uid = tidx_cb_key_value.first;
|
||||||
EventStream::Callback cb = tidx_cb_key_value.second;
|
EventStream::Callback cb = tidx_cb_key_value.second;
|
||||||
cb_info.emplace_back(cb, EventStream::Subscription(event_queue, tidx, uid));
|
cb_info.emplace_back(cb, Subscription(event_queue, tidx, uid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +69,8 @@ class ChannelWriter {
|
|||||||
*/
|
*/
|
||||||
class EventStream {
|
class EventStream {
|
||||||
public:
|
public:
|
||||||
class Subscription;
|
|
||||||
class OnEventOnceChainer;
|
class OnEventOnceChainer;
|
||||||
|
class Subscription;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a callback that will be called whenever an event arrives.
|
* Register a callback that will be called whenever an event arrives.
|
||||||
@ -99,6 +99,7 @@ class EventStream {
|
|||||||
* Get the name of the channel.
|
* Get the name of the channel.
|
||||||
*/
|
*/
|
||||||
virtual const std::string &ChannelName() = 0;
|
virtual const std::string &ChannelName() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription Service.
|
* Subscription Service.
|
||||||
*
|
*
|
||||||
@ -109,7 +110,12 @@ class EventStream {
|
|||||||
/**
|
/**
|
||||||
* Unsubscribe. Call only once.
|
* Unsubscribe. Call only once.
|
||||||
*/
|
*/
|
||||||
void unsubscribe() const;
|
void Unsubscribe() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the stream. Convenience method.
|
||||||
|
*/
|
||||||
|
void Close() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Reactor;
|
friend class Reactor;
|
||||||
@ -151,12 +157,12 @@ class EventStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename MsgType>
|
template<typename MsgType>
|
||||||
OnEventOnceChainer &ChainOnce(std::function<void(const MsgType&)> &&cb) {
|
OnEventOnceChainer &ChainOnce(std::function<void(const MsgType&, const Subscription&)> &&cb) {
|
||||||
std::function<void(const Message&, const Subscription&)> wrap =
|
std::function<void(const Message&, const Subscription&)> wrap =
|
||||||
[cb = std::move(cb)](const Message &general_msg, const Subscription &subscription) {
|
[cb = std::move(cb)](const Message &general_msg, const Subscription &subscription) {
|
||||||
const MsgType &correct_msg = dynamic_cast<const MsgType&>(general_msg);
|
const MsgType &correct_msg = dynamic_cast<const MsgType&>(general_msg);
|
||||||
subscription.unsubscribe();
|
subscription.Unsubscribe();
|
||||||
cb(correct_msg); // Warning: this can close the Channel, be careful what you put after it!
|
cb(correct_msg, subscription); // Warning: this can close the Channel, be careful what you put after it!
|
||||||
};
|
};
|
||||||
cbs_.emplace_back(typeid(MsgType), std::move(wrap));
|
cbs_.emplace_back(typeid(MsgType), std::move(wrap));
|
||||||
return *this;
|
return *this;
|
||||||
@ -196,6 +202,8 @@ private:
|
|||||||
virtual void OnEventHelper(std::type_index tidx, Callback callback) = 0;
|
virtual void OnEventHelper(std::type_index tidx, Callback callback) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using Subscription = EventStream::Subscription; // To write less.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of a channel.
|
* Implementation of a channel.
|
||||||
*
|
*
|
||||||
@ -218,7 +226,7 @@ class Channel {
|
|||||||
reactor_name_(params.reactor_name),
|
reactor_name_(params.reactor_name),
|
||||||
mutex_(params.mutex),
|
mutex_(params.mutex),
|
||||||
cvar_(params.cvar),
|
cvar_(params.cvar),
|
||||||
stream_(mutex_, channel_name_, this) {}
|
stream_(mutex_, this) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LocalChannelWriter represents the channels to reactors living in the same reactor system (write-end of the channels).
|
* LocalChannelWriter represents the channels to reactors living in the same reactor system (write-end of the channels).
|
||||||
@ -268,20 +276,20 @@ class Channel {
|
|||||||
public:
|
public:
|
||||||
friend class Channel;
|
friend class Channel;
|
||||||
|
|
||||||
LocalEventStream(std::shared_ptr<std::mutex> mutex, std::string channel_name,
|
LocalEventStream(std::shared_ptr<std::mutex> mutex, Channel *queue) : mutex_(mutex), queue_(queue) {}
|
||||||
Channel *queue) : mutex_(mutex), channel_name_(channel_name), queue_(queue) {}
|
|
||||||
std::unique_ptr<Message> AwaitEvent() {
|
|
||||||
std::unique_lock<std::mutex> lock(*mutex_);
|
|
||||||
return queue_->LockedAwaitPop(lock);
|
|
||||||
}
|
|
||||||
void OnEventHelper(std::type_index tidx, Callback callback) {
|
void OnEventHelper(std::type_index tidx, Callback callback) {
|
||||||
std::unique_lock<std::mutex> lock(*mutex_);
|
std::unique_lock<std::mutex> lock(*mutex_);
|
||||||
queue_->LockedOnEventHelper(tidx, callback);
|
queue_->LockedOnEventHelper(tidx, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &ChannelName() {
|
const std::string &ChannelName() {
|
||||||
return queue_->channel_name_;
|
return queue_->channel_name_;
|
||||||
}
|
}
|
||||||
void Close();
|
|
||||||
|
void Close() {
|
||||||
|
queue_->Close();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<std::mutex> mutex_;
|
std::shared_ptr<std::mutex> mutex_;
|
||||||
@ -289,6 +297,11 @@ class Channel {
|
|||||||
Channel *queue_;
|
Channel *queue_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the channel. Must be called from the reactor that owns the channel.
|
||||||
|
*/
|
||||||
|
void Close();
|
||||||
|
|
||||||
Channel(const Channel &other) = delete;
|
Channel(const Channel &other) = delete;
|
||||||
Channel(Channel &&other) = default;
|
Channel(Channel &&other) = default;
|
||||||
Channel &operator=(const Channel &other) = delete;
|
Channel &operator=(const Channel &other) = delete;
|
||||||
@ -318,17 +331,6 @@ private:
|
|||||||
return std::make_shared<LocalChannelWriter>(mutex_, reactor_name_, channel_name_, self_ptr_);
|
return std::make_shared<LocalChannelWriter>(mutex_, reactor_name_, channel_name_, self_ptr_);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Message> LockedAwaitPop(std::unique_lock<std::mutex> &lock) {
|
|
||||||
while (true) {
|
|
||||||
std::unique_ptr<Message> m = LockedRawPop();
|
|
||||||
if (!m) {
|
|
||||||
cvar_->wait(lock);
|
|
||||||
} else {
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Message> LockedPop() {
|
std::unique_ptr<Message> LockedPop() {
|
||||||
return LockedRawPop();
|
return LockedRawPop();
|
||||||
}
|
}
|
||||||
@ -395,13 +397,6 @@ class Reactor {
|
|||||||
*/
|
*/
|
||||||
void CloseChannel(const std::string &s);
|
void CloseChannel(const std::string &s);
|
||||||
|
|
||||||
/**
|
|
||||||
* close all channels (typically during shutdown).
|
|
||||||
*
|
|
||||||
* Should only be called from the Reactor thread.
|
|
||||||
*/
|
|
||||||
void CloseAllChannels();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Reactor name
|
* Get Reactor name
|
||||||
*/
|
*/
|
||||||
|
@ -126,11 +126,11 @@ class Master : public Reactor {
|
|||||||
|
|
||||||
// wait until every worker sends a ReturnAddressMsg back, then close
|
// wait until every worker sends a ReturnAddressMsg back, then close
|
||||||
stream->OnEvent<TextMessage>([this](const TextMessage &msg,
|
stream->OnEvent<TextMessage>([this](const TextMessage &msg,
|
||||||
const EventStream::Subscription &subscription) {
|
const Subscription &subscription) {
|
||||||
std::cout << "Message from " << msg.Address() << ":" << msg.Port() << " .. " << msg.text << "\n";
|
std::cout << "Message from " << msg.Address() << ":" << msg.Port() << " .. " << msg.text << "\n";
|
||||||
++workers_seen;
|
++workers_seen;
|
||||||
if (workers_seen == worker_mnids_.size()) {
|
if (workers_seen == worker_mnids_.size()) {
|
||||||
subscription.unsubscribe();
|
subscription.Unsubscribe();
|
||||||
// Sleep for a while so we can read output in the terminal.
|
// Sleep for a while so we can read output in the terminal.
|
||||||
// (start_distributed.py runs each process in a new tab which is
|
// (start_distributed.py runs each process in a new tab which is
|
||||||
// closed immediately after process has finished)
|
// closed immediately after process has finished)
|
||||||
@ -143,7 +143,7 @@ class Master : public Reactor {
|
|||||||
for (auto wmnid : worker_mnids_) {
|
for (auto wmnid : worker_mnids_) {
|
||||||
auto stream = memgraph.FindChannel(wmnid, "worker", "main");
|
auto stream = memgraph.FindChannel(wmnid, "worker", "main");
|
||||||
stream->OnEventOnce()
|
stream->OnEventOnce()
|
||||||
.ChainOnce<ChannelResolvedMessage>([this, stream](const ChannelResolvedMessage &msg){
|
.ChainOnce<ChannelResolvedMessage>([this, stream](const ChannelResolvedMessage &msg, const Subscription&){
|
||||||
msg.channelWriter()->Send<TextMessage>("master", "main", "hi from master");
|
msg.channelWriter()->Send<TextMessage>("master", "main", "hi from master");
|
||||||
stream->Close();
|
stream->Close();
|
||||||
});
|
});
|
||||||
@ -171,7 +171,7 @@ class Worker : public Reactor {
|
|||||||
auto stream = main_.first;
|
auto stream = main_.first;
|
||||||
// wait until master sends us a TextMessage, then reply back and close
|
// wait until master sends us a TextMessage, then reply back and close
|
||||||
stream->OnEventOnce()
|
stream->OnEventOnce()
|
||||||
.ChainOnce<TextMessage>([this](const TextMessage &msg) {
|
.ChainOnce<TextMessage>([this](const TextMessage &msg, const Subscription&) {
|
||||||
std::cout << "Message from " << msg.Address() << ":" << msg.Port() << " .. " << msg.text << "\n";
|
std::cout << "Message from " << msg.Address() << ":" << msg.Port() << " .. " << msg.text << "\n";
|
||||||
|
|
||||||
msg.GetReturnChannelWriter()
|
msg.GetReturnChannelWriter()
|
||||||
|
@ -151,12 +151,12 @@
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// stream->OnEvent<Message>([this](const Message &msg, const EventStream::Subscription& subscription) {
|
// stream->OnEvent<Message>([this](const Message &msg, const Subscription& subscription) {
|
||||||
// std::cout << "Processing Query via Callback" << std::endl;
|
// std::cout << "Processing Query via Callback" << std::endl;
|
||||||
// const Query &query =
|
// const Query &query =
|
||||||
// dynamic_cast<const Query &>(msg); // exception bad_cast
|
// dynamic_cast<const Query &>(msg); // exception bad_cast
|
||||||
// ProcessQuery(&query);
|
// ProcessQuery(&query);
|
||||||
// subscription.unsubscribe();
|
// subscription.Unsubscribe();
|
||||||
// });
|
// });
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -46,13 +46,13 @@ class ChatServer : public Reactor {
|
|||||||
|
|
||||||
auto chat = Open("chat").first;
|
auto chat = Open("chat").first;
|
||||||
|
|
||||||
chat->OnEvent<ChatACK>([](const ChatACK& ack, const EventStream::Subscription&) {
|
chat->OnEvent<ChatACK>([](const ChatACK& ack, const Subscription&) {
|
||||||
std::cout << "Received ACK from " << ack.Address() << ":"
|
std::cout << "Received ACK from " << ack.Address() << ":"
|
||||||
<< ack.Port() << " -> '" << ack.Message() << "'"
|
<< ack.Port() << " -> '" << ack.Message() << "'"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
});
|
});
|
||||||
|
|
||||||
chat->OnEvent<ChatMessage>([this](const ChatMessage& msg, const EventStream::Subscription&) {
|
chat->OnEvent<ChatMessage>([this](const ChatMessage& msg, const Subscription&) {
|
||||||
std::cout << "Received message from " << msg.Address() << ":"
|
std::cout << "Received message from " << msg.Address() << ":"
|
||||||
<< msg.Port() << " -> '" << msg.Message() << "'"
|
<< msg.Port() << " -> '" << msg.Message() << "'"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
78
experimental/distributed/tests/reactors_distributed_unit.cpp
Normal file
78
experimental/distributed/tests/reactors_distributed_unit.cpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* This test file test the Distributed Reactors API on ONLY one process (no real networking).
|
||||||
|
* In other words, we send a message from one process to itself.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "reactors_distributed.hpp"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
TEST(SimpleTests, StartAndStopServices) {
|
||||||
|
System &system = System::GetInstance();
|
||||||
|
Distributed &distributed = Distributed::GetInstance();
|
||||||
|
distributed.StartServices();
|
||||||
|
|
||||||
|
// do nothing
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
|
|
||||||
|
system.AwaitShutdown();
|
||||||
|
distributed.StopServices();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SimpleTests, SendEmptyMessage) {
|
||||||
|
struct Master : public Reactor {
|
||||||
|
Master(std::string name) : Reactor(name) {}
|
||||||
|
|
||||||
|
virtual void Run() {
|
||||||
|
Distributed::GetInstance().FindChannel("127.0.0.1", 10000, "worker", "main")
|
||||||
|
->OnEventOnce()
|
||||||
|
.ChainOnce<ChannelResolvedMessage>([this](const ChannelResolvedMessage& msg,
|
||||||
|
const Subscription& subscription) {
|
||||||
|
msg.channelWriter()->Send<Message>();
|
||||||
|
subscription.Close();
|
||||||
|
});
|
||||||
|
|
||||||
|
CloseChannel("main");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Worker : public Reactor {
|
||||||
|
Worker(std::string name) : Reactor(name) {}
|
||||||
|
|
||||||
|
virtual void Run() {
|
||||||
|
main_.first->OnEventOnce()
|
||||||
|
.ChainOnce<Message>([this](const Message&, const Subscription& subscription) {
|
||||||
|
// if this message isn't delivered, the main channel will never be closed
|
||||||
|
subscription.Close(); // close "main"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// emulate flags like it's a multiprocess system, these may be alredy set by default
|
||||||
|
FLAGS_address = "127.0.0.1";
|
||||||
|
FLAGS_port = 10000;
|
||||||
|
|
||||||
|
System &system = System::GetInstance();
|
||||||
|
Distributed &distributed = Distributed::GetInstance();
|
||||||
|
distributed.StartServices();
|
||||||
|
|
||||||
|
system.Spawn<Master>("master");
|
||||||
|
system.Spawn<Worker>("worker");
|
||||||
|
|
||||||
|
system.AwaitShutdown(); // this must be called before StopServices
|
||||||
|
distributed.StopServices();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
@ -92,7 +92,7 @@ TEST(SimpleSendTest, OneCallback) {
|
|||||||
virtual void Run() {
|
virtual void Run() {
|
||||||
EventStream* stream = main_.first;
|
EventStream* stream = main_.first;
|
||||||
|
|
||||||
stream->OnEvent<MessageInt>([this](const MessageInt &msg, const EventStream::Subscription&) {
|
stream->OnEvent<MessageInt>([this](const MessageInt &msg, const Subscription&) {
|
||||||
ASSERT_EQ(msg.x, 888);
|
ASSERT_EQ(msg.x, 888);
|
||||||
CloseChannel("main");
|
CloseChannel("main");
|
||||||
});
|
});
|
||||||
@ -132,7 +132,7 @@ TEST(SimpleSendTest, IgnoreAfterClose) {
|
|||||||
virtual void Run() {
|
virtual void Run() {
|
||||||
EventStream* stream = main_.first;
|
EventStream* stream = main_.first;
|
||||||
|
|
||||||
stream->OnEvent<MessageInt>([this](const MessageInt& msg, const EventStream::Subscription&) {
|
stream->OnEvent<MessageInt>([this](const MessageInt& msg, const Subscription&) {
|
||||||
CloseChannel("main");
|
CloseChannel("main");
|
||||||
ASSERT_EQ(msg.x, 101);
|
ASSERT_EQ(msg.x, 101);
|
||||||
});
|
});
|
||||||
@ -156,12 +156,12 @@ TEST(SimpleSendTest, DuringFirstEvent) {
|
|||||||
virtual void Run() {
|
virtual void Run() {
|
||||||
EventStream* stream = main_.first;
|
EventStream* stream = main_.first;
|
||||||
|
|
||||||
stream->OnEvent<MessageInt>([this](const Message &msg, const EventStream::Subscription &subscription) {
|
stream->OnEvent<MessageInt>([this](const Message &msg, const Subscription &subscription) {
|
||||||
const MessageInt &msgint = dynamic_cast<const MessageInt&>(msg);
|
const MessageInt &msgint = dynamic_cast<const MessageInt&>(msg);
|
||||||
if (msgint.x == 101)
|
if (msgint.x == 101)
|
||||||
FindChannel("main")->Send<MessageInt>(102);
|
FindChannel("main")->Send<MessageInt>(102);
|
||||||
if (msgint.x == 102) {
|
if (msgint.x == 102) {
|
||||||
subscription.unsubscribe();
|
subscription.Unsubscribe();
|
||||||
CloseChannel("main");
|
CloseChannel("main");
|
||||||
p_.set_value(777);
|
p_.set_value(777);
|
||||||
}
|
}
|
||||||
@ -220,19 +220,19 @@ TEST(MultipleSendTest, UnsubscribeService) {
|
|||||||
virtual void Run() {
|
virtual void Run() {
|
||||||
EventStream* stream = main_.first;
|
EventStream* stream = main_.first;
|
||||||
|
|
||||||
stream->OnEvent<MessageInt>([this](const MessageInt &msgint, const EventStream::Subscription &subscription) {
|
stream->OnEvent<MessageInt>([this](const MessageInt &msgint, const Subscription &subscription) {
|
||||||
ASSERT_TRUE(msgint.x == 55 || msgint.x == 66);
|
ASSERT_TRUE(msgint.x == 55 || msgint.x == 66);
|
||||||
++num_msgs_received;
|
++num_msgs_received;
|
||||||
if (msgint.x == 66) {
|
if (msgint.x == 66) {
|
||||||
subscription.unsubscribe(); // receive only two of them
|
subscription.Unsubscribe(); // receive only two of them
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
stream->OnEvent<MessageChar>([this](const MessageChar &msgchar, const EventStream::Subscription &subscription) {
|
stream->OnEvent<MessageChar>([this](const MessageChar &msgchar, const Subscription &subscription) {
|
||||||
char c = msgchar.x;
|
char c = msgchar.x;
|
||||||
++num_msgs_received;
|
++num_msgs_received;
|
||||||
ASSERT_TRUE(c == 'a' || c == 'b' || c == 'c');
|
ASSERT_TRUE(c == 'a' || c == 'b' || c == 'c');
|
||||||
if (num_msgs_received == 5) {
|
if (num_msgs_received == 5) {
|
||||||
subscription.unsubscribe();
|
subscription.Unsubscribe();
|
||||||
CloseChannel("main");
|
CloseChannel("main");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -281,19 +281,19 @@ TEST(MultipleSendTest, OnEvent) {
|
|||||||
EventStream* stream = main_.first;
|
EventStream* stream = main_.first;
|
||||||
correct_vals = 0;
|
correct_vals = 0;
|
||||||
|
|
||||||
stream->OnEvent<MessageInt>([this](const MessageInt &msgint, const EventStream::Subscription&) {
|
stream->OnEvent<MessageInt>([this](const MessageInt &msgint, const Subscription&) {
|
||||||
ASSERT_TRUE(msgint.x == 101 || msgint.x == 103);
|
ASSERT_TRUE(msgint.x == 101 || msgint.x == 103);
|
||||||
++correct_vals;
|
++correct_vals;
|
||||||
main_.second->Send<EndMessage>();
|
main_.second->Send<EndMessage>();
|
||||||
});
|
});
|
||||||
|
|
||||||
stream->OnEvent<MessageChar>([this](const MessageChar &msgchar, const EventStream::Subscription&) {
|
stream->OnEvent<MessageChar>([this](const MessageChar &msgchar, const Subscription&) {
|
||||||
ASSERT_TRUE(msgchar.x == 'a' || msgchar.x == 'b');
|
ASSERT_TRUE(msgchar.x == 'a' || msgchar.x == 'b');
|
||||||
++correct_vals;
|
++correct_vals;
|
||||||
main_.second->Send<EndMessage>();
|
main_.second->Send<EndMessage>();
|
||||||
});
|
});
|
||||||
|
|
||||||
stream->OnEvent<EndMessage>([this](const EndMessage&, const EventStream::Subscription&) {
|
stream->OnEvent<EndMessage>([this](const EndMessage&, const Subscription&) {
|
||||||
ASSERT_LE(correct_vals, 4);
|
ASSERT_LE(correct_vals, 4);
|
||||||
if (correct_vals == 4) {
|
if (correct_vals == 4) {
|
||||||
CloseChannel("main");
|
CloseChannel("main");
|
||||||
@ -334,13 +334,13 @@ TEST(MultipleSendTest, Chaining) {
|
|||||||
EventStream* stream = main_.first;
|
EventStream* stream = main_.first;
|
||||||
|
|
||||||
stream->OnEventOnce()
|
stream->OnEventOnce()
|
||||||
.ChainOnce<MessageInt>([this](const MessageInt &msg) {
|
.ChainOnce<MessageInt>([this](const MessageInt &msg, const Subscription&) {
|
||||||
ASSERT_EQ(msg.x, 55);
|
ASSERT_EQ(msg.x, 55);
|
||||||
})
|
})
|
||||||
.ChainOnce<MessageInt>([](const MessageInt &msg) {
|
.ChainOnce<MessageInt>([](const MessageInt &msg, const Subscription&) {
|
||||||
ASSERT_EQ(msg.x, 66);
|
ASSERT_EQ(msg.x, 66);
|
||||||
})
|
})
|
||||||
.ChainOnce<MessageInt>([this](const MessageInt &msg) {
|
.ChainOnce<MessageInt>([this](const MessageInt &msg, const Subscription&) {
|
||||||
ASSERT_EQ(msg.x, 77);
|
ASSERT_EQ(msg.x, 77);
|
||||||
CloseChannel("main");
|
CloseChannel("main");
|
||||||
});
|
});
|
||||||
@ -386,13 +386,13 @@ TEST(MultipleSendTest, ChainingInRightOrder) {
|
|||||||
EventStream* stream = main_.first;
|
EventStream* stream = main_.first;
|
||||||
|
|
||||||
stream->OnEventOnce()
|
stream->OnEventOnce()
|
||||||
.ChainOnce<MessageInt>([this](const MessageInt &msg) {
|
.ChainOnce<MessageInt>([this](const MessageInt &msg, const Subscription&) {
|
||||||
ASSERT_EQ(msg.x, 55);
|
ASSERT_EQ(msg.x, 55);
|
||||||
})
|
})
|
||||||
.ChainOnce<MessageChar>([](const MessageChar &msg) {
|
.ChainOnce<MessageChar>([](const MessageChar &msg, const Subscription&) {
|
||||||
ASSERT_EQ(msg.x, 'b');
|
ASSERT_EQ(msg.x, 'b');
|
||||||
})
|
})
|
||||||
.ChainOnce<MessageInt>([this](const MessageInt &msg) {
|
.ChainOnce<MessageInt>([this](const MessageInt &msg, const Subscription&) {
|
||||||
ASSERT_EQ(msg.x, 77);
|
ASSERT_EQ(msg.x, 77);
|
||||||
CloseChannel("main");
|
CloseChannel("main");
|
||||||
});
|
});
|
||||||
@ -440,12 +440,12 @@ TEST(MultipleSendTest, ProcessManyMessages) {
|
|||||||
EventStream* stream = main_.first;
|
EventStream* stream = main_.first;
|
||||||
vals = 0;
|
vals = 0;
|
||||||
|
|
||||||
stream->OnEvent<MessageInt>([this](const Message&, const EventStream::Subscription&) {
|
stream->OnEvent<MessageInt>([this](const Message&, const Subscription&) {
|
||||||
++vals;
|
++vals;
|
||||||
main_.second->Send<EndMessage>();
|
main_.second->Send<EndMessage>();
|
||||||
});
|
});
|
||||||
|
|
||||||
stream->OnEvent<EndMessage>([this](const Message&, const EventStream::Subscription&) {
|
stream->OnEvent<EndMessage>([this](const Message&, const Subscription&) {
|
||||||
ASSERT_LE(vals, num_tests);
|
ASSERT_LE(vals, num_tests);
|
||||||
if (vals == num_tests) {
|
if (vals == num_tests) {
|
||||||
CloseChannel("main");
|
CloseChannel("main");
|
||||||
|
Loading…
Reference in New Issue
Block a user