readme
# dispatch
a project
use a simple timer(priority queue) to simulate the event
one thread is to read file , another thread is timer, when the event be touch , then notify.
architecture:
file information :
data.h: a template data struct , include hash map(use for matched strategy) and priority queue(use for FIFO strategy )
dispatch.h: implement of two strategy. notify event and statistics average food wait time and courier wait time
error.h: error code
type.h: base type
server.h: manager source and implement read file.
demo:
int ProductOrder(Server* server, const char* file)
{
pthread_setname_np(pthread_self(), "product");
return server->ReadData(file);
}
int TryDispatchOrder(Server* server)
{
pthread_setname_np(pthread_self(), "dispatch");
return server->TryDispatchOrder();
}
int main()
{
Config conf;
conf.st = STRATEGIE_MATCH; // STRATEGIE_MATCH
string file = "data2.json";
conf.cap = DEFAULT_MAX_QUEUE_SIZE;
Server server;
server.InitServer(&conf);
thread read(ProductOrder, &server, file.c_str());
thread disp(TryDispatchOrder, &server);
read.join();
disp.join();
server.DeInitServer();
}
compile information:
compiler: gcc
cmake: above VERSION 3.16
build Server:
cmake .
make clean && make
./Server
build Test: must build Server before build Test
cd test
cmake .
make clean && make
./Test
data.h
/*
* @Author: mayihui
* @Date: 2022-09-19 20:37:23
* @LastEditors: mayihui
* @LastEditTime: 2022-09-20 00:21:04
* @Description: data struct for store the order and courier information
* if stragegie is Matched, then use the hash map to store orders and couriers
* if stragegie is FIFO, then user the priority_queue to store orders and couriers.
*/
#ifndef DATA_H
#define DATA_H
#include <iostream>
using namespace std;
#define DEFAULT_MAX_QUEUE_SIZE 1000000
template <typename NODE> class MyQueue {
public:
virtual ~MyQueue() {}
virtual bool IsEmpty(void) = 0;
virtual int Size(void) = 0;
virtual int Insert(string id, NODE data) = 0;
virtual void RegisteFree(FREE f) = 0;
virtual void MyFree(NODE order) = 0; // free the NODE
};
template <typename NODE> class HashQueue : public MyQueue<NODE> {
public:
HashQueue()
{
this->capacity = DEFAULT_MAX_QUEUE_SIZE;
myFree = nullptr;
}
HashQueue(int capacity)
{
this->capacity = capacity;
myFree = nullptr;
}
virtual ~HashQueue()
{
auto it = que.begin();
while (it != que.end()) {
auto de = it;
it++;
MyFree(de->second);
RemoveById(de->first);
}
}
int Insert(string id, NODE order)
{
if (this->que.size() >= this->capacity) {
return QUEUE_SIZE_IS_FULL;
}
if (que.count(id)) {
return QUEUE_ELEMENT_IS_EXIST;
}
que[id] = order;
return 0;
}
void RemoveById(string id)
{
if (que.count(id)) {
this->que.erase(id);
}
}
bool IsEmpty()
{
return this->que.empty();
}
bool IsExist(string id)
{
return que.count(id);
}
NODE GetValbyId(string id)
{
return que[id];
}
int Size()
{
return que.size();
}
void MyFree(NODE order)
{
if (myFree) {
myFree(order);
}
}
void RegisteFree(FREE f)
{
myFree = f;
}
private:
FREE myFree;
unsigned int capacity;
unordered_map<string, NODE> que;
};
template <typename NODE> class ProQueue : public MyQueue<NODE> {
public:
ProQueue()
{
this->capacity = DEFAULT_MAX_QUEUE_SIZE;
myFree = nullptr;
}
ProQueue(int capacity)
{
this->capacity = capacity;
myFree = nullptr;
}
virtual ~ProQueue()
{
while (!this->que.empty()) {
auto it = this->que.top();
que.pop();
MyFree(it);
}
}
int Insert(string id, NODE order)
{
(void)id;
if (this->que.size() >= this->capacity) {
return QUEUE_SIZE_IS_FULL;
}
this->que.push(order);
return 0;
}
bool IsEmpty()
{
return this->que.empty();
}
NODE GetFrontAndPop()
{
NODE order = this->que.top();
this->que.pop();
return order;
}
int Size()
{
return this->que.size();
}
void MyFree(NODE order)
{
if (myFree) {
myFree(order);
}
}
void RegisteFree(FREE f)
{
myFree = f;
}
private:
unsigned int capacity;
FREE myFree;
priority_queue<NODE, vector<NODE>, MyComparator<NODE>> que;
};
#endif
dispatch.h
/*
* @Author: mayihui
* @Date: 2022-09-19 20:37:45
* @LastEditors: mayihui
* @LastEditTime: 2022-09-20 00:15:45
* @Description: dispatch strategic implement
*
*/
#ifndef DISPATCH_H
#define DISPATCH_H
#include <iostream>
#include "type.h"
#include "data.h"
#include "timer.h"
using namespace std;
struct Param {
MyQueue<Order *> *orders;
MyQueue<Courier *> *couriers;
void *timer; // use for close timer
};
static int Randrange(int min, int max)
{
if (max <= min) {
return -1;
}
return min + rand() % (max - min);
}
class Dispatch : public Notify {
public:
Dispatch()
{
range = nullptr;
orders = 0;
foodwait = 0;
carieswait = 0;
srand((unsigned)time(NULL));
}
virtual ~Dispatch() {}
virtual int TryDispatchOrder(Order *order, MyQueue<Courier *> *couriers, MyQueue<Order *> *orders) = 0;
virtual int TryDispatchCourier(Courier *courier, MyQueue<Order *> *orders, MyQueue<Courier *> *couriers) = 0;
void DeleteOrder(Order *order, MyQueue<Order *> *orders) {
orders->MyFree(order);
}
void DeleteCourier(Courier *courier, MyQueue<Courier *> *couriers) {
couriers->MyFree(courier);
}
int NotifyEvent(int event, void *data)
{
int ret = 0;
Node *node = (Node *)data;
switch (event) {
case ORDER_PREPARED:
NotifyOrderPrepared((Order *)node->data);
ret = TryDispatchOrder((Order *)node->data, ((Param *)node->param)->couriers, ((Param *)node->param)->orders);
break;
case COURIER_ARRIVED:
NotifyCourierArrived((Courier *)node->data);
ret = TryDispatchCourier((Courier *)node->data, ((Param *)node->param)->orders,
((Param *)node->param)->couriers);
break;
case (DELETE_EVENT | ORDER_PREPARED):
DeleteOrder((Order *)node->data, ((Param *)node->param)->orders);
break;
case (DELETE_EVENT | COURIER_ARRIVED):
DeleteCourier((Courier *)node->data, ((Param *)node->param)->couriers);
break;
default:
cout<<"error event "<<event<<endl;;
}
if (IsStaties()) {
Timer *t = (Timer *)((Param *)node->param)->timer;
cout << "avg food wait time: " << GetAvgFoodWaitTime() << " ms\n";
cout << "avg couries wait time: " << GetAvgCouriersWaitTime() << " ms\n";
t->CloseTimer();
}
return ret;
}
void NotifyOrderPrepared(void *data)
{
Order *order = (Order *)data;
cout << "prepare order " << order->id << " ts " << order->prepareTime << endl;
}
void NotifyCourierArrived(void *data)
{
Courier *courier = (Courier *)data;
cout << "arrived courier " << courier->courierId << " ts " << courier->arriveTime << endl;
}
void SetSize(int size)
{
this->size = size;
}
bool IsStaties()
{
if (size <= orders) {
return true;
}
return false;
}
void RegistRand(RANDRANGE rg)
{
range = rg;
}
int CariesArrivedTime(int min, int max)
{
if (range != nullptr) {
return range(min, max);
}
return Randrange(min, max);
}
void AddFoodWait(int prepareTime)
{
if (prepareTime < 0) {
cout<<"error: invalid prepareTime: "<<prepareTime<<endl;
return;
}
this->foodwait += prepareTime;
}
int64_t GetFoodWait()
{
return foodwait;
}
void AddCariesWait(int wait)
{
if (wait < 0) {
cout<<"error: invalid wait time: "<<wait<<endl;
return;
}
this->carieswait += wait;
}
int64_t GetCariesWait()
{
return carieswait;
}
void AddOrders()
{
orders++;
}
int64_t GetOrders()
{
return orders;
}
int64_t GetAvgFoodWaitTime()
{
if (orders == 0) {
return 0;
}
return foodwait / orders;
}
int64_t GetAvgCouriersWaitTime()
{
if (orders == 0) {
return 0;
}
return carieswait / orders;
}
private:
RANDRANGE range;
int64_t foodwait;
int64_t carieswait;
int64_t orders;
int size;
};
class Matched : public Dispatch {
public:
int TryDispatchOrder(Order *order, MyQueue<Courier *> *couriers, MyQueue<Order *> *orders)
{
int ret = 0;
HashQueue<Courier *> *hs = (HashQueue<Courier *> *)couriers;
if (hs->IsExist(order->id)) {
Courier *courier = hs->GetValbyId(order->id);
AddCariesWait(order->prepareTime - courier->arriveTime);
AddOrders();
cout << "[Match] courier " << courier->courierId << " pick up order " << order->id << endl;
hs->RemoveById(courier->id);
orders->MyFree(order);
couriers->MyFree(courier);
return SUCCESS;
} else {
ret = orders->Insert(order->id, order);
if (ret != 0) {
cout<<"error order insert "<<ret<<endl;
return 0;
}
}
// not find courier
return COURIER_NOT_ATTRIVED;
}
int TryDispatchCourier(Courier *courier, MyQueue<Order *> *orders, MyQueue<Courier *> *couriers)
{
int ret = 0;
HashQueue<Order *> *od = (HashQueue<Order *> *)orders;
if (od->IsExist(courier->id)) {
Order *order = od->GetValbyId(courier->id);
AddFoodWait(courier->arriveTime - order->prepareTime);
AddOrders();
cout << "[Match] courier " << courier->courierId << " pick up order " << order->id << endl;
od->RemoveById(order->id);
orders->MyFree(order);
couriers->MyFree(courier);
return SUCCESS;
} else {
ret = couriers->Insert(courier->id, courier);
if (ret != 0) {
cout<<"error couriers insert "<<ret<<endl;
return 0;
}
}
return ORDER_NOT_PREPARE;
}
};
class FIFO : public Dispatch {
public:
int TryDispatchOrder(Order *order, MyQueue<Courier *> *couriers, MyQueue<Order *> *orders)
{
int ret = 0;
ProQueue<Courier *> *hs = (ProQueue<Courier *> *)couriers;
if (!hs->IsEmpty()) {
Courier *courier = hs->GetFrontAndPop();
AddCariesWait(order->prepareTime - courier->arriveTime);
AddOrders();
cout << "[FIFO] courier " << courier->courierId << " pick up order " << order->id << endl;
orders->MyFree(order);
couriers->MyFree(courier);
return SUCCESS;
} else {
ret = orders->Insert(order->id, order);
if (ret != 0) {
cout<<"error order insert "<<ret<<endl;
return 0;
}
}
return COURIER_NOT_ATTRIVED;
}
int TryDispatchCourier(Courier *courier, MyQueue<Order *> *orders, MyQueue<Courier *> *couriers)
{
int ret = 0;
ProQueue<Order *> *hs = (ProQueue<Order *> *)orders;
if (!hs->IsEmpty()) {
Order *order = hs->GetFrontAndPop();
AddFoodWait(courier->arriveTime - order->prepareTime);
AddOrders();
cout << "[FIFO] courier " << courier->courierId << " pick up order " << order->id << endl;
orders->MyFree(order);
couriers->MyFree(courier);
return SUCCESS;
} else {
ret = couriers->Insert(courier->id, courier);
if (ret != 0) {
cout<<"error couriers insert "<<ret<<endl;
return 0;
}
}
return ORDER_NOT_PREPARE;
}
};
#endif
error
/*
* @Author: mayihui
* @Date: 2022-09-19 20:38:11
* @LastEditors: mayihui
* @LastEditTime: 2022-09-19 23:15:02
* @Description: error code in this system
*/
#ifndef ERROR_H
#define ERROR_H
enum ErrorCode {
SUCCESS = 0,
ORDER_NOT_PREPARE,
COURIER_NOT_ATTRIVED,
QUEUE_SIZE_IS_FULL,
QUEUE_ELEMENT_IS_EXIST,
};
#endif
server
/*
* @Author: mayihui
* @Date: 2022-09-19 20:38:23
* @LastEditors: mayihui
* @LastEditTime: 2022-09-19 23:51:15
* @Description: server
*/
#ifndef SERVER_H
#define SERVER_H
#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <chrono>
#include <limits.h>
#include <condition_variable>
#include <fstream>
#include <unordered_map>
#include "json/json.h"
#include "type.h"
#include "data.h"
#include "dispatch.h"
#include "timer.h"
using namespace std;
using namespace chrono;
class Server {
private:
// std::condition_variable vals;
// std::mutex mtx; // 保护
int64_t capacity;
int stragegie; // 策略
Dispatch *dispatch;
MyQueue<Order *> *orders;
MyQueue<Courier *> *couriers;
Timer *timer;
bool dispatching;
public:
/* *
* @description: init server, init the strategic of dispatch order , init the queue of orders and couriers
* @param {Config} *conf conf information , include strategic, time
* @return 0 successed, others error
* @author: mayihui
*/
int InitServer(Config *conf);
/* *
* @description: free the queue struct of order and courier
* @return void
* @author: mayihui
*/
void DeInitServer();
/* *
* @description: register the random function by user, use for product a range rand number
* @param {RANDRANGE} rnd
* @return void
* @author: mayihui
*/
void RegistRand(RANDRANGE rnd);
/* *
* @description: start a thread to read data from file, insert event node to a timers.
* @param {char*} file
* @return 0 successed, others error
* @author: mayihui
*/
int ReadData(const char *file);
/* *
* @description: dispatch order , start the thread of timers, dispatch the order in timer notification.
* @return 0 successed, others error
* @author: mayihui
*/
int TryDispatchOrder();
/* *
* @description: get average courier wait time
* @return {*}
* @author: mayihui
*/
int GetAvgCourierWaitTime();
/* *
* @description: get average food wait time
* @return {*}
* @author: mayihui
*/
int GetAvgFoodWaitTime();
/* *
* @description:
* @param {FREE} f for free order
* @return {*}
* @author: mayihui
*/
void RegistOrderFree(FREE f);
/* *
* @description:
* @param {FREE} f for free courier
* @return {*}
* @author: mayihui
*/
void RegistCourierFree(FREE f);
};
#endif
timer
/*
* @Author: mayihui
* @Date: 2022-09-19 20:43:47
* @LastEditors: mayihui
* @LastEditTime: 2022-09-19 23:24:49
* @Description: use priority queue to implement a simple timer
*/
#ifndef TIMER_H
#define TIMER_H
#include <chrono>
#include "type.h"
using namespace chrono;
#define INTERVAL 500
struct TimerComparator {
bool operator() (Node *&arg1, Node *&arg2) {
return arg1->tms > arg2->tms;
}
};
class Timer {
public:
Timer()
{
interval = INTERVAL;
running = true;
}
int32_t SetInterval(int interval)
{
this->interval = interval; // ms
return 0;
}
void StartTimer()
{
running = true;
}
void CloseTimer()
{
running = false;
}
int32_t InsertNode(Node *node)
{
std::lock_guard<std::mutex> guard(mtx);
que.emplace(node);
return 0;
}
void TimerRun()
{
while (running) {
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
std::lock_guard<std::mutex> guard(mtx);
auto now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
while (!que.empty() && que.top()->tms < now) {
Node *t = que.top();
t->notify->NotifyEvent(t->event, t);
que.pop();
cout<<"timer"<<t->id<<endl;
delete t;
}
}
// timer exit normally
while (!que.empty()) {
Node *d = que.top();
d->notify->NotifyEvent(d->event | DELETE_EVENT, d);
que.pop();
delete d;
}
}
private:
std::mutex mtx;
priority_queue<Node*, vector<Node*>, TimerComparator> que;
bool running;
int interval; // ms
};
#endif
type
/*
* @Author: mayihui
* @Date: 2022-09-19 20:44:01
* @LastEditors: mayihui
* @LastEditTime: 2022-09-19 23:35:28
* @Description: define some type of this system
*/
#ifndef TYPE_H
#define TYPE_H
#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <chrono>
#include <condition_variable>
#include "error.h"
using namespace std;
// range random function
typedef int (*RANDRANGE)(int min, int max);
typedef void (*FREE)(void *data);
// couries information
struct Courier {
string id; // order id
string courierId; // self id
int64_t arriveTime; // courier arrived
int64_t tms;
bool operator < (const Courier &co) const
{
return arriveTime < co.arriveTime;
}
bool operator > (const Courier &co) const
{
return arriveTime > co.arriveTime;
}
};
// food information
struct Food {
string name;
int prepareTime;
};
// order information
struct Order {
string id;
Food food;
int state;
int64_t dispatchTime; // dispatch time
int64_t prepareTime; // order prepared time
int64_t tms;
bool operator < (const Order &od) const
{
return prepareTime < od.prepareTime;
}
bool operator > (const Order &od) const
{
return prepareTime > od.prepareTime;
}
};
// timer notify
class Notify {
public:
virtual int NotifyEvent(int event, void *data) = 0;
virtual ~Notify() {}
};
// timer node
struct Node {
void *data; // order or couries data
void *param; // user param
int64_t tms; // event time
int event; // events
Notify *notify; // notify process
uint64_t id; // node inner id
bool operator < (const Node &node) const
{
return tms < node.tms;
}
bool operator > (const Node &node) const
{
return tms > node.tms;
}
};
template <typename NODE>
struct MyComparator {
bool operator() (NODE &arg1, NODE &arg2) {
return arg1->tms > arg2->tms;
}
};
#define ORDER_PREPARED 0x01
#define COURIER_ARRIVED 0x02
#define DELETE_EVENT 0x10
enum STRATEGIE {
STRATEGIE_MATCH = 0x01, // match strategie
STRATEGIE_FIFO // fifo strategie
};
// config information of server
struct Config {
STRATEGIE st;
int32_t cap;
};
#define DISPATCH_SIZE_EACH_TIME 2
#define RAND_MIN_SECOND 3
#define RAND_MAX_SECOND 15
#define ONE_SECOND_MS 1000
#define DISPATCH_INTERVAL 2000 // ms
#endif
server
/*
* @Author: mayihui
* @Date: 2022-09-16 22:52:25
* @LastEditors: mayihui
* @LastEditTime: 2022-09-20 00:12:19
* @Description: implement of server
*/
#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <chrono>
#include <condition_variable>
#include <fstream>
#include <unordered_map>
#include "json/json.h"
#include "server.h"
using namespace std;
using namespace chrono;
static void FreeOrder(Order *order)
{
delete order;
}
static void FreeCourier(Courier *courier)
{
delete courier;
}
int Server::TryDispatchOrder()
{
timer->StartTimer();
timer->TimerRun();
return 0;
}
int Server::InitServer(Config *conf)
{
// default conf
if (conf == NULL) {
stragegie = STRATEGIE_MATCH;
capacity = DEFAULT_MAX_QUEUE_SIZE;
} else {
stragegie = conf->st;
capacity = conf->cap;
}
if (stragegie == STRATEGIE_MATCH) {
Matched *match = new Matched();
dispatch = (Dispatch *)match;
this->orders = new HashQueue<Order *>();
this->couriers = new HashQueue<Courier *>();
} else if (stragegie == STRATEGIE_FIFO) {
FIFO *fifo = new FIFO();
dispatch = (Dispatch *)fifo;
this->orders = new ProQueue<Order *>();
this->couriers = new ProQueue<Courier *>();
}
this->timer = new Timer();
this->orders->RegisteFree((FREE)FreeOrder);
this->couriers->RegisteFree((FREE)FreeCourier);
dispatching = true;
return 0;
}
void Server::DeInitServer()
{
if (this->dispatch) {
delete this->dispatch;
}
if (this->orders) {
delete this->orders;
}
if (this->couriers) {
delete this->couriers;
}
if (this->timer) {
this->timer->CloseTimer();
delete this->timer;
}
}
int Server::GetAvgCourierWaitTime()
{
return dispatch->GetAvgCouriersWaitTime();
}
int Server::GetAvgFoodWaitTime()
{
return dispatch->GetAvgFoodWaitTime();
}
void Server::RegistRand(RANDRANGE rnd)
{
dispatch->RegistRand(rnd);
}
void Server::RegistOrderFree(FREE f)
{
this->orders->RegisteFree(f);
}
void Server::RegistCourierFree(FREE f)
{
this->couriers->RegisteFree(f);
}
int Server::ReadData(const char *filename)
{
Json::Reader reader;
Json::Value root;
std::ifstream is;
static int64_t cid = 0;
static int64_t eventid = 0;
Param param = {
this->orders,
this->couriers,
NULL,
};
is.open(filename, std::ios::binary);
if (reader.parse(is, root, false)) {
int size = root.size();
dispatch->SetSize(size);
for (int i = 0; i < size;) {
int j = 0;
for (; j < DISPATCH_SIZE_EACH_TIME && i + j < size;) {
Order *order = new Order();
Courier *courier = new Courier();
Json::Value tmp = root[i + j];
order->id = tmp["id"].asString();
order->food.name = tmp["name"].asString();
order->food.prepareTime = tmp["prepTime"].asInt() * ONE_SECOND_MS;
order->dispatchTime = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
order->prepareTime = order->dispatchTime + order->food.prepareTime;
order->tms = order->prepareTime;
int rnd = dispatch->CariesArrivedTime(RAND_MIN_SECOND, RAND_MAX_SECOND);
courier->arriveTime = order->dispatchTime + rnd * ONE_SECOND_MS;
courier->courierId = to_string(cid);
courier->id = order->id;
courier->tms = courier->arriveTime;
cid++;
Node *orderEvent = new Node();
param.timer = this->timer;
orderEvent->tms = order->prepareTime;
orderEvent->data = order;
orderEvent->notify = dispatch;
orderEvent->event = ORDER_PREPARED;
orderEvent->id = eventid++;
orderEvent->param = ¶m;
timer->InsertNode(orderEvent);
cout << "recive order " << order->id << " ts " << order->dispatchTime<<" need to prepare for "<<
order->food.prepareTime / ONE_SECOND_MS<<" s"<< endl;
Node *courierEvent = new Node();
courierEvent->tms = courier->arriveTime;
courierEvent->data = courier;
courierEvent->notify = dispatch;
courierEvent->event = COURIER_ARRIVED;
courierEvent->param = ¶m;
courierEvent->id = eventid++;
timer->InsertNode(courierEvent);
cout << "dispatch courier " << courier->courierId << " ts " << order->dispatchTime <<
" will be arrive in " << rnd << " s" << endl;
j++;
}
i += j;
std::this_thread::sleep_for(std::chrono::milliseconds(DISPATCH_INTERVAL));
}
} else {
cout << "ERROR: open file " << filename << " failed\n";
}
cout << "close file \n";
is.close();
return 0;
}
cmake
cmake_minimum_required(VERSION 3.16)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
link_directories(
${CMAKE_SOURCE_DIR}/../lib
)
add_library(servers STATIC server.cpp)
test
#include "gtest/gtest.h"
#include "json/json.h"
#include "timer.h"
#include "server.h"
class CoutStr : public Notify {
public:
CoutStr() {
no = 0;
}
int NotifyEvent(int event, void *data) {
Node* p = (Node*)data;
EXPECT_EQ(no, event);
no++;
Timer* timer = (Timer*)p->param;
if (no == 2) {
cout<<"close timer\n";
timer->CloseTimer();
}
}
private:
int no;
};
/*
test timer
*/
TEST(Timer, BasicAssertions) {
Node *node1 = new Node();
Node *node2 = new Node();
CoutStr cs;
node1->notify = &cs;
node1->tms = 100;
node1->event = 0;
node2->notify = &cs;
node2->tms = 101;
node2->event = 1;
Timer timer;
node2->param = &timer;
timer.SetInterval(50);
timer.InsertNode(node1);
timer.InsertNode(node2);
timer.TimerRun();
}
// id matched
TEST(Matched, TryDispatchOrder) {
Order od;
od.id = to_string(0);
od.prepareTime = 10;
Courier c1, c2;
c1.id = to_string(0);
c1.arriveTime = 5;
c2.id = to_string(1);
c2.arriveTime = 0;
HashQueue<Courier*> hs;
hs.Insert(c1.id, &c1);
hs.Insert(c2.id, &c2);
HashQueue<Order*> ods;
hs.RegisteFree(NULL);
ods.RegisteFree(NULL);
Matched mt;
int ret = mt.TryDispatchOrder(&od, &hs, &ods);
EXPECT_EQ(ret, 0);
EXPECT_EQ(mt.GetAvgCouriersWaitTime(), 5);
}
// fifo matched
TEST(FIFO, TryDispatchOrder) {
Order od;
od.id = to_string(0);
od.prepareTime = 10;
Courier c1, c2;
c1.id = to_string(0);
c1.courierId = to_string(0);
c1.arriveTime = 5;
c2.id = to_string(1);
c2.courierId = to_string(1);
c2.arriveTime = 0;
ProQueue<Courier*> hs;
hs.Insert(c1.id, &c1);
hs.Insert(c2.id, &c2);
ProQueue<Order*> ods;
FIFO fi;
int ret = fi.TryDispatchOrder(&od, &hs, &ods);
EXPECT_EQ(ret, 0);
EXPECT_EQ(fi.GetAvgCouriersWaitTime(), 10);
}
TEST(HashQueue, BaseActions)
{
HashQueue<Courier*> sh_couriers(3);
Courier c1,c2,c3;
c1.id = to_string(1);
c2.id = to_string(2);
c3.id = to_string(2);
EXPECT_EQ(0, sh_couriers.Insert(c1.id, &c1));
EXPECT_EQ(0, sh_couriers.Insert(c2.id, &c2));
EXPECT_EQ(QUEUE_ELEMENT_IS_EXIST, sh_couriers.Insert(c3.id, &c3));
HashQueue<Courier*> couriers(2);
EXPECT_EQ(0, couriers.Insert(c1.id, &c1));
EXPECT_EQ(0, couriers.Insert(c2.id, &c2));
c3.id = to_string(3);
EXPECT_EQ(QUEUE_SIZE_IS_FULL, couriers.Insert(c3.id, &c3));
}
// test priority queue
TEST(ProQueue, BaseActions)
{
ProQueue<Courier*> pro_couriers;
Courier c1,c2,c3;
c1.id = to_string(1);
c1.tms = 30;
c2.id = to_string(2);
c2.tms = 2;
c3.id = to_string(2);
c3.tms = 12;
EXPECT_EQ(0, pro_couriers.Insert(c1.id, &c1));
EXPECT_EQ(0, pro_couriers.Insert(c2.id, &c2));
EXPECT_EQ(0, pro_couriers.Insert(c3.id, &c3));
EXPECT_EQ(0, pro_couriers.IsEmpty());
Courier *g1 = pro_couriers.GetFrontAndPop();
EXPECT_EQ(2, g1->tms);
EXPECT_EQ(0, pro_couriers.IsEmpty());
Courier *g2 = pro_couriers.GetFrontAndPop();
EXPECT_EQ(12, g2->tms);
EXPECT_EQ(0, pro_couriers.IsEmpty());
Courier *g3 = pro_couriers.GetFrontAndPop();
EXPECT_EQ(30, g3->tms);
EXPECT_EQ(1, pro_couriers.IsEmpty());
ProQueue<Order*> pro_orders;
Order o1,o2,o3;
o1.id = to_string(1);
o1.tms = 20;
o2.id = to_string(2);
o2.tms = 10;
o3.id = to_string(3);
o3.tms = 40;
EXPECT_EQ(0, pro_orders.Insert(o1.id, &o1));
EXPECT_EQ(0, pro_orders.Insert(o2.id, &o2));
EXPECT_EQ(0, pro_orders.Insert(o3.id, &o3));
EXPECT_EQ(0, pro_orders.IsEmpty());
Order *od1 = pro_orders.GetFrontAndPop();
EXPECT_EQ(10, od1->tms);
EXPECT_EQ(0, pro_orders.IsEmpty());
Order *od2 = pro_orders.GetFrontAndPop();
EXPECT_EQ(20, od2->tms);
EXPECT_EQ(0, pro_orders.IsEmpty());
Order *od3 = pro_orders.GetFrontAndPop();
EXPECT_EQ(40, od3->tms);
EXPECT_EQ(1, pro_orders.IsEmpty());
}
int Rand(int min, int max)
{
return 5;
}
// test server
int ProductOrder(Server* server, const char* file)
{
pthread_setname_np(pthread_self(), "product");
return server->ReadData(file);
}
int TryDispatchOrder(Server* server)
{
pthread_setname_np(pthread_self(), "dispatch");
return server->TryDispatchOrder();
}
TEST(Server, BaseActionsMatch)
{
static Config conf;
conf.st = STRATEGIE_MATCH; // STRATEGIE_MATCH
static string file = "data1.json"; // 13 3, 5 5
conf.cap = 10000;
Server *server1 = new Server();
server1->InitServer(&conf);
server1->RegistRand((RANDRANGE)Rand);
thread read(ProductOrder, server1, file.c_str());
thread disp(TryDispatchOrder, server1);
read.join();
disp.join();
EXPECT_EQ(4 * 1000 ,server1->GetAvgCourierWaitTime());
EXPECT_EQ(1 * 1000 ,server1->GetAvgFoodWaitTime());
server1->DeInitServer();
delete server1;
}
// order origin 13 3 4 14 12 6 9 8 11
// courier origin 5 5 5 5 5 5 5 5 5
// order actual 13 3 6 16 16 10 15 14 19
// courier actual 5 5 7 7 9 9 11 11 13
// match food 2 1 -> 3000 / 9 ms
// match courier 8 9 7 1 4 3 6 -> 4222 ms
// match
TEST(Server, BaseActionsMatch1)
{
static Config conf;
conf.st = STRATEGIE_MATCH; // STRATEGIE_MATCH
static string file = "data2.json";
conf.cap = DEFAULT_MAX_QUEUE_SIZE;
Server server1;
server1.InitServer(&conf);
server1.RegistRand((RANDRANGE)Rand);
thread read(ProductOrder, &server1, file.c_str());
thread disp(TryDispatchOrder, &server1);
read.join();
disp.join();
EXPECT_EQ(4222 ,server1.GetAvgCourierWaitTime());
EXPECT_EQ(333 ,server1.GetAvgFoodWaitTime());
server1.DeInitServer();
}
// order origin 13 3 4 14 12 6 9 8 11
// courier origin 5 5 5 5 5 5 5 5 5
// order actual 13 3 6 16 16 10 15 14 19
// courier actual 5 5 7 7 9 9 11 11 13
// fifo order 3 6 10 13 14 15 16 16 19
// fifo courier 5 5 7 7 9 9 11 11 13
// food wait : 2 -> 2000 / 9 ms
// courier wait: 1 3 6 5 6 5 5 6 -> 4111 ms
TEST(Server, BaseActionsFIFO1)
{
static Config conf;
conf.st = STRATEGIE_FIFO;
static string file = "data2.json";
conf.cap = DEFAULT_MAX_QUEUE_SIZE;
Server *server1 = new Server();
server1->InitServer(&conf);
server1->RegistRand((RANDRANGE)Rand);
thread read(ProductOrder, server1, file.c_str());
thread disp(TryDispatchOrder, server1);
read.join();
disp.join();
EXPECT_EQ(4111 ,server1->GetAvgCourierWaitTime());
EXPECT_EQ(222 ,server1->GetAvgFoodWaitTime());
server1->DeInitServer();
delete server1;
}
// 13 3
// 5 5
TEST(Server, BaseActionsFIFO)
{
static Config conf;
conf.st = STRATEGIE_FIFO; // STRATEGIE_FIFO
static string file = "data1.json"; // 13 3 , 5 5
conf.cap = 10000;
Server *server = new Server();
server->InitServer(&conf);
server->RegistRand((RANDRANGE)Rand); // arrived 0
thread read(ProductOrder, server, file.c_str());
thread disp(TryDispatchOrder, server);
read.join();
disp.join();
EXPECT_EQ(4 * 1000 ,server->GetAvgCourierWaitTime());
EXPECT_EQ(1 * 1000 ,server->GetAvgFoodWaitTime());
server->DeInitServer();
delete server;
}
data1.json
[
{
"id": "58e9b5fe-3fde-4a27-8e98-682e58a4a65d",
"name": "McFlury",
"prepTime": 13
},
{
"id": "2ec069e3-576f-48eb-869f-74a540ef840c",
"name": "Acai Bowl",
"prepTime": 3
}
]
data2.json
[
{
"id": "58e9b5fe-3fde-4a27-8e98-682e58a4a65d",
"name": "McFlury",
"prepTime": 13
},
{
"id": "2ec069e3-576f-48eb-869f-74a540ef840c",
"name": "Acai Bowl",
"prepTime": 3
},
{
"id": "a8cfcb76-7f24-4420-a5ba-d46dd77bdffd",
"name": "Banana Split",
"prepTime": 4
},
{
"id": "58e9b5fe-3fde-4a27-8e98-682e58a4a66d",
"name": "McFlury",
"prepTime": 14
},
{
"id": "2ec069e3-576f-48eb-869f-74a540ef840c",
"name": "Acai Bowl",
"prepTime": 12
},
{
"id": "690b85f7-8c7d-4337-bd02-04e04454c826",
"name": "Yogurt",
"prepTime": 6
},
{
"id": "972aa5b8-5d83-4d5e-8cf3-8a1a1437b18a",
"name": "Chocolate Gelato",
"prepTime": 9
},
{
"id": "c18e1242-0856-4203-a98c-7066ead3bd6b",
"name": "Cobb Salad",
"prepTime": 8
},
{
"id": "66a2611c-9a93-4ccd-bb85-98f423247bf9",
"name": "Cottage Cheese",
"prepTime": 11
}
]
cmake
cmake_minimum_required(VERSION 3.16)
project(SERVER)
set(CMAKE_BUILD_TYPE Debug)
include_directories(
${CMAKE_SOURCE_DIR}/../
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/../include
)
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
link_directories(
${CMAKE_SOURCE_DIR}/../lib
)
aux_source_directory(. DIR_SRCS)
message(${CMAKE_SOURCE_DIR})
add_executable(Test ${DIR_SRCS})
target_link_libraries(Test PUBLIC -lservers -lpthread -ljsoncpp ${CMAKE_SOURCE_DIR}/../lib/libgtest.a ${CMAKE_SOURCE_DIR}/../lib/libgtest_main.a)
#include "server.h"
int ProductOrder(Server* server, const char* file)
{
#ifdef __GNUC__
pthread_setname_np(pthread_self(), "product");
#endif
return server->ReadData(file);
}
int TryDispatchOrder(Server* server)
{
#ifdef __GNUC__
pthread_setname_np(pthread_self(), "dispatch");
#endif
return server->TryDispatchOrder();
}
int main(int argc, const char* argv[])
{
Config conf;
conf.st = STRATEGIE_FIFO; // STRATEGIE_FIFO
string file = "data.json";
if (argc == 3) {
conf.st = (STRATEGIE)atoi(argv[1]);
file = argv[2];
}
if (conf.st != STRATEGIE_MATCH && conf.st != STRATEGIE_FIFO) {
cout<<"error stragegie : "<<conf.st<<endl;
exit(0);
}
conf.cap = DEFAULT_MAX_QUEUE_SIZE;
Server server;
server.InitServer(&conf);
thread read(ProductOrder, &server, file.c_str());
thread disp(TryDispatchOrder, &server);
read.join();
disp.join();
server.DeInitServer();
}
cmake
cmake_minimum_required(VERSION 3.16)
project(SERVER)
set(CMAKE_BUILD_TYPE Debug)
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
include_directories(
${CMAKE_SOURCE_DIR}/include
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Werror")
#target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror)
link_directories(
${CMAKE_SOURCE_DIR}/lib
)
add_subdirectory(src)
aux_source_directory(. DIR_SRCS)
add_executable(Server ${DIR_SRCS})
target_link_libraries(Server PUBLIC servers -lpthread -ljsoncpp -lgtest)
网友评论