ccls/ipc.h

194 lines
4.5 KiB
C
Raw Normal View History

#pragma once
#include <iostream>
#include <chrono>
#include <string>
#include <thread>
2017-03-03 08:12:11 +00:00
#include <unordered_map>
#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;
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-05 02:16:23 +00:00
struct BaseIpcMessageElided {
virtual IpcMessageId runtime_id() const = 0;
virtual int hashed_runtime_id() const = 0;
virtual void Serialize(Writer& writer);
virtual void Deserialize(Reader& reader);
};
// Usage:
//
2017-03-05 02:16:23 +00:00
// class IpcMessage_Foo : public BaseIpcMessage<IpcMessage_Foo> {
// static IpcMessageId id;
2017-03-04 01:45:20 +00:00
//
// // BaseIpcMessage:
// ...
// }
// IpcMessageId IpcMessage_Foo::id = "Foo";
//
//
// main() {
// IpcRegistry::instance()->Register<IpcMessage_Foo>();
// }
2017-03-05 02:16:23 +00:00
//
// Note: This is a template so that the statics are stored separately
// per type.
template<typename T>
struct BaseIpcMessage : BaseIpcMessageElided {
2017-03-03 08:43:54 +00:00
BaseIpcMessage();
2017-03-03 08:12:11 +00:00
virtual ~BaseIpcMessage();
2017-03-03 08:43:54 +00:00
// Populated by IpcRegistry::RegisterAllocator.
static IpcMessageId runtime_id_;
2017-03-04 01:45:20 +00:00
static int hashed_runtime_id_;
2017-03-05 02:16:23 +00:00
// BaseIpcMessageElided:
IpcMessageId runtime_id() const override {
return runtime_id_;
}
int hashed_runtime_id() const override {
return hashed_runtime_id_;
}
};
2017-03-03 08:12:11 +00:00
struct IpcRegistry {
2017-03-05 02:16:23 +00:00
using Allocator = std::function<BaseIpcMessageElided*()>;
2017-03-03 08:12:11 +00:00
// 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>
void Register();
2017-03-05 02:16:23 +00:00
std::unique_ptr<BaseIpcMessageElided> Allocate(int id);
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-03 08:12:11 +00:00
template<typename T>
void IpcRegistry::Register() {
2017-03-03 08:12:11 +00:00
if (!allocators) {
2017-03-04 01:45:20 +00:00
allocators = MakeUnique<std::unordered_map<int, Allocator>>();
hash_to_id = MakeUnique<std::unordered_map<int, std::string>>();
2017-03-03 08:12:11 +00:00
}
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-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-03 08:12:11 +00:00
}
2017-03-03 06:16:28 +00:00
struct IpcDirectionalChannel {
// 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-05 02:16:23 +00:00
void PushMessage(BaseIpcMessageElided* message);
std::vector<std::unique_ptr<BaseIpcMessageElided>> TakeMessages();
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);
2017-03-05 02:16:23 +00:00
void SendToClient(int client_id, BaseIpcMessageElided* message);
std::vector<std::unique_ptr<BaseIpcMessageElided>> TakeMessages();
2017-03-03 06:16:28 +00:00
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);
2017-03-05 02:16:23 +00:00
void SendToServer(BaseIpcMessageElided* message);
std::vector<std::unique_ptr<BaseIpcMessageElided>> TakeMessages();
2017-03-03 06:16:28 +00:00
private:
IpcDirectionalChannel server_;
IpcDirectionalChannel client_;
2017-03-04 01:45:20 +00:00
};
2017-03-05 02:16:23 +00:00
template<typename T>
BaseIpcMessage<T>::BaseIpcMessage() {
assert(!runtime_id_.empty() && "Message is not registered using IpcRegistry::RegisterAllocator");
}
template<typename T>
BaseIpcMessage<T>::~BaseIpcMessage() {}
template<typename T>
IpcMessageId BaseIpcMessage<T>::runtime_id_;
template<typename T>
int BaseIpcMessage<T>::hashed_runtime_id_ = -1;