2022-09-20

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 = &param;
                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 = &param;
                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)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容