[
  {
    "path": "Acceptor.cc",
    "content": "//author voidccc\n\n#include <arpa/inet.h>\n#include <fcntl.h>\n#include <errno.h>\n\n#include \"Acceptor.h\"\n#include \"Channel.h\"\n#include \"IAcceptorCallback.h\"\n#include \"EventLoop.h\"\n\n#include <iostream>\nusing namespace std;\n\nAcceptor::Acceptor(EventLoop* pLoop)\n    :_listenfd(-1)\n    ,_pSocketAChannel(NULL)\n    ,_pCallback(NULL)\n    ,_pLoop(pLoop)\n{}\n\nAcceptor::~Acceptor()\n{}\n\nvoid Acceptor::start()\n{\n    _listenfd = createAndListen();\n    _pSocketAChannel = new Channel(_pLoop, _listenfd); // Memory Leak !!!\n    _pSocketAChannel->setCallback(this);\n    _pSocketAChannel->enableReading();\n}\n\nint Acceptor::createAndListen()\n{\n    int on = 1;\n    _listenfd = socket(AF_INET, SOCK_STREAM, 0);\n    struct sockaddr_in servaddr;\n    fcntl(_listenfd, F_SETFL, O_NONBLOCK); //no-block io\n    setsockopt(_listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));\n    servaddr.sin_family = AF_INET;\n    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);\n    servaddr.sin_port = htons(11111);\n\n    if(-1 == bind(_listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)))\n    {\n        cout << \"bind error, errno:\" << errno << endl;\n    }\n\n    if(-1 == listen(_listenfd, MAX_LISTENFD))\n    {\n        cout << \"listen error, errno:\" << errno << endl;\n    }\n    return _listenfd;\n}\n\nvoid Acceptor::handleRead()\n{\n    int connfd;\n    struct sockaddr_in cliaddr;\n    socklen_t clilen = sizeof(struct sockaddr_in);\n    connfd = accept(_listenfd, (sockaddr*)&cliaddr, (socklen_t*)&clilen);\n    if(connfd > 0)\n    {\n        cout << \"new connection from \"\n            << \"[\" << inet_ntoa(cliaddr.sin_addr) \n            << \":\" << ntohs(cliaddr.sin_port) << \"]\"\n            << \" new socket fd:\" << connfd\n            << endl;\n    }\n    else\n    {\n        cout << \"accept error, connfd:\" << connfd\n            << \" errno:\" << errno << endl;\n    }\n    fcntl(connfd, F_SETFL, O_NONBLOCK); //no-block io\n\n    _pCallback->newConnection(connfd);\n}\n\nvoid Acceptor::handleWrite()\n{\n\n}\n\nvoid Acceptor::setCallback(IAcceptorCallback* pCallback)\n{\n    _pCallback = pCallback;\n}\n"
  },
  {
    "path": "Acceptor.h",
    "content": "//author voidccc\n#ifndef ACCEPTOR_H\n#define ACCEPTOR_H\n\n#include \"Declear.h\"\n#include \"Define.h\"\n#include \"IChannelCallback.h\"\n\nclass Acceptor : public IChannelCallback\n{\n    public:\n        Acceptor(EventLoop* pLoop);\n        ~Acceptor();\n\n        void start();\n        void setCallback(IAcceptorCallback* pCallback);\n        virtual void handleRead();\n        virtual void handleWrite();\n    private:\n        int createAndListen();\n        int _listenfd;\n        Channel* _pSocketAChannel;\n        IAcceptorCallback* _pCallback;\n        EventLoop* _pLoop;\n};\n\n#endif\n"
  },
  {
    "path": "BlockingQueue.h",
    "content": "//author voidccc\n#ifndef BLOCKINGQUEUE_H\n#define BLOCKINGQUEUE_H\n\n#include <deque>\n#include \"Condition.h\"\n#include \"Mutex.h\"\n\nusing namespace std;\n\ntemplate<class T>\nclass BlockingQueue\n{\n    public:\n        BlockingQueue()\n            :_cond(_mutex)\n        {}\n        void put(const T& one)\n        {\n            MutexLockGuard lock(_mutex);\n            _queue.push_back(one);\n            _cond.notify();\n        }\n\n        T take()\n        {\n            MutexLockGuard lock(_mutex);\n            while(_queue.empty())\n            {\n                _cond.wait();    \n            }\n            T front(_queue.front());\n            _queue.pop_front();\n            return front;\n        }\n    private:\n        deque<T> _queue;\n        MutexLock _mutex;\n        Condition _cond;\n};\n#endif\n"
  },
  {
    "path": "Buffer.cc",
    "content": "//author voidccc\n\n#include \"Buffer.h\"\n\nBuffer::Buffer()\n{}\n\nBuffer::~Buffer()\n{}\n\nconst char* Buffer::peek()\n{\n    return _buf.c_str();\n}\n\nint Buffer::readableBytes()\n{\n    return static_cast<int>(_buf.size());\n}\n\nvoid Buffer::retrieve(int len)\n{\n    _buf = _buf.substr(len, _buf.size());\n}\n\nvoid Buffer::append(const string& data)\n{\n    _buf.append(data);\n}\n\nstring Buffer::retrieveAllAsString()\n{\n    return retrieveAsString(readableBytes());\n}\n\nstring Buffer::retrieveAsString(size_t len)\n{\n    string result(peek(), len);\n    retrieve(len);\n    return result;\n}\n"
  },
  {
    "path": "Buffer.h",
    "content": "//author voidccc\n#ifndef BUFFER_H\n#define BUFFER_H\n\n#include <string>\nusing namespace std;\n\nclass Buffer\n{\n    public:\n        Buffer();\n        ~Buffer();\n        const char* peek();\n        int readableBytes();\n        void retrieve(int len);\n        void append(const string& buf);\n        string retrieveAllAsString();\n        string retrieveAsString(size_t len);\n    private:\n        string _buf;\n};\n\n#endif\n"
  },
  {
    "path": "Channel.cc",
    "content": "//author voidccc\n\n#include <sys/epoll.h>\n\n#include \"Channel.h\"\n#include \"IChannelCallback.h\"\n#include \"EventLoop.h\"\n\n#include <iostream>\nusing namespace std;\n\nChannel::Channel(EventLoop* pLoop, int sockfd)\n    :_sockfd(sockfd)\n    ,_events(0)\n    ,_revents(0)\n    ,_index(-1)\n    ,_pCallback(NULL)\n    ,_pLoop(pLoop)\n{\n}\n\nvoid Channel::setCallback(IChannelCallback* pCallback)\n{\n    _pCallback = pCallback;\n}\n\nvoid Channel::setRevents(int revents)\n{\n    _revents = revents;\n}\n\nvoid Channel::setIndex(int index)\n{\n    _index = index;\n}\n\nvoid Channel::handleEvent()\n{\n   if(_revents & EPOLLIN)\n   {\n      _pCallback->handleRead();\n   }\n   if(_revents & EPOLLOUT)\n   {\n      _pCallback->handleWrite();\n   }\n}\n\nvoid Channel::enableReading()\n{\n    _events |= EPOLLIN;\n    update();\n}\n\nvoid Channel::enableWriting()\n{\n    _events |= EPOLLOUT;\n    update();\n}\n\nvoid Channel::disableWriting()\n{\n    _events &= ~EPOLLOUT;\n    update();\n}\n\nbool Channel::isWriting()\n{\n    return _events & EPOLLOUT;\n}\n\nvoid Channel::update()\n{\n    _pLoop->update(this);\n}\n\nint Channel::getEvents()\n{\n    return _events;\n}\n\nint Channel::getfd()\n{\n    return _sockfd;\n}\n\nint Channel::getIndex()\n{\n    return _index;\n}\n"
  },
  {
    "path": "Channel.h",
    "content": "//author voidccc\n#ifndef CHANNEL_H\n#define CHANNEL_H\n\n#include \"Declear.h\"\n\nclass Channel\n{\n    public:\n        Channel(EventLoop* pLoop, int sockfd);\n        ~Channel();\n        void setCallback(IChannelCallback* pCallback);\n        void handleEvent();\n        void setRevents(int revent);\n        void setIndex(int index);\n        void enableReading();\n        void enableWriting();\n        void disableWriting();\n        bool isWriting();\n        int getEvents();\n        int getfd();\n        int getIndex();\n    private:\n        void update();\n        int _sockfd;\n        int _events;\n        int _revents;\n        int _index;\n        IChannelCallback* _pCallback;\n        EventLoop* _pLoop;\n};\n\n#endif\n"
  },
  {
    "path": "Condition.h",
    "content": "//author voidccc\n#ifndef CONDITION_H\n#define CONDITION_H\n\n#include <pthread.h>\n#include \"Mutex.h\"\n\nclass Condition\n{\n    public:\n        Condition(MutexLock& mutex)\n            :_mutex(mutex)\n        {\n            pthread_cond_init(&_condid, NULL);        \n        }\n        ~Condition()\n        {\n            pthread_cond_destroy(&_condid); \n        }\n        void wait()\n        {\n            pthread_cond_wait(&_condid, _mutex.getPthreadMutex());\n        }\n        void notify()\n        {\n            pthread_cond_signal(&_condid); \n        }\n        void notifyAll()\n        {\n            pthread_cond_broadcast(&_condid);        \n        }\n    private:\n        MutexLock& _mutex;\n        pthread_cond_t _condid;\n};\n\n#endif\n"
  },
  {
    "path": "CurrentThread.h",
    "content": "//author voidccc\n#ifndef CURRENTTHREAD_H\n#define CURRENTTHREAD_H \n\n#include <unistd.h>\n#include <sys/syscall.h>\n\nnamespace CurrentThread\n{\n    extern __thread int t_cachedTid;\n    inline void cacheTid()\n    {\n        t_cachedTid = static_cast<int>(::syscall(SYS_gettid));\n    }\n    inline int tid()\n    {\n        if(t_cachedTid == 0)\n        {\n            cacheTid();\n        }\n        return t_cachedTid;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Declear.h",
    "content": "//author voidccc\n#ifndef DECLEAR_H\n#define DECLEAR_H\n\nclass IChannelCallback;\nclass IAcceptorCallback;\nclass Channel;\nclass Acceptor;\nclass TcpConnection;\nclass EventLoop;\nclass Epoll;\nclass IMuduoUser;\nclass Buffer;\nclass TimerQueue;\nclass Timestamp;\nclass IRun0;\nclass IRun2;\nclass Timer;\nclass Task;\nclass Thread;\nclass ThreadPool;\n\n#endif\n"
  },
  {
    "path": "Define.h",
    "content": "//author voidccc\n#ifndef DEFINE_H\n#define DEFINE_H\n\n#define MAX_LINE 100\n#define MAX_EVENTS 500\n#define MAX_LISTENFD 5\n\n#endif\n"
  },
  {
    "path": "EchoServer.cc",
    "content": "//author voidccc\n\n#include \"EchoServer.h\"\n#include \"TcpConnection.h\"\n#include \"EventLoop.h\"\n#include \"CurrentThread.h\"\n#include \"Task.h\"\n\n#include <iostream>\n\n#define MESSAGE_LENGTH 8\n\nEchoServer::EchoServer(EventLoop* pLoop)\n    :_pLoop(pLoop)\n    ,_pServer(pLoop)\n    ,_timer(-1)\n    ,_index(0)\n{\n    _pServer.setCallback(this);\n}\n\nEchoServer::~EchoServer()\n{}\n\nvoid EchoServer::start()\n{\n    _pServer.start();\n    _threadpool.start(3);\n}\n\nvoid EchoServer::onConnection(TcpConnection* pCon)\n{\n    cout << \"onConnection\" << endl;\n}\n\nvoid EchoServer::onMessage(TcpConnection* pCon, Buffer* pBuf)\n{\n    while(pBuf->readableBytes() > MESSAGE_LENGTH)\n    {\n        string message = pBuf->retrieveAsString(MESSAGE_LENGTH);\n        Task task(this, message, pCon);\n        _threadpool.addTask(task);\n    }\n}\n\nvoid EchoServer::onWriteComplate(TcpConnection* pCon)\n{\n    cout << \"onWriteComplate\" << endl;\n}\n\n//run in different therad\nvoid EchoServer::run2(const string& str, void* tcp)\n{\n    //IO blocking task or CPU busy task\n    cout << \"fib(30) = \" << fib(30) << \" tid = \" << CurrentThread::tid() << endl;\n    ((TcpConnection*)tcp)->send(str + \"\\n\");\n}\n\n//fib is short for Fibonacci, fib is a CPU busy method\nint EchoServer::fib(int n)\n{\n    return (n == 1 || n == 2) ? 1 : (fib(n-1) + fib(n-2));\n}\n"
  },
  {
    "path": "EchoServer.h",
    "content": "//author voidccc\n#ifndef ECHOSERVER_H\n#define ECHOSERVER_H\n\n#include \"IMuduoUser.h\"\n#include \"IRun.h\"\n#include \"TcpServer.h\"\n#include \"ThreadPool.h\"\n\nclass EchoServer : public IMuduoUser\n                   , public IRun2\n{\npublic:\n    EchoServer(EventLoop* pLoop);\n    ~EchoServer();\n    void start();\n    virtual void onConnection(TcpConnection* pCon);\n    virtual void onMessage(TcpConnection* pCon, Buffer* pBuf);\n    virtual void onWriteComplate(TcpConnection* pCon);\n\n    virtual void run2(const string& str, void* tcp);\nprivate:\n    int fib(int n);\n    EventLoop* _pLoop;\n    TcpServer _pServer;\n    ThreadPool _threadpool;\n    int _timer;\n    int _index;\n};\n\n#endif\n"
  },
  {
    "path": "Epoll.cc",
    "content": "//author voidccc\n\n#include <errno.h>\n\n#include \"Epoll.h\"\n#include \"Channel.h\"\n#include \"Define.h\"\n\n#include <iostream>\nusing namespace std;\n\nconst int kNew = -1;\nconst int kAdded = 1;\n\nEpoll::Epoll()\n{\n    _epollfd = ::epoll_create(1);\n    if (_epollfd <= 0)\n        cout << \"epoll_create error, errno:\" << _epollfd << endl;\n}\n\nEpoll::~Epoll()\n{}\n\nvoid Epoll::poll(vector<Channel*>* pChannels)\n{\n    int fds = ::epoll_wait(_epollfd, _events, MAX_EVENTS, -1);\n    if(fds == -1)\n    {\n        cout << \"epoll_wait error, errno:\" << errno << endl;\n        return;\n    }\n    for(int i = 0; i < fds; i++)\n    {\n        Channel* pChannel = static_cast<Channel*>(_events[i].data.ptr);\n        pChannel->setRevents(_events[i].events);\n        pChannels->push_back(pChannel);\n    }\n}\n\nvoid Epoll::update(Channel* pChannel)\n{\n    int index = pChannel->getIndex();\n    if(index == kNew)\n    {\n        struct epoll_event ev;\n        ev.data.ptr = pChannel;\n        ev.events = pChannel->getEvents();\n        int fd = pChannel->getfd();\n        pChannel->setIndex(kAdded);\n        ::epoll_ctl(_epollfd, EPOLL_CTL_ADD, fd, &ev);\n    }\n    else\n    {\n        struct epoll_event ev;\n        ev.data.ptr = pChannel;\n        ev.events = pChannel->getEvents();\n        int fd = pChannel->getfd();\n        ::epoll_ctl(_epollfd, EPOLL_CTL_MOD, fd, &ev);\n\n    }\n}\n"
  },
  {
    "path": "Epoll.h",
    "content": "//author voidccc\n#ifndef EPOLL_H\n#define EPOLL_H\n\n#include <sys/epoll.h>\n\n#include \"Declear.h\"\n#include \"Define.h\"\n\n#include <vector>\nusing namespace std;\n\nclass Epoll\n{\npublic:\n    Epoll();\n    ~Epoll();\n    void poll(vector<Channel*>* pChannels);\n    void update(Channel* pChannel);\nprivate:\n    int _epollfd;\n    struct epoll_event _events[MAX_EVENTS];\n};\n\n#endif\n"
  },
  {
    "path": "EventLoop.cc",
    "content": "//author voidccc\n\n#include <sys/eventfd.h>\n\n#include \"EventLoop.h\"\n#include \"Channel.h\"\n#include \"Epoll.h\"\n#include \"TimerQueue.h\"\n#include \"Timestamp.h\"\n#include \"Task.h\"\n#include \"CurrentThread.h\"\n\n#include <iostream>\nusing namespace std;\n\nEventLoop::EventLoop()\n    :_quit(false)\n    ,_callingPendingFunctors(false)\n    ,_pPoller(new Epoll()) // Memory Leak !!!\n    ,_threadId(CurrentThread::tid())\n    ,_pTimerQueue(new TimerQueue(this)) // Memory Leak!!!\n{\n    _eventfd = createEventfd();\n    _pEventfdChannel = new Channel(this, _eventfd); // Memory Leak !!!\n    _pEventfdChannel->setCallback(this);\n    _pEventfdChannel->enableReading();\n}\n\nEventLoop::~EventLoop()\n{}\n\nvoid EventLoop::loop()\n{\n    while(!_quit)\n    {\n        vector<Channel*> channels;\n        _pPoller->poll(&channels);\n\n        vector<Channel*>::iterator it;\n        for(it = channels.begin(); it != channels.end(); ++it)\n        {\n            (*it)->handleEvent(); \n        }\n\n        doPendingFunctors();\n    }\n}\n\nvoid EventLoop::update(Channel* pChannel)\n{\n    _pPoller->update(pChannel);\n}\n\nvoid EventLoop::queueInLoop(Task& task)\n{\n    {\n        MutexLockGuard guard(_mutex);\n        _pendingFunctors.push_back(task);\n    }\n\n    if(!isInLoopThread() || _callingPendingFunctors)\n    {\n        wakeup();\n    }\n}\n\nvoid EventLoop::runInLoop(Task& task)\n{\n    if(isInLoopThread())\n    {\n        task.doTask();\n    }\n    else\n    {\n        queueInLoop(task);\n    }\n}\n\nvoid EventLoop::wakeup()\n{\n    uint64_t one = 1;\n    ssize_t n = ::write(_eventfd, &one, sizeof one);\n    if (n != sizeof one)\n    {\n        cout << \"EventLoop::wakeup() writes \" << n << \" bytes instead of 8\" << endl;\n    }\n}\n\nint EventLoop::createEventfd()\n{\n   int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);\n   if (evtfd < 0)\n   {\n       cout << \"Failed in eventfd\" << endl;\n   }\n   return evtfd;\n}\n\nvoid EventLoop::handleRead()\n{\n    uint64_t one = 1;\n    ssize_t n = ::read(_eventfd, &one, sizeof one);\n    if (n != sizeof one)\n    {\n        cout << \"EventEventLoop::handleRead() reads \" << n << \" bytes instead of 8\" << endl;\n    }\n}\n\nvoid EventLoop::handleWrite()\n{}\n\nvoid EventLoop::doPendingFunctors()\n{\n    vector<Task> tempRuns;\n    _callingPendingFunctors = true;\n    {\n        MutexLockGuard guard(_mutex);\n        tempRuns.swap(_pendingFunctors);\n    }\n    vector<Task>::iterator it;\n    for(it = tempRuns.begin(); it != tempRuns.end(); ++it)\n    {\n        it->doTask();\n    }\n    _callingPendingFunctors = false;\n}\nint EventLoop::runAt(Timestamp when, IRun0* pRun)\n{\n    return _pTimerQueue->addTimer(pRun, when, 0.0);\n}\n\nint EventLoop::runAfter(double delay, IRun0* pRun)\n{\n    return _pTimerQueue->addTimer(pRun, Timestamp::nowAfter(delay), 0.0);\n}\n\nint EventLoop::runEvery(double interval, IRun0* pRun)\n{\n    return _pTimerQueue->addTimer(pRun, Timestamp::nowAfter(interval), interval);\n}\n\nvoid EventLoop::cancelTimer(int timerId)\n{\n    _pTimerQueue->cancelTimer(timerId);\n}\n\nbool EventLoop::isInLoopThread()\n{\n    return _threadId == CurrentThread::tid();\n}\n"
  },
  {
    "path": "EventLoop.h",
    "content": "//author voidccc\n#ifndef EVENTLOOP_H\n#define EVENTLOOP_H\n\n#include \"Declear.h\"\n#include \"IChannelCallback.h\"\n#include \"Task.h\"\n#include \"Mutex.h\"\n\n#include <vector>\nusing namespace std;\n\nclass EventLoop : public IChannelCallback\n{\n    public:\n        EventLoop();\n        ~EventLoop();\n        void loop();\n        void update(Channel* pChannel);\n        void queueInLoop(Task& task);\n        void runInLoop(Task& task);\n        int runAt(Timestamp when, IRun0* pRun);\n        int runAfter(double delay, IRun0* pRun);\n        int runEvery(double interval, IRun0* pRun);\n        void cancelTimer(int timerfd);\n        bool isInLoopThread();\n\n        virtual void handleRead();\n        virtual void handleWrite();\n    private:\n        void wakeup();\n        int createEventfd();\n        void doPendingFunctors();\n        bool _quit;\n        bool _callingPendingFunctors;\n        Epoll* _pPoller;\n        int _eventfd;\n        const pid_t _threadId;\n        Channel* _pEventfdChannel;\n        MutexLock _mutex;\n        vector<Task> _pendingFunctors;\n        TimerQueue* _pTimerQueue;\n};\n\n#endif\n"
  },
  {
    "path": "IAcceptorCallback.h",
    "content": "//author voidccc\n#ifndef IACCEPTORCALLBACK_H \n#define IACCEPTORCALLBACK_H\n\nclass IAcceptorCallback\n{\n    public:\n        virtual void newConnection(int sockfd) = 0;\n};\n\n#endif\n"
  },
  {
    "path": "IChannelCallback.h",
    "content": "//author voidccc\n#ifndef ICHANNELCALLBACK_H\n#define ICHANNELCALLBACK_H\n\nclass IChannelCallback\n{\n    public:\n        virtual void handleRead() = 0;\n        virtual void handleWrite() = 0;\n};\n\n#endif\n"
  },
  {
    "path": "IMuduoUser.h",
    "content": "//author voidccc\n#ifndef IMUDUOUSER_H\n#define IMUDUOUSER_H\n\n#include \"Declear.h\"\n#include <string>\nusing namespace std;\n\nclass IMuduoUser\n{\n    public:\n        virtual void onConnection(TcpConnection* pCon) = 0;\n        virtual void onMessage(TcpConnection* pCon, Buffer* pBuf) = 0;\n        virtual void onWriteComplate(TcpConnection* pCon) = 0;\n};\n\n#endif\n"
  },
  {
    "path": "IRun.h",
    "content": "//author voidccc\n#ifndef IRUN_H\n#define IRUN_H\n\n#include <string>\n\nusing namespace std;\n\nclass IRun0\n{\n    public:\n        virtual void run0() = 0;\n};\n\nclass IRun2\n{\n    public:\n        virtual void run2(const string& str, void* param) = 0;\n};\n\n#endif\n"
  },
  {
    "path": "Makefile",
    "content": "#####################################\n# Copyright (c) 1997 George Foot (george.foot@merton.ox.ac.uk)\n# # All rights reserved.\n# ######################################\n# #目标（可执行文档）名称，库（譬如stdcx,iostr,mysql等），头文件路径\nDESTINATION := mini-muduo\nLIBS := pthread\nINCLUDES := .\n\n\nRM := rm -f\n#C,CC或CPP文件的后缀\nPS=cc\n# GNU Make的隐含变量定义\nCC=g++\nCPPFLAGS = -g -Wall -O3 -march=native\nCPPFLAGS += $(addprefix -I,$(INCLUDES))\nCPPFLAGS += -MMD\n\n#以下部分无需修改\nSOURCE := $(wildcard *.$(PS))\nOBJS := $(patsubst %.$(PS),%.o,$(SOURCE))\nDEPS := $(patsubst %.o,%.d,$(OBJS))\nMISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))\nMISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.$(PS),$(MISSING_DEPS)))\n\n.PHONY : all deps objs clean rebuild\n\nall : $(DESTINATION)\n\ndeps : $(DEPS)\n\t\t$(CC) -MM -MMD $(SOURCE)\n\n\nobjs : $(OBJS)\n\nclean :\n\t\t@$(RM) *.o\n\t\t@$(RM) *.d\n\t\t@$(RM) $(DESTINATION)\n\nrebuild: clean all \n\nifneq ($(MISSING_DEPS),)\n$(MISSING_DEPS) :\n\t\t@$(RM) $(patsubst %.d,%.o,$@)\nendif\n\n-include $(DEPS)\n\n$(DESTINATION) : $(OBJS)\n\t\t$(CC) -o $(DESTINATION) $(OBJS) $(addprefix -l,$(LIBS))\n#结束\n"
  },
  {
    "path": "Mutex.h",
    "content": "//author voidccc\n#ifndef MUTEX_H\n#define MUTEX_H\n\n#include <pthread.h>\n\nclass MutexLock\n{\n    public:\n        MutexLock()\n        {\n            pthread_mutex_init(&_mutexid, NULL);\n        }\n        ~MutexLock()\n        {\n            pthread_mutex_destroy(&_mutexid);\n        }\n        void lock()\n        {\n            pthread_mutex_lock(&_mutexid);\n        }\n        void unlock()\n        {\n            pthread_mutex_unlock(&_mutexid);\n        }\n\n        pthread_mutex_t* getPthreadMutex()\n        {\n            return &_mutexid;       \n        }\n    private:\n        pthread_mutex_t _mutexid;\n};\n\nclass MutexLockGuard\n{\n    public:\n        MutexLockGuard(MutexLock& mutex)\n            :_mutex(mutex)\n        {\n            _mutex.lock();        \n        }\n        ~MutexLockGuard()\n        {\n            _mutex.unlock();        \n        }\n    private:\n        MutexLock& _mutex;\n};\n\n#endif\n"
  },
  {
    "path": "README.md",
    "content": "mini-muduo\n==========\na mini implementation of muduo\n\ntags:\n\nv0.01\n1 epoll example\n\nv0.02\n1 Add TcpServer, TcpServer is the first object in code.\n\nv0.03\n1 Add Channel, Channel is the observer of socket fd.\n2 Add IChannelCallback, move event handler from epoll loop to a callback function in TcpServer.\n\nv0.04\n1 Add Acceptor, Acceptor is the observer of listening socket fd.\n2 Add TcpConnection, TcpConnection is the observer of read/write socket fd.\n\nv0.05\n1 Add EventLoop, EventLoop is the wrapper of \"for\" loop.\n2 Add Epoll, Epoll is the wrapper of epoll file descriptor.\n\nv0.06\n1 Add EchoServer, EchoServer is the user of mini-muduo library.\n\nv0.07\n1 Add input/output buffer.\n\nv0.08\n1 Add input/output buffer.(Buffer and onWriteComplate)\n\nv0.09\n1 Add Timer.\n\nv0.10\n1 Better naming convention\n\nv0.11\n1 remove muti-thread specific code\n2 32-bit/64-bit support\n\nv0.12\n1 Foundations of multi-thread\n"
  },
  {
    "path": "Task.cc",
    "content": "#include \"Task.h\"\n\n#include \"IRun.h\"\n\nTask::Task(IRun0* func)\n    :_func0(func)\n    ,_func2(NULL)\n    ,_param(NULL)\n{\n\n}\n\nTask::Task(IRun2* func, const string& str, void* param)\n    :_func0(NULL)\n    ,_func2(func)\n    ,_str(str)\n    ,_param(param)\n{\n\n}\n\nvoid Task::doTask()\n{\n    if(_func0) {\n        _func0->run0();\n    } else {\n        _func2->run2(_str, _param);\n    }\n}\n"
  },
  {
    "path": "Task.h",
    "content": "//author voidccc\n#ifndef TASK_H\n#define TASK_H \n\n#include \"Declear.h\"\n#include <string>\n\nclass Task\n{\n    public:\n        Task(IRun0* func);\n        Task(IRun2* func, const std::string& str, void* param);\n        void doTask();\n    private:\n        IRun0* _func0;\n        IRun2* _func2;\n        std::string _str;\n        void* _param;\n};\n\n#endif\n"
  },
  {
    "path": "TcpConnection.cc",
    "content": "//author voidccc\n\n#include <errno.h>\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <fcntl.h>\n\n#include \"TcpConnection.h\"\n#include \"Channel.h\"\n#include \"EventLoop.h\"\n#include \"Define.h\"\n#include \"IMuduoUser.h\"\n#include \"Task.h\"\n\n#include <string.h> //for bzero\n#include <iostream>\nusing namespace std;\n\nTcpConnection::TcpConnection(int sockfd, EventLoop* pLoop)\n    :_sockfd(sockfd)\n    ,_pLoop(pLoop)\n    ,_pUser(NULL)\n{\n    _pSocketChannel = new Channel(_pLoop, _sockfd); // Memory Leak !!!\n    _pSocketChannel->setCallback(this);\n    _pSocketChannel->enableReading();\n}\n\nTcpConnection::~TcpConnection()\n{}\n\nvoid TcpConnection::handleRead()\n{\n    int sockfd = _pSocketChannel->getfd();\n    int readlength;\n    char line[MAX_LINE];\n    if(sockfd < 0)\n    {\n        cout << \"EPOLLIN sockfd < 0 error \" << endl;\n        return;\n    }\n    bzero(line, MAX_LINE);\n    if((readlength = read(sockfd, line, MAX_LINE)) < 0)\n    {\n        if(errno == ECONNRESET)\n        {\n            cout << \"ECONNREST closed socket fd:\" << sockfd << endl;\n            close(sockfd);\n        }\n    }\n    else if(readlength == 0)\n    {\n        cout << \"read 0 closed socket fd:\" << sockfd << endl;\n        close(sockfd);\n    }\n    else\n    {\n        string linestr(line, readlength);\n        _inBuf.append(linestr);\n        _pUser->onMessage(this, &_inBuf);\n    }\n}\n\nvoid TcpConnection::handleWrite()\n{\n    int sockfd = _pSocketChannel->getfd();\n    if(_pSocketChannel->isWriting())\n    {\n        int n = ::write(sockfd, _outBuf.peek(), _outBuf.readableBytes());\n        if( n > 0)\n        {\n            cout << \"write \" << n << \" bytes data again\" << endl;\n            _outBuf.retrieve(n);\n            if(_outBuf.readableBytes() == 0)\n            {\n                _pSocketChannel->disableWriting(); //remove EPOLLOUT\n                Task task(this);\n                _pLoop->queueInLoop(task); //invoke onWriteComplate\n            }\n        }\n    }\n}\n\nvoid TcpConnection::send(const string& message)\n{\n    if(_pLoop->isInLoopThread())\n    {\n        sendInLoop(message);\n    }\n    else\n    {\n        Task task(this, message, this);\n        _pLoop->runInLoop(task);\n    }\n}\n\nvoid TcpConnection::sendInLoop(const string& message)\n{\n    int n = 0;\n    if(_outBuf.readableBytes() == 0)\n    {\n        n = ::write(_sockfd, message.c_str(), message.size());\n        if(n < 0)\n            cout << \"write error\" << endl;\n        if(n == static_cast<int>(message.size()))\n        {\n            Task task(this);\n            _pLoop->queueInLoop(task); //invoke onWriteComplate\n        }\n    }\n\n    if( n < static_cast<int>(message.size()))\n    {\n        _outBuf.append(message.substr(n, message.size()));\n        if(!_pSocketChannel->isWriting())\n        {\n            _pSocketChannel->enableWriting(); //add EPOLLOUT\n        }\n    }\n}\n\nvoid TcpConnection::connectEstablished()\n{\n    if(_pUser)\n        _pUser->onConnection(this);\n}\n\nvoid TcpConnection::setUser(IMuduoUser* user)\n{\n    _pUser = user;\n}\n\nvoid TcpConnection::run0()\n{\n    _pUser->onWriteComplate(this);\n}\n\nvoid TcpConnection::run2(const string& message, void* param)\n{\n    sendInLoop(message);\n}\n"
  },
  {
    "path": "TcpConnection.h",
    "content": "//author voidccc\n#ifndef TCPCONNECTION_H\n#define TCPCONNECTION_H\n\n#include \"Declear.h\"\n#include \"IChannelCallback.h\"\n#include \"Buffer.h\"\n#include \"IRun.h\"\n\n#include <string>\nusing namespace std;\n\nclass TcpConnection : public IChannelCallback\n                      , public IRun0\n                      , public IRun2\n{\n    public:\n        TcpConnection(int sockfd, EventLoop* pLoop);\n        ~TcpConnection();\n        void send(const string& message);\n        void sendInLoop(const string& message);\n        void connectEstablished();\n        void setUser(IMuduoUser* pUser);\n\n        void setCallback(IAcceptorCallback* pCallback);\n        virtual void handleRead();\n        virtual void handleWrite();\n        virtual void run0();\n        virtual void run2(const string& message, void* param);\n    private:\n        int _sockfd;\n        Channel* _pSocketChannel;\n        EventLoop* _pLoop;\n        IMuduoUser* _pUser;\n        Buffer _inBuf;\n        Buffer _outBuf;\n};\n\n#endif\n"
  },
  {
    "path": "TcpServer.cc",
    "content": "//author voidccc\n\n#include <errno.h>\n\n#include \"TcpServer.h\"\n#include \"Channel.h\"\n#include \"Acceptor.h\"\n#include \"TcpConnection.h\"\n\n#include <vector>\n\nTcpServer::TcpServer(EventLoop* pLoop)\n    :_pAcceptor(NULL)\n    ,_pLoop(pLoop)\n    ,_pUser(NULL)\n{\n}\n\nTcpServer::~TcpServer()\n{\n}\n\nvoid TcpServer::start()\n{\n    _pAcceptor = new Acceptor(_pLoop); // Memory Leak !!!\n    _pAcceptor->setCallback(this);\n    _pAcceptor->start();\n}\n\nvoid TcpServer::newConnection(int sockfd)\n{\n    TcpConnection* tcp = new TcpConnection(sockfd, _pLoop); // Memory Leak !!!\n    _connections[sockfd] = tcp;\n    tcp->setUser(_pUser);\n    tcp->connectEstablished();\n}\n\nvoid TcpServer::setCallback(IMuduoUser* user)\n{\n    _pUser = user;\n}\n"
  },
  {
    "path": "TcpServer.h",
    "content": "//author voidccc\n#ifndef TCPSERVER_H\n#define TCPSERVER_H\n\n#include <sys/epoll.h>\n\n#include \"Declear.h\"\n#include \"Define.h\"\n#include \"IAcceptorCallback.h\"\n#include \"IMuduoUser.h\"\n\n#include <map>\nusing namespace std;\n\nclass TcpServer : public IAcceptorCallback\n{\n    public:\n        TcpServer(EventLoop* pLoop);\n        ~TcpServer();\n        void start();\n        void setCallback(IMuduoUser* pUser);\n        virtual void newConnection(int sockfd);\n    private:\n        struct epoll_event _events[MAX_EVENTS];\n        map<int, TcpConnection*> _connections;\n        Acceptor* _pAcceptor;\n        EventLoop* _pLoop;\n        IMuduoUser* _pUser;\n};\n\n#endif\n"
  },
  {
    "path": "Thread.cc",
    "content": "//author voidccc\n#include \"Thread.h\"\n\n#include <unistd.h>\n#include <sys/syscall.h>\n#include <pthread.h>\n\nnamespace CurrentThread\n{\n  __thread int t_cachedTid = 0;\n}\n\nvoid* globalRun(void* arg)\n{\n    ((Task*)arg)->doTask();\n    return 0;\n}\n\nThread::Thread(Task& task)\n    :_task(task)\n{ }\n\nvoid Thread::start()\n{\n    pthread_t t;\n    ::pthread_create(&t, NULL, globalRun, &_task);\n}\n\npid_t Thread::gettid()\n{\n    return static_cast<pid_t>(::syscall(SYS_gettid));\n}\n\n"
  },
  {
    "path": "Thread.h",
    "content": "//author voidccc\n#ifndef THREAD_H\n#define THREAD_H\n\n#include \"Declear.h\"\n#include \"Task.h\"\n\nclass Thread\n{\n    public:\n        Thread(Task& task);\n        void start();\n        pid_t gettid();\n    private:\n        Task _task;\n};\n\n#endif\n"
  },
  {
    "path": "ThreadPool.cc",
    "content": "//author voidccc\n\n#include \"ThreadPool.h\"\n#include \"Thread.h\"\n\nThreadPool::ThreadPool() { }\n\nvoid ThreadPool::start(int numThreads)\n{\n    _threads.reserve(numThreads);\n    for(int i = 0 ; i < numThreads; i++)\n    {\n        Task task(this);\n        Thread* p = new Thread(task);\n        _threads.push_back(p);\n        p->start();\n    }\n}\n\n//virtual for Thread\nvoid ThreadPool::addTask(Task& task)\n{\n    _tasks.put(task);\n}\n\n//virtual for Thread class\nvoid ThreadPool::run0()\n{\n    runInThread();\n}\n\nvoid ThreadPool::runInThread()\n{\n    while(true)\n    {\n        _tasks.take().doTask();\n    }\n}\n"
  },
  {
    "path": "ThreadPool.h",
    "content": "//author voidccc\n#ifndef THREADPOOL_H\n#define THREADPOOL_H\n\n#include \"Declear.h\"\n#include \"BlockingQueue.h\"\n#include \"Task.h\"\n#include \"IRun.h\"\n\n#include <vector>\nusing namespace std;\n\nclass ThreadPool : public IRun0\n{\n    public:\n        ThreadPool();\n        void start(int numThreads);\n        void addTask(Task& task);\n        virtual void run0();\n    private:\n        void runInThread();\n        BlockingQueue<Task> _tasks;\n        vector<Thread*> _threads;\n};\n\n#endif\n"
  },
  {
    "path": "Timer.h",
    "content": "#ifndef TIMER_H\n#define TIMER_H\n\n#include \"Declear.h\"\n\n#include \"IRun.h\"\n\nclass Timer\n{\n    public:\n        Timer(Timestamp stamp, IRun0* pRun, double interval)\n            :_stamp(stamp)\n             ,_id(stamp)\n             ,_pRun0(pRun)\n             ,_interval(interval)\n    {}\n        Timestamp getStamp()\n        {\n            return _stamp;\n        }\n        Timestamp getId()\n        {\n            return _id;\n        }\n        void timeout()\n        {\n            _pRun0->run0();\n        }\n\n        bool isRepeat()\n        {\n            return _interval > 0.0;\n        }\n\n        void moveToNext()\n        {\n            _stamp = Timestamp::nowAfter(_interval);\n        }\n    private:\n        Timestamp _stamp;\n        Timestamp _id;\n        IRun0* _pRun0;\n        double _interval;//seconds\n};\n\n#endif\n"
  },
  {
    "path": "TimerQueue.cc",
    "content": "//author voidccc\n\n#include <sys/timerfd.h>\n#include <inttypes.h>\n#include <stdio.h>\n#include <strings.h>\n\n#include \"TimerQueue.h\"\n#include \"Channel.h\"\n#include \"EventLoop.h\"\n#include \"Timestamp.h\"\n#include \"Timer.h\"\n#include \"Task.h\"\n\n#include <iostream>\n#include <string>\n\nusing namespace std;\n\n#define UINTPTR_MAX 0xffffffff\n\nTimerQueue::TimerQueue(EventLoop *pLoop)\n    :_timerfd(createTimerfd())\n    ,_pLoop(pLoop)\n    ,_pTimerfdChannel(new Channel(_pLoop, _timerfd)) // Memory Leak !!!\n{\n    _pTimerfdChannel->setCallback(this);\n    _pTimerfdChannel->enableReading();\n}\n\nTimerQueue::~TimerQueue()\n{\n    ::close(_timerfd);\n}\n\nvoid TimerQueue::run2(const string& str, void* timer)\n{\n    if(str == \"addtimer\")\n    {\n        doAddTimer((Timer*)timer);\n    }\n    else if(str == \"canceltimer\")\n    {\n        doCancelTimer((Timer*)timer);\n    }\n    else { }\n}\n\nvoid TimerQueue::doAddTimer(Timer* pTimer)\n{\n    bool earliestChanged = insert(pTimer);\n    if(earliestChanged)\n    {\n        resetTimerfd(_timerfd, pTimer->getStamp());\n    }\n}\n\nvoid TimerQueue::doCancelTimer(Timer* pTimer)\n{\n    Entry e(pTimer->getId(), pTimer);\n    TimerList::iterator it;\n    for(it = _pTimers.begin(); it != _pTimers.end(); ++it)\n    {\n        if(it->second == pTimer)\n        {\n            _pTimers.erase(it);\n            break;\n        }\n    }\n}\n\n///////////////////////////////////////\n/// Add a timer to the system\n/// @param pRun: callback interface\n/// @param when: time\n/// @param interval:\n///     0 = happen only once, no repeat\n///     n = happen after the first time every n seconds\n/// @return the process unique id of the timer\nlong TimerQueue::addTimer(IRun0* pRun, Timestamp when, double interval)\n{\n    Timer* pAddTimer = new Timer(when, pRun, interval); //Memory Leak !!!\n    string str(\"addTimer\");\n    Task task(this, str, pAddTimer);\n    _pLoop->queueInLoop(task);\n    return (long)(pAddTimer);\n}\n\nvoid TimerQueue::cancelTimer(long timerId)\n{\n    Timer* pCancel = (Timer*)(timerId);\n    string str(\"canceltimer\");\n    Task task(this, str, pCancel);\n    _pLoop->queueInLoop(task);\n}\n\nvoid TimerQueue::handleRead()\n{\n    Timestamp now(Timestamp::now());\n    readTimerfd(_timerfd, now);\n\n    vector<Entry> expired = getExpired(now);\n    vector<Entry>::iterator it;\n    for(it = expired.begin(); it != expired.end(); ++it)\n    {\n        it->second->timeout();\n    }\n    reset(expired, now);\n}\n\nvoid TimerQueue::handleWrite()\n{}\n\nint TimerQueue::createTimerfd()\n{\n    int timerfd = ::timerfd_create(CLOCK_MONOTONIC,\n            TFD_NONBLOCK | TFD_CLOEXEC);\n    if(timerfd < 0)\n    {\n        cout << \"failed in timerfd_create\" << endl;\n    }\n    return timerfd;\n}\n\nstd::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)\n{\n    std::vector<Entry> expired;\n    Entry sentry(now, reinterpret_cast<Timer*>(UINTPTR_MAX));\n    TimerList::iterator end = _pTimers.lower_bound(sentry);\n    copy(_pTimers.begin(), end, back_inserter(expired));\n    _pTimers.erase(_pTimers.begin(), end);\n    return expired;\n}\n\nvoid TimerQueue::readTimerfd(int timerfd, Timestamp now)\n{\n    uint64_t howmany;\n    ssize_t n = ::read(timerfd, &howmany, sizeof(howmany));\n    if (n != sizeof(howmany))\n    {\n        cout << \"Timer::readTimerfd() error \" << endl;\n    }\n}\n\nvoid TimerQueue::reset(const vector<Entry>& expired, Timestamp now)\n{\n    vector<Entry>::const_iterator it;\n    for(it = expired.begin(); it != expired.end(); ++it)\n    {\n        if(it->second->isRepeat())\n        {\n            it->second->moveToNext();\n            insert(it->second);\n        }\n    }\n\n    Timestamp nextExpire;\n    if(!_pTimers.empty())\n    {\n        nextExpire = _pTimers.begin()->second->getStamp();\n    }\n    if(nextExpire.valid())\n    {\n        resetTimerfd(_timerfd, nextExpire);\n    }\n}\n\nvoid TimerQueue::resetTimerfd(int timerfd, Timestamp stamp)\n{\n    struct itimerspec newValue;\n    struct itimerspec oldValue;\n    bzero(&newValue, sizeof(newValue));\n    bzero(&oldValue, sizeof(oldValue));\n    newValue.it_value = howMuchTimeFromNow(stamp);\n    int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);\n    if(ret)\n    {\n        cout << \"timerfd_settime error\" << endl;\n    }\n}\n\nbool TimerQueue::insert(Timer* pTimer)\n{\n    bool earliestChanged = false;\n    Timestamp when = pTimer->getStamp();\n    TimerList::iterator it = _pTimers.begin();\n    if(it == _pTimers.end() || when < it->first)\n    {\n        earliestChanged = true;\n    }\n    pair<TimerList::iterator, bool> result\n       = _pTimers.insert(Entry(when, pTimer));\n    if(!(result.second))\n    {\n        cout << \"_pTimers.insert() error \" << endl;\n    }\n\n    return earliestChanged;\n}\n\nstruct timespec TimerQueue::howMuchTimeFromNow(Timestamp when)\n{\n    int64_t microseconds = when.microSecondsSinceEpoch()\n        - Timestamp::now().microSecondsSinceEpoch();\n    if (microseconds < 100)\n    {\n        microseconds = 100;\n    }\n    struct timespec ts;\n    ts.tv_sec = static_cast<time_t>(\n            microseconds / Timestamp::kMicroSecondsPerSecond);\n    ts.tv_nsec = static_cast<long>(\n            (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);\n    return ts;\n}\n"
  },
  {
    "path": "TimerQueue.h",
    "content": "// author voidccc\n#ifndef TIMERQUEUE_H\n#define TIMERQUEUE_H\n\n#include \"Declear.h\"\n#include \"IChannelCallback.h\"\n#include \"IRun.h\"\n#include \"Timestamp.h\"\n\n#include <vector>\n#include <set>\nusing namespace std;\n\nclass TimerQueue : public IChannelCallback\n                 , public IRun2\n{\n    public:\n\n        TimerQueue(EventLoop* pLoop);\n        ~TimerQueue();\n        void doAddTimer(Timer* timer);\n        void doCancelTimer(Timer* timer);\n        long addTimer(IRun0* pRun,\n                Timestamp when,\n                double interval);\n        void cancelTimer(long timerId);\n\n        virtual void run2(const string& str, void* timer);\n\n        virtual void handleRead();\n        virtual void handleWrite();\n\n    private:\n        typedef std::pair<Timestamp, Timer*> Entry;\n        typedef std::set<Entry> TimerList;\n\n        int createTimerfd();\n        vector<TimerQueue::Entry> getExpired(Timestamp now);\n        void readTimerfd(int timerfd, Timestamp now);\n        void reset(const vector<Entry>& expired, Timestamp now);\n        void resetTimerfd(int timerfd, Timestamp stamp);\n        bool insert(Timer* pItem);\n        struct timespec howMuchTimeFromNow(Timestamp when);\n\n        int _timerfd;\n        TimerList _pTimers;\n        EventLoop* _pLoop;\n        Channel* _pTimerfdChannel;\n};\n\n#endif\n"
  },
  {
    "path": "Timestamp.cc",
    "content": "//author voidccc\n\n#include <sys/time.h>\n#include <stdio.h>\n#define __STDC_FORMAT_MACROS\n#include <inttypes.h>\n#undef __STDC_FORMAT_MACROS\n\n#include \"Timestamp.h\"\n\n#include <iostream>\n\nTimestamp::Timestamp(double microSeconds)\n    :_microSecondsSinceEpoch(microSeconds)\n{}\n\nTimestamp::~Timestamp()\n{}\n\nbool Timestamp::valid()\n{\n    return _microSecondsSinceEpoch > 0;\n}\n\nint64_t Timestamp::microSecondsSinceEpoch()\n{\n    return _microSecondsSinceEpoch;\n}\n\nstring Timestamp::toString() const\n{\n  char buf[32] = {0};\n  int64_t seconds = _microSecondsSinceEpoch / kMicroSecondsPerSecond;\n  int64_t microseconds = _microSecondsSinceEpoch % kMicroSecondsPerSecond;\n  snprintf(buf, sizeof(buf)-1, \"%\" PRId64 \".%06\" PRId64 \"\", seconds, microseconds);\n  return buf;\n}\n\nTimestamp Timestamp::now()\n{\n    return Timestamp(Timestamp::nowMicroSeconds());\n}\n\nTimestamp Timestamp::nowAfter(double seconds)\n{\n    return Timestamp(Timestamp::nowMicroSeconds() + kMicroSecondsPerSecond * seconds);\n}\n\ndouble Timestamp::nowMicroSeconds()\n{\n    struct timeval tv;\n    gettimeofday(&tv, NULL);\n    int64_t seconds = tv.tv_sec;\n    return seconds * kMicroSecondsPerSecond + tv.tv_usec;\n}\n\nbool operator<(Timestamp l, Timestamp r)\n{\n    return l.microSecondsSinceEpoch() < r.microSecondsSinceEpoch();\n}\n\nbool operator==(Timestamp l, Timestamp r)\n{\n    cout << \"operator ==\" << endl;\n    return l.microSecondsSinceEpoch() == r.microSecondsSinceEpoch();\n}\n\n"
  },
  {
    "path": "Timestamp.h",
    "content": "//author voidccc\n#ifndef TIMESTAMP_H\n#define TIMESTAMP_H\n\n#include <sys/types.h>\n#include <string>\nusing namespace std;\n\nclass Timestamp\n{\npublic:\n    Timestamp(double microSeconds = 0.0);\n    ~Timestamp();\n    bool valid();\n    int64_t microSecondsSinceEpoch();\n    string toString() const;\n\n    static Timestamp now();\n    static Timestamp nowAfter(double seconds);\n    static double nowMicroSeconds();\n    static const int kMicroSecondsPerSecond = 1000 * 1000;\nprivate:\n    int64_t _microSecondsSinceEpoch;\n};\nbool operator <(Timestamp l, Timestamp r);\nbool operator ==(Timestamp l, Timestamp r);\n\n#endif\n"
  },
  {
    "path": "main.cc",
    "content": "//author voidccc\n\n#include \"TcpServer.h\"\n#include \"EventLoop.h\"\n#include \"EchoServer.h\"\n\nint main(int args, char** argv)\n{\n    EventLoop loop;\n    EchoServer echoserver(&loop);\n    echoserver.start();\n    loop.loop();\n    return 0;\n}\n"
  }
]