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