CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
RequestExecutor.cpp
Go to the documentation of this file.
1
6#include "RequestExecutor.h"
7
8namespace cmlabs{
9
11// Request Executor
13
14RequestExecutor::RequestExecutor(uint32 id, const char* name) {
15 executorID = id;
16 if (name && id)
17 executorName = utils::StringFormat("%s:%u", name, id);
18 else if (name)
19 executorName = name;
20 else if (id)
21 executorName = utils::StringFormat("Executor:%u", id);
22 else
23 executorName = "";
24
25 lastRefID = 0;
26 sentCount = 0;
27 repliedCount = 0;
31 lastHeartbeat = 0;
32 shouldContinue = true;
33 isRunning = false;
34 channel = NULL;
35 manager = new NetworkManager();
36 longReqLimit = 0;
37
39 shouldContinue = false;
40 }
41}
42
44 stop();
46
47 std::map<uint64, RequestReply*>::iterator i, e;
48 mutex.enter(200, __FUNCTION__);
49 for (i = requestMap.begin(), e = requestMap.end(); i != e; i++)
50 delete(i->second);
51 requestMap.clear();
52
53 longExecQ.clear();
54 shortExecQ.clear();
55
56 delete(manager);
57 mutex.leave();
58}
59
61 if (channel)
62 channel->shutdown();
63 return true;
64}
65
67 if (channel)
68 return false;
69 longReqNames.push_back(name);
70 return true;
71}
72
73bool RequestExecutor::addGateway(uint32 id, std::string addr, uint16 port, uint8 encryption) {
75 con.clear();
76 con.id = id;
77 con.addr = addr;
78 con.port = port;
79 con.encryption = encryption;
80
81 if (!mutex.enter(200, __FUNCTION__))
82 return false;
83
84 DataMessage* msgConnect = new DataMessage();
85 msgConnect->setString("URI", "ExecutorConnect");
86 msgConnect->setString("LongRequests", utils::TextVectorConcat(longReqNames, ",", false).c_str());
87
88 if (!channel)
89 channel = manager->addTCPConnection(con.addr.c_str(), con.port, con.encryption, PROTOCOL_MESSAGE, true, 0, this, con.conID, con.location, 1000, (const char*)msgConnect->data, msgConnect->getSize());
90 else
91 con.conID = channel->addTCPConnection(con.addr.c_str(), con.port, con.encryption, PROTOCOL_MESSAGE, true, con.location, 1000, (const char*)msgConnect->data, msgConnect->getSize());
92
93 delete msgConnect;
94
95 connections.push_back(con);
96 mutex.leave();
97 return true;
98
99 //if (!con.conID) {
100 // mutex.leave();
101 // return false;
102 //}
103
104
105
106 //RequestGatewayConnection con;
107 //con.clear();
108 //con.id = id;
109 //con.addr = addr;
110 //con.port = port;
111 //con.encryption = encryption;
112
113 //if (!mutex.enter(200, __FUNCTION__))
114 // return false;
115
116 //if (!channel)
117 // channel = manager->createTCPConnection(con.addr.c_str(), con.port, con.encryption, PROTOCOL_MESSAGE, true, true, con.id, this, con.conID, con.location);
118 //else
119 // con.conID = channel->createTCPConnection(con.addr.c_str(), con.port, con.encryption, PROTOCOL_MESSAGE, true, true, con.location);
120
121 //if (!con.conID) {
122 // mutex.leave();
123 // return false;
124 //}
125
127 //DataMessage* msg = new DataMessage();
128 //msg->setString("URI", "ExecutorConnect");
129 //msg->setString("LongRequests", utils::TextVectorConcat(longReqNames, ",", false).c_str());
130 //if (!channel->sendMessage(msg, con.conID)) {
131 // delete(msg);
132 // mutex.leave();
133 // return false;
134 //}
135 //
136 //delete(msg);
137 //gateways[con.conID] = con;
138 //mutex.leave();
139 //return true;
140}
141
142
144 uint64 now = GetTimeNow();
145 std::list<RequestGatewayConnection>::iterator i, e = connections.end();
146 if (!mutex.enter(200, __FUNCTION__))
147 return false;
148 i = connections.begin();
149 while (i != e) {
150 if ((*i).conID == conid) {
151 switch (evt->type) {
154 LogPrint(0, LOG_SYSTEM, 0, "[%s] Disconnected from gateway %u on %s:%u", executorName.c_str(), conid, (*i).addr.c_str(), (*i).port);
155 (*i).lastFailTime = now;
156 //(*i).conID = 0;
157 break;
160 LogPrint(0, LOG_SYSTEM, 0, "[%s] Connected to gateway %u on %s:%u", executorName.c_str(), conid, (*i).addr.c_str(), (*i).port);
161 (*i).lastFailTime = 0;
162 (*i).lastConTime = now;
163 break;
164 default:
165 break;
166 }
167 }
168 i++;
169 }
170 delete evt;
171 mutex.leave();
172 return true;
173
174 //std::map<uint64,RequestGatewayConnection>::iterator i;
175 //if (!mutex.enter(200, __FUNCTION__))
176 // return false;
177 //if ( (i = gateways.find(conid)) != gateways.end()) {
178 // switch(evt->type) {
179 // case NETWORKEVENT_DISCONNECT:
180 // i->second.lastFailTime = now;
181 // break;
182 // case NETWORKEVENT_RECONNECT:
183 // i->second.lastFailTime = 0;
184 // break;
185 // default:
186 // break;
187 // }
188 //}
189 //mutex.leave();
190 //return true;
191}
192
194 longReqLimit = limit;
195 return true;
196}
197
199 // Network-thread ingress: wrap each request in a RequestReply that records
200 // the gateway's reference id (gatewayRef) and connection (origin), restamp
201 // the message with a local execRef used as the requestMap key, then
202 // classify short vs long — by size when longReqLimit is set, otherwise by
203 // substring match against the registered long-request names — and queue it
204 // for the worker threads. Note the queued pointer is the same object owned
205 // by the RequestReply; it is freed when the reply is sent (replyToGateway).
206 if (!msg) return false;
207 RequestReply* reply;
209 bool isLongReq;
210 //std::map<uint64,RequestGatewayConnection>::iterator i;
211 uint64 now = GetTimeNow();
212 const char* req = msg->getString("URI");
213 //LogPrint(0, LOG_SYSTEM, 0, " * Executor received message from Gateway %llu (%llu)", conid, msg->getReference());
214
215 if (!mutex.enter(1000, __FUNCTION__)) {
216 LogPrint(0, LOG_SYSTEM, 0, "Error locking Executor mutex");
217 delete(msg);
218 return true;
219 }
220
221 std::list<RequestGatewayConnection>::iterator i, e = connections.end();
222 i = connections.begin();
223 while (i != e) {
224 if ((*i).conID == conid) {
225 reply = new RequestReply();
226 reply->origin = conid;
227 reply->execRef = ++lastRefID;
228 reply->gatewayRef = msg->getReference();
229 msg->setReference(reply->execRef);
230 reply->giveRequestMessage(msg);
231 reply->setStatus(QUEUED);
232 requestMap[reply->execRef] = reply;
233
234 if (longReqLimit)
235 isLongReq = (msg->getSize() >= longReqLimit);
236 else
237 isLongReq = false;
238 if (!isLongReq) {
239 std::vector<std::string>::iterator il = longReqNames.begin(), el = longReqNames.end();
240 while (il != el) {
241 if (utils::stristr(req, il->c_str())) {
242 isLongReq = true;
243 break;
244 }
245 il++;
246 }
247 }
248 //isLongReq = ( std::find(longReqNames.begin(), longReqNames.end(), req) != longReqNames.end());
249 if (isLongReq) {
250 longExecQ.add(msg);
251 //longExecQSemaphore.signal();
253 }
254 else {
255 shortExecQ.add(msg);
256 //shortExecQSemaphore.signal();
258 }
260 mutex.leave();
261 return true;
262 }
263 i++;
264 }
265
266 delete(msg);
267 mutex.leave();
268 return true;
269}
270
272 DataMessage* msg;
273 if (msg = longExecQ.waitForAndTakeFirst(timeoutMS)) {
274 msg->setSendTime(GetTimeNow());
275 return msg;
276 }
277 else
278 return NULL;
279}
280
282 DataMessage* msg;
283 if (msg = shortExecQ.waitForAndTakeFirst(timeoutMS)) {
284 msg->setSendTime(GetTimeNow());
285 return msg;
286 }
287 else
288 return NULL;
289}
290
292 if (!msg) return false;
293 uint64 execRef = msg->getReference();
294
295 if (!mutex.enter(200, __FUNCTION__))
296 return false;
297
298 std::map<uint64,RequestReply*>::iterator i = requestMap.find(execRef);
299 //std::map<uint64,RequestGatewayConnection>::iterator con;
300
301 if (i == requestMap.end()) {
302 mutex.leave();
303 return false;
304 }
305
306 std::list<RequestGatewayConnection>::iterator iCon, eCon = connections.end();
307 iCon = connections.begin();
308 while (iCon != eCon) {
309 if ((*iCon).conID == i->second->origin) {
310 replyQ.add(msg);
311 repliedCount++;
312 mutex.leave();
313 return true;
314 }
315 iCon++;
316 }
317
318 mutex.leave();
319 return false;
320}
321
322
324 if (!msg)
325 return false;
326 uint64 execRef = msg->getReference();
327
328 if (!mutex.enter(200, __FUNCTION__))
329 return false;
330
331 std::map<uint64,RequestReply*>::iterator i = requestMap.find(execRef);
332 std::map<uint64,RequestGatewayConnection>::iterator con;
333
334 if (i == requestMap.end()) {
335 LogPrint(0, LOG_SYSTEM, 0, "Executor couldn't find request to reply to (%llu)", execRef);
336 mutex.leave();
337 //delete(msg);
338 return false;
339 }
340 RequestReply* reply = i->second;
341
342 std::list<RequestGatewayConnection>::iterator iCon, eCon = connections.end();
343 iCon = connections.begin();
344 while (iCon != eCon) {
345 if ((*iCon).conID == reply->origin) {
346 msg->setReference(reply->gatewayRef);
347 if (sendMessageToGateway(msg, (*iCon))) {
348 // success
349 //delete(msg);
350 requestMap.erase(i);
351 delete(reply);
352 sentCount++;
353 mutex.leave();
354 return true;
355 }
356 else {
357 LogPrint(0, LOG_SYSTEM, 0, " $ Executor couldn't reply to Gateway %llu (%llu)", iCon->conID, reply->gatewayRef);
358 requestMap.erase(i);
359 delete(reply);
360 mutex.leave();
361 return false;
362 }
363 }
364 iCon++;
365 }
366
367 LogPrint(0, LOG_SYSTEM, 0, " $ Executor couldn't find any Gateways to reply to (%llu)", reply->gatewayRef);
368 requestMap.erase(i);
369 delete(reply);
370 mutex.leave();
371 return false;
372}
373
374
376 // Worker loop with two duties: (1) drain replyQ — replies queued by
377 // replyToQuery() from application worker threads — and push each back to
378 // its originating gateway via replyToGateway(), which also erases the
379 // request bookkeeping (deleting the RequestReply and the original request
380 // message); (2) emit an ExecutorStatus heartbeat every heartbeatIntervalMS
381 // so gateways can track liveness and queue depth for load balancing.
382 // Incoming requests do NOT pass through here — they arrive on the network
383 // thread in receiveMessage() and go straight into shortExecQ/longExecQ.
384 DataMessage* msg;
385 isRunning = true;
386
388
389 while (shouldContinue) {
390 while (msg = replyQ.waitForAndTakeFirst(50)) {
391 if (!mutex.enter(200, __FUNCTION__)) {
392 delete(msg);
393 break;
394 }
395 if (!replyToGateway(msg)) {
396 // #######
397 }
398 delete(msg);
399 mutex.leave();
400 }
401
402 // Send status heartbeat
405 }
406 }
407 isRunning = false;
408 return true;
409}
410
411
413
414 uint64 now = GetTimeNow();
415
416 // if (!channel || !channel->isConnected(con.conID))
417 if (!channel)
418 return false;
419
420 // LogPrint(0, LOG_SYSTEM, 0, "Executor sending message to gateway...");
421
422 //if (!con.conID || !channel) {
423 // if (!channel)
424 // channel = manager->createTCPConnection(con.addr.c_str(), con.port, con.encryption, PROTOCOL_MESSAGE, true, true, con.id, this, con.conID, con.location);
425 // else
426 // con.conID = channel->createTCPConnection(con.addr.c_str(), con.port, con.encryption, PROTOCOL_MESSAGE, true, true, con.location);
427 // if (!channel || !con.conID) {
428 // if (!con.lastFailTime)
429 // LogPrint(0, LOG_SYSTEM, 0, "[%s] Unable to connect to gateway %u on %s:%u, will retry later", executorName.c_str(), con.id, con.addr.c_str(), con.port);
430 // con.lastFailTime = now;
431 // return false;
432 // }
433 // DataMessage* msgConnect = new DataMessage();
434 // msgConnect->setString("URI", "ExecutorConnect");
435 // msgConnect->setString("LongRequests", utils::TextVectorConcat(longReqNames, ",", false).c_str());
436 // if (!channel->sendMessage(msgConnect, con.conID)) {
437 // LogPrint(0, LOG_SYSTEM, 0, "[%s] Unable to communicate with gateway %u on %s:%u, will retry later", executorName.c_str(), con.id, con.addr.c_str(), con.port);
438 // con.lastFailTime = now;
439 // // clearly not working, disconnect and try again later
440 // channel->endConnection(con.conID);
441 // con.conID = 0;
442 // delete msgConnect;
443 // return false;
444 // }
445 // delete msgConnect;
446 // if (!con.lastConTime)
447 // LogPrint(0, LOG_SYSTEM, 0, "[%s] Connected to gateway %u on %s:%u", executorName.c_str(), con.id, con.addr.c_str(), con.port);
448 // else
449 // LogPrint(0, LOG_SYSTEM, 0, "[%s] Reconnected to gateway %u on %s:%u", executorName.c_str(), con.id, con.addr.c_str(), con.port);
450 // con.lastConTime = now;
451 //}
452 if (!channel->sendMessage(msg, con.conID)) {
453 if (!con.lastFailTime)
454 LogPrint(0, LOG_SYSTEM, 0, "[%s] Unable to send data to gateway %u on %s:%u, disconnecting...", executorName.c_str(), con.id, con.addr.c_str(), con.port);
455 con.lastFailTime = GetTimeNow();
456 // clearly not working, disconnect and try again later
457 //channel->endConnection(con.conID);
458 //con.conID = 0;
459 return false;
460 }
461 //if (msg->getReference())
462 // LogPrint(0, LOG_SYSTEM, 0, " + Executor replying to Gateway %u (%llu)", con.id, msg->getReference());
463 //else
464 // LogPrint(0, LOG_SYSTEM, 0, " + Executor sending Heartbeat to Gateway %u", con.id);
465 return true;
466}
467
469 // Heartbeat: broadcasts an ExecutorStatus message carrying the current
470 // short/long queue depths to every configured gateway. The gateway feeds
471 // these into its RequestConnection records for load balancing, and treats
472 // a missing heartbeat (executorHeartbeatTimeout) as executor death.
473
474 if (!conMutex.enter(1000, __FUNCTION__)) {
475 LogPrint(0, LOG_SYSTEM, 0, "Mutex error sending heartbeat from Executor %u...", executorID);
476 return 0;
477 }
478 if (!connections.size()) {
479 //LogPrint(0, LOG_SYSTEM, 0, "No connections sending heartbeat from Executor %u...", executorID);
480 conMutex.leave();
481 return 0;
482 }
483
484 DataMessage* msg = new DataMessage();
485 msg->setString("URI", "ExecutorStatus");
486 msg->setInt("ShortQSize", shortExecQ.getCount());
487 msg->setInt("LongQSize", longExecQ.getCount());
488
489 uint32 success = 0, failed = 0, notConnected = 0;
490
491 std::list<RequestGatewayConnection>::iterator iCon, eCon = connections.end();
492 iCon = connections.begin();
493 while (iCon != eCon) {
494 if (sendMessageToGateway(msg, (*iCon)))
495 success++;
496 else
497 failed++;
498 iCon++;
499 }
500 conMutex.leave();
501
502 delete(msg);
504 if (failed)
505 LogPrint(0, LOG_SYSTEM, 2, "Failed sending heartbeat from Executor %u to %u gateways (%u succeeded)...", executorID, failed, success);
506// else
507// LogPrint(0, LOG_SYSTEM, 0, "Sent heartbeat from Executor %u to %u gateways...", executorID, success);
508 return success;
509}
510
512 if (arg == NULL) thread_ret_val(1);
513 thread_ret_val((int)(((RequestExecutor*)arg)->run() ? 0 : 1));
514}
515
516
517
518
519
521 this->id = id;
522 this->processTimeMS = processTimeMS;
523 shouldContinue = true;
524 uint32 myThreadID;
525 ThreadManager::CreateThread(ShortExecutorRun, this, myThreadID, 0);
526 ThreadManager::CreateThread(LongExecutorRun, this, myThreadID, 0);
527}
528
529
531 uint32 count = 0;
532 DataMessage* replyMsg, *msg;
533 while (shouldContinue) {
534 if (msg = waitForShortRequest(20)) {
535 replyMsg = new DataMessage();
536 replyMsg->setReference(msg->getReference());
537 replyToQuery(replyMsg);
538 }
539 }
540 return true;
541}
542
544 uint32 count = 0;
545 DataMessage* replyMsg, *msg;
546 std::list<DataMessage*> storedMessages;
547 std::list<DataMessage*>::iterator i, e = storedMessages.end();
548
549 while (shouldContinue) {
550 if (msg = waitForLongRequest(20)) {
551 if (!processTimeMS) {
552 replyMsg = new DataMessage();
553 replyMsg->setReference(msg->getReference());
554 replyToQuery(replyMsg);
555 //printf("--- Replying immediately ---\n");
556 }
557 else {
558 msg->setRecvTime(GetTimeNow());
559 storedMessages.push_back(msg);
560 }
561 }
562
563 i = storedMessages.begin();
564 while (i != e) {
565 if ((msg = (*i)) && (GetTimeAgeMS(msg->getRecvTime()) >= (int32)processTimeMS)) {
566 i = storedMessages.erase(i);
567 replyMsg = new DataMessage();
568 replyMsg->setReference(msg->getReference());
569 replyToQuery(replyMsg);
570 //printf("--- Replying delayed ---\n");
571 }
572 else
573 i++;
574 }
575 }
576 return true;
577}
578
583
588
589}
#define NETWORKEVENT_CONNECT
Connection established.
#define NETWORKEVENT_DISCONNECT_RETRYING
Connection lost; reconnection attempts in progress.
#define NETWORKEVENT_DISCONNECT
Connection closed for good.
#define NETWORKEVENT_RECONNECT
Connection re-established after a failure (autoreconnect).
#define PROTOCOL_MESSAGE
CMSDK binary DataMessage protocol (size-prefixed frames).
Request-system worker side: RequestExecutor pulls requests from a RequestGateway, processes them and ...
#define LOG_SYSTEM
Definition Utils.h:197
#define thread_ret_val(ret)
Definition Utils.h:131
#define THREAD_RET
Definition Utils.h:127
#define THREAD_FUNCTION_CALL
Definition Utils.h:129
#define LogPrint
Definition Utils.h:313
#define THREAD_ARG
Definition Utils.h:130
The central Psyclone data container: a self-contained binary message with typed, named user entries.
bool setString(const char *key, const char *value)
setString(const char* key, const char* value)
bool setInt(const char *key, int64 value)
setInt(const char* key, int64 value)
DataMessageHeader * data
Pointer to the message's flat memory block (header + user entries).
uint32 getSize()
getSize() Get message size Many types of data of any size can be put into a message as user entries; ...
uint64 getReference()
getReference() Get and return message reference id
bool setRecvTime(uint64 time)
setRecvTime(uint64 time)
bool setReference(uint64 ref)
setReference(uint64 ref) Set message reference
const char * getString(const char *key)
getString(const char* key)
uint64 getRecvTime()
getRecvTime()
bool setSendTime(uint64 time)
setSendTime(uint64 time)
One logical network interface: a group of listeners/connections with shared dispatch.
Central owner of all channels, listeners and connections in a process.
uint64 lastHeartbeat
Time of last heartbeat (ms epoch).
uint64 longReceivedCount
Total long requests received.
uint32 sendStatusNow()
Send a status heartbeat (queue sizes) to all gateways.
bool replyToQuery(DataMessage *msg)
Send a reply for a previously pulled request back to its gateway.
bool shutdownNetwork()
Disconnect from all gateways and stop the network stack.
utils::WaitQueuePointer< DataMessage * > replyQ
Outgoing replies awaiting dispatch.
DataMessage * waitForShortRequest(uint32 timeoutMS)
Pull the next short request (worker thread API).
utils::Mutex mutex
Guards request bookkeeping.
NetworkManager * manager
Owned network stack.
bool replyToGateway(DataMessage *msg)
Route a finished reply to the gateway that sent the request.
std::string executorName
Display name for logging.
bool run()
Worker loop: gateway upkeep, heartbeats, reply dispatch.
uint32 threadID
Worker thread id.
uint64 shortReceivedCount
Total short requests received.
utils::WaitQueuePointer< DataMessage * > shortExecQ
Pending short requests.
int32 heartbeatIntervalMS
Interval between status heartbeats (ms).
std::map< uint64, RequestReply * > requestMap
In-flight request bookkeeping by ref id.
bool receiveMessage(DataMessage *msg, NetworkChannel *channel, uint64 conid)
NetworkReceiver hook: incoming request messages, routed to the short/long queue.
uint64 sentCount
Total messages sent to gateways.
bool addGateway(uint32 id, std::string addr, uint16 port, uint8 encryption=NOENC)
Register a gateway to connect to (repeatable for redundancy).
DataMessage * waitForLongRequest(uint32 timeoutMS)
Pull the next long request (worker thread API).
bool setLongRequestLimit(uint32 limit)
Cap the number of long requests processed concurrently.
utils::Mutex conMutex
Guards connections.
std::vector< std::string > longReqNames
Request names classified as long.
bool receiveNetworkEvent(NetworkEvent *evt, NetworkChannel *channel, uint64 conid)
NetworkReceiver hook: gateway connect/disconnect events (drives reconnection).
bool sendMessageToGateway(DataMessage *msg, RequestGatewayConnection &con)
Send a message on one gateway link.
NetworkChannel * channel
Channel carrying the gateway links.
uint32 executorID
Numeric id reported to gateways.
utils::WaitQueuePointer< DataMessage * > longExecQ
Pending long requests.
std::list< RequestGatewayConnection > connections
Configured gateways and their state.
uint32 longReqLimit
Max concurrent long requests (see setLongRequestLimit()).
uint64 lastRefID
Last issued executor reference id.
bool addLongRequestName(const char *name)
Register a request name to be treated as a long-running request.
friend THREAD_RET THREAD_FUNCTION_CALL RequestExecutorRun(THREAD_ARG arg)
Thread entry point for the RequestExecutor worker loop.
RequestExecutor(uint32 id=0, const char *name=NULL)
uint64 repliedCount
Total replies sent.
Future/handle for one in-flight request: holds the request message, the eventual reply,...
uint64 gatewayRef
Gateway-side reference id.
bool setStatus(RequestStatus status)
Set the current lifecycle status (wakes waiters when terminal).
uint64 execRef
Executor-side reference id.
uint64 origin
Originating endpoint/connection (packed uint64), for reply routing.
bool giveRequestMessage(DataMessage *msg)
Attach the outgoing request message, transferring ownership.
bool isRunning
Set by the worker while its loop is active.
virtual bool stop(uint32 timeout=200)
Ask the worker loop to finish and wait for it to do so.
bool shouldContinue
Loop-continuation flag; cleared by stop().
friend THREAD_RET THREAD_FUNCTION_CALL ShortExecutorRun(THREAD_ARG arg)
TestRequestExecutor(uint32 id, uint32 processTimeMS)
friend THREAD_RET THREAD_FUNCTION_CALL LongExecutorRun(THREAD_ARG arg)
static bool CreateThread(THREAD_FUNCTION func, void *args, uint32 &newID, uint32 reqID=0)
Create a new native thread and start it immediately.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
Definition PsyTime.cpp:69
int32 GetTimeAgeMS(uint64 t)
Age of a timestamp relative to now, in milliseconds.
Definition PsyTime.cpp:35
THREAD_RET THREAD_FUNCTION_CALL RequestExecutorRun(THREAD_ARG arg)
Thread entry point for the RequestExecutor worker loop.
std::string TextVectorConcat(std::vector< std::string >, const char *sep, bool allowEmpty=true)
Concatenate vector entries with sep.
Definition Utils.cpp:6539
const char * stristr(const char *str, const char *substr, uint32 len=0)
Case-insensitive strstr.
Definition Utils.cpp:6035
std::string StringFormat(const char *format,...)
printf into a std::string.
Definition Utils.cpp:6626
THREAD_RET THREAD_FUNCTION_CALL ShortExecutorRun(THREAD_ARG arg)
THREAD_RET THREAD_FUNCTION_CALL LongExecutorRun(THREAD_ARG arg)
Notification of a connection lifecycle change (connect, disconnect, buffer state.....
uint8 type
NETWORKEVENT_* event type.
Client/executor-side record of one gateway it connects to.
uint8 encryption
NOENC or SSLENC for this link.
uint32 id
Gateway id (configuration key).
uint64 location
Resolved endpoint packed as uint64.
uint64 conID
NetworkManager connection id (0 when disconnected).
std::string addr
Gateway host name or IP.
uint64 lastFailTime
Last failed connect time (ms epoch; drives retry backoff).
void clear()
Reset to the unconfigured/disconnected state.