2017-03-02 09:28:07 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <chrono>
|
|
|
|
#include <string>
|
|
|
|
#include <thread>
|
2017-03-03 08:12:11 +00:00
|
|
|
#include <unordered_map>
|
2017-03-02 09:28:07 +00:00
|
|
|
|
|
|
|
#include <rapidjson/document.h>
|
|
|
|
#include <rapidjson/prettywriter.h>
|
|
|
|
|
|
|
|
#include "platform.h"
|
|
|
|
#include "serializer.h"
|
|
|
|
|
|
|
|
using Writer = rapidjson::PrettyWriter<rapidjson::StringBuffer>;
|
|
|
|
using Reader = rapidjson::Document;
|
|
|
|
|
|
|
|
|
|
|
|
// Messages are funky objects. They contain potentially variable amounts of
|
|
|
|
// data and are passed between processes. This means that they need to be
|
|
|
|
// fully relocatable, ie, it is possible to memmove them in memory to a
|
|
|
|
// completely different address.
|
|
|
|
|
|
|
|
struct JsonMessage {
|
2017-03-03 08:12:11 +00:00
|
|
|
int message_id;
|
2017-03-02 09:28:07 +00:00
|
|
|
size_t payload_size;
|
|
|
|
|
|
|
|
const char* payload();
|
|
|
|
void SetPayload(size_t payload_size, const char* payload);
|
|
|
|
};
|
|
|
|
|
2017-03-03 08:12:11 +00:00
|
|
|
using IpcMessageId = std::string;
|
|
|
|
|
2017-03-02 09:28:07 +00:00
|
|
|
struct BaseIpcMessage {
|
2017-03-03 08:43:54 +00:00
|
|
|
BaseIpcMessage();
|
2017-03-03 08:12:11 +00:00
|
|
|
virtual ~BaseIpcMessage();
|
2017-03-02 09:28:07 +00:00
|
|
|
|
2017-03-03 08:12:11 +00:00
|
|
|
virtual void Serialize(Writer& writer);
|
|
|
|
virtual void Deserialize(Reader& reader);
|
|
|
|
|
|
|
|
IpcMessageId runtime_id;
|
2017-03-03 08:43:54 +00:00
|
|
|
int hashed_runtime_id = -1;
|
|
|
|
|
|
|
|
// Populated by IpcRegistry::RegisterAllocator.
|
|
|
|
static IpcMessageId runtime_id_;
|
|
|
|
static int hashed_runtime_id_;
|
2017-03-02 09:28:07 +00:00
|
|
|
|
2017-03-03 08:12:11 +00:00
|
|
|
/*
|
|
|
|
private:
|
|
|
|
template<typename T>
|
|
|
|
friend struct IpcMessage;
|
|
|
|
*/
|
2017-03-02 09:28:07 +00:00
|
|
|
};
|
|
|
|
|
2017-03-03 08:12:11 +00:00
|
|
|
struct IpcRegistry {
|
|
|
|
using Allocator = std::function<BaseIpcMessage*()>;
|
|
|
|
|
|
|
|
// Use unique_ptrs so we can initialize on first use
|
|
|
|
// (static init order might not be right).
|
|
|
|
std::unique_ptr<std::unordered_map<int, Allocator>> allocators;
|
|
|
|
std::unique_ptr<std::unordered_map<int, std::string>> hash_to_id;
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
int RegisterAllocator();
|
2017-03-02 09:28:07 +00:00
|
|
|
|
2017-03-03 08:12:11 +00:00
|
|
|
std::unique_ptr<BaseIpcMessage> Allocate(int id);
|
2017-03-02 09:28:07 +00:00
|
|
|
|
2017-03-03 08:12:11 +00:00
|
|
|
static IpcRegistry* instance() {
|
2017-03-03 08:43:54 +00:00
|
|
|
if (!instance_)
|
|
|
|
instance_ = new IpcRegistry();
|
2017-03-03 08:12:11 +00:00
|
|
|
return instance_;
|
|
|
|
}
|
|
|
|
static IpcRegistry* instance_;
|
2017-03-02 09:28:07 +00:00
|
|
|
};
|
|
|
|
|
2017-03-03 08:12:11 +00:00
|
|
|
template<typename T>
|
|
|
|
int IpcRegistry::RegisterAllocator() {
|
|
|
|
if (!allocators) {
|
|
|
|
allocators = std::make_unique<std::unordered_map<int, Allocator>>();
|
|
|
|
hash_to_id = std::make_unique<std::unordered_map<int, std::string>>();
|
|
|
|
}
|
|
|
|
|
|
|
|
IpcMessageId id = T::id;
|
|
|
|
|
|
|
|
int hash = std::hash<IpcMessageId>()(id);
|
|
|
|
auto it = allocators->find(hash);
|
|
|
|
assert(allocators->find(hash) == allocators->end() && "There is already an IPC message with the given id");
|
2017-03-02 09:28:07 +00:00
|
|
|
|
2017-03-03 08:12:11 +00:00
|
|
|
(*hash_to_id)[hash] = id;
|
|
|
|
(*allocators)[hash] = []() {
|
|
|
|
return new T();
|
|
|
|
};
|
|
|
|
|
2017-03-03 08:43:54 +00:00
|
|
|
T::runtime_id_ = id;
|
|
|
|
T::hashed_runtime_id_ = hash;
|
2017-03-02 09:28:07 +00:00
|
|
|
|
2017-03-03 08:43:54 +00:00
|
|
|
return hash;
|
2017-03-03 08:12:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-03-03 06:16:28 +00:00
|
|
|
struct IpcDirectionalChannel {
|
2017-03-02 09:28:07 +00:00
|
|
|
// NOTE: We keep all pointers in terms of char* so pointer arithmetic is
|
|
|
|
// always relative to bytes.
|
|
|
|
|
2017-03-03 06:16:28 +00:00
|
|
|
explicit IpcDirectionalChannel(const std::string& name);
|
|
|
|
~IpcDirectionalChannel();
|
2017-03-02 09:28:07 +00:00
|
|
|
|
|
|
|
void PushMessage(BaseIpcMessage* message);
|
2017-03-03 06:16:28 +00:00
|
|
|
std::vector<std::unique_ptr<BaseIpcMessage>> TakeMessages();
|
2017-03-02 09:28:07 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
JsonMessage* get_free_message() {
|
|
|
|
return reinterpret_cast<JsonMessage*>(shared->shared_start + *shared->shared_bytes_used);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pointer to process shared memory and process shared mutex.
|
|
|
|
std::unique_ptr<PlatformSharedMemory> shared;
|
|
|
|
std::unique_ptr<PlatformMutex> mutex;
|
|
|
|
|
|
|
|
// Pointer to process-local memory.
|
|
|
|
char* local_block;
|
2017-03-03 06:16:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct IpcServer {
|
|
|
|
IpcServer(const std::string& name);
|
|
|
|
|
|
|
|
void SendToClient(int client_id, BaseIpcMessage* message);
|
|
|
|
std::vector<std::unique_ptr<BaseIpcMessage>> TakeMessages();
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::string name_;
|
|
|
|
IpcDirectionalChannel server_;
|
|
|
|
std::unordered_map<int, std::unique_ptr<IpcDirectionalChannel>> clients_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct IpcClient {
|
|
|
|
IpcClient(const std::string& name, int client_id);
|
|
|
|
|
|
|
|
void SendToServer(BaseIpcMessage* message);
|
|
|
|
std::vector<std::unique_ptr<BaseIpcMessage>> TakeMessages();
|
|
|
|
|
|
|
|
private:
|
|
|
|
IpcDirectionalChannel server_;
|
|
|
|
IpcDirectionalChannel client_;
|
2017-03-02 09:28:07 +00:00
|
|
|
};
|