hmbdc
simplify-high-performance-messaging-programming
hmbdc-log.cpp
///
/// A simple example to show the usage for hmbdc lib's logging usage based on hmbdc.cpp
/// hmbdc's logger works asyn, so it doesn't penalize the logging thread much
/// we construct 40 hmbdc Clients, each one send out a Request and listen to the Response sent back by each
/// of the Clients, then send out the next Request
///
///to build: do change paths
///g++ hmbdc-log.cpp -g -std=c++1y -Wall -Werror -pthread -Ipath-to-hmbdc-lib-include path-to-hmbdc-lib/libhmbdc.a -lpthread -lrt -o hmbdc-log
///
//to use the async hmbdc Logger, need to have HMBDC_LOG_CONTEXT defined before hmbdc includes
// - could use -D in the Makefile
//the size(40 here) needs to be at least 8 bytes bigger than the largest logging item's size,
//otherwise not compiling
#define HMBDC_LOG_CONTEXT hmbdc::app::Context<40>
#include "hmbdc/app/Base.hpp"
#include <iostream>
#include <memory>
#include <unistd.h>
#include <sys/mman.h>
using namespace hmbdc::app;
using MyContext = HMBDC_LOG_CONTEXT;
using MyLogger = LoggerT<MyContext>;
struct Request
: hasTag<1001> {
Request(uint16_t srcId, uint64_t id)
: srcId(srcId)
, id(id)
{}
uint16_t srcId;
uint64_t id;
friend
std::ostream& operator << (std::ostream& os, Request const& m) {
os << "Request " << m.srcId << ' ' << m.id;
return os;
}
};
struct Response
: hasTag<1002> {
Response(Request const& r)
: srcId(r.srcId)
, respToId(r.id)
{}
uint16_t srcId;
uint64_t respToId;
friend
std::ostream& operator << (std::ostream& os, Response const& m) {
os << "Response " << m.srcId << ' ' << m.respToId;
return os;
}
};
struct Finish
: hasTag<1004> {
Finish(uint16_t srcId)
: srcId(srcId)
{}
uint16_t srcId;
friend
std::ostream& operator << (std::ostream& os, Finish const& fin) {
os << "Finish " << fin.srcId;
return os;
}
};
struct MyClient
: Client<MyClient, Request, Response> {
MyClient(MyContext* myCtx, uint64_t workLoad, uint64_t clientCount, uint16_t srcId)
: myCtx(myCtx)
, srcId(srcId)
, requestId(0u)
, workLoad(workLoad)
, clientCount(clientCount)
, respCount(0u) {
}
void kickStart() {
if (workLoad) {
Request r(srcId, requestId);
HMBDC_LOG_N(srcId, ':', "--> ", r);
myCtx->send(r);
}
}
void handleMessageCb(Request const& r) {
//HMBDC_LOG_N means a notice is logged
HMBDC_LOG_N(srcId, ':', "<-- ", r); //items logged are seperated by ,
Response resp(r);
HMBDC_LOG_N(srcId, ':', "--> ", resp);
myCtx->template send(resp);
}
void handleMessageCb(Response const& resp) {
if (resp.srcId == srcId &&
resp.respToId == requestId) {
HMBDC_LOG_N(srcId, ':', "<-- ", resp);
if (++respCount == clientCount) {
workLoad--;
respCount = 0u;
++requestId;
if (workLoad) {
Request r(srcId, requestId);
HMBDC_LOG_N(srcId, ':', "--> ", r);
myCtx->send(r);
} else {
Finish fin(srcId);
myCtx->send(fin);
HMBDC_LOG_N(srcId, ':', "--> ", fin);
}
}
}
}
void stoppedCb(std::exception const& e) override {
//HMBDC_LOG_C means a crtical error is logged
HMBDC_LOG_C(e.what());
};
std::tuple<char const*, int> schedSpec() const {
return std::make_tuple<char const*, int>(nullptr, 19);
}
MyContext* myCtx;
uint16_t srcId;
uint64_t requestId;
uint64_t workLoad;
uint64_t clientCount;
uint64_t respCount;
};
struct Monitor
: Client<Monitor, Request, Response, Finish> {
Monitor(uint16_t count)
: count(count)
, totalRequest(0u)
, totalResponse(0u)
{}
char const* hmbdcName() const { return "monitor"; }
std::tuple<char const*, int> schedSpec() const {
return std::make_tuple<char const*, int>(nullptr, 19);
}
void handleMessageCb(Finish const& fin) {
count--;
}
void handleMessageCb(Request const&) {
totalRequest++;
}
void handleMessageCb(Response const& resp) {
totalResponse++;
}
void report() const {
//HMBDC_LOG_n is the same as HMBDC_LOG_N except no code file name and line number
HMBDC_LOG_N("totalRequest= ", totalRequest);
HMBDC_LOG_N("totalResponse= ", totalResponse);
}
uint16_t count;
size_t totalRequest;
size_t totalResponse;
};
int main() {
//logger is a singleton, do this to init it once and early in program
SingletonGuardian<MyLogger> logGuard(std::cout);
//now logger is functiooning, all HMBDC_LOG_XXX macros used in any part of code will
//dump log lines to cout
//Be aware, when logGuard goes out of scope, logger is destroyed and more logging
//would crash the program for deferencing a nullptr (the logger)
const uint64_t workLoad = 2;
const uint16_t clientCount = 3u;
MyContext ctx(20u, clientCount);
std::unique_ptr<MyClient> clients[clientCount];
Monitor mon(clientCount);
//the logger singleton needs to be in message dispatch somehow, add to pool here
//or it could run in direct mode like mon does
for (uint16_t i = 0; i < clientCount; ++i) {
clients[i].reset(new MyClient(&ctx, workLoad, clientCount, i));
ctx.addToPool(*clients[i]);
}
ctx.start(3, 0x07ul, mon, 0ul);
for (uint16_t i = 0; i < clientCount; ++i) {
clients[i]->kickStart();
}
while (mon.count){
sleep(1);
}
mon.report();
sleep(1); //logger flushes
ctx.stop();
ctx.join();
}