mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-23 01:21:42 +00:00
Implement remote procedure calls.
This commit is contained in:
@@ -137,6 +137,14 @@ struct NT_RpcDefinition {
|
||||
NT_RpcResultDef *results;
|
||||
};
|
||||
|
||||
/** NetworkTables RPC Call Data */
|
||||
struct NT_RpcCallInfo {
|
||||
unsigned int rpc_id;
|
||||
unsigned int call_uid;
|
||||
struct NT_String name;
|
||||
struct NT_String params;
|
||||
};
|
||||
|
||||
/*
|
||||
* Table Functions
|
||||
*/
|
||||
@@ -268,18 +276,31 @@ void NT_RemoveConnectionListener(unsigned int conn_listener_uid);
|
||||
* Remote Procedure Call Functions
|
||||
*/
|
||||
|
||||
typedef NT_Value **(*NT_RpcCallback)(unsigned int uid, void *data,
|
||||
const char *name, size_t name_len,
|
||||
const struct NT_Value **params,
|
||||
size_t params_len, size_t *results_len);
|
||||
typedef char *(*NT_RpcCallback)(void *data, const char *name, size_t name_len,
|
||||
const char *params, size_t params_len,
|
||||
size_t *results_len);
|
||||
|
||||
unsigned int NT_CreateRpc(const char *name, size_t name_len,
|
||||
const struct NT_RpcDefinition *def, void *data,
|
||||
NT_RpcCallback callback);
|
||||
void NT_DeleteRpc(unsigned int rpc_uid);
|
||||
unsigned int NT_CallRpc(const char *name, size_t name_len,
|
||||
const struct NT_Value **params, size_t params_len);
|
||||
struct NT_Value **NT_GetRpcResult(unsigned int result_uid, size_t *results_len);
|
||||
void NT_CreateRpc(const char *name, size_t name_len, const char *def,
|
||||
size_t def_len, void *data, NT_RpcCallback callback);
|
||||
void NT_CreatePolledRpc(const char *name, size_t name_len, const char *def,
|
||||
size_t def_len);
|
||||
|
||||
int NT_PollRpc(int blocking, struct NT_RpcCallInfo* call_info);
|
||||
void NT_PostRpcResponse(unsigned int rpc_id, unsigned int call_uid,
|
||||
const char *result, size_t result_len);
|
||||
|
||||
unsigned int NT_CallRpc(const char *name, size_t name_len, const char *params,
|
||||
size_t params_len);
|
||||
char *NT_GetRpcResult(int blocking, unsigned int call_uid, size_t *result_len);
|
||||
|
||||
char *NT_PackRpcDefinition(const struct NT_RpcDefinition *def,
|
||||
size_t *packed_len);
|
||||
int NT_UnpackRpcDefinition(const char *packed, size_t packed_len,
|
||||
struct NT_RpcDefinition *def);
|
||||
char *NT_PackRpcValues(const struct NT_Value **values, size_t values_len,
|
||||
size_t *packed_len);
|
||||
struct NT_Value **NT_UnpackRpcValues(const char *packed, size_t packed_len,
|
||||
const NT_Type *types, size_t types_len);
|
||||
|
||||
/*
|
||||
* Client/Server Functions
|
||||
@@ -319,6 +340,10 @@ void NT_InitString(struct NT_String *str);
|
||||
|
||||
void NT_DisposeConnectionInfoArray(struct NT_ConnectionInfo *arr, size_t count);
|
||||
|
||||
void NT_DisposeRpcDefinition(struct NT_RpcDefinition *def);
|
||||
|
||||
void NT_DisposeRpcCallInfo(struct NT_RpcCallInfo *call_info);
|
||||
|
||||
/* timestamp */
|
||||
unsigned long long NT_Now(void);
|
||||
|
||||
|
||||
@@ -50,12 +50,19 @@ struct ConnectionInfo {
|
||||
|
||||
/** NetworkTables RPC Parameter Definition */
|
||||
struct RpcParamDef {
|
||||
RpcParamDef() = default;
|
||||
RpcParamDef(StringRef name_, std::shared_ptr<Value> def_value_)
|
||||
: name(name_), def_value(def_value_) {}
|
||||
|
||||
std::string name;
|
||||
std::shared_ptr<Value> def_value;
|
||||
};
|
||||
|
||||
/** NetworkTables RPC Result Definition */
|
||||
struct RpcResultDef {
|
||||
RpcResultDef() = default;
|
||||
RpcResultDef(StringRef name_, NT_Type type_) : name(name_), type(type_) {}
|
||||
|
||||
std::string name;
|
||||
NT_Type type;
|
||||
};
|
||||
@@ -68,6 +75,14 @@ struct RpcDefinition {
|
||||
std::vector<RpcResultDef> results;
|
||||
};
|
||||
|
||||
/** NetworkTables RPC Call Data */
|
||||
struct RpcCallInfo {
|
||||
unsigned int rpc_id;
|
||||
unsigned int call_uid;
|
||||
std::string name;
|
||||
std::string params;
|
||||
};
|
||||
|
||||
/*
|
||||
* Table Functions
|
||||
*/
|
||||
@@ -183,15 +198,24 @@ void RemoveConnectionListener(unsigned int conn_listener_uid);
|
||||
* Remote Procedure Call Functions
|
||||
*/
|
||||
|
||||
typedef std::function<std::vector<std::shared_ptr<Value>>(
|
||||
unsigned int uid, StringRef name, ArrayRef<std::shared_ptr<Value>> params)>
|
||||
typedef std::function<std::string(StringRef name, StringRef params)>
|
||||
RpcCallback;
|
||||
|
||||
unsigned int CreateRpc(StringRef name, const RpcDefinition& def,
|
||||
RpcCallback callback);
|
||||
void DeleteRpc(unsigned int rpc_uid);
|
||||
unsigned int CallRpc(StringRef name, ArrayRef<std::shared_ptr<Value>> params);
|
||||
std::vector<std::shared_ptr<Value>> GetRpcResult(unsigned int result_uid);
|
||||
void CreateRpc(StringRef name, StringRef def, RpcCallback callback);
|
||||
void CreatePolledRpc(StringRef name, StringRef def);
|
||||
|
||||
bool PollRpc(bool blocking, RpcCallInfo* call_info);
|
||||
void PostRpcResponse(unsigned int rpc_id, unsigned int call_uid,
|
||||
StringRef result);
|
||||
|
||||
unsigned int CallRpc(StringRef name, StringRef params);
|
||||
bool GetRpcResult(bool blocking, unsigned int call_uid, std::string* result);
|
||||
|
||||
std::string PackRpcDefinition(const RpcDefinition& def);
|
||||
bool UnpackRpcDefinition(StringRef packed, RpcDefinition *def);
|
||||
std::string PackRpcValues(ArrayRef<std::shared_ptr<Value>> values);
|
||||
std::vector<std::shared_ptr<Value>> UnpackRpcValues(StringRef packed,
|
||||
ArrayRef<NT_Type> types);
|
||||
|
||||
/*
|
||||
* Client/Server Functions
|
||||
|
||||
17
ntcore.def
17
ntcore.def
@@ -13,10 +13,6 @@ NT_AddEntryListener @10
|
||||
NT_RemoveEntryListener @11
|
||||
NT_AddConnectionListener @12
|
||||
NT_RemoveConnectionListener @13
|
||||
NT_CreateRpc @14
|
||||
NT_DeleteRpc @15
|
||||
NT_CallRpc @16
|
||||
NT_GetRpcResult @17
|
||||
NT_SetNetworkIdentity @18
|
||||
NT_StartServer @19
|
||||
NT_StopServer @20
|
||||
@@ -33,4 +29,15 @@ NT_InitString @30
|
||||
NT_DisposeConnectionInfoArray @31
|
||||
NT_Now @32
|
||||
NT_SetLogger @33
|
||||
|
||||
NT_CreateRpc @34
|
||||
NT_CreatePolledRpc @35
|
||||
NT_PollRpc @36
|
||||
NT_PostRpcRepsonse @37
|
||||
NT_CallRpc @38
|
||||
NT_GetRpcResult @39
|
||||
NT_PackRpcDefinition @40
|
||||
NT_UnpackRpcDefinition @41
|
||||
NT_PackRpcValues @42
|
||||
NT_UnpackRpcValues @43
|
||||
NT_DisposeRpcDefinition @44
|
||||
NT_DisposeRpcCallInfo @45
|
||||
|
||||
@@ -94,7 +94,7 @@ void DispatcherBase::Stop() {
|
||||
if (m_dispatch_thread.joinable()) m_dispatch_thread.join();
|
||||
if (m_clientserver_thread.joinable()) m_clientserver_thread.join();
|
||||
|
||||
std::vector<Connection> conns;
|
||||
std::vector<std::shared_ptr<NetworkConnection>> conns;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_user_mutex);
|
||||
conns.swap(m_connections);
|
||||
@@ -135,21 +135,14 @@ std::vector<ConnectionInfo> DispatcherBase::GetConnections() const {
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_user_mutex);
|
||||
for (auto& conn : m_connections) {
|
||||
if (conn.net->state() != NetworkConnection::kActive) continue;
|
||||
conns.emplace_back(conn.net->info());
|
||||
if (conn->state() != NetworkConnection::kActive) continue;
|
||||
conns.emplace_back(conn->info());
|
||||
}
|
||||
|
||||
return conns;
|
||||
}
|
||||
|
||||
void DispatcherBase::DispatchThreadMain() {
|
||||
// local copy of active m_connections
|
||||
struct ConnectionRef {
|
||||
NetworkConnection* net;
|
||||
NetworkConnection::Outgoing outgoing;
|
||||
};
|
||||
std::vector<ConnectionRef> connections;
|
||||
|
||||
auto timeout_time = std::chrono::steady_clock::now();
|
||||
int count = 0;
|
||||
std::unique_lock<std::mutex> flush_lock(m_flush_mutex);
|
||||
@@ -171,19 +164,15 @@ void DispatcherBase::DispatchThreadMain() {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// make a local copy of the connections list (so we don't hold the lock)
|
||||
connections.resize(0);
|
||||
{
|
||||
std::lock_guard<std::mutex> user_lock(m_user_mutex);
|
||||
bool reconnect = false;
|
||||
for (auto& conn : m_connections) {
|
||||
if (conn.net->state() == NetworkConnection::kActive) {
|
||||
connections.push_back(ConnectionRef());
|
||||
connections.back().net = conn.net.get();
|
||||
connections.back().outgoing.swap(conn.outgoing);
|
||||
conn.last_update.resize(0); // clear "previous" updates
|
||||
}
|
||||
if (!m_server && conn.net->state() == NetworkConnection::kDead)
|
||||
// post outgoing messages if connection is active
|
||||
if (conn->state() == NetworkConnection::kActive) conn->PostOutgoing();
|
||||
|
||||
// if client, reconnect if connection died
|
||||
if (!m_server && conn->state() == NetworkConnection::kDead)
|
||||
reconnect = true;
|
||||
}
|
||||
// reconnect if we disconnected (and a reconnect is not in progress)
|
||||
@@ -192,105 +181,6 @@ void DispatcherBase::DispatchThreadMain() {
|
||||
m_reconnect_cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
// send outgoing messages
|
||||
for (auto& conn : connections) {
|
||||
if (!conn.outgoing.empty())
|
||||
conn.net->outgoing().emplace(std::move(conn.outgoing));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DispatcherBase::Connection::QueueOutgoing(std::shared_ptr<Message> msg) {
|
||||
// Merge with previous. One case we don't combine: delete/assign loop.
|
||||
switch (msg->type()) {
|
||||
case Message::kEntryAssign:
|
||||
case Message::kEntryUpdate: {
|
||||
// don't do this for unassigned id's
|
||||
unsigned int id = msg->id();
|
||||
if (id == 0xffff) {
|
||||
outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
if (id < last_update.size() && last_update[id].first != 0) {
|
||||
// overwrite the previous one for this id
|
||||
auto& oldmsg = outgoing[last_update[id].first - 1];
|
||||
if (oldmsg && oldmsg->Is(Message::kEntryAssign) &&
|
||||
msg->Is(Message::kEntryUpdate)) {
|
||||
// need to update assignment with new seq_num and value
|
||||
oldmsg = Message::EntryAssign(oldmsg->str(), id, msg->seq_num_uid(),
|
||||
msg->value(), oldmsg->flags());
|
||||
} else
|
||||
oldmsg = msg; // easy update
|
||||
} else {
|
||||
// new, but remember it
|
||||
std::size_t pos = outgoing.size();
|
||||
outgoing.push_back(msg);
|
||||
if (id >= last_update.size()) last_update.resize(id + 1);
|
||||
last_update[id].first = pos + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message::kEntryDelete: {
|
||||
// don't do this for unassigned id's
|
||||
unsigned int id = msg->id();
|
||||
if (id == 0xffff) {
|
||||
outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
// clear previous updates
|
||||
if (id < last_update.size()) {
|
||||
if (last_update[id].first != 0) {
|
||||
outgoing[last_update[id].first - 1].reset();
|
||||
last_update[id].first = 0;
|
||||
}
|
||||
if (last_update[id].second != 0) {
|
||||
outgoing[last_update[id].second - 1].reset();
|
||||
last_update[id].second = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// add deletion
|
||||
outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
case Message::kFlagsUpdate: {
|
||||
// don't do this for unassigned id's
|
||||
unsigned int id = msg->id();
|
||||
if (id == 0xffff) {
|
||||
outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
if (id < last_update.size() && last_update[id].second != 0) {
|
||||
// overwrite the previous one for this id
|
||||
outgoing[last_update[id].second - 1] = msg;
|
||||
} else {
|
||||
// new, but remember it
|
||||
std::size_t pos = outgoing.size();
|
||||
outgoing.push_back(msg);
|
||||
if (id >= last_update.size()) last_update.resize(id + 1);
|
||||
last_update[id].second = pos + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message::kClearEntries: {
|
||||
// knock out all previous assigns/updates!
|
||||
for (auto& i : outgoing) {
|
||||
if (!i) continue;
|
||||
auto t = i->type();
|
||||
if (t == Message::kEntryAssign || t == Message::kEntryUpdate ||
|
||||
t == Message::kFlagsUpdate || t == Message::kEntryDelete ||
|
||||
t == Message::kClearEntries)
|
||||
i.reset();
|
||||
}
|
||||
last_update.resize(0);
|
||||
outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,12 +189,12 @@ void DispatcherBase::QueueOutgoing(std::shared_ptr<Message> msg,
|
||||
NetworkConnection* except) {
|
||||
std::lock_guard<std::mutex> user_lock(m_user_mutex);
|
||||
for (auto& conn : m_connections) {
|
||||
if (conn.net.get() == except) continue;
|
||||
if (only && conn.net.get() != only) continue;
|
||||
auto state = conn.net->state();
|
||||
if (conn.get() == except) continue;
|
||||
if (only && conn.get() != only) continue;
|
||||
auto state = conn->state();
|
||||
if (state != NetworkConnection::kSynchronized &&
|
||||
state != NetworkConnection::kActive) continue;
|
||||
conn.QueueOutgoing(msg);
|
||||
conn->QueueOutgoing(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,18 +214,18 @@ void DispatcherBase::ServerThreadMain() {
|
||||
|
||||
// add to connections list
|
||||
using namespace std::placeholders;
|
||||
std::unique_ptr<NetworkConnection> conn_unique(new NetworkConnection(
|
||||
std::move(stream),
|
||||
m_notifier,
|
||||
auto conn = std::make_shared<NetworkConnection>(
|
||||
std::move(stream), m_notifier,
|
||||
std::bind(&Dispatcher::ServerHandshake, this, _1, _2, _3),
|
||||
std::bind(&Storage::GetEntryType, &m_storage, _1),
|
||||
std::bind(&Storage::ProcessIncoming, &m_storage, _1, _2)));
|
||||
auto conn = conn_unique.get();
|
||||
std::bind(&Storage::GetEntryType, &m_storage, _1));
|
||||
conn->set_process_incoming(
|
||||
std::bind(&Storage::ProcessIncoming, &m_storage, _1, _2,
|
||||
std::weak_ptr<NetworkConnection>(conn)));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_user_mutex);
|
||||
m_connections.emplace_back(std::move(conn_unique));
|
||||
m_connections.emplace_back(conn);
|
||||
conn->Start();
|
||||
}
|
||||
conn->Start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,15 +243,15 @@ void DispatcherBase::ClientThreadMain(
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_user_mutex);
|
||||
using namespace std::placeholders;
|
||||
std::unique_ptr<NetworkConnection> conn_unique(new NetworkConnection(
|
||||
std::move(stream),
|
||||
m_notifier,
|
||||
auto conn = std::make_shared<NetworkConnection>(
|
||||
std::move(stream), m_notifier,
|
||||
std::bind(&Dispatcher::ClientHandshake, this, _1, _2, _3),
|
||||
std::bind(&Storage::GetEntryType, &m_storage, _1),
|
||||
std::bind(&Storage::ProcessIncoming, &m_storage, _1, _2)));
|
||||
auto conn = conn_unique.get();
|
||||
std::bind(&Storage::GetEntryType, &m_storage, _1));
|
||||
conn->set_process_incoming(
|
||||
std::bind(&Storage::ProcessIncoming, &m_storage, _1, _2,
|
||||
std::weak_ptr<NetworkConnection>(conn)));
|
||||
m_connections.resize(0); // disconnect any current
|
||||
m_connections.emplace_back(std::move(conn_unique));
|
||||
m_connections.emplace_back(conn);
|
||||
conn->set_proto_rev(m_reconnect_proto_rev);
|
||||
conn->Start();
|
||||
|
||||
@@ -514,7 +404,8 @@ bool DispatcherBase::ServerHandshake(
|
||||
// get the next message (blocks)
|
||||
msg = get_msg();
|
||||
}
|
||||
for (auto& msg : incoming) m_storage.ProcessIncoming(msg, &conn);
|
||||
for (auto& msg : incoming)
|
||||
m_storage.ProcessIncoming(msg, &conn, std::weak_ptr<NetworkConnection>());
|
||||
}
|
||||
|
||||
INFO("server: client CONNECTED: " << conn.stream().getPeerIP() << " port "
|
||||
|
||||
@@ -81,24 +81,7 @@ class DispatcherBase {
|
||||
|
||||
// Mutex for user-accessible items
|
||||
mutable std::mutex m_user_mutex;
|
||||
struct Connection {
|
||||
Connection() = default;
|
||||
explicit Connection(std::unique_ptr<NetworkConnection> net_)
|
||||
: net(std::move(net_)) {}
|
||||
Connection(Connection&& rhs) {
|
||||
net = std::move(rhs.net);
|
||||
outgoing = std::move(rhs.outgoing);
|
||||
last_update = std::move(rhs.last_update);
|
||||
}
|
||||
Connection(const Connection&) = delete;
|
||||
Connection& operator=(const Connection&) = delete;
|
||||
void QueueOutgoing(std::shared_ptr<Message> msg);
|
||||
|
||||
std::unique_ptr<NetworkConnection> net;
|
||||
NetworkConnection::Outgoing outgoing;
|
||||
std::vector<std::pair<std::size_t, std::size_t>> last_update;
|
||||
};
|
||||
std::vector<Connection> m_connections;
|
||||
std::vector<std::shared_ptr<NetworkConnection>> m_connections;
|
||||
std::string m_identity;
|
||||
|
||||
std::atomic_bool m_active; // set to false to terminate threads
|
||||
|
||||
@@ -17,16 +17,17 @@
|
||||
|
||||
using namespace nt;
|
||||
|
||||
std::atomic_uint NetworkConnection::s_uid;
|
||||
|
||||
NetworkConnection::NetworkConnection(std::unique_ptr<NetworkStream> stream,
|
||||
Notifier& notifier,
|
||||
HandshakeFunc handshake,
|
||||
Message::GetEntryTypeFunc get_entry_type,
|
||||
ProcessIncomingFunc process_incoming)
|
||||
: m_stream(std::move(stream)),
|
||||
Message::GetEntryTypeFunc get_entry_type)
|
||||
: m_uid(s_uid.fetch_add(1)),
|
||||
m_stream(std::move(stream)),
|
||||
m_notifier(notifier),
|
||||
m_handshake(handshake),
|
||||
m_get_entry_type(get_entry_type),
|
||||
m_process_incoming(process_incoming) {
|
||||
m_get_entry_type(get_entry_type) {
|
||||
m_active = false;
|
||||
m_proto_rev = 0x0300;
|
||||
m_state = static_cast<int>(kCreated);
|
||||
@@ -153,3 +154,106 @@ void NetworkConnection::WriteThreadMain() {
|
||||
m_active = false;
|
||||
if (m_stream) m_stream->close(); // also kill read thread
|
||||
}
|
||||
|
||||
void NetworkConnection::QueueOutgoing(std::shared_ptr<Message> msg) {
|
||||
std::lock_guard<std::mutex> lock(m_pending_mutex);
|
||||
|
||||
// Merge with previous. One case we don't combine: delete/assign loop.
|
||||
switch (msg->type()) {
|
||||
case Message::kEntryAssign:
|
||||
case Message::kEntryUpdate: {
|
||||
// don't do this for unassigned id's
|
||||
unsigned int id = msg->id();
|
||||
if (id == 0xffff) {
|
||||
m_pending_outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
if (id < m_pending_update.size() && m_pending_update[id].first != 0) {
|
||||
// overwrite the previous one for this id
|
||||
auto& oldmsg = m_pending_outgoing[m_pending_update[id].first - 1];
|
||||
if (oldmsg && oldmsg->Is(Message::kEntryAssign) &&
|
||||
msg->Is(Message::kEntryUpdate)) {
|
||||
// need to update assignment with new seq_num and value
|
||||
oldmsg = Message::EntryAssign(oldmsg->str(), id, msg->seq_num_uid(),
|
||||
msg->value(), oldmsg->flags());
|
||||
} else
|
||||
oldmsg = msg; // easy update
|
||||
} else {
|
||||
// new, but remember it
|
||||
std::size_t pos = m_pending_outgoing.size();
|
||||
m_pending_outgoing.push_back(msg);
|
||||
if (id >= m_pending_update.size()) m_pending_update.resize(id + 1);
|
||||
m_pending_update[id].first = pos + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message::kEntryDelete: {
|
||||
// don't do this for unassigned id's
|
||||
unsigned int id = msg->id();
|
||||
if (id == 0xffff) {
|
||||
m_pending_outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
// clear previous updates
|
||||
if (id < m_pending_update.size()) {
|
||||
if (m_pending_update[id].first != 0) {
|
||||
m_pending_outgoing[m_pending_update[id].first - 1].reset();
|
||||
m_pending_update[id].first = 0;
|
||||
}
|
||||
if (m_pending_update[id].second != 0) {
|
||||
m_pending_outgoing[m_pending_update[id].second - 1].reset();
|
||||
m_pending_update[id].second = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// add deletion
|
||||
m_pending_outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
case Message::kFlagsUpdate: {
|
||||
// don't do this for unassigned id's
|
||||
unsigned int id = msg->id();
|
||||
if (id == 0xffff) {
|
||||
m_pending_outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
if (id < m_pending_update.size() && m_pending_update[id].second != 0) {
|
||||
// overwrite the previous one for this id
|
||||
m_pending_outgoing[m_pending_update[id].second - 1] = msg;
|
||||
} else {
|
||||
// new, but remember it
|
||||
std::size_t pos = m_pending_outgoing.size();
|
||||
m_pending_outgoing.push_back(msg);
|
||||
if (id >= m_pending_update.size()) m_pending_update.resize(id + 1);
|
||||
m_pending_update[id].second = pos + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message::kClearEntries: {
|
||||
// knock out all previous assigns/updates!
|
||||
for (auto& i : m_pending_outgoing) {
|
||||
if (!i) continue;
|
||||
auto t = i->type();
|
||||
if (t == Message::kEntryAssign || t == Message::kEntryUpdate ||
|
||||
t == Message::kFlagsUpdate || t == Message::kEntryDelete ||
|
||||
t == Message::kClearEntries)
|
||||
i.reset();
|
||||
}
|
||||
m_pending_update.resize(0);
|
||||
m_pending_outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
m_pending_outgoing.push_back(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkConnection::PostOutgoing() {
|
||||
std::lock_guard<std::mutex> lock(m_pending_mutex);
|
||||
if (m_pending_outgoing.empty()) return;
|
||||
m_outgoing.emplace(std::move(m_pending_outgoing));
|
||||
m_pending_outgoing.resize(0);
|
||||
m_pending_update.resize(0);
|
||||
}
|
||||
|
||||
@@ -39,10 +39,14 @@ class NetworkConnection {
|
||||
NetworkConnection(std::unique_ptr<NetworkStream> stream,
|
||||
Notifier& notifier,
|
||||
HandshakeFunc handshake,
|
||||
Message::GetEntryTypeFunc get_entry_type,
|
||||
ProcessIncomingFunc process_incoming);
|
||||
Message::GetEntryTypeFunc get_entry_type);
|
||||
~NetworkConnection();
|
||||
|
||||
// Set the input processor function. This must be called before Start().
|
||||
void set_process_incoming(ProcessIncomingFunc func) {
|
||||
m_process_incoming = func;
|
||||
}
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
@@ -50,7 +54,11 @@ class NetworkConnection {
|
||||
|
||||
bool active() const { return m_active; }
|
||||
NetworkStream& stream() { return *m_stream; }
|
||||
OutgoingQueue& outgoing() { return m_outgoing; }
|
||||
|
||||
void QueueOutgoing(std::shared_ptr<Message> msg);
|
||||
void PostOutgoing();
|
||||
|
||||
unsigned int uid() const { return m_uid; }
|
||||
|
||||
unsigned int proto_rev() const { return m_proto_rev; }
|
||||
void set_proto_rev(unsigned int proto_rev) { m_proto_rev = proto_rev; }
|
||||
@@ -70,6 +78,9 @@ class NetworkConnection {
|
||||
void ReadThreadMain();
|
||||
void WriteThreadMain();
|
||||
|
||||
static std::atomic_uint s_uid;
|
||||
|
||||
unsigned int m_uid;
|
||||
std::unique_ptr<NetworkStream> m_stream;
|
||||
Notifier& m_notifier;
|
||||
OutgoingQueue m_outgoing;
|
||||
@@ -84,6 +95,10 @@ class NetworkConnection {
|
||||
mutable std::mutex m_remote_id_mutex;
|
||||
std::string m_remote_id;
|
||||
std::atomic_ullong m_last_update;
|
||||
|
||||
std::mutex m_pending_mutex;
|
||||
Outgoing m_pending_outgoing;
|
||||
std::vector<std::pair<std::size_t, std::size_t>> m_pending_update;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
119
src/RpcServer.cpp
Normal file
119
src/RpcServer.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "RpcServer.h"
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
using namespace nt;
|
||||
|
||||
ATOMIC_STATIC_INIT(RpcServer)
|
||||
|
||||
RpcServer::RpcServer() {
|
||||
m_active = false;
|
||||
m_terminating = false;
|
||||
}
|
||||
|
||||
RpcServer::~RpcServer() {
|
||||
Stop();
|
||||
m_terminating = true;
|
||||
m_poll_cond.notify_all();
|
||||
}
|
||||
|
||||
void RpcServer::Start() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (m_active) return;
|
||||
m_active = true;
|
||||
}
|
||||
m_thread = std::thread(&RpcServer::ThreadMain, this);
|
||||
}
|
||||
|
||||
void RpcServer::Stop() {
|
||||
m_active = false;
|
||||
if (m_thread.joinable()) {
|
||||
// send notification so the thread terminates
|
||||
m_call_cond.notify_one();
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void RpcServer::ProcessRpc(StringRef name, std::shared_ptr<Message> msg,
|
||||
RpcCallback func, unsigned int conn_id,
|
||||
SendMsgFunc send_response) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
if (func)
|
||||
m_call_queue.emplace(name, msg, func, conn_id, send_response);
|
||||
else
|
||||
m_poll_queue.emplace(name, msg, func, conn_id, send_response);
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (func)
|
||||
m_call_cond.notify_one();
|
||||
else
|
||||
m_poll_cond.notify_one();
|
||||
}
|
||||
|
||||
bool RpcServer::PollRpc(bool blocking, RpcCallInfo* call_info) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
while (m_poll_queue.empty()) {
|
||||
if (!blocking || m_terminating) return false;
|
||||
m_poll_cond.wait(lock);
|
||||
}
|
||||
|
||||
auto& item = m_poll_queue.front();
|
||||
unsigned int call_uid = (item.conn_id << 16) | item.msg->seq_num_uid();
|
||||
call_info->rpc_id = item.msg->id();
|
||||
call_info->call_uid = call_uid;
|
||||
call_info->name = std::move(item.name);
|
||||
call_info->params = item.msg->str();
|
||||
m_response_map.insert(std::make_pair(std::make_pair(item.msg->id(), call_uid),
|
||||
item.send_response));
|
||||
m_poll_queue.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
void RpcServer::PostRpcResponse(unsigned int rpc_id, unsigned int call_uid,
|
||||
llvm::StringRef result) {
|
||||
auto i = m_response_map.find(std::make_pair(rpc_id, call_uid));
|
||||
if (i == m_response_map.end()) {
|
||||
WARNING("posting RPC response to nonexistent call (or duplicate response)");
|
||||
return;
|
||||
}
|
||||
(i->getSecond())(Message::RpcResponse(rpc_id, call_uid, result));
|
||||
m_response_map.erase(i);
|
||||
}
|
||||
|
||||
void RpcServer::ThreadMain() {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
std::string tmp;
|
||||
while (m_active) {
|
||||
while (m_call_queue.empty()) {
|
||||
m_call_cond.wait(lock);
|
||||
if (!m_active) return;
|
||||
}
|
||||
|
||||
while (!m_call_queue.empty()) {
|
||||
auto item = std::move(m_call_queue.front());
|
||||
m_call_queue.pop();
|
||||
|
||||
DEBUG4("rpc calling " << item.name);
|
||||
|
||||
if (item.name.empty() || !item.msg || !item.func || !item.send_response)
|
||||
continue;
|
||||
|
||||
// Don't hold mutex during callback execution!
|
||||
lock.unlock();
|
||||
auto result = item.func(item.name, item.msg->str());
|
||||
item.send_response(Message::RpcResponse(item.msg->id(),
|
||||
item.msg->seq_num_uid(), result));
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
88
src/RpcServer.h
Normal file
88
src/RpcServer.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NT_RPCSERVER_H_
|
||||
#define NT_RPCSERVER_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "llvm/DenseMap.h"
|
||||
#include "atomic_static.h"
|
||||
#include "Message.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class RpcServer {
|
||||
friend class RpcServerTest;
|
||||
public:
|
||||
static RpcServer& GetInstance() {
|
||||
ATOMIC_STATIC(RpcServer, instance);
|
||||
return instance;
|
||||
}
|
||||
~RpcServer();
|
||||
|
||||
typedef std::function<void(std::shared_ptr<Message>)> SendMsgFunc;
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
bool active() const { return m_active; }
|
||||
|
||||
void ProcessRpc(StringRef name, std::shared_ptr<Message> msg,
|
||||
RpcCallback func, unsigned int conn_id,
|
||||
SendMsgFunc send_response);
|
||||
|
||||
bool PollRpc(bool blocking, RpcCallInfo* call_info);
|
||||
void PostRpcResponse(unsigned int rpc_id, unsigned int call_uid,
|
||||
llvm::StringRef result);
|
||||
|
||||
private:
|
||||
RpcServer();
|
||||
|
||||
void ThreadMain();
|
||||
|
||||
std::atomic_bool m_active;
|
||||
std::atomic_bool m_terminating;
|
||||
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_call_cond, m_poll_cond;
|
||||
|
||||
struct RpcCall {
|
||||
RpcCall(StringRef name_, std::shared_ptr<Message> msg_, RpcCallback func_,
|
||||
unsigned int conn_id_, SendMsgFunc send_response_)
|
||||
: name(name_),
|
||||
msg(msg_),
|
||||
func(func_),
|
||||
conn_id(conn_id_),
|
||||
send_response(send_response_) {}
|
||||
|
||||
std::string name;
|
||||
std::shared_ptr<Message> msg;
|
||||
RpcCallback func;
|
||||
unsigned int conn_id;
|
||||
SendMsgFunc send_response;
|
||||
};
|
||||
std::queue<RpcCall> m_call_queue, m_poll_queue;
|
||||
|
||||
llvm::DenseMap<std::pair<unsigned int, unsigned int>, SendMsgFunc>
|
||||
m_response_map;
|
||||
|
||||
std::thread m_thread;
|
||||
|
||||
ATOMIC_STATIC_DECL(RpcServer)
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NT_RPCSERVER_H_
|
||||
181
src/Storage.cpp
181
src/Storage.cpp
@@ -20,9 +20,15 @@ using namespace nt;
|
||||
|
||||
ATOMIC_STATIC_INIT(Storage)
|
||||
|
||||
Storage::Storage(Notifier& notifier) : m_notifier(notifier) {}
|
||||
Storage::Storage(Notifier& notifier, RpcServer& rpc_server)
|
||||
: m_notifier(notifier), m_rpc_server(rpc_server) {
|
||||
m_terminating = false;
|
||||
}
|
||||
|
||||
Storage::~Storage() {}
|
||||
Storage::~Storage() {
|
||||
m_terminating = true;
|
||||
m_rpc_results_cond.notify_all();
|
||||
}
|
||||
|
||||
void Storage::SetOutgoing(QueueOutgoingFunc queue_outgoing, bool server) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
@@ -43,7 +49,8 @@ NT_Type Storage::GetEntryType(unsigned int id) const {
|
||||
}
|
||||
|
||||
void Storage::ProcessIncoming(std::shared_ptr<Message> msg,
|
||||
NetworkConnection* conn) {
|
||||
NetworkConnection* conn,
|
||||
std::weak_ptr<NetworkConnection> conn_weak) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
switch (msg->type()) {
|
||||
case Message::kKeepAlive:
|
||||
@@ -267,10 +274,36 @@ void Storage::ProcessIncoming(std::shared_ptr<Message> msg,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message::kExecuteRpc:
|
||||
case Message::kRpcResponse:
|
||||
// TODO
|
||||
case Message::kExecuteRpc: {
|
||||
if (!m_server) return; // only process on server
|
||||
unsigned int id = msg->id();
|
||||
if (id >= m_idmap.size() || !m_idmap[id]) {
|
||||
// ignore call to non-existent RPC
|
||||
// this can happen due to deleted entries
|
||||
lock.unlock();
|
||||
DEBUG("received RPC call to unknown entry");
|
||||
return;
|
||||
}
|
||||
Entry* entry = m_idmap[id];
|
||||
if (!entry->value->IsRpc()) {
|
||||
lock.unlock();
|
||||
DEBUG("received RPC call to non-RPC entry");
|
||||
return;
|
||||
}
|
||||
m_rpc_server.ProcessRpc(entry->name, msg, entry->rpc_callback,
|
||||
conn->uid(), [=](std::shared_ptr<Message> msg) {
|
||||
auto c = conn_weak.lock();
|
||||
if (c) c->QueueOutgoing(msg);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Message::kRpcResponse: {
|
||||
if (m_server) return; // only process on client
|
||||
m_rpc_results.insert(std::make_pair(
|
||||
std::make_pair(msg->id(), msg->seq_num_uid()), msg->str()));
|
||||
m_rpc_results_cond.notify_all();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -478,10 +511,11 @@ void Storage::DeleteEntry(StringRef name) {
|
||||
if (i == m_entries.end()) return;
|
||||
Entry* entry = i->getValue().get();
|
||||
unsigned int id = entry->id;
|
||||
bool had_value = entry->value != nullptr;
|
||||
m_entries.erase(i); // erase from map
|
||||
if (id < m_idmap.size()) m_idmap[id] = nullptr;
|
||||
|
||||
if (!entry->value) return;
|
||||
if (!had_value) return;
|
||||
|
||||
// if it had a value, generate message
|
||||
// don't send an update if we don't have an assigned id yet
|
||||
@@ -1003,3 +1037,136 @@ next_line:
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Storage::CreateRpc(StringRef name, StringRef def, RpcCallback callback) {
|
||||
if (name.empty() || def.empty() || !callback) return;
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
if (!m_server) return; // only server can create RPCs
|
||||
|
||||
auto& new_entry = m_entries[name];
|
||||
if (!new_entry) new_entry.reset(new Entry(name));
|
||||
Entry* entry = new_entry.get();
|
||||
auto old_value = entry->value;
|
||||
auto value = Value::MakeRpc(def);
|
||||
entry->value = value;
|
||||
|
||||
// set up the new callback
|
||||
entry->rpc_callback = callback;
|
||||
|
||||
// start the RPC server
|
||||
if (!m_rpc_server.active()) m_rpc_server.Start();
|
||||
|
||||
if (old_value && *old_value == *value) return;
|
||||
|
||||
// assign an id if it doesn't have one
|
||||
if (entry->id == 0xffff) {
|
||||
unsigned int id = m_idmap.size();
|
||||
entry->id = id;
|
||||
m_idmap.push_back(entry);
|
||||
}
|
||||
|
||||
// generate message
|
||||
if (!m_queue_outgoing) return;
|
||||
auto queue_outgoing = m_queue_outgoing;
|
||||
if (!old_value || old_value->type() != value->type()) {
|
||||
++entry->seq_num;
|
||||
auto msg = Message::EntryAssign(name, entry->id, entry->seq_num.value(),
|
||||
value, entry->flags);
|
||||
lock.unlock();
|
||||
queue_outgoing(msg, nullptr, nullptr);
|
||||
} else {
|
||||
++entry->seq_num;
|
||||
auto msg = Message::EntryUpdate(entry->id, entry->seq_num.value(), value);
|
||||
lock.unlock();
|
||||
queue_outgoing(msg, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Storage::CreatePolledRpc(StringRef name, StringRef def) {
|
||||
if (name.empty() || def.empty()) return;
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
if (!m_server) return; // only server can create RPCs
|
||||
|
||||
auto& new_entry = m_entries[name];
|
||||
if (!new_entry) new_entry.reset(new Entry(name));
|
||||
Entry* entry = new_entry.get();
|
||||
auto old_value = entry->value;
|
||||
auto value = Value::MakeRpc(def);
|
||||
entry->value = value;
|
||||
|
||||
// a nullptr callback indicates a polled RPC
|
||||
entry->rpc_callback = nullptr;
|
||||
|
||||
if (old_value && *old_value == *value) return;
|
||||
|
||||
// assign an id if it doesn't have one
|
||||
if (entry->id == 0xffff) {
|
||||
unsigned int id = m_idmap.size();
|
||||
entry->id = id;
|
||||
m_idmap.push_back(entry);
|
||||
}
|
||||
|
||||
// generate message
|
||||
if (!m_queue_outgoing) return;
|
||||
auto queue_outgoing = m_queue_outgoing;
|
||||
if (!old_value || old_value->type() != value->type()) {
|
||||
++entry->seq_num;
|
||||
auto msg = Message::EntryAssign(name, entry->id, entry->seq_num.value(),
|
||||
value, entry->flags);
|
||||
lock.unlock();
|
||||
queue_outgoing(msg, nullptr, nullptr);
|
||||
} else {
|
||||
++entry->seq_num;
|
||||
auto msg = Message::EntryUpdate(entry->id, entry->seq_num.value(), value);
|
||||
lock.unlock();
|
||||
queue_outgoing(msg, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Storage::CallRpc(StringRef name, StringRef params) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
auto i = m_entries.find(name);
|
||||
if (i == m_entries.end()) return 0;
|
||||
auto& entry = i->getValue();
|
||||
if (!entry->value->IsRpc()) return 0;
|
||||
|
||||
++entry->rpc_call_uid;
|
||||
if (entry->rpc_call_uid > 0xffff) entry->rpc_call_uid = 0;
|
||||
unsigned int combined_uid = (entry->id << 16) | entry->rpc_call_uid;
|
||||
auto msg = Message::ExecuteRpc(entry->id, entry->rpc_call_uid, params);
|
||||
if (m_server) {
|
||||
// RPCs are unlikely to be used locally on the server, but handle it
|
||||
// gracefully anyway.
|
||||
auto rpc_callback = entry->rpc_callback;
|
||||
lock.unlock();
|
||||
m_rpc_server.ProcessRpc(
|
||||
name, msg, rpc_callback, 0xffffU, [this](std::shared_ptr<Message> msg) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_rpc_results.insert(std::make_pair(
|
||||
std::make_pair(msg->id(), msg->seq_num_uid()), msg->str()));
|
||||
m_rpc_results_cond.notify_all();
|
||||
});
|
||||
} else {
|
||||
auto queue_outgoing = m_queue_outgoing;
|
||||
lock.unlock();
|
||||
queue_outgoing(msg, nullptr, nullptr);
|
||||
}
|
||||
return combined_uid;
|
||||
}
|
||||
|
||||
bool Storage::GetRpcResult(bool blocking, unsigned int call_uid,
|
||||
std::string* result) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
for (;;) {
|
||||
auto i =
|
||||
m_rpc_results.find(std::make_pair(call_uid >> 16, call_uid & 0xffff));
|
||||
if (i == m_rpc_results.end()) {
|
||||
if (!blocking || m_terminating) return false;
|
||||
m_rpc_results_cond.wait(lock);
|
||||
continue;
|
||||
}
|
||||
result->swap(i->getSecond());
|
||||
m_rpc_results.erase(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "llvm/DenseMap.h"
|
||||
#include "llvm/StringMap.h"
|
||||
#include "atomic_static.h"
|
||||
#include "Message.h"
|
||||
#include "Notifier.h"
|
||||
#include "ntcore_cpp.h"
|
||||
#include "RpcServer.h"
|
||||
#include "SequenceNumber.h"
|
||||
|
||||
namespace nt {
|
||||
@@ -46,7 +48,8 @@ class Storage {
|
||||
|
||||
NT_Type GetEntryType(unsigned int id) const;
|
||||
|
||||
void ProcessIncoming(std::shared_ptr<Message> msg, NetworkConnection* conn);
|
||||
void ProcessIncoming(std::shared_ptr<Message> msg, NetworkConnection* conn,
|
||||
std::weak_ptr<NetworkConnection> conn_weak);
|
||||
void GetInitialAssignments(NetworkConnection& conn,
|
||||
std::vector<std::shared_ptr<Message>>* msgs);
|
||||
void ApplyInitialAssignments(NetworkConnection& conn,
|
||||
@@ -72,14 +75,23 @@ class Storage {
|
||||
std::istream& is,
|
||||
std::function<void(std::size_t line, const char* msg)> warn);
|
||||
|
||||
// RPC configuration needs to come through here as RPC definitions are
|
||||
// actually special Storage value types.
|
||||
void CreateRpc(StringRef name, StringRef def, RpcCallback callback);
|
||||
void CreatePolledRpc(StringRef name, StringRef def);
|
||||
|
||||
unsigned int CallRpc(StringRef name, StringRef params);
|
||||
bool GetRpcResult(bool blocking, unsigned int call_uid, std::string* result);
|
||||
|
||||
private:
|
||||
Storage() : Storage(Notifier::GetInstance()) {}
|
||||
Storage(Notifier& notifier);
|
||||
Storage() : Storage(Notifier::GetInstance(), RpcServer::GetInstance()) {}
|
||||
Storage(Notifier& notifier, RpcServer& rpcserver);
|
||||
Storage(const Storage&) = delete;
|
||||
Storage& operator=(const Storage&) = delete;
|
||||
|
||||
struct Entry {
|
||||
Entry(llvm::StringRef name_) : name(name_), flags(0), id(0xffff) {}
|
||||
Entry(llvm::StringRef name_)
|
||||
: name(name_), flags(0), id(0xffff), rpc_call_uid(0) {}
|
||||
bool IsPersistent() const { return (flags & NT_PERSISTENT) != 0; }
|
||||
|
||||
std::string name;
|
||||
@@ -87,18 +99,26 @@ class Storage {
|
||||
unsigned int flags;
|
||||
unsigned int id;
|
||||
SequenceNumber seq_num;
|
||||
RpcCallback rpc_callback;
|
||||
unsigned int rpc_call_uid;
|
||||
};
|
||||
|
||||
typedef llvm::StringMap<std::unique_ptr<Entry>> EntriesMap;
|
||||
typedef std::vector<Entry*> IdMap;
|
||||
typedef llvm::DenseMap<std::pair<unsigned int, unsigned int>, std::string>
|
||||
RpcResultMap;
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
EntriesMap m_entries;
|
||||
IdMap m_idmap;
|
||||
RpcResultMap m_rpc_results;
|
||||
std::atomic_bool m_terminating;
|
||||
std::condition_variable m_rpc_results_cond;
|
||||
|
||||
QueueOutgoingFunc m_queue_outgoing;
|
||||
bool m_server;
|
||||
bool m_server = true;
|
||||
Notifier& m_notifier;
|
||||
RpcServer& m_rpc_server;
|
||||
|
||||
ATOMIC_STATIC_DECL(Storage)
|
||||
};
|
||||
|
||||
198
src/ntcore_c.cpp
198
src/ntcore_c.cpp
@@ -37,6 +37,41 @@ static void ConvertToC(const ConnectionInfo& in, NT_ConnectionInfo* out) {
|
||||
out->protocol_version = in.protocol_version;
|
||||
}
|
||||
|
||||
static void ConvertToC(const RpcParamDef& in, NT_RpcParamDef* out) {
|
||||
ConvertToC(in.name, &out->name);
|
||||
NT_InitValue(&out->def_value);
|
||||
ConvertToC(*in.def_value, &out->def_value);
|
||||
}
|
||||
|
||||
static void ConvertToC(const RpcResultDef& in, NT_RpcResultDef* out) {
|
||||
ConvertToC(in.name, &out->name);
|
||||
out->type = in.type;
|
||||
}
|
||||
|
||||
static void ConvertToC(const RpcDefinition& in, NT_RpcDefinition* out) {
|
||||
out->version = in.version;
|
||||
ConvertToC(in.name, &out->name);
|
||||
|
||||
out->num_params = in.params.size();
|
||||
out->params = static_cast<NT_RpcParamDef*>(
|
||||
std::malloc(in.params.size() * sizeof(NT_RpcParamDef)));
|
||||
for (size_t i = 0; i < in.params.size(); ++i)
|
||||
ConvertToC(in.params[i], &out->params[i]);
|
||||
|
||||
out->num_results = in.results.size();
|
||||
out->results = static_cast<NT_RpcResultDef*>(
|
||||
std::malloc(in.results.size() * sizeof(NT_RpcResultDef)));
|
||||
for (size_t i = 0; i < in.results.size(); ++i)
|
||||
ConvertToC(in.results[i], &out->results[i]);
|
||||
}
|
||||
|
||||
static void ConvertToC(const RpcCallInfo& in, NT_RpcCallInfo* out) {
|
||||
out->rpc_id = in.rpc_id;
|
||||
out->call_uid = in.call_uid;
|
||||
ConvertToC(in.name, &out->name);
|
||||
ConvertToC(in.params, &out->params);
|
||||
}
|
||||
|
||||
static void DisposeConnectionInfo(NT_ConnectionInfo *info) {
|
||||
std::free(info->remote_id.str);
|
||||
std::free(info->remote_name);
|
||||
@@ -164,72 +199,108 @@ void NT_RemoveConnectionListener(unsigned int conn_listener_uid) {
|
||||
* Remote Procedure Call Functions
|
||||
*/
|
||||
|
||||
unsigned int NT_CreateRpc(const char *name, size_t name_len,
|
||||
const NT_RpcDefinition *def, void *data,
|
||||
NT_RpcCallback callback) {
|
||||
return nt::CreateRpc(
|
||||
StringRef(name, name_len),
|
||||
ConvertFromC(*def),
|
||||
[=](unsigned int uid, StringRef name,
|
||||
ArrayRef<std::shared_ptr<Value>> params)
|
||||
-> std::vector<std::shared_ptr<Value>> {
|
||||
// convert params to NT_Value* array
|
||||
std::vector<const NT_Value*> params_c(params.size());
|
||||
for (size_t i = 0; i < params.size(); ++i)
|
||||
params_c[i] = ¶ms[i]->value();
|
||||
|
||||
void NT_CreateRpc(const char *name, size_t name_len, const char *def,
|
||||
size_t def_len, void *data, NT_RpcCallback callback) {
|
||||
nt::CreateRpc(
|
||||
StringRef(name, name_len), StringRef(def, def_len),
|
||||
[=](StringRef name, StringRef params) -> std::string {
|
||||
size_t results_len;
|
||||
NT_Value** results_c = callback(uid, data, name.data(), name.size(),
|
||||
params_c.data(), params.size(),
|
||||
&results_len);
|
||||
|
||||
// convert results to Value array
|
||||
std::vector<std::shared_ptr<Value>> results;
|
||||
results.reserve(results_len);
|
||||
for (size_t i = 0; i < results_len; ++i)
|
||||
results.push_back(ConvertFromC(*results_c[i]));
|
||||
|
||||
// dispose the C version
|
||||
for (size_t i = 0; i < results_len; ++i) {
|
||||
NT_DisposeValue(results_c[i]);
|
||||
std::free(results_c[i]);
|
||||
}
|
||||
char* results_c = callback(data, name.data(), name.size(),
|
||||
params.data(), params.size(), &results_len);
|
||||
std::string results(results_c, results_len);
|
||||
std::free(results_c);
|
||||
|
||||
return results;
|
||||
});
|
||||
}
|
||||
|
||||
void NT_DeleteRpc(unsigned int rpc_uid) {
|
||||
nt::DeleteRpc(rpc_uid);
|
||||
void NT_CreatePolledRpc(const char *name, size_t name_len, const char *def,
|
||||
size_t def_len) {
|
||||
nt::CreatePolledRpc(StringRef(name, name_len), StringRef(def, def_len));
|
||||
}
|
||||
|
||||
int NT_PollRpc(int blocking, NT_RpcCallInfo* call_info) {
|
||||
RpcCallInfo call_info_cpp;
|
||||
if (!nt::PollRpc(blocking, &call_info_cpp))
|
||||
return 0;
|
||||
ConvertToC(call_info_cpp, call_info);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void NT_PostRpcResponse(unsigned int rpc_id, unsigned int call_uid,
|
||||
const char *result, size_t result_len) {
|
||||
nt::PostRpcResponse(rpc_id, call_uid, StringRef(result, result_len));
|
||||
}
|
||||
|
||||
unsigned int NT_CallRpc(const char *name, size_t name_len,
|
||||
const NT_Value **params, size_t params_len) {
|
||||
// create input vector
|
||||
std::vector<std::shared_ptr<Value>> params_v;
|
||||
params_v.reserve(params_len);
|
||||
for (size_t i = 0; i < params_len; ++i)
|
||||
params_v.push_back(ConvertFromC(*params[i]));
|
||||
|
||||
// make the call
|
||||
return nt::CallRpc(StringRef(name, name_len), params_v);
|
||||
const char *params, size_t params_len) {
|
||||
return nt::CallRpc(StringRef(name, name_len), StringRef(params, params_len));
|
||||
}
|
||||
|
||||
NT_Value **NT_GetRpcResult(unsigned int result_uid, size_t *results_len) {
|
||||
auto results_v = nt::GetRpcResult(result_uid);
|
||||
*results_len = results_v.size();
|
||||
if (results_v.size() == 0) return nullptr;
|
||||
char *NT_GetRpcResult(int blocking, unsigned int call_uid, size_t *result_len) {
|
||||
std::string result;
|
||||
if (!nt::GetRpcResult(blocking, call_uid, &result)) return nullptr;
|
||||
|
||||
// convert result
|
||||
*result_len = result.size();
|
||||
char *result_cstr;
|
||||
ConvertToC(result, &result_cstr);
|
||||
return result_cstr;
|
||||
}
|
||||
|
||||
char *NT_PackRpcDefinition(const NT_RpcDefinition *def, size_t *packed_len) {
|
||||
auto packed = nt::PackRpcDefinition(ConvertFromC(*def));
|
||||
|
||||
// convert result
|
||||
*packed_len = packed.size();
|
||||
char *packed_cstr;
|
||||
ConvertToC(packed, &packed_cstr);
|
||||
return packed_cstr;
|
||||
}
|
||||
|
||||
int NT_UnpackRpcDefinition(const char *packed, size_t packed_len,
|
||||
NT_RpcDefinition *def) {
|
||||
nt::RpcDefinition def_v;
|
||||
if (!nt::UnpackRpcDefinition(StringRef(packed, packed_len), &def_v))
|
||||
return 0;
|
||||
|
||||
// convert result
|
||||
ConvertToC(def_v, def);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *NT_PackRpcValues(const NT_Value **values, size_t values_len,
|
||||
size_t *packed_len) {
|
||||
// create input vector
|
||||
std::vector<std::shared_ptr<Value>> values_v;
|
||||
values_v.reserve(values_len);
|
||||
for (size_t i = 0; i < values_len; ++i)
|
||||
values_v.push_back(ConvertFromC(*values[i]));
|
||||
|
||||
// make the call
|
||||
auto packed = nt::PackRpcValues(values_v);
|
||||
|
||||
// convert result
|
||||
*packed_len = packed.size();
|
||||
char *packed_cstr;
|
||||
ConvertToC(packed, &packed_cstr);
|
||||
return packed_cstr;
|
||||
}
|
||||
|
||||
NT_Value **NT_UnpackRpcValues(const char *packed, size_t packed_len,
|
||||
const NT_Type *types, size_t types_len) {
|
||||
auto values_v = nt::UnpackRpcValues(StringRef(packed, packed_len),
|
||||
ArrayRef<NT_Type>(types, types_len));
|
||||
if (values_v.size() == 0) return nullptr;
|
||||
|
||||
// create array and copy into it
|
||||
NT_Value** results = static_cast<NT_Value**>(
|
||||
std::malloc(results_v.size() * sizeof(NT_Value*)));
|
||||
for (size_t i = 0; i < results_v.size(); ++i) {
|
||||
results[i] = static_cast<NT_Value*>(std::malloc(sizeof(NT_Value)));
|
||||
NT_InitValue(results[i]);
|
||||
ConvertToC(*results_v[i], results[i]);
|
||||
NT_Value** values = static_cast<NT_Value**>(
|
||||
std::malloc(values_v.size() * sizeof(NT_Value*)));
|
||||
for (size_t i = 0; i < values_v.size(); ++i) {
|
||||
values[i] = static_cast<NT_Value*>(std::malloc(sizeof(NT_Value)));
|
||||
NT_InitValue(values[i]);
|
||||
ConvertToC(*values_v[i], values[i]);
|
||||
}
|
||||
return results;
|
||||
return values;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -342,3 +413,26 @@ void NT_DisposeConnectionInfoArray(NT_ConnectionInfo *arr, size_t count) {
|
||||
for (size_t i = 0; i < count; i++) DisposeConnectionInfo(&arr[i]);
|
||||
std::free(arr);
|
||||
}
|
||||
|
||||
void NT_DisposeRpcDefinition(NT_RpcDefinition *def) {
|
||||
NT_DisposeString(&def->name);
|
||||
|
||||
for (size_t i = 0; i < def->num_params; ++i) {
|
||||
NT_DisposeString(&def->params[i].name);
|
||||
NT_DisposeValue(&def->params[i].def_value);
|
||||
}
|
||||
std::free(def->params);
|
||||
def->params = nullptr;
|
||||
def->num_params = 0;
|
||||
|
||||
for (size_t i = 0; i < def->num_results; ++i)
|
||||
NT_DisposeString(&def->results[i].name);
|
||||
std::free(def->results);
|
||||
def->results = nullptr;
|
||||
def->num_results = 0;
|
||||
}
|
||||
|
||||
void NT_DisposeRpcCallInfo(NT_RpcCallInfo *call_info) {
|
||||
NT_DisposeString(&call_info->name);
|
||||
NT_DisposeString(&call_info->params);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
#include "Dispatcher.h"
|
||||
#include "Log.h"
|
||||
#include "Notifier.h"
|
||||
#include "RpcServer.h"
|
||||
#include "Storage.h"
|
||||
#include "WireDecoder.h"
|
||||
#include "WireEncoder.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
@@ -91,20 +94,111 @@ void RemoveConnectionListener(unsigned int conn_listener_uid) {
|
||||
* Remote Procedure Call Functions
|
||||
*/
|
||||
|
||||
unsigned int CreateRpc(StringRef name, const RpcDefinition& def,
|
||||
RpcCallback callback) {
|
||||
return 0;
|
||||
void CreateRpc(StringRef name, StringRef def, RpcCallback callback) {
|
||||
Storage::GetInstance().CreateRpc(name, def, callback);
|
||||
}
|
||||
|
||||
void DeleteRpc(unsigned int rpc_uid) {}
|
||||
|
||||
unsigned int CallRpc(StringRef name,
|
||||
ArrayRef<std::shared_ptr<Value>> params) {
|
||||
return 0;
|
||||
void CreatePolledRpc(StringRef name, StringRef def) {
|
||||
Storage::GetInstance().CreatePolledRpc(name, def);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Value>> GetRpcResult(unsigned int result_uid) {
|
||||
return std::vector<std::shared_ptr<Value>>();
|
||||
bool PollRpc(bool blocking, RpcCallInfo* call_info) {
|
||||
return RpcServer::GetInstance().PollRpc(blocking, call_info);
|
||||
}
|
||||
|
||||
void PostRpcResponse(unsigned int rpc_id, unsigned int call_uid,
|
||||
StringRef result) {
|
||||
RpcServer::GetInstance().PostRpcResponse(rpc_id, call_uid, result);
|
||||
}
|
||||
|
||||
unsigned int CallRpc(StringRef name, StringRef params) {
|
||||
return Storage::GetInstance().CallRpc(name, params);
|
||||
}
|
||||
|
||||
bool GetRpcResult(bool blocking, unsigned int call_uid, std::string* result) {
|
||||
return Storage::GetInstance().GetRpcResult(blocking, call_uid, result);
|
||||
}
|
||||
|
||||
std::string PackRpcDefinition(const RpcDefinition& def) {
|
||||
WireEncoder enc(0x0300);
|
||||
enc.Write8(def.version);
|
||||
enc.WriteString(def.name);
|
||||
|
||||
// parameters
|
||||
unsigned int params_size = def.params.size();
|
||||
if (params_size > 0xff) params_size = 0xff;
|
||||
enc.Write8(params_size);
|
||||
for (std::size_t i = 0; i < params_size; ++i) {
|
||||
enc.WriteType(def.params[i].def_value->type());
|
||||
enc.WriteString(def.params[i].name);
|
||||
enc.WriteValue(*def.params[i].def_value);
|
||||
}
|
||||
|
||||
// results
|
||||
unsigned int results_size = def.results.size();
|
||||
if (results_size > 0xff) results_size = 0xff;
|
||||
enc.Write8(results_size);
|
||||
for (std::size_t i = 0; i < results_size; ++i) {
|
||||
enc.WriteType(def.results[i].type);
|
||||
enc.WriteString(def.results[i].name);
|
||||
}
|
||||
|
||||
return enc.ToStringRef();
|
||||
}
|
||||
|
||||
bool UnpackRpcDefinition(StringRef packed, RpcDefinition* def) {
|
||||
raw_mem_istream is(packed.data(), packed.size());
|
||||
WireDecoder dec(is, 0x0300);
|
||||
if (!dec.Read8(&def->version)) return false;
|
||||
if (!dec.ReadString(&def->name)) return false;
|
||||
|
||||
// parameters
|
||||
unsigned int params_size;
|
||||
if (!dec.Read8(¶ms_size)) return false;
|
||||
def->params.resize(0);
|
||||
def->params.reserve(params_size);
|
||||
for (std::size_t i = 0; i < params_size; ++i) {
|
||||
RpcParamDef pdef;
|
||||
NT_Type type;
|
||||
if (!dec.ReadType(&type)) return false;
|
||||
if (!dec.ReadString(&pdef.name)) return false;
|
||||
pdef.def_value = dec.ReadValue(type);
|
||||
if (!pdef.def_value) return false;
|
||||
def->params.emplace_back(std::move(pdef));
|
||||
}
|
||||
|
||||
// results
|
||||
unsigned int results_size;
|
||||
if (!dec.Read8(&results_size)) return false;
|
||||
def->results.resize(0);
|
||||
def->results.reserve(results_size);
|
||||
for (std::size_t i = 0; i < results_size; ++i) {
|
||||
RpcResultDef rdef;
|
||||
if (!dec.ReadType(&rdef.type)) return false;
|
||||
if (!dec.ReadString(&rdef.name)) return false;
|
||||
def->results.emplace_back(std::move(rdef));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string PackRpcValues(ArrayRef<std::shared_ptr<Value>> values) {
|
||||
WireEncoder enc(0x0300);
|
||||
for (auto& value : values) enc.WriteValue(*value);
|
||||
return enc.ToStringRef();
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Value>> UnpackRpcValues(StringRef packed,
|
||||
ArrayRef<NT_Type> types) {
|
||||
raw_mem_istream is(packed.data(), packed.size());
|
||||
WireDecoder dec(is, 0x0300);
|
||||
std::vector<std::shared_ptr<Value>> vec;
|
||||
for (auto type : types) {
|
||||
auto item = dec.ReadValue(type);
|
||||
if (!item) return std::vector<std::shared_ptr<Value>>();
|
||||
vec.emplace_back(std::move(item));
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user