mirror of
https://github.com/Relintai/rcpp_framework.git
synced 2025-04-20 01:43:12 +02:00
Removed the trantor module. I had to merge it with the drogon modules (temporarily).
This commit is contained in:
parent
e50dc2560d
commit
cde728db20
@ -1,27 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
Import("env_mod")
|
||||
Import("env")
|
||||
|
||||
env_mod.core_sources = []
|
||||
|
||||
env_mod.add_source_files(env_mod.core_sources, "trantor/net/*.cc")
|
||||
env_mod.add_source_files(env_mod.core_sources, "trantor/net/inner/*.cc")
|
||||
env_mod.add_source_files(env_mod.core_sources, "trantor/net/inner/poller/*.cc")
|
||||
|
||||
env_mod.add_source_files(env_mod.core_sources, "trantor/utils/AsyncFileLogger.cc")
|
||||
env_mod.add_source_files(env_mod.core_sources, "trantor/utils/ConcurrentTaskQueue.cc")
|
||||
env_mod.add_source_files(env_mod.core_sources, "trantor/utils/Date.cc")
|
||||
env_mod.add_source_files(env_mod.core_sources, "trantor/utils/Logger.cc")
|
||||
env_mod.add_source_files(env_mod.core_sources, "trantor/utils/LogStream.cc")
|
||||
env_mod.add_source_files(env_mod.core_sources, "trantor/utils/MsgBuffer.cc")
|
||||
env_mod.add_source_files(env_mod.core_sources, "trantor/utils/SerialTaskQueue.cc")
|
||||
env_mod.add_source_files(env_mod.core_sources, "trantor/utils/TimingWheel.cc")
|
||||
|
||||
#windows only
|
||||
#env_mod.add_source_files(env_mod.core_sources, "third_party/wepoll/*.c")
|
||||
#env_mod.add_source_files(env_mod.core_sources, "trantor/utils/WindowsSupport.cc")
|
||||
|
||||
# Build it all as a library
|
||||
lib = env_mod.add_library("trantor", env_mod.core_sources)
|
||||
env.Prepend(LIBS=[lib])
|
@ -1,34 +0,0 @@
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
|
||||
|
||||
def is_active():
|
||||
return True
|
||||
|
||||
|
||||
def get_name():
|
||||
return "trantor"
|
||||
|
||||
|
||||
def can_build():
|
||||
return False
|
||||
|
||||
|
||||
def get_opts():
|
||||
return []
|
||||
|
||||
def get_flags():
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def configure(env):
|
||||
#env.Append(CXX=["-std=c++17"])
|
||||
|
||||
env.Prepend(CPPPATH=["#modules/trantor"])
|
||||
env.Prepend(CPPPATH=["#modules/trantor/trantor/net"])
|
||||
env.Prepend(CPPPATH=["#modules/trantor/trantor/net/inner"])
|
||||
env.Prepend(CPPPATH=["#modules/trantor/trantor/utils"])
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
255976d89866d556efd24264f65765f3e02bc32b
|
@ -1,57 +0,0 @@
|
||||
// Trantor - A non-blocking I/O based TCP network library, using C++14/17,
|
||||
// Copyright (c) 2016-2021, Tao An. All rights reserved.
|
||||
// https://github.com/an-tao/trantor
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of Tao An nor the names of other contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Muduo - A reactor-based C++ network library for Linux
|
||||
// Copyright (c) 2010, Shuo Chen. All rights reserved.
|
||||
// http://code.google.com/p/muduo/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of Shuo Chen nor the names of other contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,49 +0,0 @@
|
||||
# TRANTOR
|
||||
|
||||
[](https://travis-ci.org/an-tao/trantor)
|
||||
[](https://ci.appveyor.com/project/an-tao/trantor/branch/master)
|
||||
[](https://lgtm.com/projects/g/an-tao/trantor/context:cpp)
|
||||
|
||||
|
||||
## Overview
|
||||
A non-blocking I/O cross-platform TCP network library, using C++14.
|
||||
Drawing on the design of Muduo Library
|
||||
|
||||
## suported platforms
|
||||
- Linux
|
||||
- MacOS
|
||||
- UNIX(BSD)
|
||||
- Windows
|
||||
|
||||
## Feature highlights
|
||||
- non-blocking I/O
|
||||
- cross-platform
|
||||
- Thread pool
|
||||
- Lock free design
|
||||
- Support SSL
|
||||
- Server and Client
|
||||
|
||||
|
||||
## Build
|
||||
```shell
|
||||
git clone https://github.com/an-tao/trantor.git
|
||||
cd trantor
|
||||
cmake -Bbuild -H.
|
||||
cd build
|
||||
make -j
|
||||
```
|
||||
|
||||
## Licensing
|
||||
Trantor - A non-blocking I/O based TCP network library, using C++14.
|
||||
|
||||
Copyright (c) 2016-2021, Tao An. All rights reserved.
|
||||
|
||||
https://github.com/an-tao/trantor
|
||||
|
||||
For more information see [License](License)
|
||||
|
||||
## Community
|
||||
[Gitter](https://gitter.im/drogon-web/community)
|
||||
|
||||
## Documentation
|
||||
[DocsForge](https://trantor.docsforge.com/)
|
@ -1,42 +0,0 @@
|
||||
|
||||
#ifndef TRANTOR_EXPORT_H
|
||||
#define TRANTOR_EXPORT_H
|
||||
|
||||
#ifdef TRANTOR_STATIC_DEFINE
|
||||
# define TRANTOR_EXPORT
|
||||
# define TRANTOR_NO_EXPORT
|
||||
#else
|
||||
# ifndef TRANTOR_EXPORT
|
||||
# ifdef trantor_EXPORTS
|
||||
/* We are building this library */
|
||||
# define TRANTOR_EXPORT
|
||||
# else
|
||||
/* We are using this library */
|
||||
# define TRANTOR_EXPORT
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# ifndef TRANTOR_NO_EXPORT
|
||||
# define TRANTOR_NO_EXPORT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef TRANTOR_DEPRECATED
|
||||
# define TRANTOR_DEPRECATED __attribute__ ((__deprecated__))
|
||||
#endif
|
||||
|
||||
#ifndef TRANTOR_DEPRECATED_EXPORT
|
||||
# define TRANTOR_DEPRECATED_EXPORT TRANTOR_EXPORT TRANTOR_DEPRECATED
|
||||
#endif
|
||||
|
||||
#ifndef TRANTOR_DEPRECATED_NO_EXPORT
|
||||
# define TRANTOR_DEPRECATED_NO_EXPORT TRANTOR_NO_EXPORT TRANTOR_DEPRECATED
|
||||
#endif
|
||||
|
||||
#if 0 /* DEFINE_NO_DEPRECATED */
|
||||
# ifndef TRANTOR_NO_DEPRECATED
|
||||
# define TRANTOR_NO_DEPRECATED
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* TRANTOR_EXPORT_H */
|
@ -1,110 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Channel.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Channel.h"
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#ifdef _WIN32
|
||||
#include "Wepoll.h"
|
||||
#define POLLIN EPOLLIN
|
||||
#define POLLPRI EPOLLPRI
|
||||
#define POLLOUT EPOLLOUT
|
||||
#define POLLHUP EPOLLHUP
|
||||
#define POLLNVAL 0
|
||||
#define POLLERR EPOLLERR
|
||||
#else
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#include <iostream>
|
||||
namespace trantor
|
||||
{
|
||||
const int Channel::kNoneEvent = 0;
|
||||
|
||||
const int Channel::kReadEvent = POLLIN | POLLPRI;
|
||||
const int Channel::kWriteEvent = POLLOUT;
|
||||
|
||||
Channel::Channel(EventLoop *loop, int fd)
|
||||
: loop_(loop), fd_(fd), events_(0), revents_(0), index_(-1), tied_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Channel::remove()
|
||||
{
|
||||
assert(events_ == kNoneEvent);
|
||||
addedToLoop_ = false;
|
||||
loop_->removeChannel(this);
|
||||
}
|
||||
|
||||
void Channel::update()
|
||||
{
|
||||
loop_->updateChannel(this);
|
||||
}
|
||||
|
||||
void Channel::handleEvent()
|
||||
{
|
||||
// LOG_TRACE<<"revents_="<<revents_;
|
||||
if (tied_)
|
||||
{
|
||||
std::shared_ptr<void> guard = tie_.lock();
|
||||
if (guard)
|
||||
{
|
||||
handleEventSafely();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
handleEventSafely();
|
||||
}
|
||||
}
|
||||
void Channel::handleEventSafely()
|
||||
{
|
||||
if (eventCallback_)
|
||||
{
|
||||
eventCallback_();
|
||||
return;
|
||||
}
|
||||
if ((revents_ & POLLHUP) && !(revents_ & POLLIN))
|
||||
{
|
||||
// LOG_TRACE<<"handle close";
|
||||
if (closeCallback_)
|
||||
closeCallback_();
|
||||
}
|
||||
if (revents_ & (POLLNVAL | POLLERR))
|
||||
{
|
||||
// LOG_TRACE<<"handle error";
|
||||
if (errorCallback_)
|
||||
errorCallback_();
|
||||
}
|
||||
#ifdef __linux__
|
||||
if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))
|
||||
#else
|
||||
if (revents_ & (POLLIN | POLLPRI))
|
||||
#endif
|
||||
{
|
||||
// LOG_TRACE<<"handle read";
|
||||
if (readCallback_)
|
||||
readCallback_();
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if ((revents_ & POLLOUT) && !(revents_ & POLLHUP))
|
||||
#else
|
||||
if (revents_ & POLLOUT)
|
||||
#endif
|
||||
{
|
||||
// LOG_TRACE<<"handle write";
|
||||
if (writeCallback_)
|
||||
writeCallback_();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace trantor
|
@ -1,314 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file Channel.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/exports.h>
|
||||
#include <functional>
|
||||
#include <assert.h>
|
||||
#include <memory>
|
||||
namespace trantor
|
||||
{
|
||||
class EventLoop;
|
||||
/**
|
||||
* @brief This class is used to implement reactor pattern. A Channel object
|
||||
* manages a socket fd. Users use a Channel object to receive write or read
|
||||
* events on the socket it manages.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT Channel : NonCopyable
|
||||
{
|
||||
public:
|
||||
using EventCallback = std::function<void()>;
|
||||
/**
|
||||
* @brief Construct a new Channel instance.
|
||||
*
|
||||
* @param loop The event loop in which the channel works.
|
||||
* @param fd The socket fd.
|
||||
*/
|
||||
Channel(EventLoop *loop, int fd);
|
||||
|
||||
/**
|
||||
* @brief Set the read callback.
|
||||
*
|
||||
* @param cb The callback is called when read event occurs on the socket.
|
||||
* @note One should call the enableReading() method to ensure that the
|
||||
* callback would be called when some data is received on the socket.
|
||||
*/
|
||||
void setReadCallback(const EventCallback &cb)
|
||||
{
|
||||
readCallback_ = cb;
|
||||
};
|
||||
void setReadCallback(EventCallback &&cb)
|
||||
{
|
||||
readCallback_ = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the write callback.
|
||||
*
|
||||
* @param cb The callback is called when write event occurs on the socket.
|
||||
* @note One should call the enableWriting() method to ensure that the
|
||||
* callback would be called when the socket can be written.
|
||||
*/
|
||||
void setWriteCallback(const EventCallback &cb)
|
||||
{
|
||||
writeCallback_ = cb;
|
||||
};
|
||||
void setWriteCallback(EventCallback &&cb)
|
||||
{
|
||||
writeCallback_ = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the close callback.
|
||||
*
|
||||
* @param cb The callback is called when the socket is closed.
|
||||
*/
|
||||
void setCloseCallback(const EventCallback &cb)
|
||||
{
|
||||
closeCallback_ = cb;
|
||||
}
|
||||
void setCloseCallback(EventCallback &&cb)
|
||||
{
|
||||
closeCallback_ = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the error callback.
|
||||
*
|
||||
* @param cb The callback is called when an error occurs on the socket.
|
||||
*/
|
||||
void setErrorCallback(const EventCallback &cb)
|
||||
{
|
||||
errorCallback_ = cb;
|
||||
}
|
||||
void setErrorCallback(EventCallback &&cb)
|
||||
{
|
||||
errorCallback_ = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the event callback.
|
||||
*
|
||||
* @param cb The callback is called when any event occurs on the socket.
|
||||
* @note If the event callback is set to the channel, any other callback
|
||||
* wouldn't be called again.
|
||||
*/
|
||||
void setEventCallback(const EventCallback &cb)
|
||||
{
|
||||
eventCallback_ = cb;
|
||||
}
|
||||
void setEventCallback(EventCallback &&cb)
|
||||
{
|
||||
eventCallback_ = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the fd of the socket.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int fd() const
|
||||
{
|
||||
return fd_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the events enabled on the socket.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int events() const
|
||||
{
|
||||
return events_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the events that occurred on the socket.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int revents() const
|
||||
{
|
||||
return revents_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether there is no event enabled on the socket.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isNoneEvent() const
|
||||
{
|
||||
return events_ == kNoneEvent;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Disable all events on the socket.
|
||||
*
|
||||
*/
|
||||
void disableAll()
|
||||
{
|
||||
events_ = kNoneEvent;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove the socket from the poller in the event loop.
|
||||
*
|
||||
*/
|
||||
void remove();
|
||||
|
||||
/**
|
||||
* @brief Return the event loop.
|
||||
*
|
||||
* @return EventLoop*
|
||||
*/
|
||||
EventLoop *ownerLoop()
|
||||
{
|
||||
return loop_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Enable the read event on the socket.
|
||||
*
|
||||
*/
|
||||
void enableReading()
|
||||
{
|
||||
events_ |= kReadEvent;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable the read event on the socket.
|
||||
*
|
||||
*/
|
||||
void disableReading()
|
||||
{
|
||||
events_ &= ~kReadEvent;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the write event on the socket.
|
||||
*
|
||||
*/
|
||||
void enableWriting()
|
||||
{
|
||||
events_ |= kWriteEvent;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable the write event on the socket.
|
||||
*
|
||||
*/
|
||||
void disableWriting()
|
||||
{
|
||||
events_ &= ~kWriteEvent;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether the write event is enabled on the socket.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isWriting() const
|
||||
{
|
||||
return events_ & kWriteEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether the read event is enabled on the socket.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isReading() const
|
||||
{
|
||||
return events_ & kReadEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set and update the events enabled.
|
||||
*
|
||||
* @param events
|
||||
*/
|
||||
void updateEvents(int events)
|
||||
{
|
||||
events_ = events;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This method is used to ensure that the callback owner is valid
|
||||
* when a callback is called.
|
||||
*
|
||||
* @param obj The callback owner. Usually, the owner is also the owner of
|
||||
* the channel.
|
||||
* @note The 'obj' is kept in a weak_ptr object, so this method does not
|
||||
* cause a circular reference problem.
|
||||
*/
|
||||
void tie(const std::shared_ptr<void> &obj)
|
||||
{
|
||||
tie_ = obj;
|
||||
tied_ = true;
|
||||
}
|
||||
|
||||
static const int kNoneEvent;
|
||||
static const int kReadEvent;
|
||||
static const int kWriteEvent;
|
||||
|
||||
private:
|
||||
friend class EventLoop;
|
||||
friend class EpollPoller;
|
||||
friend class KQueue;
|
||||
void update();
|
||||
void handleEvent();
|
||||
void handleEventSafely();
|
||||
int setRevents(int revt)
|
||||
{
|
||||
// LOG_TRACE<<"revents="<<revt;
|
||||
revents_ = revt;
|
||||
return revt;
|
||||
};
|
||||
int index()
|
||||
{
|
||||
return index_;
|
||||
};
|
||||
void setIndex(int index)
|
||||
{
|
||||
index_ = index;
|
||||
};
|
||||
EventLoop *loop_;
|
||||
const int fd_;
|
||||
int events_;
|
||||
int revents_;
|
||||
int index_;
|
||||
bool addedToLoop_{false};
|
||||
EventCallback readCallback_;
|
||||
EventCallback writeCallback_;
|
||||
EventCallback errorCallback_;
|
||||
EventCallback closeCallback_;
|
||||
EventCallback eventCallback_;
|
||||
std::weak_ptr<void> tie_;
|
||||
bool tied_;
|
||||
};
|
||||
} // namespace trantor
|
@ -1,372 +0,0 @@
|
||||
// Copyright 2010, Shuo Chen. All rights reserved.
|
||||
// http://code.google.com/p/muduo/
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Shuo Chen (chenshuo at chenshuo dot com)
|
||||
|
||||
// Taken from Muduo and modified
|
||||
// Copyright 2016, Tao An. All rights reserved.
|
||||
// https://github.com/an-tao/trantor
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Tao An
|
||||
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
|
||||
#include "Poller.h"
|
||||
#include "TimerQueue.h"
|
||||
#include "Channel.h"
|
||||
|
||||
#include <thread>
|
||||
#include <assert.h>
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
using ssize_t = long long;
|
||||
#else
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#include <iostream>
|
||||
#ifdef __linux__
|
||||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
#include <functional>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
#ifdef __linux__
|
||||
int createEventfd()
|
||||
{
|
||||
int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
||||
if (evtfd < 0)
|
||||
{
|
||||
std::cout << "Failed in eventfd" << std::endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
return evtfd;
|
||||
}
|
||||
const int kPollTimeMs = 10000;
|
||||
#endif
|
||||
thread_local EventLoop *t_loopInThisThread = nullptr;
|
||||
|
||||
EventLoop::EventLoop()
|
||||
: looping_(false),
|
||||
threadId_(std::this_thread::get_id()),
|
||||
quit_(false),
|
||||
poller_(Poller::newPoller(this)),
|
||||
currentActiveChannel_(nullptr),
|
||||
eventHandling_(false),
|
||||
timerQueue_(new TimerQueue(this)),
|
||||
#ifdef __linux__
|
||||
wakeupFd_(createEventfd()),
|
||||
wakeupChannelPtr_(new Channel(this, wakeupFd_)),
|
||||
#endif
|
||||
threadLocalLoopPtr_(&t_loopInThisThread)
|
||||
{
|
||||
if (t_loopInThisThread)
|
||||
{
|
||||
LOG_FATAL << "There is already an EventLoop in this thread";
|
||||
exit(-1);
|
||||
}
|
||||
t_loopInThisThread = this;
|
||||
#ifdef __linux__
|
||||
wakeupChannelPtr_->setReadCallback(std::bind(&EventLoop::wakeupRead, this));
|
||||
wakeupChannelPtr_->enableReading();
|
||||
#elif !defined _WIN32
|
||||
auto r = pipe(wakeupFd_);
|
||||
(void)r;
|
||||
assert(!r);
|
||||
fcntl(wakeupFd_[0], F_SETFL, O_NONBLOCK | O_CLOEXEC);
|
||||
fcntl(wakeupFd_[1], F_SETFL, O_NONBLOCK | O_CLOEXEC);
|
||||
wakeupChannelPtr_ =
|
||||
std::unique_ptr<Channel>(new Channel(this, wakeupFd_[0]));
|
||||
wakeupChannelPtr_->setReadCallback(std::bind(&EventLoop::wakeupRead, this));
|
||||
wakeupChannelPtr_->enableReading();
|
||||
#else
|
||||
poller_->setEventCallback([](uint64_t event) { assert(event == 1); });
|
||||
#endif
|
||||
}
|
||||
#ifdef __linux__
|
||||
void EventLoop::resetTimerQueue()
|
||||
{
|
||||
assertInLoopThread();
|
||||
assert(!looping_);
|
||||
timerQueue_->reset();
|
||||
}
|
||||
#endif
|
||||
void EventLoop::resetAfterFork()
|
||||
{
|
||||
poller_->resetAfterFork();
|
||||
}
|
||||
EventLoop::~EventLoop()
|
||||
{
|
||||
quit();
|
||||
assert(!looping_);
|
||||
t_loopInThisThread = nullptr;
|
||||
#ifdef __linux__
|
||||
close(wakeupFd_);
|
||||
#elif defined _WIN32
|
||||
#else
|
||||
close(wakeupFd_[0]);
|
||||
close(wakeupFd_[1]);
|
||||
#endif
|
||||
}
|
||||
EventLoop *EventLoop::getEventLoopOfCurrentThread()
|
||||
{
|
||||
return t_loopInThisThread;
|
||||
}
|
||||
void EventLoop::updateChannel(Channel *channel)
|
||||
{
|
||||
assert(channel->ownerLoop() == this);
|
||||
assertInLoopThread();
|
||||
poller_->updateChannel(channel);
|
||||
}
|
||||
void EventLoop::removeChannel(Channel *channel)
|
||||
{
|
||||
assert(channel->ownerLoop() == this);
|
||||
assertInLoopThread();
|
||||
if (eventHandling_)
|
||||
{
|
||||
assert(currentActiveChannel_ == channel ||
|
||||
std::find(activeChannels_.begin(),
|
||||
activeChannels_.end(),
|
||||
channel) == activeChannels_.end());
|
||||
}
|
||||
poller_->removeChannel(channel);
|
||||
}
|
||||
void EventLoop::quit()
|
||||
{
|
||||
quit_ = true;
|
||||
|
||||
Func f;
|
||||
while (funcsOnQuit_.dequeue(f))
|
||||
{
|
||||
f();
|
||||
}
|
||||
|
||||
// There is a chance that loop() just executes while(!quit_) and exits,
|
||||
// then EventLoop destructs, then we are accessing an invalid object.
|
||||
// Can be fixed using mutex_ in both places.
|
||||
if (!isInLoopThread())
|
||||
{
|
||||
wakeup();
|
||||
}
|
||||
}
|
||||
void EventLoop::loop()
|
||||
{
|
||||
assert(!looping_);
|
||||
assertInLoopThread();
|
||||
looping_ = true;
|
||||
quit_ = false;
|
||||
|
||||
while (!quit_)
|
||||
{
|
||||
activeChannels_.clear();
|
||||
#ifdef __linux__
|
||||
poller_->poll(kPollTimeMs, &activeChannels_);
|
||||
#else
|
||||
poller_->poll(static_cast<int>(timerQueue_->getTimeout()),
|
||||
&activeChannels_);
|
||||
timerQueue_->processTimers();
|
||||
#endif
|
||||
// TODO sort channel by priority
|
||||
// std::cout<<"after ->poll()"<<std::endl;
|
||||
eventHandling_ = true;
|
||||
for (auto it = activeChannels_.begin(); it != activeChannels_.end();
|
||||
++it)
|
||||
{
|
||||
currentActiveChannel_ = *it;
|
||||
currentActiveChannel_->handleEvent();
|
||||
}
|
||||
currentActiveChannel_ = NULL;
|
||||
eventHandling_ = false;
|
||||
// std::cout << "looping" << endl;
|
||||
doRunInLoopFuncs();
|
||||
}
|
||||
looping_ = false;
|
||||
}
|
||||
void EventLoop::abortNotInLoopThread()
|
||||
{
|
||||
LOG_FATAL << "It is forbidden to run loop on threads other than event-loop "
|
||||
"thread";
|
||||
exit(1);
|
||||
}
|
||||
void EventLoop::runInLoop(const Func &cb)
|
||||
{
|
||||
if (isInLoopThread())
|
||||
{
|
||||
cb();
|
||||
}
|
||||
else
|
||||
{
|
||||
queueInLoop(cb);
|
||||
}
|
||||
}
|
||||
void EventLoop::runInLoop(Func &&cb)
|
||||
{
|
||||
if (isInLoopThread())
|
||||
{
|
||||
cb();
|
||||
}
|
||||
else
|
||||
{
|
||||
queueInLoop(std::move(cb));
|
||||
}
|
||||
}
|
||||
void EventLoop::queueInLoop(const Func &cb)
|
||||
{
|
||||
funcs_.enqueue(cb);
|
||||
if (!isInLoopThread() || !looping_)
|
||||
{
|
||||
wakeup();
|
||||
}
|
||||
}
|
||||
void EventLoop::queueInLoop(Func &&cb)
|
||||
{
|
||||
funcs_.enqueue(std::move(cb));
|
||||
if (!isInLoopThread() || !looping_)
|
||||
{
|
||||
wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
TimerId EventLoop::runAt(const Date &time, const Func &cb)
|
||||
{
|
||||
auto microSeconds =
|
||||
time.microSecondsSinceEpoch() - Date::now().microSecondsSinceEpoch();
|
||||
std::chrono::steady_clock::time_point tp =
|
||||
std::chrono::steady_clock::now() +
|
||||
std::chrono::microseconds(microSeconds);
|
||||
return timerQueue_->addTimer(cb, tp, std::chrono::microseconds(0));
|
||||
}
|
||||
TimerId EventLoop::runAt(const Date &time, Func &&cb)
|
||||
{
|
||||
auto microSeconds =
|
||||
time.microSecondsSinceEpoch() - Date::now().microSecondsSinceEpoch();
|
||||
std::chrono::steady_clock::time_point tp =
|
||||
std::chrono::steady_clock::now() +
|
||||
std::chrono::microseconds(microSeconds);
|
||||
return timerQueue_->addTimer(std::move(cb),
|
||||
tp,
|
||||
std::chrono::microseconds(0));
|
||||
}
|
||||
TimerId EventLoop::runAfter(double delay, const Func &cb)
|
||||
{
|
||||
return runAt(Date::date().after(delay), cb);
|
||||
}
|
||||
TimerId EventLoop::runAfter(double delay, Func &&cb)
|
||||
{
|
||||
return runAt(Date::date().after(delay), std::move(cb));
|
||||
}
|
||||
TimerId EventLoop::runEvery(double interval, const Func &cb)
|
||||
{
|
||||
std::chrono::microseconds dur(
|
||||
static_cast<std::chrono::microseconds::rep>(interval * 1000000));
|
||||
auto tp = std::chrono::steady_clock::now() + dur;
|
||||
return timerQueue_->addTimer(cb, tp, dur);
|
||||
}
|
||||
TimerId EventLoop::runEvery(double interval, Func &&cb)
|
||||
{
|
||||
std::chrono::microseconds dur(
|
||||
static_cast<std::chrono::microseconds::rep>(interval * 1000000));
|
||||
auto tp = std::chrono::steady_clock::now() + dur;
|
||||
return timerQueue_->addTimer(std::move(cb), tp, dur);
|
||||
}
|
||||
void EventLoop::invalidateTimer(TimerId id)
|
||||
{
|
||||
if (isRunning() && timerQueue_)
|
||||
timerQueue_->invalidateTimer(id);
|
||||
}
|
||||
void EventLoop::doRunInLoopFuncs()
|
||||
{
|
||||
callingFuncs_ = true;
|
||||
{
|
||||
// the destructor for the Func may itself insert a new entry into the
|
||||
// queue
|
||||
while (!funcs_.empty())
|
||||
{
|
||||
Func func;
|
||||
while (funcs_.dequeue(func))
|
||||
{
|
||||
func();
|
||||
}
|
||||
}
|
||||
}
|
||||
callingFuncs_ = false;
|
||||
}
|
||||
void EventLoop::wakeup()
|
||||
{
|
||||
// if (!looping_)
|
||||
// return;
|
||||
uint64_t tmp = 1;
|
||||
#ifdef __linux__
|
||||
int ret = write(wakeupFd_, &tmp, sizeof(tmp));
|
||||
(void)ret;
|
||||
#elif defined _WIN32
|
||||
poller_->postEvent(1);
|
||||
#else
|
||||
int ret = write(wakeupFd_[1], &tmp, sizeof(tmp));
|
||||
(void)ret;
|
||||
#endif
|
||||
}
|
||||
void EventLoop::wakeupRead()
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
#ifdef __linux__
|
||||
uint64_t tmp;
|
||||
ret = read(wakeupFd_, &tmp, sizeof(tmp));
|
||||
#elif defined _WIN32
|
||||
#else
|
||||
uint64_t tmp;
|
||||
ret = read(wakeupFd_[0], &tmp, sizeof(tmp));
|
||||
#endif
|
||||
if (ret < 0)
|
||||
LOG_SYSERR << "wakeup read error";
|
||||
}
|
||||
|
||||
void EventLoop::moveToCurrentThread()
|
||||
{
|
||||
if (isRunning())
|
||||
{
|
||||
LOG_FATAL << "EventLoop cannot be moved when running";
|
||||
exit(-1);
|
||||
}
|
||||
if (isInLoopThread())
|
||||
{
|
||||
LOG_WARN << "This EventLoop is already in the current thread";
|
||||
return;
|
||||
}
|
||||
if (t_loopInThisThread)
|
||||
{
|
||||
LOG_FATAL << "There is already an EventLoop in this thread, you cannot "
|
||||
"move another in";
|
||||
exit(-1);
|
||||
}
|
||||
*threadLocalLoopPtr_ = nullptr;
|
||||
t_loopInThisThread = this;
|
||||
threadLocalLoopPtr_ = &t_loopInThisThread;
|
||||
threadId_ = std::this_thread::get_id();
|
||||
}
|
||||
|
||||
void EventLoop::runOnQuit(Func &&cb)
|
||||
{
|
||||
funcsOnQuit_.enqueue(std::move(cb));
|
||||
}
|
||||
|
||||
void EventLoop::runOnQuit(const Func &cb)
|
||||
{
|
||||
funcsOnQuit_.enqueue(cb);
|
||||
}
|
||||
|
||||
} // namespace trantor
|
@ -1,321 +0,0 @@
|
||||
// Copyright 2010, Shuo Chen. All rights reserved.
|
||||
// http://code.google.com/p/muduo/
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Shuo Chen (chenshuo at chenshuo dot com)
|
||||
|
||||
// Taken from Muduo and modified
|
||||
// Copyright 2016, Tao An. All rights reserved.
|
||||
// https://github.com/an-tao/trantor
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Tao An
|
||||
|
||||
#pragma once
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/utils/Date.h>
|
||||
#include <trantor/utils/LockFreeQueue.h>
|
||||
#include <trantor/exports.h>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
class Poller;
|
||||
class TimerQueue;
|
||||
class Channel;
|
||||
using ChannelList = std::vector<Channel *>;
|
||||
using Func = std::function<void()>;
|
||||
using TimerId = uint64_t;
|
||||
enum
|
||||
{
|
||||
InvalidTimerId = 0
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief As the name implies, this class represents an event loop that runs in
|
||||
* a perticular thread. The event loop can handle network I/O events and timers
|
||||
* in asynchronous mode.
|
||||
* @note An event loop object always belongs to a separate thread, and there is
|
||||
* one event loop object at most in a thread. We can call an event loop object
|
||||
* the event loop of the thread it belongs to, or call that thread the thread of
|
||||
* the event loop.
|
||||
*/
|
||||
class TRANTOR_EXPORT EventLoop : NonCopyable
|
||||
{
|
||||
public:
|
||||
EventLoop();
|
||||
~EventLoop();
|
||||
|
||||
/**
|
||||
* @brief Run the event loop. This method will be blocked until the event
|
||||
* loop exits.
|
||||
*
|
||||
*/
|
||||
void loop();
|
||||
|
||||
/**
|
||||
* @brief Let the event loop quit.
|
||||
*
|
||||
*/
|
||||
void quit();
|
||||
|
||||
/**
|
||||
* @brief Assertion that the current thread is the thread to which the event
|
||||
* loop belongs. If the assertion fails, the program aborts.
|
||||
*/
|
||||
void assertInLoopThread()
|
||||
{
|
||||
if (!isInLoopThread())
|
||||
{
|
||||
abortNotInLoopThread();
|
||||
}
|
||||
};
|
||||
#ifdef __linux__
|
||||
/**
|
||||
* @brief Make the timer queue works after calling the fork() function.
|
||||
*
|
||||
*/
|
||||
void resetTimerQueue();
|
||||
#endif
|
||||
/**
|
||||
* @brief Make the event loop works after calling the fork() function.
|
||||
*
|
||||
*/
|
||||
void resetAfterFork();
|
||||
|
||||
/**
|
||||
* @brief Return true if the current thread is the thread to which the event
|
||||
* loop belongs.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isInLoopThread() const
|
||||
{
|
||||
return threadId_ == std::this_thread::get_id();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get the event loop of the current thread. Return nullptr if there
|
||||
* is no event loop in the current thread.
|
||||
*
|
||||
* @return EventLoop*
|
||||
*/
|
||||
static EventLoop *getEventLoopOfCurrentThread();
|
||||
|
||||
/**
|
||||
* @brief Run the function f in the thread of the event loop.
|
||||
*
|
||||
* @param f
|
||||
* @note If the current thread is the thread of the event loop, the function
|
||||
* f is executed directly before the method exiting.
|
||||
*/
|
||||
void runInLoop(const Func &f);
|
||||
void runInLoop(Func &&f);
|
||||
|
||||
/**
|
||||
* @brief Run the function f in the thread of the event loop.
|
||||
*
|
||||
* @param f
|
||||
* @note The difference between this method and the runInLoop() method is
|
||||
* that the function f is executed after the method exiting no matter if the
|
||||
* current thread is the thread of the event loop.
|
||||
*/
|
||||
void queueInLoop(const Func &f);
|
||||
void queueInLoop(Func &&f);
|
||||
|
||||
/**
|
||||
* @brief Run a function at a time point.
|
||||
*
|
||||
* @param time The time to run the function.
|
||||
* @param cb The function to run.
|
||||
* @return TimerId The ID of the timer.
|
||||
*/
|
||||
TimerId runAt(const Date &time, const Func &cb);
|
||||
TimerId runAt(const Date &time, Func &&cb);
|
||||
|
||||
/**
|
||||
* @brief Run a function after a period of time.
|
||||
*
|
||||
* @param delay Represent the period of time in seconds.
|
||||
* @param cb The function to run.
|
||||
* @return TimerId The ID of the timer.
|
||||
*/
|
||||
TimerId runAfter(double delay, const Func &cb);
|
||||
TimerId runAfter(double delay, Func &&cb);
|
||||
|
||||
/**
|
||||
* @brief Run a function after a period of time.
|
||||
* @note Users could use chrono literals to represent a time duration
|
||||
* For example:
|
||||
* @code
|
||||
runAfter(5s, task);
|
||||
runAfter(10min, task);
|
||||
@endcode
|
||||
*/
|
||||
TimerId runAfter(const std::chrono::duration<double> &delay, const Func &cb)
|
||||
{
|
||||
return runAfter(delay.count(), cb);
|
||||
}
|
||||
TimerId runAfter(const std::chrono::duration<double> &delay, Func &&cb)
|
||||
{
|
||||
return runAfter(delay.count(), std::move(cb));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Repeatedly run a function every period of time.
|
||||
*
|
||||
* @param interval The duration in seconds.
|
||||
* @param cb The function to run.
|
||||
* @return TimerId The ID of the timer.
|
||||
*/
|
||||
TimerId runEvery(double interval, const Func &cb);
|
||||
TimerId runEvery(double interval, Func &&cb);
|
||||
|
||||
/**
|
||||
* @brief Repeatedly run a function every period of time.
|
||||
* Users could use chrono literals to represent a time duration
|
||||
* For example:
|
||||
* @code
|
||||
runEvery(5s, task);
|
||||
runEvery(10min, task);
|
||||
runEvery(0.1h, task);
|
||||
@endcode
|
||||
*/
|
||||
TimerId runEvery(const std::chrono::duration<double> &interval,
|
||||
const Func &cb)
|
||||
{
|
||||
return runEvery(interval.count(), cb);
|
||||
}
|
||||
TimerId runEvery(const std::chrono::duration<double> &interval, Func &&cb)
|
||||
{
|
||||
return runEvery(interval.count(), std::move(cb));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Invalidate the timer identified by the given ID.
|
||||
*
|
||||
* @param id The ID of the timer.
|
||||
*/
|
||||
void invalidateTimer(TimerId id);
|
||||
|
||||
/**
|
||||
* @brief Move the EventLoop to the current thread, this method must be
|
||||
* called before the loop is running.
|
||||
*
|
||||
*/
|
||||
void moveToCurrentThread();
|
||||
|
||||
/**
|
||||
* @brief Update channel status. This method is usually used internally.
|
||||
*
|
||||
* @param chl
|
||||
*/
|
||||
void updateChannel(Channel *chl);
|
||||
|
||||
/**
|
||||
* @brief Remove a channel from the event loop. This method is usually used
|
||||
* internally.
|
||||
*
|
||||
* @param chl
|
||||
*/
|
||||
void removeChannel(Channel *chl);
|
||||
|
||||
/**
|
||||
* @brief Return the index of the event loop.
|
||||
*
|
||||
* @return size_t
|
||||
*/
|
||||
size_t index()
|
||||
{
|
||||
return index_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the index of the event loop.
|
||||
*
|
||||
* @param index
|
||||
*/
|
||||
void setIndex(size_t index)
|
||||
{
|
||||
index_ = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return true if the event loop is running.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isRunning()
|
||||
{
|
||||
return looping_ && (!quit_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the event loop is calling a function.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isCallingFunctions()
|
||||
{
|
||||
return callingFuncs_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Run functions when the event loop quits
|
||||
*
|
||||
* @param cb the function to run
|
||||
* @note the function runs on the thread that quits the EventLoop
|
||||
*/
|
||||
void runOnQuit(Func &&cb);
|
||||
void runOnQuit(const Func &cb);
|
||||
|
||||
private:
|
||||
void abortNotInLoopThread();
|
||||
void wakeup();
|
||||
void wakeupRead();
|
||||
bool looping_;
|
||||
std::thread::id threadId_;
|
||||
bool quit_;
|
||||
std::unique_ptr<Poller> poller_;
|
||||
|
||||
ChannelList activeChannels_;
|
||||
Channel *currentActiveChannel_;
|
||||
|
||||
bool eventHandling_;
|
||||
MpscQueue<Func> funcs_;
|
||||
std::unique_ptr<TimerQueue> timerQueue_;
|
||||
MpscQueue<Func> funcsOnQuit_;
|
||||
bool callingFuncs_{false};
|
||||
#ifdef __linux__
|
||||
int wakeupFd_;
|
||||
std::unique_ptr<Channel> wakeupChannelPtr_;
|
||||
#elif defined _WIN32
|
||||
#else
|
||||
int wakeupFd_[2];
|
||||
std::unique_ptr<Channel> wakeupChannelPtr_;
|
||||
#endif
|
||||
|
||||
void doRunInLoopFuncs();
|
||||
#ifdef _WIN32
|
||||
size_t index_{size_t(-1)};
|
||||
#else
|
||||
size_t index_{std::numeric_limits<size_t>::max()};
|
||||
#endif
|
||||
EventLoop **threadLocalLoopPtr_;
|
||||
};
|
||||
|
||||
} // namespace trantor
|
@ -1,72 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file EventLoopThread.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
using namespace trantor;
|
||||
EventLoopThread::EventLoopThread(const std::string &threadName)
|
||||
: loop_(nullptr),
|
||||
loopThreadName_(threadName),
|
||||
thread_([this]() { loopFuncs(); })
|
||||
{
|
||||
auto f = promiseForLoopPointer_.get_future();
|
||||
loop_ = f.get();
|
||||
}
|
||||
EventLoopThread::~EventLoopThread()
|
||||
{
|
||||
run();
|
||||
if (loop_)
|
||||
{
|
||||
loop_->quit();
|
||||
}
|
||||
if (thread_.joinable())
|
||||
{
|
||||
thread_.join();
|
||||
}
|
||||
}
|
||||
// void EventLoopThread::stop() {
|
||||
// if(loop_)
|
||||
// loop_->quit();
|
||||
//}
|
||||
void EventLoopThread::wait()
|
||||
{
|
||||
thread_.join();
|
||||
}
|
||||
void EventLoopThread::loopFuncs()
|
||||
{
|
||||
#ifdef __linux__
|
||||
::prctl(PR_SET_NAME, loopThreadName_.c_str());
|
||||
#endif
|
||||
EventLoop loop;
|
||||
loop.queueInLoop([this]() { promiseForLoop_.set_value(1); });
|
||||
promiseForLoopPointer_.set_value(&loop);
|
||||
auto f = promiseForRun_.get_future();
|
||||
(void)f.get();
|
||||
loop.loop();
|
||||
// LOG_DEBUG << "loop out";
|
||||
loop_ = NULL;
|
||||
}
|
||||
void EventLoopThread::run()
|
||||
{
|
||||
std::call_once(once_, [this]() {
|
||||
auto f = promiseForLoop_.get_future();
|
||||
promiseForRun_.set_value(1);
|
||||
// Make sure the event loop loops before returning.
|
||||
(void)f.get();
|
||||
});
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file EventLoopThread.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/exports.h>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <condition_variable>
|
||||
#include <future>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
/**
|
||||
* @brief This class represents an event loop thread.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT EventLoopThread : NonCopyable
|
||||
{
|
||||
public:
|
||||
explicit EventLoopThread(const std::string &threadName = "EventLoopThread");
|
||||
~EventLoopThread();
|
||||
|
||||
/**
|
||||
* @brief Wait for the event loop to exit.
|
||||
* @note This method blocks the current thread until the event loop exits.
|
||||
*/
|
||||
void wait();
|
||||
|
||||
/**
|
||||
* @brief Get the pointer of the event loop of the thread.
|
||||
*
|
||||
* @return EventLoop*
|
||||
*/
|
||||
EventLoop *getLoop() const
|
||||
{
|
||||
return loop_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Run the event loop of the thread. This method doesn't block the
|
||||
* current thread.
|
||||
*
|
||||
*/
|
||||
void run();
|
||||
|
||||
private:
|
||||
EventLoop *loop_;
|
||||
std::string loopThreadName_;
|
||||
void loopFuncs();
|
||||
std::promise<EventLoop *> promiseForLoopPointer_;
|
||||
std::promise<int> promiseForRun_;
|
||||
std::promise<int> promiseForLoop_;
|
||||
std::once_flag once_;
|
||||
std::thread thread_;
|
||||
};
|
||||
|
||||
} // namespace trantor
|
@ -1,72 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* EventLoopThreadPool.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <trantor/net/EventLoopThreadPool.h>
|
||||
using namespace trantor;
|
||||
EventLoopThreadPool::EventLoopThreadPool(size_t threadNum,
|
||||
const std::string &name)
|
||||
: loopIndex_(0)
|
||||
{
|
||||
for (size_t i = 0; i < threadNum; ++i)
|
||||
{
|
||||
loopThreadVector_.emplace_back(std::make_shared<EventLoopThread>(name));
|
||||
}
|
||||
}
|
||||
void EventLoopThreadPool::start()
|
||||
{
|
||||
for (unsigned int i = 0; i < loopThreadVector_.size(); ++i)
|
||||
{
|
||||
loopThreadVector_[i]->run();
|
||||
}
|
||||
}
|
||||
// void EventLoopThreadPool::stop(){
|
||||
// for(unsigned int i=0;i<loopThreadVector_.size();i++)
|
||||
// {
|
||||
// loopThreadVector_[i].stop();
|
||||
// }
|
||||
//}
|
||||
void EventLoopThreadPool::wait()
|
||||
{
|
||||
for (unsigned int i = 0; i < loopThreadVector_.size(); ++i)
|
||||
{
|
||||
loopThreadVector_[i]->wait();
|
||||
}
|
||||
}
|
||||
EventLoop *EventLoopThreadPool::getNextLoop()
|
||||
{
|
||||
if (loopThreadVector_.size() > 0)
|
||||
{
|
||||
EventLoop *loop = loopThreadVector_[loopIndex_]->getLoop();
|
||||
++loopIndex_;
|
||||
if (loopIndex_ >= loopThreadVector_.size())
|
||||
loopIndex_ = 0;
|
||||
return loop;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
EventLoop *EventLoopThreadPool::getLoop(size_t id)
|
||||
{
|
||||
if (id < loopThreadVector_.size())
|
||||
return loopThreadVector_[id]->getLoop();
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<EventLoop *> EventLoopThreadPool::getLoops() const
|
||||
{
|
||||
std::vector<EventLoop *> ret;
|
||||
for (auto &loopThread : loopThreadVector_)
|
||||
{
|
||||
ret.push_back(loopThread->getLoop());
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file EventLoopThreadPool.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <trantor/exports.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
/**
|
||||
* @brief This class represents a pool of EventLoopThread objects
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT EventLoopThreadPool : NonCopyable
|
||||
{
|
||||
public:
|
||||
EventLoopThreadPool() = delete;
|
||||
|
||||
/**
|
||||
* @brief Construct a new event loop thread pool instance.
|
||||
*
|
||||
* @param threadNum The number of threads
|
||||
* @param name The name of the EventLoopThreadPool object.
|
||||
*/
|
||||
EventLoopThreadPool(size_t threadNum,
|
||||
const std::string &name = "EventLoopThreadPool");
|
||||
|
||||
/**
|
||||
* @brief Run all event loops in the pool.
|
||||
* @note This function doesn't block the current thread.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* @brief Wait for all event loops in the pool to quit.
|
||||
*
|
||||
* @note This function blocks the current thread.
|
||||
*/
|
||||
void wait();
|
||||
|
||||
/**
|
||||
* @brief Return the number of the event loop.
|
||||
*
|
||||
* @return size_t
|
||||
*/
|
||||
size_t size()
|
||||
{
|
||||
return loopThreadVector_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the next event loop in the pool.
|
||||
*
|
||||
* @return EventLoop*
|
||||
*/
|
||||
EventLoop *getNextLoop();
|
||||
|
||||
/**
|
||||
* @brief Get the event loop in the `id` position in the pool.
|
||||
*
|
||||
* @param id The id of the first event loop is zero. If the id >= the number
|
||||
* of event loops, nullptr is returned.
|
||||
* @return EventLoop*
|
||||
*/
|
||||
EventLoop *getLoop(size_t id);
|
||||
|
||||
/**
|
||||
* @brief Get all event loops in the pool.
|
||||
*
|
||||
* @return std::vector<EventLoop *>
|
||||
*/
|
||||
std::vector<EventLoop *> getLoops() const;
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<EventLoopThread>> loopThreadVector_;
|
||||
size_t loopIndex_;
|
||||
};
|
||||
} // namespace trantor
|
@ -1,232 +0,0 @@
|
||||
// Copyright 2010, Shuo Chen. All rights reserved.
|
||||
// http://code.google.com/p/muduo/
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Shuo Chen (chenshuo at chenshuo dot com)
|
||||
|
||||
#include <trantor/net/InetAddress.h>
|
||||
|
||||
#include <trantor/utils/Logger.h>
|
||||
//#include <muduo/net/Endian.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
struct in6_addr_uint
|
||||
{
|
||||
union
|
||||
{
|
||||
u_char Byte[16];
|
||||
u_short Word[8];
|
||||
uint32_t __s6_addr32[4];
|
||||
} uext;
|
||||
};
|
||||
#else
|
||||
#include <strings.h> // memset
|
||||
#include <netinet/tcp.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
// INADDR_ANY use (type)value casting.
|
||||
static const in_addr_t kInaddrAny = INADDR_ANY;
|
||||
static const in_addr_t kInaddrLoopback = INADDR_LOOPBACK;
|
||||
|
||||
// /* Structure describing an Internet socket address. */
|
||||
// struct sockaddr_in {
|
||||
// sa_family_t sin_family; /* address family: AF_INET */
|
||||
// uint16_t sin_port; /* port in network byte order */
|
||||
// struct in_addr sin_addr; /* internet address */
|
||||
// };
|
||||
|
||||
// /* Internet address. */
|
||||
// typedef uint32_t in_addr_t;
|
||||
// struct in_addr {
|
||||
// in_addr_t s_addr; /* address in network byte order */
|
||||
// };
|
||||
|
||||
// struct sockaddr_in6 {
|
||||
// sa_family_t sin6_family; /* address family: AF_INET6 */
|
||||
// uint16_t sin6_port; /* port in network byte order */
|
||||
// uint32_t sin6_flowinfo; /* IPv6 flow information */
|
||||
// struct in6_addr sin6_addr; /* IPv6 address */
|
||||
// uint32_t sin6_scope_id; /* IPv6 scope-id */
|
||||
// };
|
||||
|
||||
using namespace trantor;
|
||||
|
||||
/*
|
||||
#ifdef __linux__
|
||||
#if !(__GNUC_PREREQ(4, 6))
|
||||
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||
#endif
|
||||
#endif
|
||||
*/
|
||||
|
||||
InetAddress::InetAddress(uint16_t port, bool loopbackOnly, bool ipv6)
|
||||
: isIpV6_(ipv6)
|
||||
{
|
||||
if (ipv6)
|
||||
{
|
||||
memset(&addr6_, 0, sizeof(addr6_));
|
||||
addr6_.sin6_family = AF_INET6;
|
||||
in6_addr ip = loopbackOnly ? in6addr_loopback : in6addr_any;
|
||||
addr6_.sin6_addr = ip;
|
||||
addr6_.sin6_port = htons(port);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&addr_, 0, sizeof(addr_));
|
||||
addr_.sin_family = AF_INET;
|
||||
in_addr_t ip = loopbackOnly ? kInaddrLoopback : kInaddrAny;
|
||||
addr_.sin_addr.s_addr = htonl(ip);
|
||||
addr_.sin_port = htons(port);
|
||||
}
|
||||
isUnspecified_ = false;
|
||||
}
|
||||
|
||||
InetAddress::InetAddress(const std::string &ip, uint16_t port, bool ipv6)
|
||||
: isIpV6_(ipv6)
|
||||
{
|
||||
if (ipv6)
|
||||
{
|
||||
memset(&addr6_, 0, sizeof(addr6_));
|
||||
addr6_.sin6_family = AF_INET6;
|
||||
addr6_.sin6_port = htons(port);
|
||||
if (::inet_pton(AF_INET6, ip.c_str(), &addr6_.sin6_addr) <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&addr_, 0, sizeof(addr_));
|
||||
addr_.sin_family = AF_INET;
|
||||
addr_.sin_port = htons(port);
|
||||
if (::inet_pton(AF_INET, ip.c_str(), &addr_.sin_addr) <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
isUnspecified_ = false;
|
||||
}
|
||||
|
||||
std::string InetAddress::toIpPort() const
|
||||
{
|
||||
char buf[64] = "";
|
||||
uint16_t port = ntohs(addr_.sin_port);
|
||||
snprintf(buf, sizeof(buf), ":%u", port);
|
||||
return toIp() + std::string(buf);
|
||||
}
|
||||
bool InetAddress::isIntranetIp() const
|
||||
{
|
||||
if (addr_.sin_family == AF_INET)
|
||||
{
|
||||
uint32_t ip_addr = ntohl(addr_.sin_addr.s_addr);
|
||||
if ((ip_addr >= 0x0A000000 && ip_addr <= 0x0AFFFFFF) ||
|
||||
(ip_addr >= 0xAC100000 && ip_addr <= 0xAC1FFFFF) ||
|
||||
(ip_addr >= 0xC0A80000 && ip_addr <= 0xC0A8FFFF) ||
|
||||
ip_addr == 0x7f000001)
|
||||
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto addrP = ip6NetEndian();
|
||||
// Loopback ip
|
||||
if (*addrP == 0 && *(addrP + 1) == 0 && *(addrP + 2) == 0 &&
|
||||
ntohl(*(addrP + 3)) == 1)
|
||||
return true;
|
||||
// Privated ip is prefixed by FEC0::/10 or FE80::/10, need testing
|
||||
auto i32 = (ntohl(*addrP) & 0xffc00000);
|
||||
if (i32 == 0xfec00000 || i32 == 0xfe800000)
|
||||
return true;
|
||||
if (*addrP == 0 && *(addrP + 1) == 0 && ntohl(*(addrP + 2)) == 0xffff)
|
||||
{
|
||||
// the IPv6 version of an IPv4 IP address
|
||||
uint32_t ip_addr = ntohl(*(addrP + 3));
|
||||
if ((ip_addr >= 0x0A000000 && ip_addr <= 0x0AFFFFFF) ||
|
||||
(ip_addr >= 0xAC100000 && ip_addr <= 0xAC1FFFFF) ||
|
||||
(ip_addr >= 0xC0A80000 && ip_addr <= 0xC0A8FFFF) ||
|
||||
ip_addr == 0x7f000001)
|
||||
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InetAddress::isLoopbackIp() const
|
||||
{
|
||||
if (!isIpV6())
|
||||
{
|
||||
uint32_t ip_addr = ntohl(addr_.sin_addr.s_addr);
|
||||
if (ip_addr == 0x7f000001)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto addrP = ip6NetEndian();
|
||||
if (*addrP == 0 && *(addrP + 1) == 0 && *(addrP + 2) == 0 &&
|
||||
ntohl(*(addrP + 3)) == 1)
|
||||
return true;
|
||||
// the IPv6 version of an IPv4 loopback address
|
||||
if (*addrP == 0 && *(addrP + 1) == 0 && ntohl(*(addrP + 2)) == 0xffff &&
|
||||
ntohl(*(addrP + 3)) == 0x7f000001)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string InetAddress::toIp() const
|
||||
{
|
||||
char buf[64];
|
||||
if (addr_.sin_family == AF_INET)
|
||||
{
|
||||
#if defined _MSC_VER && _MSC_VER >= 1900
|
||||
::inet_ntop(AF_INET, (PVOID)&addr_.sin_addr, buf, sizeof(buf));
|
||||
#else
|
||||
::inet_ntop(AF_INET, &addr_.sin_addr, buf, sizeof(buf));
|
||||
#endif
|
||||
}
|
||||
else if (addr_.sin_family == AF_INET6)
|
||||
{
|
||||
#if defined _MSC_VER && _MSC_VER >= 1900
|
||||
::inet_ntop(AF_INET6, (PVOID)&addr6_.sin6_addr, buf, sizeof(buf));
|
||||
#else
|
||||
::inet_ntop(AF_INET6, &addr6_.sin6_addr, buf, sizeof(buf));
|
||||
#endif
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
uint32_t InetAddress::ipNetEndian() const
|
||||
{
|
||||
// assert(family() == AF_INET);
|
||||
return addr_.sin_addr.s_addr;
|
||||
}
|
||||
|
||||
const uint32_t *InetAddress::ip6NetEndian() const
|
||||
{
|
||||
// assert(family() == AF_INET6);
|
||||
#ifdef __linux__
|
||||
return addr6_.sin6_addr.s6_addr32;
|
||||
#elif defined _WIN32
|
||||
// TODO is this OK ?
|
||||
const struct in6_addr_uint *addr_temp =
|
||||
reinterpret_cast<const struct in6_addr_uint *>(&addr6_.sin6_addr);
|
||||
return (*addr_temp).uext.__s6_addr32;
|
||||
#else
|
||||
return addr6_.sin6_addr.__u6_addr.__u6_addr32;
|
||||
#endif
|
||||
}
|
||||
uint16_t InetAddress::toPort() const
|
||||
{
|
||||
return ntohs(portNetEndian());
|
||||
}
|
@ -1,226 +0,0 @@
|
||||
// Copyright 2010, Shuo Chen. All rights reserved.
|
||||
// http://code.google.com/p/muduo/
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Shuo Chen (chenshuo at chenshuo dot com)
|
||||
//
|
||||
// This is a public header file, it must only include public header files.
|
||||
|
||||
// Taken from Muduo and modified
|
||||
// Copyright 2016, Tao An. All rights reserved.
|
||||
// https://github.com/an-tao/trantor
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Tao An
|
||||
|
||||
#ifndef MUDUO_NET_INETADDRESS_H
|
||||
#define MUDUO_NET_INETADDRESS_H
|
||||
|
||||
#include <trantor/utils/Date.h>
|
||||
#include <trantor/exports.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
using sa_family_t = unsigned short;
|
||||
using in_addr_t = uint32_t;
|
||||
using uint16_t = unsigned short;
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
namespace trantor
|
||||
{
|
||||
/**
|
||||
* @brief Wrapper of sockaddr_in. This is an POD interface class.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT InetAddress
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs an endpoint with given port number. Mostly used in
|
||||
* TcpServer listening.
|
||||
*
|
||||
* @param port
|
||||
* @param loopbackOnly
|
||||
* @param ipv6
|
||||
*/
|
||||
InetAddress(uint16_t port = 0,
|
||||
bool loopbackOnly = false,
|
||||
bool ipv6 = false);
|
||||
|
||||
/**
|
||||
* @brief Constructs an endpoint with given ip and port.
|
||||
*
|
||||
* @param ip A IPv4 or IPv6 address.
|
||||
* @param port
|
||||
* @param ipv6
|
||||
*/
|
||||
InetAddress(const std::string &ip, uint16_t port, bool ipv6 = false);
|
||||
|
||||
/**
|
||||
* @brief Constructs an endpoint with given struct `sockaddr_in`. Mostly
|
||||
* used when accepting new connections
|
||||
*
|
||||
* @param addr
|
||||
*/
|
||||
explicit InetAddress(const struct sockaddr_in &addr)
|
||||
: addr_(addr), isUnspecified_(false)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs an IPv6 endpoint with given struct `sockaddr_in6`.
|
||||
* Mostly used when accepting new connections
|
||||
*
|
||||
* @param addr
|
||||
*/
|
||||
explicit InetAddress(const struct sockaddr_in6 &addr)
|
||||
: addr6_(addr), isIpV6_(true), isUnspecified_(false)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the sin_family of the endpoint.
|
||||
*
|
||||
* @return sa_family_t
|
||||
*/
|
||||
sa_family_t family() const
|
||||
{
|
||||
return addr_.sin_family;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the IP string of the endpoint.
|
||||
*
|
||||
* @return std::string
|
||||
*/
|
||||
std::string toIp() const;
|
||||
|
||||
/**
|
||||
* @brief Return the IP and port string of the endpoint.
|
||||
*
|
||||
* @return std::string
|
||||
*/
|
||||
std::string toIpPort() const;
|
||||
|
||||
/**
|
||||
* @brief Return the port number of the endpoint.
|
||||
*
|
||||
* @return uint16_t
|
||||
*/
|
||||
uint16_t toPort() const;
|
||||
|
||||
/**
|
||||
* @brief Check if the endpoint is IPv4 or IPv6.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isIpV6() const
|
||||
{
|
||||
return isIpV6_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return true if the endpoint is an intranet endpoint.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isIntranetIp() const;
|
||||
|
||||
/**
|
||||
* @brief Return true if the endpoint is a loopback endpoint.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isLoopbackIp() const;
|
||||
|
||||
/**
|
||||
* @brief Get the pointer to the sockaddr struct.
|
||||
*
|
||||
* @return const struct sockaddr*
|
||||
*/
|
||||
const struct sockaddr *getSockAddr() const
|
||||
{
|
||||
return static_cast<const struct sockaddr *>((void *)(&addr6_));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the sockaddr_in6 struct in the endpoint.
|
||||
*
|
||||
* @param addr6
|
||||
*/
|
||||
void setSockAddrInet6(const struct sockaddr_in6 &addr6)
|
||||
{
|
||||
addr6_ = addr6;
|
||||
isIpV6_ = (addr6_.sin6_family == AF_INET6);
|
||||
isUnspecified_ = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the integer value of the IP(v4) in net endian byte order.
|
||||
*
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t ipNetEndian() const;
|
||||
|
||||
/**
|
||||
* @brief Return the pointer to the integer value of the IP(v6) in net
|
||||
* endian byte order.
|
||||
*
|
||||
* @return const uint32_t*
|
||||
*/
|
||||
const uint32_t *ip6NetEndian() const;
|
||||
|
||||
/**
|
||||
* @brief Return the port number in net endian byte order.
|
||||
*
|
||||
* @return uint16_t
|
||||
*/
|
||||
uint16_t portNetEndian() const
|
||||
{
|
||||
return addr_.sin_port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the port number in net endian byte order.
|
||||
*
|
||||
* @param port
|
||||
*/
|
||||
void setPortNetEndian(uint16_t port)
|
||||
{
|
||||
addr_.sin_port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return true if the address is not initalized.
|
||||
*/
|
||||
inline bool isUnspecified() const
|
||||
{
|
||||
return isUnspecified_;
|
||||
}
|
||||
|
||||
private:
|
||||
union
|
||||
{
|
||||
struct sockaddr_in addr_;
|
||||
struct sockaddr_in6 addr6_;
|
||||
};
|
||||
bool isIpV6_{false};
|
||||
bool isUnspecified_{true};
|
||||
};
|
||||
|
||||
} // namespace trantor
|
||||
|
||||
#endif // MUDUO_NET_INETADDRESS_H
|
@ -1,57 +0,0 @@
|
||||
// Copyright 2016, Tao An. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Tao An
|
||||
|
||||
#pragma once
|
||||
#include <trantor/exports.h>
|
||||
#include <memory>
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <trantor/net/InetAddress.h>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
/**
|
||||
* @brief This class represents an asynchronous DNS resolver.
|
||||
* @note Although the c-ares library is not essential, it is recommended to
|
||||
* install it for higher performance
|
||||
*/
|
||||
class TRANTOR_EXPORT Resolver
|
||||
{
|
||||
public:
|
||||
using Callback = std::function<void(const trantor::InetAddress&)>;
|
||||
|
||||
/**
|
||||
* @brief Create a new DNS resolver.
|
||||
*
|
||||
* @param loop The event loop in which the DNS resolver runs.
|
||||
* @param timeout The timeout in seconds for DNS.
|
||||
* @return std::shared_ptr<Resolver>
|
||||
*/
|
||||
static std::shared_ptr<Resolver> newResolver(EventLoop* loop = nullptr,
|
||||
size_t timeout = 60);
|
||||
|
||||
/**
|
||||
* @brief Resolve an address asynchronously.
|
||||
*
|
||||
* @param hostname
|
||||
* @param callback
|
||||
*/
|
||||
virtual void resolve(const std::string& hostname,
|
||||
const Callback& callback) = 0;
|
||||
|
||||
virtual ~Resolver()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether the c-ares library is used.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
static bool isCAresUsed();
|
||||
};
|
||||
} // namespace trantor
|
@ -1,228 +0,0 @@
|
||||
// Copyright 2010, Shuo Chen. All rights reserved.
|
||||
// http://code.google.com/p/muduo/
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Shuo Chen (chenshuo at chenshuo dot com)
|
||||
//
|
||||
|
||||
// Taken from muduo and modified by an tao
|
||||
|
||||
#include <trantor/net/TcpClient.h>
|
||||
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include "Connector.h"
|
||||
#include "inner/TcpConnectionImpl.h"
|
||||
#include <trantor/net/EventLoop.h>
|
||||
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Socket.h"
|
||||
|
||||
#include <stdio.h> // snprintf
|
||||
|
||||
using namespace trantor;
|
||||
using namespace std::placeholders;
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
// void removeConnector(const ConnectorPtr &)
|
||||
// {
|
||||
// // connector->
|
||||
// }
|
||||
#ifndef _WIN32
|
||||
TcpClient::IgnoreSigPipe TcpClient::initObj;
|
||||
#endif
|
||||
|
||||
static void defaultConnectionCallback(const TcpConnectionPtr &conn)
|
||||
{
|
||||
LOG_TRACE << conn->localAddr().toIpPort() << " -> "
|
||||
<< conn->peerAddr().toIpPort() << " is "
|
||||
<< (conn->connected() ? "UP" : "DOWN");
|
||||
// do not call conn->forceClose(), because some users want to register
|
||||
// message callback only.
|
||||
}
|
||||
|
||||
static void defaultMessageCallback(const TcpConnectionPtr &, MsgBuffer *buf)
|
||||
{
|
||||
buf->retrieveAll();
|
||||
}
|
||||
|
||||
} // namespace trantor
|
||||
|
||||
TcpClient::TcpClient(EventLoop *loop,
|
||||
const InetAddress &serverAddr,
|
||||
const std::string &nameArg)
|
||||
: loop_(loop),
|
||||
connector_(new Connector(loop, serverAddr, false)),
|
||||
name_(nameArg),
|
||||
connectionCallback_(defaultConnectionCallback),
|
||||
messageCallback_(defaultMessageCallback),
|
||||
retry_(false),
|
||||
connect_(true)
|
||||
{
|
||||
connector_->setNewConnectionCallback(
|
||||
std::bind(&TcpClient::newConnection, this, _1));
|
||||
connector_->setErrorCallback([this]() {
|
||||
if (connectionErrorCallback_)
|
||||
{
|
||||
connectionErrorCallback_();
|
||||
}
|
||||
});
|
||||
LOG_TRACE << "TcpClient::TcpClient[" << name_ << "] - connector ";
|
||||
}
|
||||
|
||||
TcpClient::~TcpClient()
|
||||
{
|
||||
LOG_TRACE << "TcpClient::~TcpClient[" << name_ << "] - connector ";
|
||||
TcpConnectionImplPtr conn;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
conn = std::dynamic_pointer_cast<TcpConnectionImpl>(connection_);
|
||||
}
|
||||
if (conn)
|
||||
{
|
||||
assert(loop_ == conn->getLoop());
|
||||
// TODO: not 100% safe, if we are in different thread
|
||||
auto loop = loop_;
|
||||
loop_->runInLoop([conn, loop]() {
|
||||
conn->setCloseCallback([loop](const TcpConnectionPtr &connPtr) {
|
||||
loop->queueInLoop([connPtr]() {
|
||||
static_cast<TcpConnectionImpl *>(connPtr.get())
|
||||
->connectDestroyed();
|
||||
});
|
||||
});
|
||||
});
|
||||
conn->forceClose();
|
||||
}
|
||||
else
|
||||
{
|
||||
/// TODO need test in this condition
|
||||
connector_->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void TcpClient::connect()
|
||||
{
|
||||
// TODO: check state
|
||||
LOG_TRACE << "TcpClient::connect[" << name_ << "] - connecting to "
|
||||
<< connector_->serverAddress().toIpPort();
|
||||
connect_ = true;
|
||||
connector_->start();
|
||||
}
|
||||
|
||||
void TcpClient::disconnect()
|
||||
{
|
||||
connect_ = false;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (connection_)
|
||||
{
|
||||
connection_->shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TcpClient::stop()
|
||||
{
|
||||
connect_ = false;
|
||||
connector_->stop();
|
||||
}
|
||||
|
||||
void TcpClient::newConnection(int sockfd)
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
InetAddress peerAddr(Socket::getPeerAddr(sockfd));
|
||||
InetAddress localAddr(Socket::getLocalAddr(sockfd));
|
||||
// TODO poll with zero timeout to double confirm the new connection
|
||||
// TODO use make_shared if necessary
|
||||
std::shared_ptr<TcpConnectionImpl> conn;
|
||||
if (sslCtxPtr_)
|
||||
{
|
||||
#ifdef USE_OPENSSL
|
||||
conn = std::make_shared<TcpConnectionImpl>(loop_,
|
||||
sockfd,
|
||||
localAddr,
|
||||
peerAddr,
|
||||
sslCtxPtr_,
|
||||
false,
|
||||
validateCert_,
|
||||
SSLHostName_);
|
||||
#else
|
||||
LOG_FATAL << "OpenSSL is not found in your system!";
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
conn = std::make_shared<TcpConnectionImpl>(loop_,
|
||||
sockfd,
|
||||
localAddr,
|
||||
peerAddr);
|
||||
}
|
||||
conn->setConnectionCallback(connectionCallback_);
|
||||
conn->setRecvMsgCallback(messageCallback_);
|
||||
conn->setWriteCompleteCallback(writeCompleteCallback_);
|
||||
conn->setCloseCallback(std::bind(&TcpClient::removeConnection, this, _1));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
connection_ = conn;
|
||||
}
|
||||
conn->setSSLErrorCallback([this](SSLError err) {
|
||||
if (sslErrorCallback_)
|
||||
{
|
||||
sslErrorCallback_(err);
|
||||
}
|
||||
});
|
||||
conn->connectEstablished();
|
||||
}
|
||||
|
||||
void TcpClient::removeConnection(const TcpConnectionPtr &conn)
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
assert(loop_ == conn->getLoop());
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
assert(connection_ == conn);
|
||||
connection_.reset();
|
||||
}
|
||||
|
||||
loop_->queueInLoop(
|
||||
std::bind(&TcpConnectionImpl::connectDestroyed,
|
||||
std::dynamic_pointer_cast<TcpConnectionImpl>(conn)));
|
||||
if (retry_ && connect_)
|
||||
{
|
||||
LOG_TRACE << "TcpClient::connect[" << name_ << "] - Reconnecting to "
|
||||
<< connector_->serverAddress().toIpPort();
|
||||
connector_->restart();
|
||||
}
|
||||
}
|
||||
|
||||
void TcpClient::enableSSL(
|
||||
bool useOldTLS,
|
||||
bool validateCert,
|
||||
std::string hostname,
|
||||
const std::vector<std::pair<std::string, std::string>> &sslConfCmds)
|
||||
{
|
||||
#ifdef USE_OPENSSL
|
||||
/* Create a new OpenSSL context */
|
||||
sslCtxPtr_ = newSSLContext(useOldTLS, validateCert, sslConfCmds);
|
||||
validateCert_ = validateCert;
|
||||
if (!hostname.empty())
|
||||
{
|
||||
std::transform(hostname.begin(),
|
||||
hostname.end(),
|
||||
hostname.begin(),
|
||||
tolower);
|
||||
SSLHostName_ = std::move(hostname);
|
||||
}
|
||||
|
||||
#else
|
||||
LOG_FATAL << "OpenSSL is not found in your system!";
|
||||
abort();
|
||||
#endif
|
||||
}
|
@ -1,247 +0,0 @@
|
||||
// taken from muduo
|
||||
// Copyright 2010, Shuo Chen. All rights reserved.
|
||||
// http://code.google.com/p/muduo/
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Shuo Chen (chenshuo at chenshuo dot com)
|
||||
//
|
||||
// This is a public header file, it must only include public header files.
|
||||
|
||||
// Copyright 2016, Tao An. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Tao An
|
||||
|
||||
#pragma once
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <trantor/net/InetAddress.h>
|
||||
#include <trantor/net/TcpConnection.h>
|
||||
#include <trantor/exports.h>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <signal.h>
|
||||
namespace trantor
|
||||
{
|
||||
class Connector;
|
||||
using ConnectorPtr = std::shared_ptr<Connector>;
|
||||
class SSLContext;
|
||||
/**
|
||||
* @brief This class represents a TCP client.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT TcpClient : NonCopyable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new TCP client instance.
|
||||
*
|
||||
* @param loop The event loop in which the client runs.
|
||||
* @param serverAddr The address of the server.
|
||||
* @param nameArg The name of the client.
|
||||
*/
|
||||
TcpClient(EventLoop *loop,
|
||||
const InetAddress &serverAddr,
|
||||
const std::string &nameArg);
|
||||
~TcpClient();
|
||||
|
||||
/**
|
||||
* @brief Connect to the server.
|
||||
*
|
||||
*/
|
||||
void connect();
|
||||
|
||||
/**
|
||||
* @brief Disconnect from the server.
|
||||
*
|
||||
*/
|
||||
void disconnect();
|
||||
|
||||
/**
|
||||
* @brief Stop connecting to the server.
|
||||
*
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* @brief Get the TCP connection to the server.
|
||||
*
|
||||
* @return TcpConnectionPtr
|
||||
*/
|
||||
TcpConnectionPtr connection() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return connection_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the event loop.
|
||||
*
|
||||
* @return EventLoop*
|
||||
*/
|
||||
EventLoop *getLoop() const
|
||||
{
|
||||
return loop_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether the client re-connect to the server.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool retry() const
|
||||
{
|
||||
return retry_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable retrying.
|
||||
*
|
||||
*/
|
||||
void enableRetry()
|
||||
{
|
||||
retry_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the name of the client.
|
||||
*
|
||||
* @return const std::string&
|
||||
*/
|
||||
const std::string &name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the connection callback.
|
||||
*
|
||||
* @param cb The callback is called when the connection to the server is
|
||||
* established or closed.
|
||||
*/
|
||||
void setConnectionCallback(const ConnectionCallback &cb)
|
||||
{
|
||||
connectionCallback_ = cb;
|
||||
}
|
||||
void setConnectionCallback(ConnectionCallback &&cb)
|
||||
{
|
||||
connectionCallback_ = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the connection error callback.
|
||||
*
|
||||
* @param cb The callback is called when an error occurs during connecting
|
||||
* to the server.
|
||||
*/
|
||||
void setConnectionErrorCallback(const ConnectionErrorCallback &cb)
|
||||
{
|
||||
connectionErrorCallback_ = cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the message callback.
|
||||
*
|
||||
* @param cb The callback is called when some data is received from the
|
||||
* server.
|
||||
*/
|
||||
void setMessageCallback(const RecvMessageCallback &cb)
|
||||
{
|
||||
messageCallback_ = cb;
|
||||
}
|
||||
void setMessageCallback(RecvMessageCallback &&cb)
|
||||
{
|
||||
messageCallback_ = std::move(cb);
|
||||
}
|
||||
/// Set write complete callback.
|
||||
/// Not thread safe.
|
||||
|
||||
/**
|
||||
* @brief Set the write complete callback.
|
||||
*
|
||||
* @param cb The callback is called when data to send is written to the
|
||||
* socket.
|
||||
*/
|
||||
void setWriteCompleteCallback(const WriteCompleteCallback &cb)
|
||||
{
|
||||
writeCompleteCallback_ = cb;
|
||||
}
|
||||
void setWriteCompleteCallback(WriteCompleteCallback &&cb)
|
||||
{
|
||||
writeCompleteCallback_ = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the callback for errors of SSL
|
||||
* @param cb The callback is called when an SSL error occurs.
|
||||
*/
|
||||
void setSSLErrorCallback(const SSLErrorCallback &cb)
|
||||
{
|
||||
sslErrorCallback_ = cb;
|
||||
}
|
||||
void setSSLErrorCallback(SSLErrorCallback &&cb)
|
||||
{
|
||||
sslErrorCallback_ = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable SSL encryption.
|
||||
* @param useOldTLS If true, the TLS 1.0 and 1.1 are supported by the
|
||||
* client.
|
||||
* @param validateCert If true, we try to validate if the peer's SSL cert
|
||||
* is valid.
|
||||
* @param hostname The server hostname for SNI. If it is empty, the SNI is
|
||||
* not used.
|
||||
* @param sslConfCmds The commands used to call the SSL_CONF_cmd function in
|
||||
* OpenSSL.
|
||||
* @note It's well known that TLS 1.0 and 1.1 are not considered secure in
|
||||
* 2020. And it's a good practice to only use TLS 1.2 and above.
|
||||
*/
|
||||
void enableSSL(bool useOldTLS = false,
|
||||
bool validateCert = true,
|
||||
std::string hostname = "",
|
||||
const std::vector<std::pair<std::string, std::string>>
|
||||
&sslConfCmds = {});
|
||||
|
||||
private:
|
||||
/// Not thread safe, but in loop
|
||||
void newConnection(int sockfd);
|
||||
/// Not thread safe, but in loop
|
||||
void removeConnection(const TcpConnectionPtr &conn);
|
||||
|
||||
EventLoop *loop_;
|
||||
ConnectorPtr connector_; // avoid revealing Connector
|
||||
const std::string name_;
|
||||
ConnectionCallback connectionCallback_;
|
||||
ConnectionErrorCallback connectionErrorCallback_;
|
||||
RecvMessageCallback messageCallback_;
|
||||
WriteCompleteCallback writeCompleteCallback_;
|
||||
SSLErrorCallback sslErrorCallback_;
|
||||
std::atomic_bool retry_; // atomic
|
||||
std::atomic_bool connect_; // atomic
|
||||
// always in loop thread
|
||||
mutable std::mutex mutex_;
|
||||
TcpConnectionPtr connection_; // @GuardedBy mutex_
|
||||
std::shared_ptr<SSLContext> sslCtxPtr_;
|
||||
bool validateCert_{false};
|
||||
std::string SSLHostName_;
|
||||
#ifndef _WIN32
|
||||
class IgnoreSigPipe
|
||||
{
|
||||
public:
|
||||
IgnoreSigPipe()
|
||||
{
|
||||
::signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
};
|
||||
|
||||
static IgnoreSigPipe initObj;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace trantor
|
@ -1,261 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file TcpConnection.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <trantor/exports.h>
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <trantor/net/InetAddress.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/utils/MsgBuffer.h>
|
||||
#include <trantor/net/callbacks.h>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
class SSLContext;
|
||||
TRANTOR_EXPORT std::shared_ptr<SSLContext> newSSLServerContext(
|
||||
const std::string &certPath,
|
||||
const std::string &keyPath,
|
||||
bool useOldTLS = false,
|
||||
const std::vector<std::pair<std::string, std::string>> &sslConfCmds = {});
|
||||
/**
|
||||
* @brief This class represents a TCP connection.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT TcpConnection
|
||||
{
|
||||
public:
|
||||
TcpConnection() = default;
|
||||
virtual ~TcpConnection(){};
|
||||
|
||||
/**
|
||||
* @brief Send some data to the peer.
|
||||
*
|
||||
* @param msg
|
||||
* @param len
|
||||
*/
|
||||
virtual void send(const char *msg, size_t len) = 0;
|
||||
virtual void send(const void *msg, size_t len) = 0;
|
||||
virtual void send(const std::string &msg) = 0;
|
||||
virtual void send(std::string &&msg) = 0;
|
||||
virtual void send(const MsgBuffer &buffer) = 0;
|
||||
virtual void send(MsgBuffer &&buffer) = 0;
|
||||
virtual void send(const std::shared_ptr<std::string> &msgPtr) = 0;
|
||||
virtual void send(const std::shared_ptr<MsgBuffer> &msgPtr) = 0;
|
||||
|
||||
/**
|
||||
* @brief Send a file to the peer.
|
||||
*
|
||||
* @param fileName
|
||||
* @param offset
|
||||
* @param length
|
||||
*/
|
||||
virtual void sendFile(const char *fileName,
|
||||
size_t offset = 0,
|
||||
size_t length = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the local address of the connection.
|
||||
*
|
||||
* @return const InetAddress&
|
||||
*/
|
||||
virtual const InetAddress &localAddr() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the remote address of the connection.
|
||||
*
|
||||
* @return const InetAddress&
|
||||
*/
|
||||
virtual const InetAddress &peerAddr() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return true if the connection is established.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
virtual bool connected() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return false if the connection is established.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
virtual bool disconnected() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the buffer in which the received data stored.
|
||||
*
|
||||
* @return MsgBuffer*
|
||||
*/
|
||||
virtual MsgBuffer *getRecvBuffer() = 0;
|
||||
|
||||
/**
|
||||
* @brief Set the high water mark callback
|
||||
*
|
||||
* @param cb The callback is called when the data in sending buffer is
|
||||
* larger than the water mark.
|
||||
* @param markLen The water mark in bytes.
|
||||
*/
|
||||
virtual void setHighWaterMarkCallback(const HighWaterMarkCallback &cb,
|
||||
size_t markLen) = 0;
|
||||
|
||||
/**
|
||||
* @brief Set the TCP_NODELAY option to the socket.
|
||||
*
|
||||
* @param on
|
||||
*/
|
||||
virtual void setTcpNoDelay(bool on) = 0;
|
||||
|
||||
/**
|
||||
* @brief Shutdown the connection.
|
||||
* @note This method only closes the writing direction.
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief Close the connection forcefully.
|
||||
*
|
||||
*/
|
||||
virtual void forceClose() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the event loop in which the connection I/O is handled.
|
||||
*
|
||||
* @return EventLoop*
|
||||
*/
|
||||
virtual EventLoop *getLoop() = 0;
|
||||
|
||||
/**
|
||||
* @brief Set the custom data on the connection.
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
void setContext(const std::shared_ptr<void> &context)
|
||||
{
|
||||
contextPtr_ = context;
|
||||
}
|
||||
void setContext(std::shared_ptr<void> &&context)
|
||||
{
|
||||
contextPtr_ = std::move(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the custom data from the connection.
|
||||
*
|
||||
* @tparam T
|
||||
* @return std::shared_ptr<T>
|
||||
*/
|
||||
template <typename T>
|
||||
std::shared_ptr<T> getContext() const
|
||||
{
|
||||
return std::static_pointer_cast<T>(contextPtr_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return true if the custom data is set by user.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool hasContext() const
|
||||
{
|
||||
return (bool)contextPtr_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear the custom data.
|
||||
*
|
||||
*/
|
||||
void clearContext()
|
||||
{
|
||||
contextPtr_.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Call this method to avoid being kicked off by TcpServer, refer to
|
||||
* the kickoffIdleConnections method in the TcpServer class.
|
||||
*
|
||||
*/
|
||||
virtual void keepAlive() = 0;
|
||||
|
||||
/**
|
||||
* @brief Return true if the keepAlive() method is called.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
virtual bool isKeepAlive() = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the number of bytes sent
|
||||
*
|
||||
* @return size_t
|
||||
*/
|
||||
virtual size_t bytesSent() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the number of bytes received.
|
||||
*
|
||||
* @return size_t
|
||||
*/
|
||||
virtual size_t bytesReceived() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Check whether the connection is SSL encrypted.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
virtual bool isSSLConnection() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Start the SSL encryption on the connection (as a client).
|
||||
*
|
||||
* @param callback The callback is called when the SSL connection is
|
||||
* established.
|
||||
* @param hostname The server hostname for SNI. If it is empty, the SNI is
|
||||
* not used.
|
||||
* @param sslConfCmds The commands used to call the SSL_CONF_cmd function in
|
||||
* OpenSSL.
|
||||
*/
|
||||
virtual void startClientEncryption(
|
||||
std::function<void()> callback,
|
||||
bool useOldTLS = false,
|
||||
bool validateCert = true,
|
||||
std::string hostname = "",
|
||||
const std::vector<std::pair<std::string, std::string>> &sslConfCmds =
|
||||
{}) = 0;
|
||||
|
||||
/**
|
||||
* @brief Start the SSL encryption on the connection (as a server).
|
||||
*
|
||||
* @param ctx The SSL context.
|
||||
* @param callback The callback is called when the SSL connection is
|
||||
* established.
|
||||
*/
|
||||
virtual void startServerEncryption(const std::shared_ptr<SSLContext> &ctx,
|
||||
std::function<void()> callback) = 0;
|
||||
|
||||
protected:
|
||||
bool validateCert_ = false;
|
||||
|
||||
private:
|
||||
std::shared_ptr<void> contextPtr_;
|
||||
};
|
||||
|
||||
} // namespace trantor
|
@ -1,201 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file TcpServer.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/trantor
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Trantor
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Acceptor.h"
|
||||
#include "inner/TcpConnectionImpl.h"
|
||||
#include <trantor/net/TcpServer.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
using namespace trantor;
|
||||
using namespace std::placeholders;
|
||||
|
||||
TcpServer::TcpServer(EventLoop *loop,
|
||||
const InetAddress &address,
|
||||
const std::string &name,
|
||||
bool reUseAddr,
|
||||
bool reUsePort)
|
||||
: loop_(loop),
|
||||
acceptorPtr_(new Acceptor(loop, address, reUseAddr, reUsePort)),
|
||||
serverName_(name),
|
||||
recvMessageCallback_([](const TcpConnectionPtr &, MsgBuffer *buffer) {
|
||||
LOG_ERROR << "unhandled recv message [" << buffer->readableBytes()
|
||||
<< " bytes]";
|
||||
buffer->retrieveAll();
|
||||
})
|
||||
{
|
||||
acceptorPtr_->setNewConnectionCallback(
|
||||
std::bind(&TcpServer::newConnection, this, _1, _2));
|
||||
}
|
||||
|
||||
TcpServer::~TcpServer()
|
||||
{
|
||||
// loop_->assertInLoopThread();
|
||||
LOG_TRACE << "TcpServer::~TcpServer [" << serverName_ << "] destructing";
|
||||
}
|
||||
|
||||
void TcpServer::newConnection(int sockfd, const InetAddress &peer)
|
||||
{
|
||||
LOG_TRACE << "new connection:fd=" << sockfd
|
||||
<< " address=" << peer.toIpPort();
|
||||
// test code for blocking or nonblocking
|
||||
// std::vector<char> str(1024*1024*100);
|
||||
// for(int i=0;i<str.size();i++)
|
||||
// str[i]='A';
|
||||
// LOG_TRACE<<"vector size:"<<str.size();
|
||||
// size_t n=write(sockfd,&str[0],str.size());
|
||||
// LOG_TRACE<<"write "<<n<<" bytes";
|
||||
loop_->assertInLoopThread();
|
||||
EventLoop *ioLoop = NULL;
|
||||
if (loopPoolPtr_ && loopPoolPtr_->size() > 0)
|
||||
{
|
||||
ioLoop = loopPoolPtr_->getNextLoop();
|
||||
}
|
||||
if (ioLoop == NULL)
|
||||
ioLoop = loop_;
|
||||
std::shared_ptr<TcpConnectionImpl> newPtr;
|
||||
if (sslCtxPtr_)
|
||||
{
|
||||
#ifdef USE_OPENSSL
|
||||
newPtr = std::make_shared<TcpConnectionImpl>(
|
||||
ioLoop,
|
||||
sockfd,
|
||||
InetAddress(Socket::getLocalAddr(sockfd)),
|
||||
peer,
|
||||
sslCtxPtr_);
|
||||
#else
|
||||
LOG_FATAL << "OpenSSL is not found in your system!";
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
newPtr = std::make_shared<TcpConnectionImpl>(
|
||||
ioLoop, sockfd, InetAddress(Socket::getLocalAddr(sockfd)), peer);
|
||||
}
|
||||
|
||||
if (idleTimeout_ > 0)
|
||||
{
|
||||
assert(timingWheelMap_[ioLoop]);
|
||||
newPtr->enableKickingOff(idleTimeout_, timingWheelMap_[ioLoop]);
|
||||
}
|
||||
newPtr->setRecvMsgCallback(recvMessageCallback_);
|
||||
|
||||
newPtr->setConnectionCallback(
|
||||
[this](const TcpConnectionPtr &connectionPtr) {
|
||||
if (connectionCallback_)
|
||||
connectionCallback_(connectionPtr);
|
||||
});
|
||||
newPtr->setWriteCompleteCallback(
|
||||
[this](const TcpConnectionPtr &connectionPtr) {
|
||||
if (writeCompleteCallback_)
|
||||
writeCompleteCallback_(connectionPtr);
|
||||
});
|
||||
newPtr->setCloseCallback(std::bind(&TcpServer::connectionClosed, this, _1));
|
||||
connSet_.insert(newPtr);
|
||||
newPtr->connectEstablished();
|
||||
}
|
||||
|
||||
void TcpServer::start()
|
||||
{
|
||||
loop_->runInLoop([this]() {
|
||||
assert(!started_);
|
||||
started_ = true;
|
||||
if (idleTimeout_ > 0)
|
||||
{
|
||||
timingWheelMap_[loop_] =
|
||||
std::make_shared<TimingWheel>(loop_,
|
||||
idleTimeout_,
|
||||
1.0F,
|
||||
idleTimeout_ < 500
|
||||
? idleTimeout_ + 1
|
||||
: 100);
|
||||
if (loopPoolPtr_)
|
||||
{
|
||||
auto loopNum = loopPoolPtr_->size();
|
||||
while (loopNum > 0)
|
||||
{
|
||||
// LOG_TRACE << "new Wheel loopNum=" << loopNum;
|
||||
auto poolLoop = loopPoolPtr_->getNextLoop();
|
||||
timingWheelMap_[poolLoop] =
|
||||
std::make_shared<TimingWheel>(poolLoop,
|
||||
idleTimeout_,
|
||||
1.0F,
|
||||
idleTimeout_ < 500
|
||||
? idleTimeout_ + 1
|
||||
: 100);
|
||||
--loopNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_TRACE << "map size=" << timingWheelMap_.size();
|
||||
acceptorPtr_->listen();
|
||||
});
|
||||
}
|
||||
void TcpServer::stop()
|
||||
{
|
||||
loop_->runInLoop([this]() { acceptorPtr_.reset(); });
|
||||
for (auto connection : connSet_)
|
||||
{
|
||||
connection->forceClose();
|
||||
}
|
||||
loopPoolPtr_.reset();
|
||||
for (auto &iter : timingWheelMap_)
|
||||
{
|
||||
std::promise<int> pro;
|
||||
auto f = pro.get_future();
|
||||
iter.second->getLoop()->runInLoop([&iter, &pro]() mutable {
|
||||
iter.second.reset();
|
||||
pro.set_value(1);
|
||||
});
|
||||
f.get();
|
||||
}
|
||||
}
|
||||
void TcpServer::connectionClosed(const TcpConnectionPtr &connectionPtr)
|
||||
{
|
||||
LOG_TRACE << "connectionClosed";
|
||||
// loop_->assertInLoopThread();
|
||||
loop_->runInLoop([this, connectionPtr]() {
|
||||
size_t n = connSet_.erase(connectionPtr);
|
||||
(void)n;
|
||||
assert(n == 1);
|
||||
});
|
||||
|
||||
static_cast<TcpConnectionImpl *>(connectionPtr.get())->connectDestroyed();
|
||||
}
|
||||
|
||||
const std::string TcpServer::ipPort() const
|
||||
{
|
||||
return acceptorPtr_->addr().toIpPort();
|
||||
}
|
||||
|
||||
const trantor::InetAddress &TcpServer::address() const
|
||||
{
|
||||
return acceptorPtr_->addr();
|
||||
}
|
||||
|
||||
void TcpServer::enableSSL(
|
||||
const std::string &certPath,
|
||||
const std::string &keyPath,
|
||||
bool useOldTLS,
|
||||
const std::vector<std::pair<std::string, std::string>> &sslConfCmds)
|
||||
{
|
||||
#ifdef USE_OPENSSL
|
||||
/* Create a new OpenSSL context */
|
||||
sslCtxPtr_ = newSSLServerContext(certPath, keyPath, useOldTLS, sslConfCmds);
|
||||
#else
|
||||
LOG_FATAL << "OpenSSL is not found in your system!";
|
||||
abort();
|
||||
#endif
|
||||
}
|
@ -1,250 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file TcpServer.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/trantor
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Trantor
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <trantor/net/callbacks.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/net/EventLoopThreadPool.h>
|
||||
#include <trantor/net/InetAddress.h>
|
||||
#include <trantor/net/TcpConnection.h>
|
||||
#include <trantor/utils/TimingWheel.h>
|
||||
#include <trantor/exports.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <signal.h>
|
||||
namespace trantor
|
||||
{
|
||||
class Acceptor;
|
||||
class SSLContext;
|
||||
/**
|
||||
* @brief This class represents a TCP server.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT TcpServer : NonCopyable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new TCP server instance.
|
||||
*
|
||||
* @param loop The event loop in which the acceptor of the server is
|
||||
* handled.
|
||||
* @param address The address of the server.
|
||||
* @param name The name of the server.
|
||||
* @param reUseAddr The SO_REUSEADDR option.
|
||||
* @param reUsePort The SO_REUSEPORT option.
|
||||
*/
|
||||
TcpServer(EventLoop *loop,
|
||||
const InetAddress &address,
|
||||
const std::string &name,
|
||||
bool reUseAddr = true,
|
||||
bool reUsePort = true);
|
||||
~TcpServer();
|
||||
|
||||
/**
|
||||
* @brief Start the server.
|
||||
*
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* @brief Stop the server.
|
||||
*
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* @brief Set the number of event loops in which the I/O of connections to
|
||||
* the server is handled.
|
||||
*
|
||||
* @param num
|
||||
*/
|
||||
void setIoLoopNum(size_t num)
|
||||
{
|
||||
assert(!started_);
|
||||
loopPoolPtr_ = std::make_shared<EventLoopThreadPool>(num);
|
||||
loopPoolPtr_->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the event loops pool in which the I/O of connections to
|
||||
* the server is handled.
|
||||
*
|
||||
* @param pool
|
||||
*/
|
||||
void setIoLoopThreadPool(const std::shared_ptr<EventLoopThreadPool> &pool)
|
||||
{
|
||||
assert(pool->size() > 0);
|
||||
assert(!started_);
|
||||
loopPoolPtr_ = pool;
|
||||
loopPoolPtr_->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the message callback.
|
||||
*
|
||||
* @param cb The callback is called when some data is received on a
|
||||
* connection to the server.
|
||||
*/
|
||||
void setRecvMessageCallback(const RecvMessageCallback &cb)
|
||||
{
|
||||
recvMessageCallback_ = cb;
|
||||
}
|
||||
void setRecvMessageCallback(RecvMessageCallback &&cb)
|
||||
{
|
||||
recvMessageCallback_ = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the connection callback.
|
||||
*
|
||||
* @param cb The callback is called when a connection is established or
|
||||
* closed.
|
||||
*/
|
||||
void setConnectionCallback(const ConnectionCallback &cb)
|
||||
{
|
||||
connectionCallback_ = cb;
|
||||
}
|
||||
void setConnectionCallback(ConnectionCallback &&cb)
|
||||
{
|
||||
connectionCallback_ = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the write complete callback.
|
||||
*
|
||||
* @param cb The callback is called when data to send is written to the
|
||||
* socket of a connection.
|
||||
*/
|
||||
void setWriteCompleteCallback(const WriteCompleteCallback &cb)
|
||||
{
|
||||
writeCompleteCallback_ = cb;
|
||||
}
|
||||
void setWriteCompleteCallback(WriteCompleteCallback &&cb)
|
||||
{
|
||||
writeCompleteCallback_ = std::move(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the name of the server.
|
||||
*
|
||||
* @return const std::string&
|
||||
*/
|
||||
const std::string &name() const
|
||||
{
|
||||
return serverName_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the IP and port string of the server.
|
||||
*
|
||||
* @return const std::string
|
||||
*/
|
||||
const std::string ipPort() const;
|
||||
|
||||
/**
|
||||
* @brief Get the address of the server.
|
||||
*
|
||||
* @return const trantor::InetAddress&
|
||||
*/
|
||||
const trantor::InetAddress &address() const;
|
||||
|
||||
/**
|
||||
* @brief Get the event loop of the server.
|
||||
*
|
||||
* @return EventLoop*
|
||||
*/
|
||||
EventLoop *getLoop() const
|
||||
{
|
||||
return loop_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the I/O event loops of the server.
|
||||
*
|
||||
* @return std::vector<EventLoop *>
|
||||
*/
|
||||
std::vector<EventLoop *> getIoLoops() const
|
||||
{
|
||||
return loopPoolPtr_->getLoops();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief An idle connection is a connection that has no read or write, kick
|
||||
* off it after timeout seconds.
|
||||
*
|
||||
* @param timeout
|
||||
*/
|
||||
void kickoffIdleConnections(size_t timeout)
|
||||
{
|
||||
loop_->runInLoop([this, timeout]() {
|
||||
assert(!started_);
|
||||
idleTimeout_ = timeout;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable SSL encryption.
|
||||
*
|
||||
* @param certPath The path of the certificate file.
|
||||
* @param keyPath The path of the private key file.
|
||||
* @param useOldTLS If true, the TLS 1.0 and 1.1 are supported by the
|
||||
* server.
|
||||
* @param sslConfCmds The commands used to call the SSL_CONF_cmd function in
|
||||
* OpenSSL.
|
||||
* @note It's well known that TLS 1.0 and 1.1 are not considered secure in
|
||||
* 2020. And it's a good practice to only use TLS 1.2 and above.
|
||||
*/
|
||||
void enableSSL(const std::string &certPath,
|
||||
const std::string &keyPath,
|
||||
bool useOldTLS = false,
|
||||
const std::vector<std::pair<std::string, std::string>>
|
||||
&sslConfCmds = {});
|
||||
|
||||
private:
|
||||
EventLoop *loop_;
|
||||
std::unique_ptr<Acceptor> acceptorPtr_;
|
||||
void newConnection(int fd, const InetAddress &peer);
|
||||
std::string serverName_;
|
||||
std::set<TcpConnectionPtr> connSet_;
|
||||
|
||||
RecvMessageCallback recvMessageCallback_;
|
||||
ConnectionCallback connectionCallback_;
|
||||
WriteCompleteCallback writeCompleteCallback_;
|
||||
|
||||
size_t idleTimeout_{0};
|
||||
std::map<EventLoop *, std::shared_ptr<TimingWheel>> timingWheelMap_;
|
||||
void connectionClosed(const TcpConnectionPtr &connectionPtr);
|
||||
std::shared_ptr<EventLoopThreadPool> loopPoolPtr_;
|
||||
#ifndef _WIN32
|
||||
class IgnoreSigPipe
|
||||
{
|
||||
public:
|
||||
IgnoreSigPipe()
|
||||
{
|
||||
::signal(SIGPIPE, SIG_IGN);
|
||||
LOG_TRACE << "Ignore SIGPIPE";
|
||||
}
|
||||
};
|
||||
|
||||
IgnoreSigPipe initObj;
|
||||
#endif
|
||||
bool started_{false};
|
||||
|
||||
// OpenSSL SSL context Object;
|
||||
std::shared_ptr<SSLContext> sslCtxPtr_;
|
||||
};
|
||||
|
||||
} // namespace trantor
|
@ -1,43 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* callbacks.h
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
namespace trantor
|
||||
{
|
||||
enum class SSLError
|
||||
{
|
||||
kSSLHandshakeError,
|
||||
kSSLInvalidCertificate
|
||||
};
|
||||
using TimerCallback = std::function<void()>;
|
||||
|
||||
// the data has been read to (buf, len)
|
||||
class TcpConnection;
|
||||
class MsgBuffer;
|
||||
using TcpConnectionPtr = std::shared_ptr<TcpConnection>;
|
||||
// tcp server and connection callback
|
||||
using RecvMessageCallback =
|
||||
std::function<void(const TcpConnectionPtr &, MsgBuffer *)>;
|
||||
using ConnectionErrorCallback = std::function<void()>;
|
||||
using ConnectionCallback = std::function<void(const TcpConnectionPtr &)>;
|
||||
using CloseCallback = std::function<void(const TcpConnectionPtr &)>;
|
||||
using WriteCompleteCallback = std::function<void(const TcpConnectionPtr &)>;
|
||||
using HighWaterMarkCallback =
|
||||
std::function<void(const TcpConnectionPtr &, const size_t)>;
|
||||
using SSLErrorCallback = std::function<void(SSLError)>;
|
||||
|
||||
} // namespace trantor
|
@ -1,97 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Acceptor.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Acceptor.h"
|
||||
using namespace trantor;
|
||||
|
||||
#ifndef O_CLOEXEC
|
||||
#define O_CLOEXEC O_NOINHERIT
|
||||
#endif
|
||||
|
||||
Acceptor::Acceptor(EventLoop *loop,
|
||||
const InetAddress &addr,
|
||||
bool reUseAddr,
|
||||
bool reUsePort)
|
||||
|
||||
:
|
||||
#ifndef _WIN32
|
||||
idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC)),
|
||||
#endif
|
||||
sock_(
|
||||
Socket::createNonblockingSocketOrDie(addr.getSockAddr()->sa_family)),
|
||||
addr_(addr),
|
||||
loop_(loop),
|
||||
acceptChannel_(loop, sock_.fd())
|
||||
{
|
||||
sock_.setReuseAddr(reUseAddr);
|
||||
sock_.setReusePort(reUsePort);
|
||||
sock_.bindAddress(addr_);
|
||||
acceptChannel_.setReadCallback(std::bind(&Acceptor::readCallback, this));
|
||||
if (addr_.toPort() == 0)
|
||||
{
|
||||
addr_ = InetAddress{Socket::getLocalAddr(sock_.fd())};
|
||||
}
|
||||
}
|
||||
Acceptor::~Acceptor()
|
||||
{
|
||||
acceptChannel_.disableAll();
|
||||
acceptChannel_.remove();
|
||||
#ifndef _WIN32
|
||||
::close(idleFd_);
|
||||
#endif
|
||||
}
|
||||
void Acceptor::listen()
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
sock_.listen();
|
||||
acceptChannel_.enableReading();
|
||||
}
|
||||
|
||||
void Acceptor::readCallback()
|
||||
{
|
||||
InetAddress peer;
|
||||
int newsock = sock_.accept(&peer);
|
||||
if (newsock >= 0)
|
||||
{
|
||||
if (newConnectionCallback_)
|
||||
{
|
||||
newConnectionCallback_(newsock, peer);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef _WIN32
|
||||
::close(newsock);
|
||||
#else
|
||||
closesocket(newsock);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_SYSERR << "Accpetor::readCallback";
|
||||
// Read the section named "The special problem of
|
||||
// accept()ing when you can't" in libev's doc.
|
||||
// By Marc Lehmann, author of libev.
|
||||
/// errno is thread safe
|
||||
#ifndef _WIN32
|
||||
if (errno == EMFILE)
|
||||
{
|
||||
::close(idleFd_);
|
||||
idleFd_ = sock_.accept(&peer);
|
||||
::close(idleFd_);
|
||||
idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Acceptor.h
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include "Socket.h"
|
||||
#include <trantor/net/InetAddress.h>
|
||||
#include "Channel.h"
|
||||
#include <functional>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
using NewConnectionCallback = std::function<void(int fd, const InetAddress &)>;
|
||||
class Acceptor : NonCopyable
|
||||
{
|
||||
public:
|
||||
Acceptor(EventLoop *loop,
|
||||
const InetAddress &addr,
|
||||
bool reUseAddr = true,
|
||||
bool reUsePort = true);
|
||||
~Acceptor();
|
||||
const InetAddress &addr() const
|
||||
{
|
||||
return addr_;
|
||||
}
|
||||
void setNewConnectionCallback(const NewConnectionCallback &cb)
|
||||
{
|
||||
newConnectionCallback_ = cb;
|
||||
};
|
||||
void listen();
|
||||
|
||||
protected:
|
||||
#ifndef _WIN32
|
||||
int idleFd_;
|
||||
#endif
|
||||
Socket sock_;
|
||||
InetAddress addr_;
|
||||
EventLoop *loop_;
|
||||
NewConnectionCallback newConnectionCallback_;
|
||||
Channel acceptChannel_;
|
||||
void readCallback();
|
||||
};
|
||||
} // namespace trantor
|
@ -1,258 +0,0 @@
|
||||
// Copyright 2016, Tao An. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Tao An
|
||||
|
||||
#include "AresResolver.h"
|
||||
#include <trantor/net/Channel.h>
|
||||
#include <ares.h>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h> // inet_ntop
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace trantor;
|
||||
using namespace std::placeholders;
|
||||
|
||||
namespace
|
||||
{
|
||||
double getSeconds(struct timeval* tv)
|
||||
{
|
||||
if (tv)
|
||||
return double(tv->tv_sec) + double(tv->tv_usec) / 1000000.0;
|
||||
else
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
const char* getSocketType(int type)
|
||||
{
|
||||
if (type == SOCK_DGRAM)
|
||||
return "UDP";
|
||||
else if (type == SOCK_STREAM)
|
||||
return "TCP";
|
||||
else
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Resolver::isCAresUsed()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
AresResolver::LibraryInitializer::LibraryInitializer()
|
||||
{
|
||||
ares_library_init(ARES_LIB_INIT_ALL);
|
||||
}
|
||||
AresResolver::LibraryInitializer::~LibraryInitializer()
|
||||
{
|
||||
ares_library_cleanup();
|
||||
}
|
||||
|
||||
AresResolver::LibraryInitializer AresResolver::libraryInitializer_;
|
||||
|
||||
std::shared_ptr<Resolver> Resolver::newResolver(trantor::EventLoop* loop,
|
||||
size_t timeout)
|
||||
{
|
||||
return std::make_shared<AresResolver>(loop, timeout);
|
||||
}
|
||||
|
||||
AresResolver::AresResolver(EventLoop* loop, size_t timeout)
|
||||
: loop_(loop), timeout_(timeout)
|
||||
{
|
||||
if (!loop)
|
||||
{
|
||||
loop_ = getLoop();
|
||||
}
|
||||
}
|
||||
void AresResolver::init()
|
||||
{
|
||||
if (!ctx_)
|
||||
{
|
||||
struct ares_options options;
|
||||
int optmask = ARES_OPT_FLAGS;
|
||||
options.flags = ARES_FLAG_NOCHECKRESP;
|
||||
options.flags |= ARES_FLAG_STAYOPEN;
|
||||
options.flags |= ARES_FLAG_IGNTC; // UDP only
|
||||
optmask |= ARES_OPT_SOCK_STATE_CB;
|
||||
options.sock_state_cb = &AresResolver::ares_sock_statecallback_;
|
||||
options.sock_state_cb_data = this;
|
||||
optmask |= ARES_OPT_TIMEOUT;
|
||||
options.timeout = 2;
|
||||
// optmask |= ARES_OPT_LOOKUPS;
|
||||
// options.lookups = lookups;
|
||||
|
||||
int status = ares_init_options(&ctx_, &options, optmask);
|
||||
if (status != ARES_SUCCESS)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
ares_set_socket_callback(ctx_,
|
||||
&AresResolver::ares_sock_createcallback_,
|
||||
this);
|
||||
}
|
||||
}
|
||||
AresResolver::~AresResolver()
|
||||
{
|
||||
if (ctx_)
|
||||
ares_destroy(ctx_);
|
||||
}
|
||||
|
||||
void AresResolver::resolveInLoop(const std::string& hostname,
|
||||
const Callback& cb)
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
#ifdef _WIN32
|
||||
if (hostname == "localhost")
|
||||
{
|
||||
const static trantor::InetAddress localhost_{"127.0.0.1", 0};
|
||||
cb(localhost_);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
init();
|
||||
QueryData* queryData = new QueryData(this, cb, hostname);
|
||||
ares_gethostbyname(ctx_,
|
||||
hostname.c_str(),
|
||||
AF_INET,
|
||||
&AresResolver::ares_hostcallback_,
|
||||
queryData);
|
||||
struct timeval tv;
|
||||
struct timeval* tvp = ares_timeout(ctx_, NULL, &tv);
|
||||
double timeout = getSeconds(tvp);
|
||||
// LOG_DEBUG << "timeout " << timeout << " active " << timerActive_;
|
||||
if (!timerActive_ && timeout >= 0.0)
|
||||
{
|
||||
loop_->runAfter(timeout,
|
||||
std::bind(&AresResolver::onTimer, shared_from_this()));
|
||||
timerActive_ = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void AresResolver::onRead(int sockfd)
|
||||
{
|
||||
ares_process_fd(ctx_, sockfd, ARES_SOCKET_BAD);
|
||||
}
|
||||
|
||||
void AresResolver::onTimer()
|
||||
{
|
||||
assert(timerActive_ == true);
|
||||
ares_process_fd(ctx_, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
|
||||
struct timeval tv;
|
||||
struct timeval* tvp = ares_timeout(ctx_, NULL, &tv);
|
||||
double timeout = getSeconds(tvp);
|
||||
|
||||
if (timeout < 0)
|
||||
{
|
||||
timerActive_ = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
loop_->runAfter(timeout,
|
||||
std::bind(&AresResolver::onTimer, shared_from_this()));
|
||||
}
|
||||
}
|
||||
|
||||
void AresResolver::onQueryResult(int status,
|
||||
struct hostent* result,
|
||||
const std::string& hostname,
|
||||
const Callback& callback)
|
||||
{
|
||||
LOG_TRACE << "onQueryResult " << status;
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = 0;
|
||||
if (result)
|
||||
{
|
||||
addr.sin_addr = *reinterpret_cast<in_addr*>(result->h_addr);
|
||||
}
|
||||
InetAddress inet(addr);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(globalMutex());
|
||||
auto& addrItem = globalCache()[hostname];
|
||||
addrItem.first = addr.sin_addr;
|
||||
addrItem.second = trantor::Date::date();
|
||||
}
|
||||
callback(inet);
|
||||
}
|
||||
|
||||
void AresResolver::onSockCreate(int sockfd, int type)
|
||||
{
|
||||
(void)type;
|
||||
loop_->assertInLoopThread();
|
||||
assert(channels_.find(sockfd) == channels_.end());
|
||||
Channel* channel = new Channel(loop_, sockfd);
|
||||
channel->setReadCallback(std::bind(&AresResolver::onRead, this, sockfd));
|
||||
channel->enableReading();
|
||||
channels_[sockfd].reset(channel);
|
||||
}
|
||||
|
||||
void AresResolver::onSockStateChange(int sockfd, bool read, bool write)
|
||||
{
|
||||
(void)write;
|
||||
loop_->assertInLoopThread();
|
||||
ChannelList::iterator it = channels_.find(sockfd);
|
||||
assert(it != channels_.end());
|
||||
if (read)
|
||||
{
|
||||
// update
|
||||
// if (write) { } else { }
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove
|
||||
it->second->disableAll();
|
||||
it->second->remove();
|
||||
channels_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void AresResolver::ares_hostcallback_(void* data,
|
||||
int status,
|
||||
int timeouts,
|
||||
struct hostent* hostent)
|
||||
{
|
||||
(void)timeouts;
|
||||
QueryData* query = static_cast<QueryData*>(data);
|
||||
|
||||
query->owner_->onQueryResult(status,
|
||||
hostent,
|
||||
query->hostname_,
|
||||
query->callback_);
|
||||
delete query;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
int AresResolver::ares_sock_createcallback_(SOCKET sockfd, int type, void* data)
|
||||
#else
|
||||
int AresResolver::ares_sock_createcallback_(int sockfd, int type, void* data)
|
||||
#endif
|
||||
{
|
||||
LOG_TRACE << "sockfd=" << sockfd << " type=" << getSocketType(type);
|
||||
static_cast<AresResolver*>(data)->onSockCreate(sockfd, type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AresResolver::ares_sock_statecallback_(void* data,
|
||||
#ifdef _WIN32
|
||||
SOCKET sockfd,
|
||||
#else
|
||||
int sockfd,
|
||||
#endif
|
||||
int read,
|
||||
int write)
|
||||
{
|
||||
LOG_TRACE << "sockfd=" << sockfd << " read=" << read << " write=" << write;
|
||||
static_cast<AresResolver*>(data)->onSockStateChange(sockfd, read, write);
|
||||
}
|
@ -1,148 +0,0 @@
|
||||
// Copyright 2016, Tao An. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Tao An
|
||||
|
||||
#pragma once
|
||||
#include <trantor/net/Resolver.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
struct hostent;
|
||||
struct ares_channeldata;
|
||||
using ares_channel = struct ares_channeldata*;
|
||||
}
|
||||
namespace trantor
|
||||
{
|
||||
class AresResolver : public Resolver,
|
||||
public NonCopyable,
|
||||
public std::enable_shared_from_this<AresResolver>
|
||||
{
|
||||
public:
|
||||
AresResolver(trantor::EventLoop* loop, size_t timeout);
|
||||
~AresResolver();
|
||||
|
||||
virtual void resolve(const std::string& hostname,
|
||||
const Callback& cb) override
|
||||
{
|
||||
bool cached = false;
|
||||
InetAddress inet;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(globalMutex());
|
||||
auto iter = globalCache().find(hostname);
|
||||
if (iter != globalCache().end())
|
||||
{
|
||||
auto& cachedAddr = iter->second;
|
||||
if (timeout_ == 0 ||
|
||||
cachedAddr.second.after(timeout_) > trantor::Date::date())
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = 0;
|
||||
addr.sin_addr = cachedAddr.first;
|
||||
inet = InetAddress(addr);
|
||||
cached = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cached)
|
||||
{
|
||||
cb(inet);
|
||||
return;
|
||||
}
|
||||
if (loop_->isInLoopThread())
|
||||
{
|
||||
resolveInLoop(hostname, cb);
|
||||
}
|
||||
else
|
||||
{
|
||||
loop_->queueInLoop([thisPtr = shared_from_this(), hostname, cb]() {
|
||||
thisPtr->resolveInLoop(hostname, cb);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct QueryData
|
||||
{
|
||||
AresResolver* owner_;
|
||||
Callback callback_;
|
||||
std::string hostname_;
|
||||
QueryData(AresResolver* o,
|
||||
const Callback& cb,
|
||||
const std::string& hostname)
|
||||
: owner_(o), callback_(cb), hostname_(hostname)
|
||||
{
|
||||
}
|
||||
};
|
||||
void resolveInLoop(const std::string& hostname, const Callback& cb);
|
||||
void init();
|
||||
trantor::EventLoop* loop_;
|
||||
ares_channel ctx_{nullptr};
|
||||
bool timerActive_{false};
|
||||
using ChannelList = std::map<int, std::unique_ptr<trantor::Channel>>;
|
||||
ChannelList channels_;
|
||||
static std::unordered_map<std::string,
|
||||
std::pair<struct in_addr, trantor::Date>>&
|
||||
globalCache()
|
||||
{
|
||||
static std::unordered_map<std::string,
|
||||
std::pair<struct in_addr, trantor::Date>>
|
||||
dnsCache;
|
||||
return dnsCache;
|
||||
}
|
||||
static std::mutex& globalMutex()
|
||||
{
|
||||
static std::mutex mutex_;
|
||||
return mutex_;
|
||||
}
|
||||
static EventLoop* getLoop()
|
||||
{
|
||||
static EventLoopThread loopThread;
|
||||
loopThread.run();
|
||||
return loopThread.getLoop();
|
||||
}
|
||||
const size_t timeout_{60};
|
||||
|
||||
void onRead(int sockfd);
|
||||
void onTimer();
|
||||
void onQueryResult(int status,
|
||||
struct hostent* result,
|
||||
const std::string& hostname,
|
||||
const Callback& callback);
|
||||
void onSockCreate(int sockfd, int type);
|
||||
void onSockStateChange(int sockfd, bool read, bool write);
|
||||
|
||||
static void ares_hostcallback_(void* data,
|
||||
int status,
|
||||
int timeouts,
|
||||
struct hostent* hostent);
|
||||
#ifdef _WIN32
|
||||
static int ares_sock_createcallback_(SOCKET sockfd, int type, void* data);
|
||||
#else
|
||||
static int ares_sock_createcallback_(int sockfd, int type, void* data);
|
||||
#endif
|
||||
static void ares_sock_statecallback_(void* data,
|
||||
#ifdef _WIN32
|
||||
SOCKET sockfd,
|
||||
#else
|
||||
int sockfd,
|
||||
#endif
|
||||
int read,
|
||||
int write);
|
||||
struct LibraryInitializer
|
||||
{
|
||||
LibraryInitializer();
|
||||
~LibraryInitializer();
|
||||
};
|
||||
static LibraryInitializer libraryInitializer_;
|
||||
};
|
||||
} // namespace trantor
|
@ -1,261 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file Connector.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Connector.h"
|
||||
#include "Channel.h"
|
||||
#include "Socket.h"
|
||||
|
||||
using namespace trantor;
|
||||
|
||||
Connector::Connector(EventLoop *loop, const InetAddress &addr, bool retry)
|
||||
: loop_(loop), serverAddr_(addr), retry_(retry)
|
||||
{
|
||||
}
|
||||
Connector::Connector(EventLoop *loop, InetAddress &&addr, bool retry)
|
||||
: loop_(loop), serverAddr_(std::move(addr)), retry_(retry)
|
||||
{
|
||||
}
|
||||
|
||||
void Connector::start()
|
||||
{
|
||||
connect_ = true;
|
||||
loop_->runInLoop([this]() { startInLoop(); });
|
||||
}
|
||||
void Connector::restart()
|
||||
{
|
||||
}
|
||||
void Connector::stop()
|
||||
{
|
||||
}
|
||||
|
||||
void Connector::startInLoop()
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
assert(status_ == Status::Disconnected);
|
||||
if (connect_)
|
||||
{
|
||||
connect();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG << "do not connect";
|
||||
}
|
||||
}
|
||||
void Connector::connect()
|
||||
{
|
||||
int sockfd = Socket::createNonblockingSocketOrDie(serverAddr_.family());
|
||||
errno = 0;
|
||||
int ret = Socket::connect(sockfd, serverAddr_);
|
||||
int savedErrno = (ret == 0) ? 0 : errno;
|
||||
switch (savedErrno)
|
||||
{
|
||||
case 0:
|
||||
case EINPROGRESS:
|
||||
case EINTR:
|
||||
case EISCONN:
|
||||
LOG_TRACE << "connecting";
|
||||
connecting(sockfd);
|
||||
break;
|
||||
|
||||
case EAGAIN:
|
||||
case EADDRINUSE:
|
||||
case EADDRNOTAVAIL:
|
||||
case ECONNREFUSED:
|
||||
case ENETUNREACH:
|
||||
if (retry_)
|
||||
{
|
||||
retry(sockfd);
|
||||
}
|
||||
break;
|
||||
|
||||
case EACCES:
|
||||
case EPERM:
|
||||
case EAFNOSUPPORT:
|
||||
case EALREADY:
|
||||
case EBADF:
|
||||
case EFAULT:
|
||||
case ENOTSOCK:
|
||||
LOG_SYSERR << "connect error in Connector::startInLoop "
|
||||
<< savedErrno;
|
||||
#ifndef _WIN32
|
||||
::close(sockfd);
|
||||
#else
|
||||
closesocket(sockfd);
|
||||
#endif
|
||||
if (errorCallback_)
|
||||
errorCallback_();
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_SYSERR << "Unexpected error in Connector::startInLoop "
|
||||
<< savedErrno;
|
||||
#ifndef _WIN32
|
||||
::close(sockfd);
|
||||
#else
|
||||
closesocket(sockfd);
|
||||
#endif
|
||||
if (errorCallback_)
|
||||
errorCallback_();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Connector::connecting(int sockfd)
|
||||
{
|
||||
status_ = Status::Connecting;
|
||||
assert(!channelPtr_);
|
||||
channelPtr_.reset(new Channel(loop_, sockfd));
|
||||
channelPtr_->setWriteCallback(
|
||||
std::bind(&Connector::handleWrite, shared_from_this()));
|
||||
channelPtr_->setErrorCallback(
|
||||
std::bind(&Connector::handleError, shared_from_this()));
|
||||
channelPtr_->setCloseCallback(
|
||||
std::bind(&Connector::handleError, shared_from_this()));
|
||||
LOG_TRACE << "connecting:" << sockfd;
|
||||
channelPtr_->enableWriting();
|
||||
}
|
||||
|
||||
int Connector::removeAndResetChannel()
|
||||
{
|
||||
channelPtr_->disableAll();
|
||||
channelPtr_->remove();
|
||||
int sockfd = channelPtr_->fd();
|
||||
// Can't reset channel_ here, because we are inside Channel::handleEvent
|
||||
loop_->queueInLoop([this]() { channelPtr_.reset(); });
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
void Connector::handleWrite()
|
||||
{
|
||||
if (status_ == Status::Connecting)
|
||||
{
|
||||
int sockfd = removeAndResetChannel();
|
||||
int err = Socket::getSocketError(sockfd);
|
||||
if (err)
|
||||
{
|
||||
LOG_WARN << "Connector::handleWrite - SO_ERROR = " << err << " "
|
||||
<< strerror_tl(err);
|
||||
if (retry_)
|
||||
{
|
||||
retry(sockfd);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef _WIN32
|
||||
::close(sockfd);
|
||||
#else
|
||||
closesocket(sockfd);
|
||||
#endif
|
||||
}
|
||||
if (errorCallback_)
|
||||
{
|
||||
errorCallback_();
|
||||
}
|
||||
}
|
||||
else if (Socket::isSelfConnect(sockfd))
|
||||
{
|
||||
LOG_WARN << "Connector::handleWrite - Self connect";
|
||||
if (retry_)
|
||||
{
|
||||
retry(sockfd);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef _WIN32
|
||||
::close(sockfd);
|
||||
#else
|
||||
closesocket(sockfd);
|
||||
#endif
|
||||
}
|
||||
if (errorCallback_)
|
||||
{
|
||||
errorCallback_();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status_ = Status::Connected;
|
||||
if (connect_)
|
||||
{
|
||||
newConnectionCallback_(sockfd);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef _WIN32
|
||||
::close(sockfd);
|
||||
#else
|
||||
closesocket(sockfd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// what happened?
|
||||
assert(status_ == Status::Disconnected);
|
||||
}
|
||||
}
|
||||
|
||||
void Connector::handleError()
|
||||
{
|
||||
if (status_ == Status::Connecting)
|
||||
{
|
||||
status_ = Status::Disconnected;
|
||||
int sockfd = removeAndResetChannel();
|
||||
int err = Socket::getSocketError(sockfd);
|
||||
LOG_TRACE << "SO_ERROR = " << err << " " << strerror_tl(err);
|
||||
if (retry_)
|
||||
{
|
||||
retry(sockfd);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef _WIN32
|
||||
::close(sockfd);
|
||||
#else
|
||||
closesocket(sockfd);
|
||||
#endif
|
||||
}
|
||||
if (errorCallback_)
|
||||
{
|
||||
errorCallback_();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Connector::retry(int sockfd)
|
||||
{
|
||||
assert(retry_);
|
||||
#ifndef _WIN32
|
||||
::close(sockfd);
|
||||
#else
|
||||
closesocket(sockfd);
|
||||
#endif
|
||||
status_ = Status::Disconnected;
|
||||
if (connect_)
|
||||
{
|
||||
LOG_INFO << "Connector::retry - Retry connecting to "
|
||||
<< serverAddr_.toIpPort() << " in " << retryInterval_
|
||||
<< " milliseconds. ";
|
||||
loop_->runAfter(retryInterval_ / 1000.0,
|
||||
std::bind(&Connector::startInLoop, shared_from_this()));
|
||||
retryInterval_ = retryInterval_ * 2;
|
||||
if (retryInterval_ > maxRetryInterval_)
|
||||
retryInterval_ = maxRetryInterval_;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG << "do not connect";
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Connector.h
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <trantor/net/InetAddress.h>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
class Connector : public NonCopyable,
|
||||
public std::enable_shared_from_this<Connector>
|
||||
{
|
||||
public:
|
||||
using NewConnectionCallback = std::function<void(int sockfd)>;
|
||||
using ConnectionErrorCallback = std::function<void()>;
|
||||
Connector(EventLoop *loop, const InetAddress &addr, bool retry = true);
|
||||
Connector(EventLoop *loop, InetAddress &&addr, bool retry = true);
|
||||
void setNewConnectionCallback(const NewConnectionCallback &cb)
|
||||
{
|
||||
newConnectionCallback_ = cb;
|
||||
}
|
||||
void setNewConnectionCallback(NewConnectionCallback &&cb)
|
||||
{
|
||||
newConnectionCallback_ = std::move(cb);
|
||||
}
|
||||
void setErrorCallback(const ConnectionErrorCallback &cb)
|
||||
{
|
||||
errorCallback_ = cb;
|
||||
}
|
||||
void setErrorCallback(ConnectionErrorCallback &&cb)
|
||||
{
|
||||
errorCallback_ = std::move(cb);
|
||||
}
|
||||
const InetAddress &serverAddress() const
|
||||
{
|
||||
return serverAddr_;
|
||||
}
|
||||
void start();
|
||||
void restart();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
NewConnectionCallback newConnectionCallback_;
|
||||
ConnectionErrorCallback errorCallback_;
|
||||
enum class Status
|
||||
{
|
||||
Disconnected,
|
||||
Connecting,
|
||||
Connected
|
||||
};
|
||||
static constexpr int kMaxRetryDelayMs = 30 * 1000;
|
||||
static constexpr int kInitRetryDelayMs = 500;
|
||||
std::shared_ptr<Channel> channelPtr_;
|
||||
EventLoop *loop_;
|
||||
InetAddress serverAddr_;
|
||||
|
||||
std::atomic_bool connect_{false};
|
||||
std::atomic<Status> status_{Status::Disconnected};
|
||||
|
||||
int retryInterval_{kInitRetryDelayMs};
|
||||
int maxRetryInterval_{kMaxRetryDelayMs};
|
||||
|
||||
bool retry_;
|
||||
|
||||
void startInLoop();
|
||||
void connect();
|
||||
void connecting(int sockfd);
|
||||
int removeAndResetChannel();
|
||||
void handleWrite();
|
||||
void handleError();
|
||||
void retry(int sockfd);
|
||||
};
|
||||
|
||||
} // namespace trantor
|
@ -1,95 +0,0 @@
|
||||
#include "NormalResolver.h"
|
||||
#include <trantor/utils/Logger.h>
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <strings.h> // memset
|
||||
#endif
|
||||
|
||||
using namespace trantor;
|
||||
|
||||
std::shared_ptr<Resolver> Resolver::newResolver(trantor::EventLoop *,
|
||||
size_t timeout)
|
||||
{
|
||||
return std::make_shared<NormalResolver>(timeout);
|
||||
}
|
||||
bool Resolver::isCAresUsed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
void NormalResolver::resolve(const std::string &hostname,
|
||||
const Callback &callback)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(globalMutex());
|
||||
auto iter = globalCache().find(hostname);
|
||||
if (iter != globalCache().end())
|
||||
{
|
||||
auto &cachedAddr = iter->second;
|
||||
if (timeout_ == 0 || cachedAddr.second.after(static_cast<double>(
|
||||
timeout_)) > trantor::Date::date())
|
||||
{
|
||||
callback(cachedAddr.first);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
concurrentTaskQueue().runTaskInQueue(
|
||||
[thisPtr = shared_from_this(), callback, hostname]() {
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(thisPtr->globalMutex());
|
||||
auto iter = thisPtr->globalCache().find(hostname);
|
||||
if (iter != thisPtr->globalCache().end())
|
||||
{
|
||||
auto &cachedAddr = iter->second;
|
||||
if (thisPtr->timeout_ == 0 ||
|
||||
cachedAddr.second.after(static_cast<double>(
|
||||
thisPtr->timeout_)) > trantor::Date::date())
|
||||
{
|
||||
callback(cachedAddr.first);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
struct addrinfo hints, *res;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
auto error = getaddrinfo(hostname.data(), nullptr, &hints, &res);
|
||||
if (error == -1 || res == nullptr)
|
||||
{
|
||||
LOG_SYSERR << "InetAddress::resolve";
|
||||
callback(InetAddress{});
|
||||
return;
|
||||
}
|
||||
InetAddress inet;
|
||||
if (res->ai_family == AF_INET)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr = *reinterpret_cast<struct sockaddr_in *>(res->ai_addr);
|
||||
inet = InetAddress(addr);
|
||||
}
|
||||
else if (res->ai_family == AF_INET6)
|
||||
{
|
||||
struct sockaddr_in6 addr;
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr = *reinterpret_cast<struct sockaddr_in6 *>(res->ai_addr);
|
||||
inet = InetAddress(addr);
|
||||
}
|
||||
callback(inet);
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(thisPtr->globalMutex());
|
||||
auto &addrItem = thisPtr->globalCache()[hostname];
|
||||
addrItem.first = inet;
|
||||
addrItem.second = trantor::Date::date();
|
||||
}
|
||||
return;
|
||||
});
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
// Copyright 2016, Tao An. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the License file.
|
||||
|
||||
// Author: Tao An
|
||||
|
||||
#pragma once
|
||||
#include <trantor/net/Resolver.h>
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/utils/ConcurrentTaskQueue.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
constexpr size_t kResolveBufferLength{16 * 1024};
|
||||
class NormalResolver : public Resolver,
|
||||
public NonCopyable,
|
||||
public std::enable_shared_from_this<NormalResolver>
|
||||
{
|
||||
public:
|
||||
virtual void resolve(const std::string& hostname,
|
||||
const Callback& callback) override;
|
||||
explicit NormalResolver(size_t timeout)
|
||||
: timeout_(timeout), resolveBuffer_(kResolveBufferLength)
|
||||
{
|
||||
}
|
||||
virtual ~NormalResolver()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
static std::unordered_map<std::string,
|
||||
std::pair<trantor::InetAddress, trantor::Date>>&
|
||||
globalCache()
|
||||
{
|
||||
static std::unordered_map<
|
||||
std::string,
|
||||
std::pair<trantor::InetAddress, trantor::Date>>
|
||||
dnsCache_;
|
||||
return dnsCache_;
|
||||
}
|
||||
static std::mutex& globalMutex()
|
||||
{
|
||||
static std::mutex mutex_;
|
||||
return mutex_;
|
||||
}
|
||||
static trantor::ConcurrentTaskQueue& concurrentTaskQueue()
|
||||
{
|
||||
static trantor::ConcurrentTaskQueue queue(
|
||||
std::thread::hardware_concurrency() < 8
|
||||
? 8
|
||||
: std::thread::hardware_concurrency(),
|
||||
"Dns Queue");
|
||||
return queue;
|
||||
}
|
||||
const size_t timeout_;
|
||||
std::vector<char> resolveBuffer_;
|
||||
};
|
||||
} // namespace trantor
|
@ -1,32 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Poller.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Poller.h"
|
||||
#ifdef __linux__
|
||||
#include "poller/EpollPoller.h"
|
||||
#elif defined _WIN32
|
||||
#include "Wepoll.h"
|
||||
#include "poller/EpollPoller.h"
|
||||
#else
|
||||
#include "poller/KQueue.h"
|
||||
#endif
|
||||
using namespace trantor;
|
||||
Poller *Poller::newPoller(EventLoop *loop)
|
||||
{
|
||||
#if defined __linux__ || defined _WIN32
|
||||
return new EpollPoller(loop);
|
||||
#else
|
||||
return new KQueue(loop);
|
||||
#endif
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Poller.h
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "NonCopyable.h"
|
||||
#include "EventLoop.h"
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
class Channel;
|
||||
#ifdef _WIN32
|
||||
using EventCallback = std::function<void(uint64_t)>;
|
||||
#endif
|
||||
class Poller : NonCopyable
|
||||
{
|
||||
public:
|
||||
explicit Poller(EventLoop *loop) : ownerLoop_(loop){};
|
||||
virtual ~Poller()
|
||||
{
|
||||
}
|
||||
void assertInLoopThread()
|
||||
{
|
||||
ownerLoop_->assertInLoopThread();
|
||||
}
|
||||
virtual void poll(int timeoutMs, ChannelList *activeChannels) = 0;
|
||||
virtual void updateChannel(Channel *channel) = 0;
|
||||
virtual void removeChannel(Channel *channel) = 0;
|
||||
#ifdef _WIN32
|
||||
virtual void postEvent(uint64_t event) = 0;
|
||||
virtual void setEventCallback(const EventCallback &cb) = 0;
|
||||
#endif
|
||||
virtual void resetAfterFork()
|
||||
{
|
||||
}
|
||||
static Poller *newPoller(EventLoop *loop);
|
||||
|
||||
private:
|
||||
EventLoop *ownerLoop_;
|
||||
};
|
||||
} // namespace trantor
|
@ -1,248 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Socket.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include "Socket.h"
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
|
||||
using namespace trantor;
|
||||
|
||||
bool Socket::isSelfConnect(int sockfd)
|
||||
{
|
||||
struct sockaddr_in6 localaddr = getLocalAddr(sockfd);
|
||||
struct sockaddr_in6 peeraddr = getPeerAddr(sockfd);
|
||||
if (localaddr.sin6_family == AF_INET)
|
||||
{
|
||||
const struct sockaddr_in *laddr4 =
|
||||
reinterpret_cast<struct sockaddr_in *>(&localaddr);
|
||||
const struct sockaddr_in *raddr4 =
|
||||
reinterpret_cast<struct sockaddr_in *>(&peeraddr);
|
||||
return laddr4->sin_port == raddr4->sin_port &&
|
||||
laddr4->sin_addr.s_addr == raddr4->sin_addr.s_addr;
|
||||
}
|
||||
else if (localaddr.sin6_family == AF_INET6)
|
||||
{
|
||||
return localaddr.sin6_port == peeraddr.sin6_port &&
|
||||
memcmp(&localaddr.sin6_addr,
|
||||
&peeraddr.sin6_addr,
|
||||
sizeof localaddr.sin6_addr) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::bindAddress(const InetAddress &localaddr)
|
||||
{
|
||||
assert(sockFd_ > 0);
|
||||
int ret;
|
||||
if (localaddr.isIpV6())
|
||||
ret = ::bind(sockFd_, localaddr.getSockAddr(), sizeof(sockaddr_in6));
|
||||
else
|
||||
ret = ::bind(sockFd_, localaddr.getSockAddr(), sizeof(sockaddr_in));
|
||||
|
||||
if (ret == 0)
|
||||
return;
|
||||
else
|
||||
{
|
||||
LOG_SYSERR << ", Bind address failed at " << localaddr.toIpPort();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
void Socket::listen()
|
||||
{
|
||||
assert(sockFd_ > 0);
|
||||
int ret = ::listen(sockFd_, SOMAXCONN);
|
||||
if (ret < 0)
|
||||
{
|
||||
LOG_SYSERR << "listen failed";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
int Socket::accept(InetAddress *peeraddr)
|
||||
{
|
||||
struct sockaddr_in6 addr6;
|
||||
memset(&addr6, 0, sizeof(addr6));
|
||||
socklen_t size = sizeof(addr6);
|
||||
#ifdef __linux__
|
||||
int connfd = ::accept4(sockFd_,
|
||||
(struct sockaddr *)&addr6,
|
||||
&size,
|
||||
SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
#else
|
||||
int connfd =
|
||||
static_cast<int>(::accept(sockFd_, (struct sockaddr *)&addr6, &size));
|
||||
setNonBlockAndCloseOnExec(connfd);
|
||||
#endif
|
||||
if (connfd >= 0)
|
||||
{
|
||||
peeraddr->setSockAddrInet6(addr6);
|
||||
}
|
||||
return connfd;
|
||||
}
|
||||
void Socket::closeWrite()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
if (::shutdown(sockFd_, SHUT_WR) < 0)
|
||||
#else
|
||||
if (::shutdown(sockFd_, SD_SEND) < 0)
|
||||
#endif
|
||||
{
|
||||
LOG_SYSERR << "sockets::shutdownWrite";
|
||||
}
|
||||
}
|
||||
int Socket::read(char *buffer, uint64_t len)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return ::read(sockFd_, buffer, len);
|
||||
#else
|
||||
return recv(sockFd_, buffer, static_cast<int>(len), 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct sockaddr_in6 Socket::getLocalAddr(int sockfd)
|
||||
{
|
||||
struct sockaddr_in6 localaddr;
|
||||
memset(&localaddr, 0, sizeof(localaddr));
|
||||
socklen_t addrlen = static_cast<socklen_t>(sizeof localaddr);
|
||||
if (::getsockname(sockfd,
|
||||
static_cast<struct sockaddr *>((void *)(&localaddr)),
|
||||
&addrlen) < 0)
|
||||
{
|
||||
LOG_SYSERR << "sockets::getLocalAddr";
|
||||
}
|
||||
return localaddr;
|
||||
}
|
||||
|
||||
struct sockaddr_in6 Socket::getPeerAddr(int sockfd)
|
||||
{
|
||||
struct sockaddr_in6 peeraddr;
|
||||
memset(&peeraddr, 0, sizeof(peeraddr));
|
||||
socklen_t addrlen = static_cast<socklen_t>(sizeof peeraddr);
|
||||
if (::getpeername(sockfd,
|
||||
static_cast<struct sockaddr *>((void *)(&peeraddr)),
|
||||
&addrlen) < 0)
|
||||
{
|
||||
LOG_SYSERR << "sockets::getPeerAddr";
|
||||
}
|
||||
return peeraddr;
|
||||
}
|
||||
|
||||
void Socket::setTcpNoDelay(bool on)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
char optval = on ? 1 : 0;
|
||||
#else
|
||||
int optval = on ? 1 : 0;
|
||||
#endif
|
||||
::setsockopt(sockFd_,
|
||||
IPPROTO_TCP,
|
||||
TCP_NODELAY,
|
||||
&optval,
|
||||
static_cast<socklen_t>(sizeof optval));
|
||||
// TODO CHECK
|
||||
}
|
||||
|
||||
void Socket::setReuseAddr(bool on)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
char optval = on ? 1 : 0;
|
||||
#else
|
||||
int optval = on ? 1 : 0;
|
||||
#endif
|
||||
::setsockopt(sockFd_,
|
||||
SOL_SOCKET,
|
||||
SO_REUSEADDR,
|
||||
&optval,
|
||||
static_cast<socklen_t>(sizeof optval));
|
||||
// TODO CHECK
|
||||
}
|
||||
|
||||
void Socket::setReusePort(bool on)
|
||||
{
|
||||
#ifdef SO_REUSEPORT
|
||||
#ifdef _WIN32
|
||||
char optval = on ? 1 : 0;
|
||||
#else
|
||||
int optval = on ? 1 : 0;
|
||||
#endif
|
||||
int ret = ::setsockopt(sockFd_,
|
||||
SOL_SOCKET,
|
||||
SO_REUSEPORT,
|
||||
&optval,
|
||||
static_cast<socklen_t>(sizeof optval));
|
||||
if (ret < 0 && on)
|
||||
{
|
||||
LOG_SYSERR << "SO_REUSEPORT failed.";
|
||||
}
|
||||
#else
|
||||
if (on)
|
||||
{
|
||||
LOG_ERROR << "SO_REUSEPORT is not supported.";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Socket::setKeepAlive(bool on)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
char optval = on ? 1 : 0;
|
||||
#else
|
||||
int optval = on ? 1 : 0;
|
||||
#endif
|
||||
::setsockopt(sockFd_,
|
||||
SOL_SOCKET,
|
||||
SO_KEEPALIVE,
|
||||
&optval,
|
||||
static_cast<socklen_t>(sizeof optval));
|
||||
// TODO CHECK
|
||||
}
|
||||
|
||||
int Socket::getSocketError()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
char optval;
|
||||
#else
|
||||
int optval;
|
||||
#endif
|
||||
socklen_t optlen = static_cast<socklen_t>(sizeof optval);
|
||||
|
||||
if (::getsockopt(sockFd_, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
|
||||
{
|
||||
return errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
return optval;
|
||||
}
|
||||
}
|
||||
|
||||
Socket::~Socket()
|
||||
{
|
||||
LOG_TRACE << "Socket deconstructed:" << sockFd_;
|
||||
if (sockFd_ >= 0)
|
||||
#ifndef _WIN32
|
||||
close(sockFd_);
|
||||
#else
|
||||
closesocket(sockFd_);
|
||||
#endif
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Socket.h
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/net/InetAddress.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <string>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
class Socket : NonCopyable
|
||||
{
|
||||
public:
|
||||
static int createNonblockingSocketOrDie(int family)
|
||||
{
|
||||
#ifdef __linux__
|
||||
int sock = ::socket(family,
|
||||
SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
|
||||
IPPROTO_TCP);
|
||||
#else
|
||||
int sock = static_cast<int>(::socket(family, SOCK_STREAM, IPPROTO_TCP));
|
||||
setNonBlockAndCloseOnExec(sock);
|
||||
#endif
|
||||
if (sock < 0)
|
||||
{
|
||||
LOG_SYSERR << "sockets::createNonblockingOrDie";
|
||||
exit(1);
|
||||
}
|
||||
LOG_TRACE << "sock=" << sock;
|
||||
return sock;
|
||||
}
|
||||
|
||||
static int getSocketError(int sockfd)
|
||||
{
|
||||
int optval;
|
||||
socklen_t optlen = static_cast<socklen_t>(sizeof optval);
|
||||
#ifdef _WIN32
|
||||
if (::getsockopt(
|
||||
sockfd, SOL_SOCKET, SO_ERROR, (char *)&optval, &optlen) < 0)
|
||||
#else
|
||||
if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
|
||||
#endif
|
||||
{
|
||||
return errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
return optval;
|
||||
}
|
||||
}
|
||||
|
||||
static int connect(int sockfd, const InetAddress &addr)
|
||||
{
|
||||
if (addr.isIpV6())
|
||||
return ::connect(sockfd,
|
||||
addr.getSockAddr(),
|
||||
static_cast<socklen_t>(
|
||||
sizeof(struct sockaddr_in6)));
|
||||
else
|
||||
return ::connect(sockfd,
|
||||
addr.getSockAddr(),
|
||||
static_cast<socklen_t>(
|
||||
sizeof(struct sockaddr_in)));
|
||||
}
|
||||
|
||||
static bool isSelfConnect(int sockfd);
|
||||
|
||||
explicit Socket(int sockfd) : sockFd_(sockfd)
|
||||
{
|
||||
}
|
||||
~Socket();
|
||||
/// abort if address in use
|
||||
void bindAddress(const InetAddress &localaddr);
|
||||
/// abort if address in use
|
||||
void listen();
|
||||
int accept(InetAddress *peeraddr);
|
||||
void closeWrite();
|
||||
int read(char *buffer, uint64_t len);
|
||||
int fd()
|
||||
{
|
||||
return sockFd_;
|
||||
}
|
||||
static struct sockaddr_in6 getLocalAddr(int sockfd);
|
||||
static struct sockaddr_in6 getPeerAddr(int sockfd);
|
||||
|
||||
///
|
||||
/// Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm).
|
||||
///
|
||||
void setTcpNoDelay(bool on);
|
||||
|
||||
///
|
||||
/// Enable/disable SO_REUSEADDR
|
||||
///
|
||||
void setReuseAddr(bool on);
|
||||
|
||||
///
|
||||
/// Enable/disable SO_REUSEPORT
|
||||
///
|
||||
void setReusePort(bool on);
|
||||
|
||||
///
|
||||
/// Enable/disable SO_KEEPALIVE
|
||||
///
|
||||
void setKeepAlive(bool on);
|
||||
int getSocketError();
|
||||
|
||||
protected:
|
||||
int sockFd_;
|
||||
|
||||
public:
|
||||
// taken from muduo
|
||||
static void setNonBlockAndCloseOnExec(int sockfd)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// TODO how to set FD_CLOEXEC on windows? is it necessary?
|
||||
u_long arg = 1;
|
||||
auto ret = ioctlsocket(sockfd, (long)FIONBIO, &arg);
|
||||
if (ret)
|
||||
{
|
||||
LOG_ERROR << "ioctlsocket error";
|
||||
}
|
||||
#else
|
||||
// non-block
|
||||
int flags = ::fcntl(sockfd, F_GETFL, 0);
|
||||
flags |= O_NONBLOCK;
|
||||
int ret = ::fcntl(sockfd, F_SETFL, flags);
|
||||
// TODO check
|
||||
|
||||
// close-on-exec
|
||||
flags = ::fcntl(sockfd, F_GETFD, 0);
|
||||
flags |= FD_CLOEXEC;
|
||||
ret = ::fcntl(sockfd, F_SETFD, flags);
|
||||
// TODO check
|
||||
|
||||
(void)ret;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace trantor
|
File diff suppressed because it is too large
Load Diff
@ -1,344 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file TcpConnectionImpl.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/net/TcpConnection.h>
|
||||
#include <trantor/utils/TimingWheel.h>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <thread>
|
||||
#include <array>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
#ifdef USE_OPENSSL
|
||||
enum class SSLStatus
|
||||
{
|
||||
Handshaking,
|
||||
Connecting,
|
||||
Connected,
|
||||
DisConnecting,
|
||||
DisConnected
|
||||
};
|
||||
class SSLContext;
|
||||
class SSLConn;
|
||||
|
||||
std::shared_ptr<SSLContext> newSSLContext(
|
||||
bool useOldTLS,
|
||||
bool validateCert,
|
||||
const std::vector<std::pair<std::string, std::string>> &sslConfCmds);
|
||||
std::shared_ptr<SSLContext> newSSLServerContext(
|
||||
const std::string &certPath,
|
||||
const std::string &keyPath,
|
||||
bool useOldTLS,
|
||||
const std::vector<std::pair<std::string, std::string>> &sslConfCmds);
|
||||
// void initServerSSLContext(const std::shared_ptr<SSLContext> &ctx,
|
||||
// const std::string &certPath,
|
||||
// const std::string &keyPath);
|
||||
#endif
|
||||
class Channel;
|
||||
class Socket;
|
||||
class TcpServer;
|
||||
void removeConnection(EventLoop *loop, const TcpConnectionPtr &conn);
|
||||
class TcpConnectionImpl : public TcpConnection,
|
||||
public NonCopyable,
|
||||
public std::enable_shared_from_this<TcpConnectionImpl>
|
||||
{
|
||||
friend class TcpServer;
|
||||
friend class TcpClient;
|
||||
friend void trantor::removeConnection(EventLoop *loop,
|
||||
const TcpConnectionPtr &conn);
|
||||
|
||||
public:
|
||||
class KickoffEntry
|
||||
{
|
||||
public:
|
||||
explicit KickoffEntry(const std::weak_ptr<TcpConnection> &conn)
|
||||
: conn_(conn)
|
||||
{
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
conn_.reset();
|
||||
}
|
||||
~KickoffEntry()
|
||||
{
|
||||
auto conn = conn_.lock();
|
||||
if (conn)
|
||||
{
|
||||
conn->forceClose();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<TcpConnection> conn_;
|
||||
};
|
||||
|
||||
TcpConnectionImpl(EventLoop *loop,
|
||||
int socketfd,
|
||||
const InetAddress &localAddr,
|
||||
const InetAddress &peerAddr);
|
||||
#ifdef USE_OPENSSL
|
||||
TcpConnectionImpl(EventLoop *loop,
|
||||
int socketfd,
|
||||
const InetAddress &localAddr,
|
||||
const InetAddress &peerAddr,
|
||||
const std::shared_ptr<SSLContext> &ctxPtr,
|
||||
bool isServer = true,
|
||||
bool validateCert = true,
|
||||
const std::string &hostname = "");
|
||||
#endif
|
||||
virtual ~TcpConnectionImpl();
|
||||
virtual void send(const char *msg, size_t len) override;
|
||||
virtual void send(const void *msg, size_t len) override;
|
||||
virtual void send(const std::string &msg) override;
|
||||
virtual void send(std::string &&msg) override;
|
||||
virtual void send(const MsgBuffer &buffer) override;
|
||||
virtual void send(MsgBuffer &&buffer) override;
|
||||
virtual void send(const std::shared_ptr<std::string> &msgPtr) override;
|
||||
virtual void send(const std::shared_ptr<MsgBuffer> &msgPtr) override;
|
||||
virtual void sendFile(const char *fileName,
|
||||
size_t offset = 0,
|
||||
size_t length = 0) override;
|
||||
|
||||
virtual const InetAddress &localAddr() const override
|
||||
{
|
||||
return localAddr_;
|
||||
}
|
||||
virtual const InetAddress &peerAddr() const override
|
||||
{
|
||||
return peerAddr_;
|
||||
}
|
||||
|
||||
virtual bool connected() const override
|
||||
{
|
||||
return status_ == ConnStatus::Connected;
|
||||
}
|
||||
virtual bool disconnected() const override
|
||||
{
|
||||
return status_ == ConnStatus::Disconnected;
|
||||
}
|
||||
|
||||
// virtual MsgBuffer* getSendBuffer() override{ return &writeBuffer_;}
|
||||
virtual MsgBuffer *getRecvBuffer() override
|
||||
{
|
||||
return &readBuffer_;
|
||||
}
|
||||
// set callbacks
|
||||
virtual void setHighWaterMarkCallback(const HighWaterMarkCallback &cb,
|
||||
size_t markLen) override
|
||||
{
|
||||
highWaterMarkCallback_ = cb;
|
||||
highWaterMarkLen_ = markLen;
|
||||
}
|
||||
|
||||
virtual void keepAlive() override
|
||||
{
|
||||
idleTimeout_ = 0;
|
||||
auto entry = kickoffEntry_.lock();
|
||||
if (entry)
|
||||
{
|
||||
entry->reset();
|
||||
}
|
||||
}
|
||||
virtual bool isKeepAlive() override
|
||||
{
|
||||
return idleTimeout_ == 0;
|
||||
}
|
||||
virtual void setTcpNoDelay(bool on) override;
|
||||
virtual void shutdown() override;
|
||||
virtual void forceClose() override;
|
||||
virtual EventLoop *getLoop() override
|
||||
{
|
||||
return loop_;
|
||||
}
|
||||
|
||||
virtual size_t bytesSent() const override
|
||||
{
|
||||
return bytesSent_;
|
||||
}
|
||||
virtual size_t bytesReceived() const override
|
||||
{
|
||||
return bytesReceived_;
|
||||
}
|
||||
virtual void startClientEncryption(
|
||||
std::function<void()> callback,
|
||||
bool useOldTLS = false,
|
||||
bool validateCert = true,
|
||||
std::string hostname = "",
|
||||
const std::vector<std::pair<std::string, std::string>> &sslConfCmds =
|
||||
{}) override;
|
||||
virtual void startServerEncryption(const std::shared_ptr<SSLContext> &ctx,
|
||||
std::function<void()> callback) override;
|
||||
virtual bool isSSLConnection() const override
|
||||
{
|
||||
return isEncrypted_;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Internal use only.
|
||||
|
||||
std::weak_ptr<KickoffEntry> kickoffEntry_;
|
||||
std::weak_ptr<TimingWheel> timingWheelWeakPtr_;
|
||||
size_t idleTimeout_{0};
|
||||
Date lastTimingWheelUpdateTime_;
|
||||
|
||||
void enableKickingOff(size_t timeout,
|
||||
const std::shared_ptr<TimingWheel> &timingWheel)
|
||||
{
|
||||
assert(timingWheel);
|
||||
assert(timingWheel->getLoop() == loop_);
|
||||
assert(timeout > 0);
|
||||
auto entry = std::make_shared<KickoffEntry>(shared_from_this());
|
||||
kickoffEntry_ = entry;
|
||||
timingWheelWeakPtr_ = timingWheel;
|
||||
idleTimeout_ = timeout;
|
||||
timingWheel->insertEntry(timeout, entry);
|
||||
}
|
||||
void extendLife();
|
||||
#ifndef _WIN32
|
||||
void sendFile(int sfd, size_t offset = 0, size_t length = 0);
|
||||
#else
|
||||
void sendFile(FILE *fp, size_t offset = 0, size_t length = 0);
|
||||
#endif
|
||||
void setRecvMsgCallback(const RecvMessageCallback &cb)
|
||||
{
|
||||
recvMsgCallback_ = cb;
|
||||
}
|
||||
void setConnectionCallback(const ConnectionCallback &cb)
|
||||
{
|
||||
connectionCallback_ = cb;
|
||||
}
|
||||
void setWriteCompleteCallback(const WriteCompleteCallback &cb)
|
||||
{
|
||||
writeCompleteCallback_ = cb;
|
||||
}
|
||||
void setCloseCallback(const CloseCallback &cb)
|
||||
{
|
||||
closeCallback_ = cb;
|
||||
}
|
||||
void setSSLErrorCallback(const SSLErrorCallback &cb)
|
||||
{
|
||||
sslErrorCallback_ = cb;
|
||||
}
|
||||
|
||||
void connectDestroyed();
|
||||
virtual void connectEstablished();
|
||||
|
||||
protected:
|
||||
struct BufferNode
|
||||
{
|
||||
#ifndef _WIN32
|
||||
int sendFd_{-1};
|
||||
off_t offset_;
|
||||
#else
|
||||
FILE *sendFp_{nullptr};
|
||||
long long offset_;
|
||||
#endif
|
||||
ssize_t fileBytesToSend_;
|
||||
std::shared_ptr<MsgBuffer> msgBuffer_;
|
||||
~BufferNode()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
if (sendFd_ >= 0)
|
||||
close(sendFd_);
|
||||
#else
|
||||
if (sendFp_)
|
||||
fclose(sendFp_);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
using BufferNodePtr = std::shared_ptr<BufferNode>;
|
||||
enum class ConnStatus
|
||||
{
|
||||
Disconnected,
|
||||
Connecting,
|
||||
Connected,
|
||||
Disconnecting
|
||||
};
|
||||
bool isEncrypted_{false};
|
||||
EventLoop *loop_;
|
||||
std::unique_ptr<Channel> ioChannelPtr_;
|
||||
std::unique_ptr<Socket> socketPtr_;
|
||||
MsgBuffer readBuffer_;
|
||||
std::list<BufferNodePtr> writeBufferList_;
|
||||
void readCallback();
|
||||
void writeCallback();
|
||||
InetAddress localAddr_, peerAddr_;
|
||||
ConnStatus status_{ConnStatus::Connecting};
|
||||
// callbacks
|
||||
RecvMessageCallback recvMsgCallback_;
|
||||
ConnectionCallback connectionCallback_;
|
||||
CloseCallback closeCallback_;
|
||||
WriteCompleteCallback writeCompleteCallback_;
|
||||
HighWaterMarkCallback highWaterMarkCallback_;
|
||||
SSLErrorCallback sslErrorCallback_;
|
||||
void handleClose();
|
||||
void handleError();
|
||||
// virtual void sendInLoop(const std::string &msg);
|
||||
|
||||
void sendFileInLoop(const BufferNodePtr &file);
|
||||
#ifndef _WIN32
|
||||
void sendInLoop(const void *buffer, size_t length);
|
||||
ssize_t writeInLoop(const void *buffer, size_t length);
|
||||
#else
|
||||
void sendInLoop(const char *buffer, size_t length);
|
||||
ssize_t writeInLoop(const char *buffer, size_t length);
|
||||
#endif
|
||||
size_t highWaterMarkLen_;
|
||||
std::string name_;
|
||||
|
||||
uint64_t sendNum_{0};
|
||||
std::mutex sendNumMutex_;
|
||||
|
||||
size_t bytesSent_{0};
|
||||
size_t bytesReceived_{0};
|
||||
|
||||
std::unique_ptr<std::vector<char>> fileBufferPtr_;
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
private:
|
||||
void doHandshaking();
|
||||
bool validatePeerCertificate();
|
||||
struct SSLEncryption
|
||||
{
|
||||
SSLStatus statusOfSSL_ = SSLStatus::Handshaking;
|
||||
// OpenSSL
|
||||
std::shared_ptr<SSLContext> sslCtxPtr_;
|
||||
std::unique_ptr<SSLConn> sslPtr_;
|
||||
std::unique_ptr<std::array<char, 8192>> sendBufferPtr_;
|
||||
bool isServer_{false};
|
||||
bool isUpgrade_{false};
|
||||
std::function<void()> upgradeCallback_;
|
||||
std::string hostname_;
|
||||
};
|
||||
std::unique_ptr<SSLEncryption> sslEncryptionPtr_;
|
||||
void startClientEncryptionInLoop(
|
||||
std::function<void()> &&callback,
|
||||
bool useOldTLS,
|
||||
bool validateCert,
|
||||
const std::string &hostname,
|
||||
const std::vector<std::pair<std::string, std::string>> &sslConfCmds);
|
||||
void startServerEncryptionInLoop(const std::shared_ptr<SSLContext> &ctx,
|
||||
std::function<void()> &&callback);
|
||||
#endif
|
||||
};
|
||||
|
||||
using TcpConnectionImplPtr = std::shared_ptr<TcpConnectionImpl>;
|
||||
|
||||
} // namespace trantor
|
@ -1,64 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Timer.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Timer.h"
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/net/EventLoop.h>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
std::atomic<TimerId> Timer::timersCreated_ = ATOMIC_VAR_INIT(InvalidTimerId);
|
||||
Timer::Timer(const TimerCallback &cb,
|
||||
const TimePoint &when,
|
||||
const TimeInterval &interval)
|
||||
: callback_(cb),
|
||||
when_(when),
|
||||
interval_(interval),
|
||||
repeat_(interval.count() > 0),
|
||||
id_(++timersCreated_)
|
||||
{
|
||||
}
|
||||
Timer::Timer(TimerCallback &&cb,
|
||||
const TimePoint &when,
|
||||
const TimeInterval &interval)
|
||||
: callback_(std::move(cb)),
|
||||
when_(when),
|
||||
interval_(interval),
|
||||
repeat_(interval.count() > 0),
|
||||
id_(++timersCreated_)
|
||||
{
|
||||
// LOG_TRACE<<"Timer move contrustor";
|
||||
}
|
||||
void Timer::run() const
|
||||
{
|
||||
callback_();
|
||||
}
|
||||
void Timer::restart(const TimePoint &now)
|
||||
{
|
||||
if (repeat_)
|
||||
{
|
||||
when_ = now + interval_;
|
||||
}
|
||||
else
|
||||
when_ = std::chrono::steady_clock::now();
|
||||
}
|
||||
bool Timer::operator<(const Timer &t) const
|
||||
{
|
||||
return when_ < t.when_;
|
||||
}
|
||||
bool Timer::operator>(const Timer &t) const
|
||||
{
|
||||
return when_ > t.when_;
|
||||
}
|
||||
} // namespace trantor
|
@ -1,68 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Timer.h
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/net/callbacks.h>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
using TimerId = uint64_t;
|
||||
using TimePoint = std::chrono::steady_clock::time_point;
|
||||
using TimeInterval = std::chrono::microseconds;
|
||||
class Timer : public NonCopyable
|
||||
{
|
||||
public:
|
||||
Timer(const TimerCallback &cb,
|
||||
const TimePoint &when,
|
||||
const TimeInterval &interval);
|
||||
Timer(TimerCallback &&cb,
|
||||
const TimePoint &when,
|
||||
const TimeInterval &interval);
|
||||
~Timer()
|
||||
{
|
||||
// std::cout<<"Timer unconstract!"<<std::endl;
|
||||
}
|
||||
void run() const;
|
||||
void restart(const TimePoint &now);
|
||||
bool operator<(const Timer &t) const;
|
||||
bool operator>(const Timer &t) const;
|
||||
const TimePoint &when() const
|
||||
{
|
||||
return when_;
|
||||
}
|
||||
bool isRepeat()
|
||||
{
|
||||
return repeat_;
|
||||
}
|
||||
TimerId id()
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
private:
|
||||
TimerCallback callback_;
|
||||
TimePoint when_;
|
||||
const TimeInterval interval_;
|
||||
const bool repeat_;
|
||||
const TimerId id_;
|
||||
static std::atomic<TimerId> timersCreated_;
|
||||
};
|
||||
|
||||
} // namespace trantor
|
@ -1,294 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file TimerQueue.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <trantor/net/EventLoop.h>
|
||||
|
||||
#include "TimerQueue.h"
|
||||
#include "Channel.h"
|
||||
#ifdef __linux__
|
||||
#include <sys/timerfd.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace trantor;
|
||||
#ifdef __linux__
|
||||
static int createTimerfd()
|
||||
{
|
||||
int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
||||
if (timerfd < 0)
|
||||
{
|
||||
std::cerr << "create timerfd failed!" << std::endl;
|
||||
}
|
||||
return timerfd;
|
||||
}
|
||||
|
||||
static struct timespec howMuchTimeFromNow(const TimePoint &when)
|
||||
{
|
||||
auto microSeconds = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
when - std::chrono::steady_clock::now())
|
||||
.count();
|
||||
if (microSeconds < 100)
|
||||
{
|
||||
microSeconds = 100;
|
||||
}
|
||||
struct timespec ts;
|
||||
ts.tv_sec = static_cast<time_t>(microSeconds / 1000000);
|
||||
ts.tv_nsec = static_cast<long>((microSeconds % 1000000) * 1000);
|
||||
return ts;
|
||||
}
|
||||
static void resetTimerfd(int timerfd, const TimePoint &expiration)
|
||||
{
|
||||
// wake up loop by timerfd_settime()
|
||||
struct itimerspec newValue;
|
||||
struct itimerspec oldValue;
|
||||
memset(&newValue, 0, sizeof(newValue));
|
||||
memset(&oldValue, 0, sizeof(oldValue));
|
||||
newValue.it_value = howMuchTimeFromNow(expiration);
|
||||
int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);
|
||||
if (ret)
|
||||
{
|
||||
// LOG_SYSERR << "timerfd_settime()";
|
||||
}
|
||||
}
|
||||
static void readTimerfd(int timerfd, const TimePoint &)
|
||||
{
|
||||
uint64_t howmany;
|
||||
ssize_t n = ::read(timerfd, &howmany, sizeof howmany);
|
||||
if (n != sizeof howmany)
|
||||
{
|
||||
LOG_ERROR << "TimerQueue::handleRead() reads " << n
|
||||
<< " bytes instead of 8";
|
||||
}
|
||||
}
|
||||
|
||||
void TimerQueue::handleRead()
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
readTimerfd(timerfd_, now);
|
||||
|
||||
std::vector<TimerPtr> expired = getExpired(now);
|
||||
|
||||
callingExpiredTimers_ = true;
|
||||
// cancelingTimers_.clear();
|
||||
// safe to callback outside critical section
|
||||
for (auto const &timerPtr : expired)
|
||||
{
|
||||
if (timerIdSet_.find(timerPtr->id()) != timerIdSet_.end())
|
||||
{
|
||||
timerPtr->run();
|
||||
}
|
||||
}
|
||||
callingExpiredTimers_ = false;
|
||||
|
||||
reset(expired, now);
|
||||
}
|
||||
#else
|
||||
static int64_t howMuchTimeFromNow(const TimePoint &when)
|
||||
{
|
||||
auto microSeconds = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
when - std::chrono::steady_clock::now())
|
||||
.count();
|
||||
if (microSeconds < 1000)
|
||||
{
|
||||
microSeconds = 1000;
|
||||
}
|
||||
return microSeconds / 1000;
|
||||
}
|
||||
void TimerQueue::processTimers()
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
|
||||
std::vector<TimerPtr> expired = getExpired(now);
|
||||
|
||||
callingExpiredTimers_ = true;
|
||||
// cancelingTimers_.clear();
|
||||
// safe to callback outside critical section
|
||||
for (auto const &timerPtr : expired)
|
||||
{
|
||||
if (timerIdSet_.find(timerPtr->id()) != timerIdSet_.end())
|
||||
{
|
||||
timerPtr->run();
|
||||
}
|
||||
}
|
||||
callingExpiredTimers_ = false;
|
||||
|
||||
reset(expired, now);
|
||||
}
|
||||
#endif
|
||||
///////////////////////////////////////
|
||||
TimerQueue::TimerQueue(EventLoop *loop)
|
||||
: loop_(loop),
|
||||
#ifdef __linux__
|
||||
timerfd_(createTimerfd()),
|
||||
timerfdChannelPtr_(new Channel(loop, timerfd_)),
|
||||
#endif
|
||||
timers_(),
|
||||
callingExpiredTimers_(false)
|
||||
{
|
||||
#ifdef __linux__
|
||||
timerfdChannelPtr_->setReadCallback(
|
||||
std::bind(&TimerQueue::handleRead, this));
|
||||
// we are always reading the timerfd, we disarm it with timerfd_settime.
|
||||
timerfdChannelPtr_->enableReading();
|
||||
#endif
|
||||
}
|
||||
#ifdef __linux__
|
||||
void TimerQueue::reset()
|
||||
{
|
||||
loop_->runInLoop([this]() {
|
||||
timerfdChannelPtr_->disableAll();
|
||||
timerfdChannelPtr_->remove();
|
||||
close(timerfd_);
|
||||
timerfd_ = createTimerfd();
|
||||
timerfdChannelPtr_ = std::make_shared<Channel>(loop_, timerfd_);
|
||||
timerfdChannelPtr_->setReadCallback(
|
||||
std::bind(&TimerQueue::handleRead, this));
|
||||
// we are always reading the timerfd, we disarm it with timerfd_settime.
|
||||
timerfdChannelPtr_->enableReading();
|
||||
if (!timers_.empty())
|
||||
{
|
||||
const auto nextExpire = timers_.top()->when();
|
||||
resetTimerfd(timerfd_, nextExpire);
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif
|
||||
TimerQueue::~TimerQueue()
|
||||
{
|
||||
#ifdef __linux__
|
||||
auto chlPtr = timerfdChannelPtr_;
|
||||
auto fd = timerfd_;
|
||||
loop_->runInLoop([chlPtr, fd]() {
|
||||
chlPtr->disableAll();
|
||||
chlPtr->remove();
|
||||
::close(fd);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
TimerId TimerQueue::addTimer(const TimerCallback &cb,
|
||||
const TimePoint &when,
|
||||
const TimeInterval &interval)
|
||||
{
|
||||
std::shared_ptr<Timer> timerPtr =
|
||||
std::make_shared<Timer>(cb, when, interval);
|
||||
|
||||
loop_->runInLoop([this, timerPtr]() { addTimerInLoop(timerPtr); });
|
||||
return timerPtr->id();
|
||||
}
|
||||
TimerId TimerQueue::addTimer(TimerCallback &&cb,
|
||||
const TimePoint &when,
|
||||
const TimeInterval &interval)
|
||||
{
|
||||
std::shared_ptr<Timer> timerPtr =
|
||||
std::make_shared<Timer>(std::move(cb), when, interval);
|
||||
|
||||
loop_->runInLoop([this, timerPtr]() { addTimerInLoop(timerPtr); });
|
||||
return timerPtr->id();
|
||||
}
|
||||
void TimerQueue::addTimerInLoop(const TimerPtr &timer)
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
timerIdSet_.insert(timer->id());
|
||||
if (insert(timer))
|
||||
{
|
||||
// the earliest timer changed
|
||||
#ifdef __linux__
|
||||
resetTimerfd(timerfd_, timer->when());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void TimerQueue::invalidateTimer(TimerId id)
|
||||
{
|
||||
loop_->runInLoop([this, id]() { timerIdSet_.erase(id); });
|
||||
}
|
||||
|
||||
bool TimerQueue::insert(const TimerPtr &timerPtr)
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
bool earliestChanged = false;
|
||||
if (timers_.size() == 0 || *timerPtr < *timers_.top())
|
||||
{
|
||||
earliestChanged = true;
|
||||
}
|
||||
timers_.push(timerPtr);
|
||||
// std::cout<<"after push new
|
||||
// timer:"<<timerPtr->when().microSecondsSinceEpoch()/1000000<<std::endl;
|
||||
return earliestChanged;
|
||||
}
|
||||
#ifndef __linux__
|
||||
int64_t TimerQueue::getTimeout() const
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
if (timers_.empty())
|
||||
{
|
||||
return 10000;
|
||||
}
|
||||
else
|
||||
{
|
||||
return howMuchTimeFromNow(timers_.top()->when());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<TimerPtr> TimerQueue::getExpired(const TimePoint &now)
|
||||
{
|
||||
std::vector<TimerPtr> expired;
|
||||
while (!timers_.empty())
|
||||
{
|
||||
if (timers_.top()->when() < now)
|
||||
{
|
||||
expired.push_back(timers_.top());
|
||||
timers_.pop();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return expired;
|
||||
}
|
||||
void TimerQueue::reset(const std::vector<TimerPtr> &expired,
|
||||
const TimePoint &now)
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
for (auto const &timerPtr : expired)
|
||||
{
|
||||
auto iter = timerIdSet_.find(timerPtr->id());
|
||||
if (iter != timerIdSet_.end())
|
||||
{
|
||||
if (timerPtr->isRepeat())
|
||||
{
|
||||
timerPtr->restart(now);
|
||||
insert(timerPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
timerIdSet_.erase(iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __linux__
|
||||
if (!timers_.empty())
|
||||
{
|
||||
const auto nextExpire = timers_.top()->when();
|
||||
resetTimerfd(timerfd_, nextExpire);
|
||||
}
|
||||
#endif
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* TimerQueue.h
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/net/callbacks.h>
|
||||
#include "Timer.h"
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <unordered_set>
|
||||
namespace trantor
|
||||
{
|
||||
// class Timer;
|
||||
class EventLoop;
|
||||
class Channel;
|
||||
using TimerPtr = std::shared_ptr<Timer>;
|
||||
struct TimerPtrComparer
|
||||
{
|
||||
bool operator()(const TimerPtr &x, const TimerPtr &y) const
|
||||
{
|
||||
return *x > *y;
|
||||
}
|
||||
};
|
||||
|
||||
class TimerQueue : NonCopyable
|
||||
{
|
||||
public:
|
||||
explicit TimerQueue(EventLoop *loop);
|
||||
~TimerQueue();
|
||||
TimerId addTimer(const TimerCallback &cb,
|
||||
const TimePoint &when,
|
||||
const TimeInterval &interval);
|
||||
TimerId addTimer(TimerCallback &&cb,
|
||||
const TimePoint &when,
|
||||
const TimeInterval &interval);
|
||||
void addTimerInLoop(const TimerPtr &timer);
|
||||
void invalidateTimer(TimerId id);
|
||||
#ifdef __linux__
|
||||
void reset();
|
||||
#else
|
||||
int64_t getTimeout() const;
|
||||
void processTimers();
|
||||
#endif
|
||||
protected:
|
||||
EventLoop *loop_;
|
||||
#ifdef __linux__
|
||||
int timerfd_;
|
||||
std::shared_ptr<Channel> timerfdChannelPtr_;
|
||||
void handleRead();
|
||||
#endif
|
||||
std::priority_queue<TimerPtr, std::vector<TimerPtr>, TimerPtrComparer>
|
||||
timers_;
|
||||
|
||||
bool callingExpiredTimers_;
|
||||
bool insert(const TimerPtr &timePtr);
|
||||
std::vector<TimerPtr> getExpired();
|
||||
void reset(const std::vector<TimerPtr> &expired, const TimePoint &now);
|
||||
std::vector<TimerPtr> getExpired(const TimePoint &now);
|
||||
|
||||
private:
|
||||
std::unordered_set<uint64_t> timerIdSet_;
|
||||
};
|
||||
} // namespace trantor
|
@ -1,241 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* EpollPoller.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include "Channel.h"
|
||||
#include "EpollPoller.h"
|
||||
#ifdef __linux__
|
||||
#include <poll.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <strings.h>
|
||||
#include <iostream>
|
||||
#elif defined _WIN32
|
||||
#include "Wepoll.h"
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include <winsock2.h>
|
||||
#include <fcntl.h>
|
||||
#define EPOLL_CLOEXEC _O_NOINHERIT
|
||||
#endif
|
||||
namespace trantor
|
||||
{
|
||||
#if defined __linux__ || defined _WIN32
|
||||
|
||||
#if defined __linux__
|
||||
static_assert(EPOLLIN == POLLIN, "EPOLLIN != POLLIN");
|
||||
static_assert(EPOLLPRI == POLLPRI, "EPOLLPRI != POLLPRI");
|
||||
static_assert(EPOLLOUT == POLLOUT, "EPOLLOUT != POLLOUT");
|
||||
static_assert(EPOLLRDHUP == POLLRDHUP, "EPOLLRDHUP != POLLRDHUP");
|
||||
static_assert(EPOLLERR == POLLERR, "EPOLLERR != POLLERR");
|
||||
static_assert(EPOLLHUP == POLLHUP, "EPOLLHUP != POLLHUP");
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
const int kNew = -1;
|
||||
const int kAdded = 1;
|
||||
const int kDeleted = 2;
|
||||
} // namespace
|
||||
|
||||
EpollPoller::EpollPoller(EventLoop *loop)
|
||||
: Poller(loop),
|
||||
#ifdef _WIN32
|
||||
// wepoll does not suppor flags
|
||||
epollfd_(::epoll_create1(0)),
|
||||
#else
|
||||
epollfd_(::epoll_create1(EPOLL_CLOEXEC)),
|
||||
#endif
|
||||
events_(kInitEventListSize)
|
||||
{
|
||||
}
|
||||
EpollPoller::~EpollPoller()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
epoll_close(epollfd_);
|
||||
#else
|
||||
close(epollfd_);
|
||||
#endif
|
||||
}
|
||||
#ifdef _WIN32
|
||||
void EpollPoller::postEvent(uint64_t event)
|
||||
{
|
||||
epoll_post_signal(epollfd_, event);
|
||||
}
|
||||
#endif
|
||||
void EpollPoller::poll(int timeoutMs, ChannelList *activeChannels)
|
||||
{
|
||||
int numEvents = ::epoll_wait(epollfd_,
|
||||
&*events_.begin(),
|
||||
static_cast<int>(events_.size()),
|
||||
timeoutMs);
|
||||
int savedErrno = errno;
|
||||
// Timestamp now(Timestamp::now());
|
||||
if (numEvents > 0)
|
||||
{
|
||||
// LOG_TRACE << numEvents << " events happended";
|
||||
fillActiveChannels(numEvents, activeChannels);
|
||||
if (static_cast<size_t>(numEvents) == events_.size())
|
||||
{
|
||||
events_.resize(events_.size() * 2);
|
||||
}
|
||||
}
|
||||
else if (numEvents == 0)
|
||||
{
|
||||
// std::cout << "nothing happended" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// error happens, log uncommon ones
|
||||
if (savedErrno != EINTR)
|
||||
{
|
||||
errno = savedErrno;
|
||||
LOG_SYSERR << "EPollEpollPoller::poll()";
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
void EpollPoller::fillActiveChannels(int numEvents,
|
||||
ChannelList *activeChannels) const
|
||||
{
|
||||
assert(static_cast<size_t>(numEvents) <= events_.size());
|
||||
for (int i = 0; i < numEvents; ++i)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (events_[i].events == EPOLLEVENT)
|
||||
{
|
||||
eventCallback_(events_[i].data.u64);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
Channel *channel = static_cast<Channel *>(events_[i].data.ptr);
|
||||
#ifndef NDEBUG
|
||||
int fd = channel->fd();
|
||||
ChannelMap::const_iterator it = channels_.find(fd);
|
||||
assert(it != channels_.end());
|
||||
assert(it->second == channel);
|
||||
#endif
|
||||
channel->setRevents(events_[i].events);
|
||||
activeChannels->push_back(channel);
|
||||
}
|
||||
// LOG_TRACE<<"active Channels num:"<<activeChannels->size();
|
||||
}
|
||||
void EpollPoller::updateChannel(Channel *channel)
|
||||
{
|
||||
assertInLoopThread();
|
||||
const int index = channel->index();
|
||||
// LOG_TRACE << "fd = " << channel->fd()
|
||||
// << " events = " << channel->events() << " index = " << index;
|
||||
if (index == kNew || index == kDeleted)
|
||||
{
|
||||
// a new one, add with EPOLL_CTL_ADD
|
||||
#ifndef NDEBUG
|
||||
int fd = channel->fd();
|
||||
if (index == kNew)
|
||||
{
|
||||
assert(channels_.find(fd) == channels_.end());
|
||||
channels_[fd] = channel;
|
||||
}
|
||||
else
|
||||
{ // index == kDeleted
|
||||
assert(channels_.find(fd) != channels_.end());
|
||||
assert(channels_[fd] == channel);
|
||||
}
|
||||
#endif
|
||||
channel->setIndex(kAdded);
|
||||
update(EPOLL_CTL_ADD, channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// update existing one with EPOLL_CTL_MOD/DEL
|
||||
#ifndef NDEBUG
|
||||
int fd = channel->fd();
|
||||
(void)fd;
|
||||
assert(channels_.find(fd) != channels_.end());
|
||||
assert(channels_[fd] == channel);
|
||||
#endif
|
||||
assert(index == kAdded);
|
||||
if (channel->isNoneEvent())
|
||||
{
|
||||
update(EPOLL_CTL_DEL, channel);
|
||||
channel->setIndex(kDeleted);
|
||||
}
|
||||
else
|
||||
{
|
||||
update(EPOLL_CTL_MOD, channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
void EpollPoller::removeChannel(Channel *channel)
|
||||
{
|
||||
EpollPoller::assertInLoopThread();
|
||||
#ifndef NDEBUG
|
||||
int fd = channel->fd();
|
||||
assert(channels_.find(fd) != channels_.end());
|
||||
assert(channels_[fd] == channel);
|
||||
size_t n = channels_.erase(fd);
|
||||
(void)n;
|
||||
assert(n == 1);
|
||||
#endif
|
||||
assert(channel->isNoneEvent());
|
||||
int index = channel->index();
|
||||
assert(index == kAdded || index == kDeleted);
|
||||
if (index == kAdded)
|
||||
{
|
||||
update(EPOLL_CTL_DEL, channel);
|
||||
}
|
||||
channel->setIndex(kNew);
|
||||
}
|
||||
void EpollPoller::update(int operation, Channel *channel)
|
||||
{
|
||||
struct epoll_event event;
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.events = channel->events();
|
||||
event.data.ptr = channel;
|
||||
int fd = channel->fd();
|
||||
if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
|
||||
{
|
||||
if (operation == EPOLL_CTL_DEL)
|
||||
{
|
||||
// LOG_SYSERR << "epoll_ctl op =" << operationToString(operation) <<
|
||||
// " fd =" << fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation)
|
||||
// << " fd =" << fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
EpollPoller::EpollPoller(EventLoop *loop) : Poller(loop)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
EpollPoller::~EpollPoller()
|
||||
{
|
||||
}
|
||||
void EpollPoller::poll(int, ChannelList *)
|
||||
{
|
||||
}
|
||||
void EpollPoller::updateChannel(Channel *)
|
||||
{
|
||||
}
|
||||
void EpollPoller::removeChannel(Channel *)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace trantor
|
@ -1,64 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* EpollPoller.h
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Poller.h"
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/net/EventLoop.h>
|
||||
|
||||
#if defined __linux__ || defined _WIN32
|
||||
#include <memory>
|
||||
#include <map>
|
||||
using EventList = std::vector<struct epoll_event>;
|
||||
#endif
|
||||
namespace trantor
|
||||
{
|
||||
class Channel;
|
||||
|
||||
class EpollPoller : public Poller
|
||||
{
|
||||
public:
|
||||
explicit EpollPoller(EventLoop *loop);
|
||||
virtual ~EpollPoller();
|
||||
virtual void poll(int timeoutMs, ChannelList *activeChannels) override;
|
||||
virtual void updateChannel(Channel *channel) override;
|
||||
virtual void removeChannel(Channel *channel) override;
|
||||
#ifdef _WIN32
|
||||
virtual void postEvent(uint64_t event) override;
|
||||
virtual void setEventCallback(const EventCallback &cb) override
|
||||
{
|
||||
eventCallback_ = cb;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
#if defined __linux__ || defined _WIN32
|
||||
static const int kInitEventListSize = 16;
|
||||
#ifdef _WIN32
|
||||
void *epollfd_;
|
||||
EventCallback eventCallback_{[](uint64_t event) {}};
|
||||
#else
|
||||
int epollfd_;
|
||||
#endif
|
||||
EventList events_;
|
||||
void update(int operation, Channel *channel);
|
||||
#ifndef NDEBUG
|
||||
using ChannelMap = std::map<int, Channel *>;
|
||||
ChannelMap channels_;
|
||||
#endif
|
||||
void fillActiveChannels(int numEvents, ChannelList *activeChannels) const;
|
||||
#endif
|
||||
};
|
||||
} // namespace trantor
|
@ -1,249 +0,0 @@
|
||||
#include "KQueue.h"
|
||||
#include "Channel.h"
|
||||
#ifdef USE_KQUEUE
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#endif
|
||||
namespace trantor
|
||||
{
|
||||
#ifdef USE_KQUEUE
|
||||
namespace
|
||||
{
|
||||
const int kNew = -1;
|
||||
const int kAdded = 1;
|
||||
const int kDeleted = 2;
|
||||
} // namespace
|
||||
|
||||
KQueue::KQueue(EventLoop *loop)
|
||||
: Poller(loop), kqfd_(kqueue()), events_(kInitEventListSize)
|
||||
{
|
||||
assert(kqfd_ >= 0);
|
||||
}
|
||||
|
||||
KQueue::~KQueue()
|
||||
{
|
||||
close(kqfd_);
|
||||
}
|
||||
|
||||
void KQueue::resetAfterFork()
|
||||
{
|
||||
close(kqfd_);
|
||||
kqfd_ = kqueue();
|
||||
for (auto &ch : channels_)
|
||||
{
|
||||
ch.second.first = 0;
|
||||
if (ch.second.second->isReading() || ch.second.second->isWriting())
|
||||
{
|
||||
update(ch.second.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KQueue::poll(int timeoutMs, ChannelList *activeChannels)
|
||||
{
|
||||
struct timespec timeout;
|
||||
timeout.tv_sec = timeoutMs / 1000;
|
||||
timeout.tv_nsec = (timeoutMs % 1000) * 1000000;
|
||||
|
||||
int numEvents = kevent(kqfd_,
|
||||
NULL,
|
||||
0,
|
||||
events_.data(),
|
||||
static_cast<int>(events_.size()),
|
||||
&timeout);
|
||||
int savedErrno = errno;
|
||||
// Timestamp now(Timestamp::now());
|
||||
if (numEvents > 0)
|
||||
{
|
||||
// LOG_TRACE << numEvents << " events happended";
|
||||
fillActiveChannels(numEvents, activeChannels);
|
||||
if (static_cast<size_t>(numEvents) == events_.size())
|
||||
{
|
||||
events_.resize(events_.size() * 2);
|
||||
}
|
||||
}
|
||||
else if (numEvents == 0)
|
||||
{
|
||||
// std::cout << "nothing happended" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// error happens, log uncommon ones
|
||||
if (savedErrno != EINTR)
|
||||
{
|
||||
errno = savedErrno;
|
||||
LOG_SYSERR << "KQueue::poll()";
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void KQueue::fillActiveChannels(int numEvents,
|
||||
ChannelList *activeChannels) const
|
||||
{
|
||||
assert(static_cast<size_t>(numEvents) <= events_.size());
|
||||
for (int i = 0; i < numEvents; ++i)
|
||||
{
|
||||
Channel *channel = static_cast<Channel *>(events_[i].udata);
|
||||
assert(channels_.find(channel->fd()) != channels_.end());
|
||||
int events = events_[i].filter;
|
||||
if (events == EVFILT_READ)
|
||||
{
|
||||
channel->setRevents(POLLIN);
|
||||
}
|
||||
else if (events == EVFILT_WRITE)
|
||||
{
|
||||
channel->setRevents(POLLOUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR << "events=" << events;
|
||||
continue;
|
||||
}
|
||||
activeChannels->push_back(channel);
|
||||
}
|
||||
}
|
||||
|
||||
void KQueue::updateChannel(Channel *channel)
|
||||
{
|
||||
assertInLoopThread();
|
||||
const int index = channel->index();
|
||||
// LOG_TRACE << "fd = " << channel->fd()
|
||||
// << " events = " << channel->events() << " index = " << index;
|
||||
if (index == kNew || index == kDeleted)
|
||||
{
|
||||
if (index == kNew)
|
||||
{
|
||||
assert(channels_.find(channel->fd()) == channels_.end());
|
||||
}
|
||||
else
|
||||
{ // index == kDeleted
|
||||
assert(channels_.find(channel->fd()) != channels_.end());
|
||||
assert(channels_[channel->fd()].second == channel);
|
||||
}
|
||||
update(channel);
|
||||
channel->setIndex(kAdded);
|
||||
}
|
||||
else
|
||||
{
|
||||
// update existing one
|
||||
int fd = channel->fd();
|
||||
(void)fd;
|
||||
assert(channels_.find(fd) != channels_.end());
|
||||
assert(index == kAdded);
|
||||
if (channel->isNoneEvent())
|
||||
{
|
||||
update(channel);
|
||||
channel->setIndex(kDeleted);
|
||||
}
|
||||
else
|
||||
{
|
||||
update(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
void KQueue::removeChannel(Channel *channel)
|
||||
{
|
||||
assertInLoopThread();
|
||||
int fd = channel->fd();
|
||||
assert(channels_.find(fd) != channels_.end());
|
||||
assert(channel->isNoneEvent());
|
||||
int index = channel->index();
|
||||
assert(index == kAdded || index == kDeleted);
|
||||
|
||||
if (index == kAdded)
|
||||
{
|
||||
update(channel);
|
||||
}
|
||||
|
||||
size_t n = channels_.erase(fd);
|
||||
(void)n;
|
||||
assert(n == 1);
|
||||
channel->setIndex(kNew);
|
||||
}
|
||||
|
||||
void KQueue::update(Channel *channel)
|
||||
{
|
||||
struct kevent ev[2];
|
||||
int n = 0;
|
||||
auto events = channel->events();
|
||||
int oldEvents = 0;
|
||||
if (channels_.find(channel->fd()) != channels_.end())
|
||||
{
|
||||
oldEvents = channels_[channel->fd()].first;
|
||||
}
|
||||
|
||||
auto fd = channel->fd();
|
||||
channels_[fd] = {events, channel};
|
||||
|
||||
if ((events & Channel::kReadEvent) && (!(oldEvents & Channel::kReadEvent)))
|
||||
{
|
||||
EV_SET(&ev[n++],
|
||||
fd,
|
||||
EVFILT_READ,
|
||||
EV_ADD | EV_ENABLE,
|
||||
0,
|
||||
0,
|
||||
(void *)(intptr_t)channel);
|
||||
}
|
||||
else if ((!(events & Channel::kReadEvent)) &&
|
||||
(oldEvents & Channel::kReadEvent))
|
||||
{
|
||||
EV_SET(&ev[n++],
|
||||
fd,
|
||||
EVFILT_READ,
|
||||
EV_DELETE,
|
||||
0,
|
||||
0,
|
||||
(void *)(intptr_t)channel);
|
||||
}
|
||||
if ((events & Channel::kWriteEvent) &&
|
||||
(!(oldEvents & Channel::kWriteEvent)))
|
||||
{
|
||||
EV_SET(&ev[n++],
|
||||
fd,
|
||||
EVFILT_WRITE,
|
||||
EV_ADD | EV_ENABLE,
|
||||
0,
|
||||
0,
|
||||
(void *)(intptr_t)channel);
|
||||
}
|
||||
else if ((!(events & Channel::kWriteEvent)) &&
|
||||
(oldEvents & Channel::kWriteEvent))
|
||||
{
|
||||
EV_SET(&ev[n++],
|
||||
fd,
|
||||
EVFILT_WRITE,
|
||||
EV_DELETE,
|
||||
0,
|
||||
0,
|
||||
(void *)(intptr_t)channel);
|
||||
}
|
||||
kevent(kqfd_, ev, n, NULL, 0, NULL);
|
||||
}
|
||||
#else
|
||||
KQueue::KQueue(EventLoop *loop) : Poller(loop)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
KQueue::~KQueue()
|
||||
{
|
||||
}
|
||||
void KQueue::poll(int, ChannelList *)
|
||||
{
|
||||
}
|
||||
void KQueue::updateChannel(Channel *)
|
||||
{
|
||||
}
|
||||
void KQueue::removeChannel(Channel *)
|
||||
{
|
||||
}
|
||||
void KQueue::resetAfterFork()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
} // namespace trantor
|
@ -1,55 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* KQueue.h
|
||||
* An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/trantor
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Trantor
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "../Poller.h"
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/net/EventLoop.h>
|
||||
|
||||
#if (defined(__unix__) && !defined(__linux__)) || \
|
||||
(defined(__APPLE__) && defined(__MACH__))
|
||||
#define USE_KQUEUE
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
using EventList = std::vector<struct kevent>;
|
||||
#endif
|
||||
namespace trantor
|
||||
{
|
||||
class Channel;
|
||||
|
||||
class KQueue : public Poller
|
||||
{
|
||||
public:
|
||||
explicit KQueue(EventLoop *loop);
|
||||
virtual ~KQueue();
|
||||
virtual void poll(int timeoutMs, ChannelList *activeChannels) override;
|
||||
virtual void updateChannel(Channel *channel) override;
|
||||
virtual void removeChannel(Channel *channel) override;
|
||||
virtual void resetAfterFork() override;
|
||||
|
||||
private:
|
||||
#ifdef USE_KQUEUE
|
||||
static const int kInitEventListSize = 16;
|
||||
int kqfd_;
|
||||
EventList events_;
|
||||
using ChannelMap = std::unordered_map<int, std::pair<int, Channel *>>;
|
||||
ChannelMap channels_;
|
||||
|
||||
void fillActiveChannels(int numEvents, ChannelList *activeChannels) const;
|
||||
void update(Channel *channel);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace trantor
|
@ -1,45 +0,0 @@
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/utils/AsyncFileLogger.h>
|
||||
#include <stdlib.h>
|
||||
#include <thread>
|
||||
int main()
|
||||
{
|
||||
trantor::AsyncFileLogger asyncFileLogger;
|
||||
asyncFileLogger.setFileName("async_test");
|
||||
asyncFileLogger.startLogging();
|
||||
trantor::Logger::setOutputFunction(
|
||||
[&](const char *msg, const uint64_t len) {
|
||||
asyncFileLogger.output(msg, len);
|
||||
},
|
||||
[&]() { asyncFileLogger.flush(); });
|
||||
asyncFileLogger.setFileSizeLimit(100000000);
|
||||
// LOG_DEBUG<<"debug log!"<<1;
|
||||
// LOG_TRACE<<"trace log!"<<2;
|
||||
// LOG_INFO<<"info log!"<<3;
|
||||
// LOG_WARN<<"warning log!"<<4;
|
||||
// if(1)
|
||||
// LOG_ERROR<<"error log!"<<5;
|
||||
// std::thread thread_([](){
|
||||
// LOG_FATAL<<"fatal log!"<<6;
|
||||
// });
|
||||
//
|
||||
// FILE *fp=fopen("/notexistfile","rb");
|
||||
// if(fp==NULL)
|
||||
// {
|
||||
// LOG_SYSERR<<"syserr log!"<<7;
|
||||
// }
|
||||
|
||||
int i = 0;
|
||||
while (i < 1000000)
|
||||
{
|
||||
++i;
|
||||
if (i % 100 == 0)
|
||||
{
|
||||
LOG_ERROR << "this is the " << i << "th log";
|
||||
continue;
|
||||
}
|
||||
LOG_INFO << "this is the " << i << "th log";
|
||||
++i;
|
||||
LOG_DEBUG << "this is the " << i << "th log";
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/utils/AsyncFileLogger.h>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
int main()
|
||||
{
|
||||
trantor::AsyncFileLogger asyncFileLogger;
|
||||
asyncFileLogger.setFileName("async_test");
|
||||
asyncFileLogger.startLogging();
|
||||
trantor::Logger::setOutputFunction(
|
||||
[&](const char *msg, const uint64_t len) {
|
||||
asyncFileLogger.output(msg, len);
|
||||
},
|
||||
[&]() { asyncFileLogger.flush(); });
|
||||
asyncFileLogger.setFileSizeLimit(100000000);
|
||||
int i = 0;
|
||||
while (i < 1000000)
|
||||
{
|
||||
++i;
|
||||
if (i % 100 == 0)
|
||||
{
|
||||
LOG_ERROR << "this is the " << i << "th log";
|
||||
continue;
|
||||
}
|
||||
LOG_INFO << "this is the " << i << "th log";
|
||||
++i;
|
||||
LOG_DEBUG << "this is the " << i << "th log";
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
add_executable(ssl_server_test SSLServerTest.cc)
|
||||
add_executable(ssl_client_test SSLClientTest.cc)
|
||||
add_executable(serial_task_queue_test1 SerialTaskQueueTest1.cc)
|
||||
add_executable(serial_task_queue_test2 SerialTaskQueueTest2.cc)
|
||||
add_executable(timer_test TimerTest.cc)
|
||||
add_executable(timer_test1 TimerTest1.cc)
|
||||
add_executable(run_in_loop_test1 RunInLoopTest1.cc)
|
||||
add_executable(run_in_loop_test2 RunInLoopTest2.cc)
|
||||
add_executable(logger_test LoggerTest.cc)
|
||||
add_executable(async_file_logger_test AsyncFileLoggerTest.cc)
|
||||
add_executable(tcp_server_test TcpServerTest.cc)
|
||||
add_executable(concurrent_task_queue_test ConcurrentTaskQueueTest.cc)
|
||||
add_executable(tcp_client_test TcpClientTest.cc)
|
||||
add_executable(async_file_logger_test1 AsyncFileLoggerTest1.cc)
|
||||
add_executable(sendfile_test SendfileTest.cc)
|
||||
add_executable(timing_wheel_test TimingWheelTest.cc)
|
||||
add_executable(kickoff_test KickoffTest.cc)
|
||||
add_executable(dns_test DnsTest.cc)
|
||||
add_executable(delayed_ssl_server_test DelayedSSLServerTest.cc)
|
||||
add_executable(delayed_ssl_client_test DelayedSSLClientTest.cc)
|
||||
add_executable(run_on_quit_test RunOnQuitTest.cc)
|
||||
set(targets_list
|
||||
ssl_server_test
|
||||
ssl_client_test
|
||||
serial_task_queue_test1
|
||||
serial_task_queue_test2
|
||||
timer_test
|
||||
timer_test1
|
||||
run_in_loop_test1
|
||||
run_in_loop_test2
|
||||
logger_test
|
||||
async_file_logger_test
|
||||
tcp_server_test
|
||||
concurrent_task_queue_test
|
||||
tcp_client_test
|
||||
async_file_logger_test1
|
||||
sendfile_test
|
||||
timing_wheel_test
|
||||
kickoff_test
|
||||
dns_test
|
||||
delayed_ssl_server_test
|
||||
delayed_ssl_client_test
|
||||
run_on_quit_test)
|
||||
|
||||
set_property(TARGET ${targets_list} PROPERTY CXX_STANDARD 14)
|
||||
set_property(TARGET ${targets_list} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
set_property(TARGET ${targets_list} PROPERTY CXX_EXTENSIONS OFF)
|
||||
|
||||
foreach(T ${targets_list})
|
||||
target_link_libraries(${T} PRIVATE trantor)
|
||||
endforeach()
|
@ -1,37 +0,0 @@
|
||||
#include <trantor/utils/ConcurrentTaskQueue.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
int main()
|
||||
{
|
||||
trantor::ConcurrentTaskQueue queue(5, "concurrT");
|
||||
std::atomic_int sum;
|
||||
sum = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
queue.runTaskInQueue([&sum]() {
|
||||
LOG_DEBUG << "add sum";
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
++sum;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
queue.runTaskInQueue([&sum]() {
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
LOG_DEBUG << "sum=" << sum;
|
||||
std::this_thread::sleep_for(100us);
|
||||
}
|
||||
});
|
||||
|
||||
getc(stdin);
|
||||
LOG_DEBUG << "sum=" << sum;
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
#include <trantor/net/TcpClient.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
using namespace trantor;
|
||||
#define USE_IPV6 0
|
||||
int main()
|
||||
{
|
||||
trantor::Logger::setLogLevel(trantor::Logger::kTrace);
|
||||
LOG_DEBUG << "TcpClient class test!";
|
||||
EventLoop loop;
|
||||
#if USE_IPV6
|
||||
InetAddress serverAddr("::1", 8888, true);
|
||||
#else
|
||||
InetAddress serverAddr("127.0.0.1", 8888);
|
||||
#endif
|
||||
std::shared_ptr<trantor::TcpClient> client[10];
|
||||
std::atomic_int connCount;
|
||||
connCount = 10;
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
client[i] = std::make_shared<trantor::TcpClient>(&loop,
|
||||
serverAddr,
|
||||
"tcpclienttest");
|
||||
client[i]->setConnectionCallback(
|
||||
[i, &loop, &connCount](const TcpConnectionPtr &conn) {
|
||||
if (conn->connected())
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG << i << " disconnected";
|
||||
--connCount;
|
||||
if (connCount == 0)
|
||||
loop.quit();
|
||||
}
|
||||
});
|
||||
client[i]->setMessageCallback(
|
||||
[](const TcpConnectionPtr &conn, MsgBuffer *buf) {
|
||||
auto msg = std::string(buf->peek(), buf->readableBytes());
|
||||
|
||||
LOG_INFO << msg;
|
||||
if (msg == "hello")
|
||||
{
|
||||
buf->retrieveAll();
|
||||
conn->startClientEncryption(
|
||||
[conn]() {
|
||||
LOG_INFO << "SSL established";
|
||||
conn->send("Hello");
|
||||
},
|
||||
false,
|
||||
false);
|
||||
}
|
||||
if (conn->isSSLConnection())
|
||||
{
|
||||
buf->retrieveAll();
|
||||
}
|
||||
});
|
||||
client[i]->connect();
|
||||
}
|
||||
loop.loop();
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
#include <trantor/net/TcpServer.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
using namespace trantor;
|
||||
#define USE_IPV6 0
|
||||
int main()
|
||||
{
|
||||
LOG_DEBUG << "test start";
|
||||
Logger::setLogLevel(Logger::kTrace);
|
||||
EventLoopThread loopThread;
|
||||
loopThread.run();
|
||||
#if USE_IPV6
|
||||
InetAddress addr(8888, true, true);
|
||||
#else
|
||||
InetAddress addr(8888);
|
||||
#endif
|
||||
TcpServer server(loopThread.getLoop(), addr, "test");
|
||||
auto ctx = newSSLServerContext("server.pem", "server.pem", {});
|
||||
LOG_INFO << "start";
|
||||
server.setRecvMessageCallback(
|
||||
[](const TcpConnectionPtr &connectionPtr, MsgBuffer *buffer) {
|
||||
LOG_DEBUG << std::string{buffer->peek(), buffer->readableBytes()};
|
||||
connectionPtr->send(*buffer);
|
||||
buffer->retrieveAll();
|
||||
connectionPtr->shutdown();
|
||||
});
|
||||
server.setConnectionCallback([ctx](const TcpConnectionPtr &connPtr) {
|
||||
if (connPtr->connected())
|
||||
{
|
||||
LOG_DEBUG << "New connection";
|
||||
connPtr->send("hello");
|
||||
connPtr->startServerEncryption(ctx, [] {
|
||||
LOG_INFO << "SSL established";
|
||||
});
|
||||
}
|
||||
else if (connPtr->disconnected())
|
||||
{
|
||||
LOG_DEBUG << "connection disconnected";
|
||||
}
|
||||
});
|
||||
server.setIoLoopNum(3);
|
||||
server.start();
|
||||
loopThread.wait();
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#include <trantor/net/Resolver.h>
|
||||
#include <iostream>
|
||||
void dns(const std::shared_ptr<trantor::Resolver> &resolver)
|
||||
{
|
||||
auto now = trantor::Date::now();
|
||||
resolver->resolve("www.baidu.com", [now](const trantor::InetAddress &addr) {
|
||||
auto interval = trantor::Date::now().microSecondsSinceEpoch() -
|
||||
now.microSecondsSinceEpoch();
|
||||
std::cout << "baidu:" << addr.toIp() << " " << interval / 1000 << "ms"
|
||||
<< std::endl;
|
||||
});
|
||||
resolver->resolve("www.google.com",
|
||||
[now](const trantor::InetAddress &addr) {
|
||||
auto interval =
|
||||
trantor::Date::now().microSecondsSinceEpoch() -
|
||||
now.microSecondsSinceEpoch();
|
||||
std::cout << "google:" << addr.toIp() << " "
|
||||
<< interval / 1000 << "ms" << std::endl;
|
||||
});
|
||||
resolver->resolve("www.sina.com", [now](const trantor::InetAddress &addr) {
|
||||
auto interval = trantor::Date::now().microSecondsSinceEpoch() -
|
||||
now.microSecondsSinceEpoch();
|
||||
std::cout << "sina:" << addr.toIp() << " " << interval / 1000 << "ms"
|
||||
<< std::endl;
|
||||
});
|
||||
resolver->resolve("www.xjfisfjaskfeiakdjfg.com",
|
||||
[now](const trantor::InetAddress &addr) {
|
||||
auto interval =
|
||||
trantor::Date::now().microSecondsSinceEpoch() -
|
||||
now.microSecondsSinceEpoch();
|
||||
std::cout << "bad address:" << addr.toIp() << " "
|
||||
<< interval / 1000 << "ms" << std::endl;
|
||||
});
|
||||
resolver->resolve("localhost", [now](const trantor::InetAddress &addr) {
|
||||
auto interval = trantor::Date::now().microSecondsSinceEpoch() -
|
||||
now.microSecondsSinceEpoch();
|
||||
std::cout << "localhost:" << addr.toIp() << " " << interval / 1000
|
||||
<< "ms" << std::endl;
|
||||
});
|
||||
}
|
||||
int main()
|
||||
{
|
||||
trantor::EventLoop loop;
|
||||
auto resolver = trantor::Resolver::newResolver(&loop);
|
||||
dns(resolver);
|
||||
loop.runAfter(1.0, [resolver]() { dns(resolver); });
|
||||
loop.loop();
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
#include <trantor/net/TcpServer.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
using namespace trantor;
|
||||
#define USE_IPV6 0
|
||||
int main()
|
||||
{
|
||||
LOG_DEBUG << "test start";
|
||||
Logger::setLogLevel(Logger::kTrace);
|
||||
EventLoop loop;
|
||||
#if USE_IPV6
|
||||
InetAddress addr(8888, true, true);
|
||||
#else
|
||||
InetAddress addr(8888);
|
||||
#endif
|
||||
TcpServer server(&loop, addr, "test");
|
||||
server.kickoffIdleConnections(10);
|
||||
server.setRecvMessageCallback(
|
||||
[](const TcpConnectionPtr &connectionPtr, MsgBuffer *buffer) {
|
||||
// LOG_DEBUG<<"recv callback!";
|
||||
std::cout << std::string(buffer->peek(), buffer->readableBytes());
|
||||
connectionPtr->send(buffer->peek(), buffer->readableBytes());
|
||||
buffer->retrieveAll();
|
||||
});
|
||||
int n = 0;
|
||||
server.setConnectionCallback([&n](const TcpConnectionPtr &connPtr) {
|
||||
if (connPtr->connected())
|
||||
{
|
||||
++n;
|
||||
if (n % 2 == 0)
|
||||
{
|
||||
connPtr->keepAlive();
|
||||
}
|
||||
LOG_DEBUG << "New connection";
|
||||
}
|
||||
else if (connPtr->disconnected())
|
||||
{
|
||||
LOG_DEBUG << "connection disconnected";
|
||||
}
|
||||
});
|
||||
server.setIoLoopNum(3);
|
||||
server.start();
|
||||
loop.loop();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,30 +0,0 @@
|
||||
//
|
||||
// Created by antao on 1/14/17.
|
||||
//
|
||||
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
using namespace std::chrono_literals;
|
||||
int main()
|
||||
{
|
||||
trantor::EventLoop loop;
|
||||
std::thread thread([&loop]() {
|
||||
std::this_thread::sleep_for(3s);
|
||||
loop.runInLoop([&loop]() {
|
||||
std::cout << "runInLoop called in other thread" << std::endl;
|
||||
loop.queueInLoop(
|
||||
[]() { std::cout << "queueInLoop in runInLoop" << std::endl; });
|
||||
});
|
||||
});
|
||||
loop.runInLoop([]() { std::cout << "runInLoop 1" << std::endl; });
|
||||
loop.runInLoop([]() { std::cout << "runInLoop 2" << std::endl; });
|
||||
loop.queueInLoop([]() { std::cout << "queueInLoop 1" << std::endl; });
|
||||
loop.runAfter(1.5, []() { std::cout << "run after 1.5" << std::endl; });
|
||||
loop.loop();
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
std::atomic<uint64_t> counter;
|
||||
counter = 0;
|
||||
std::promise<int> pro;
|
||||
auto ft = pro.get_future();
|
||||
trantor::EventLoopThread loopThread;
|
||||
|
||||
auto loop = loopThread.getLoop();
|
||||
loop->runInLoop([&counter, &pro, loop]() {
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
loop->queueInLoop([&counter, &pro]() {
|
||||
++counter;
|
||||
if (counter.load() == 110000)
|
||||
pro.set_value(1);
|
||||
});
|
||||
}
|
||||
});
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
std::thread([&counter, loop, &pro]() {
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
loop->runInLoop([&counter, &pro]() {
|
||||
++counter;
|
||||
if (counter.load() == 110000)
|
||||
pro.set_value(1);
|
||||
});
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
loopThread.run();
|
||||
ft.get();
|
||||
std::cout << "counter=" << counter.load() << std::endl;
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
std::atomic<bool> flag(false);
|
||||
{
|
||||
trantor::EventLoopThread thr;
|
||||
thr.getLoop()->runOnQuit([&]() { flag = true; });
|
||||
thr.run();
|
||||
thr.getLoop()->quit();
|
||||
}
|
||||
|
||||
if (flag == false)
|
||||
{
|
||||
std::cerr << "Test failed\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Success\n";
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
#include <trantor/net/TcpClient.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
using namespace trantor;
|
||||
#define USE_IPV6 0
|
||||
int main()
|
||||
{
|
||||
trantor::Logger::setLogLevel(trantor::Logger::kTrace);
|
||||
LOG_DEBUG << "TcpClient class test!";
|
||||
EventLoop loop;
|
||||
#if USE_IPV6
|
||||
InetAddress serverAddr("::1", 8888, true);
|
||||
#else
|
||||
InetAddress serverAddr("127.0.0.1", 8888);
|
||||
#endif
|
||||
std::shared_ptr<trantor::TcpClient> client[10];
|
||||
std::atomic_int connCount;
|
||||
connCount = 1;
|
||||
for (int i = 0; i < connCount; ++i)
|
||||
{
|
||||
client[i] = std::make_shared<trantor::TcpClient>(&loop,
|
||||
serverAddr,
|
||||
"tcpclienttest");
|
||||
client[i]->enableSSL(false, false);
|
||||
client[i]->setConnectionCallback(
|
||||
[i, &loop, &connCount](const TcpConnectionPtr &conn) {
|
||||
if (conn->connected())
|
||||
{
|
||||
LOG_DEBUG << i << " connected!";
|
||||
char tmp[20];
|
||||
sprintf(tmp, "%d client!!", i);
|
||||
conn->send(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG << i << " disconnected";
|
||||
--connCount;
|
||||
if (connCount == 0)
|
||||
loop.quit();
|
||||
}
|
||||
});
|
||||
client[i]->setMessageCallback(
|
||||
[](const TcpConnectionPtr &conn, MsgBuffer *buf) {
|
||||
LOG_DEBUG << std::string(buf->peek(), buf->readableBytes());
|
||||
buf->retrieveAll();
|
||||
conn->shutdown();
|
||||
});
|
||||
client[i]->connect();
|
||||
}
|
||||
loop.loop();
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#include <trantor/net/TcpServer.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
using namespace trantor;
|
||||
#define USE_IPV6 0
|
||||
int main()
|
||||
{
|
||||
LOG_DEBUG << "test start";
|
||||
Logger::setLogLevel(Logger::kTrace);
|
||||
EventLoopThread loopThread;
|
||||
loopThread.run();
|
||||
#if USE_IPV6
|
||||
InetAddress addr(8888, true, true);
|
||||
#else
|
||||
InetAddress addr(8888);
|
||||
#endif
|
||||
TcpServer server(loopThread.getLoop(), addr, "test");
|
||||
server.enableSSL("server.pem", "server.pem");
|
||||
server.setRecvMessageCallback(
|
||||
[](const TcpConnectionPtr &connectionPtr, MsgBuffer *buffer) {
|
||||
// LOG_DEBUG<<"recv callback!";
|
||||
std::cout << std::string(buffer->peek(), buffer->readableBytes());
|
||||
connectionPtr->send(buffer->peek(), buffer->readableBytes());
|
||||
buffer->retrieveAll();
|
||||
connectionPtr->forceClose();
|
||||
});
|
||||
server.setConnectionCallback([](const TcpConnectionPtr &connPtr) {
|
||||
if (connPtr->connected())
|
||||
{
|
||||
LOG_DEBUG << "New connection";
|
||||
connPtr->send("Hello world\r\n");
|
||||
}
|
||||
else if (connPtr->disconnected())
|
||||
{
|
||||
LOG_DEBUG << "connection disconnected";
|
||||
}
|
||||
});
|
||||
server.setIoLoopNum(3);
|
||||
server.start();
|
||||
loopThread.wait();
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
#include <trantor/net/TcpServer.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace trantor;
|
||||
#define USE_IPV6 0
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
std::cout << "usage:" << argv[0] << " filename" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::cout << "filename:" << argv[1] << std::endl;
|
||||
struct stat filestat;
|
||||
if (stat(argv[1], &filestat) < 0)
|
||||
{
|
||||
perror("");
|
||||
exit(1);
|
||||
}
|
||||
std::cout << "file len=" << filestat.st_size << std::endl;
|
||||
|
||||
auto fp = fopen(argv[1], "rb");
|
||||
|
||||
if (fp == nullptr)
|
||||
{
|
||||
perror("");
|
||||
exit(1);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
LOG_DEBUG << "test start";
|
||||
|
||||
Logger::setLogLevel(Logger::kTrace);
|
||||
EventLoopThread loopThread;
|
||||
loopThread.run();
|
||||
|
||||
#if USE_IPV6
|
||||
InetAddress addr(1207, true, true);
|
||||
#else
|
||||
InetAddress addr(1207);
|
||||
#endif
|
||||
TcpServer server(loopThread.getLoop(), addr, "test");
|
||||
server.setRecvMessageCallback(
|
||||
[](const TcpConnectionPtr &connectionPtr, MsgBuffer *buffer) {
|
||||
// LOG_DEBUG<<"recv callback!";
|
||||
});
|
||||
int counter = 0;
|
||||
server.setConnectionCallback(
|
||||
[argv, &counter](const TcpConnectionPtr &connPtr) {
|
||||
if (connPtr->connected())
|
||||
{
|
||||
LOG_DEBUG << "New connection";
|
||||
std::thread t([connPtr, argv, &counter]() {
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
connPtr->sendFile(argv[1]);
|
||||
char str[64];
|
||||
++counter;
|
||||
sprintf(str, "\n%d files sent!\n", counter);
|
||||
connPtr->send(str, strlen(str));
|
||||
}
|
||||
});
|
||||
t.detach();
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
connPtr->sendFile(argv[1]);
|
||||
char str[64];
|
||||
++counter;
|
||||
sprintf(str, "\n%d files sent!\n", counter);
|
||||
connPtr->send(str, strlen(str));
|
||||
}
|
||||
}
|
||||
else if (connPtr->disconnected())
|
||||
{
|
||||
LOG_DEBUG << "connection disconnected";
|
||||
}
|
||||
});
|
||||
server.setIoLoopNum(3);
|
||||
server.start();
|
||||
loopThread.wait();
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#include <trantor/utils/SerialTaskQueue.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
int main()
|
||||
{
|
||||
trantor::Logger::setLogLevel(trantor::Logger::kTrace);
|
||||
trantor::SerialTaskQueue queue1("test queue1");
|
||||
trantor::SerialTaskQueue queue2("");
|
||||
queue1.runTaskInQueue([&]() {
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
printf("task(%s) i=%d\n", queue1.getName().c_str(), i);
|
||||
}
|
||||
});
|
||||
queue2.runTaskInQueue([&]() {
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
printf("task(%s) i=%d\n", queue2.getName().c_str(), i);
|
||||
}
|
||||
});
|
||||
queue1.waitAllTasksFinished();
|
||||
queue2.waitAllTasksFinished();
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
#include <trantor/utils/SerialTaskQueue.h>
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
std::atomic<uint64_t> counter;
|
||||
counter = 0;
|
||||
std::promise<int> pro;
|
||||
auto ft = pro.get_future();
|
||||
trantor::SerialTaskQueue queue("");
|
||||
queue.runTaskInQueue([&counter, &pro, &queue]() {
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
queue.runTaskInQueue([&counter, &pro]() {
|
||||
++counter;
|
||||
if (counter.load() == 110000)
|
||||
pro.set_value(1);
|
||||
});
|
||||
}
|
||||
});
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
std::thread([&counter, &queue, &pro]() {
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
queue.runTaskInQueue([&counter, &pro]() {
|
||||
++counter;
|
||||
if (counter.load() == 110000)
|
||||
pro.set_value(1);
|
||||
});
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
ft.get();
|
||||
std::cout << "counter=" << counter.load() << std::endl;
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
#include <trantor/net/TcpClient.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
using namespace trantor;
|
||||
#define USE_IPV6 0
|
||||
int main()
|
||||
{
|
||||
trantor::Logger::setLogLevel(trantor::Logger::kTrace);
|
||||
LOG_DEBUG << "TcpClient class test!";
|
||||
EventLoop loop;
|
||||
#if USE_IPV6
|
||||
InetAddress serverAddr("::1", 8888, true);
|
||||
#else
|
||||
InetAddress serverAddr("127.0.0.1", 8888);
|
||||
#endif
|
||||
std::shared_ptr<trantor::TcpClient> client[10];
|
||||
std::atomic_int connCount;
|
||||
connCount = 10;
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
client[i] = std::make_shared<trantor::TcpClient>(&loop,
|
||||
serverAddr,
|
||||
"tcpclienttest");
|
||||
client[i]->setConnectionCallback(
|
||||
[i, &loop, &connCount](const TcpConnectionPtr &conn) {
|
||||
if (conn->connected())
|
||||
{
|
||||
LOG_DEBUG << i << " connected!";
|
||||
char tmp[20];
|
||||
sprintf(tmp, "%d client!!", i);
|
||||
conn->send(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG << i << " disconnected";
|
||||
--connCount;
|
||||
if (connCount == 0)
|
||||
loop.quit();
|
||||
}
|
||||
});
|
||||
client[i]->setMessageCallback(
|
||||
[](const TcpConnectionPtr &conn, MsgBuffer *buf) {
|
||||
LOG_DEBUG << std::string(buf->peek(), buf->readableBytes());
|
||||
buf->retrieveAll();
|
||||
conn->shutdown();
|
||||
});
|
||||
client[i]->connect();
|
||||
}
|
||||
loop.loop();
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
#include <trantor/net/TcpServer.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
using namespace trantor;
|
||||
#define USE_IPV6 0
|
||||
int main()
|
||||
{
|
||||
LOG_DEBUG << "test start";
|
||||
Logger::setLogLevel(Logger::kTrace);
|
||||
EventLoopThread loopThread;
|
||||
loopThread.run();
|
||||
#if USE_IPV6
|
||||
InetAddress addr(8888, true, true);
|
||||
#else
|
||||
InetAddress addr(8888);
|
||||
#endif
|
||||
TcpServer server(loopThread.getLoop(), addr, "test");
|
||||
server.setRecvMessageCallback(
|
||||
[](const TcpConnectionPtr &connectionPtr, MsgBuffer *buffer) {
|
||||
// LOG_DEBUG<<"recv callback!";
|
||||
std::cout << std::string(buffer->peek(), buffer->readableBytes());
|
||||
connectionPtr->send(buffer->peek(), buffer->readableBytes());
|
||||
buffer->retrieveAll();
|
||||
// connectionPtr->forceClose();
|
||||
});
|
||||
server.setConnectionCallback([](const TcpConnectionPtr &connPtr) {
|
||||
if (connPtr->connected())
|
||||
{
|
||||
LOG_DEBUG << "New connection";
|
||||
}
|
||||
else if (connPtr->disconnected())
|
||||
{
|
||||
LOG_DEBUG << "connection disconnected";
|
||||
}
|
||||
});
|
||||
server.setIoLoopNum(3);
|
||||
server.start();
|
||||
loopThread.wait();
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
using namespace std::literals;
|
||||
|
||||
int main()
|
||||
{
|
||||
trantor::Logger::setLogLevel(trantor::Logger::kTrace);
|
||||
trantor::EventLoop loop;
|
||||
auto id1 = loop.runAfter(1s, []() {
|
||||
LOG_ERROR << "This info shouldn't be displayed!";
|
||||
});
|
||||
loop.invalidateTimer(id1);
|
||||
auto id2 = loop.runEvery(0.3s, []() {
|
||||
LOG_ERROR << "This timer will be invalidated after 3 second;";
|
||||
});
|
||||
std::thread thread([id2, &loop]() {
|
||||
std::this_thread::sleep_for(3s);
|
||||
loop.invalidateTimer(id2);
|
||||
});
|
||||
thread.detach();
|
||||
loop.runEvery(3, []() { LOG_DEBUG << " runEvery 3s"; });
|
||||
loop.runAt(trantor::Date::date().after(10),
|
||||
[]() { LOG_DEBUG << "runAt 10s later"; });
|
||||
loop.runAfter(5, []() { std::cout << "runAt 5s later" << std::endl; });
|
||||
loop.runEvery(1, []() { std::cout << "runEvery 1s" << std::endl; });
|
||||
loop.runAfter(4, []() { std::cout << "runAfter 4s" << std::endl; });
|
||||
loop.runAfter(10min,
|
||||
[]() { std::cout << "*********** run after 10 min\n"; });
|
||||
loop.loop();
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <iostream>
|
||||
int main()
|
||||
{
|
||||
trantor::EventLoop loop;
|
||||
LOG_FATAL << trantor::Date::date().roundDay().microSecondsSinceEpoch();
|
||||
trantor::Date begin = trantor::Date::date().roundSecond().after(2);
|
||||
auto id = loop.runAt(begin, [begin, &loop]() {
|
||||
LOG_DEBUG << "test begin:";
|
||||
srand((unsigned int)time(NULL));
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
{
|
||||
int aa = rand() % 10000;
|
||||
double s = (double)aa / 1000.0 + 1;
|
||||
loop.runAt(begin.after(s),
|
||||
[s]() { LOG_ERROR << "run After:" << s; });
|
||||
}
|
||||
LOG_DEBUG << "timer created!";
|
||||
});
|
||||
std::cout << "id=" << id << std::endl;
|
||||
loop.loop();
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#include <trantor/utils/TimingWheel.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <memory>
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
MyClass();
|
||||
MyClass(MyClass &&) = default;
|
||||
MyClass(const MyClass &) = default;
|
||||
MyClass &operator=(MyClass &&) = default;
|
||||
MyClass &operator=(const MyClass &) = default;
|
||||
~MyClass();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
MyClass::MyClass()
|
||||
{
|
||||
}
|
||||
|
||||
MyClass::~MyClass()
|
||||
{
|
||||
LOG_DEBUG << "MyClass destructed!";
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
LOG_DEBUG << "start";
|
||||
trantor::EventLoop loop;
|
||||
std::weak_ptr<MyClass> weakEntry;
|
||||
trantor::TimingWheel wheel(&loop, 75, 0.1, 100);
|
||||
{
|
||||
auto entry = std::shared_ptr<MyClass>(new MyClass);
|
||||
|
||||
wheel.insertEntry(75, entry);
|
||||
weakEntry = entry;
|
||||
}
|
||||
// loop.runAfter(5.0, [&]() {
|
||||
// wheel.insertEntry(10, weakEntry.lock());
|
||||
// });
|
||||
// loop.runAfter(0.5,[&](){
|
||||
// wheel.insertEntry(28,weakEntry.lock());
|
||||
// });
|
||||
// loop.runEvery(1.0,[](){
|
||||
// LOG_DEBUG<<"tick";
|
||||
// });
|
||||
loop.loop();
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/vLeZ4B80b22y
|
||||
JSF9WoAEGo5D0GLlzSxL7rRZSd81h3YMKpsgFT4WkZxs9sqoRW/OTaqMGuEzGt6a
|
||||
54RMwycQwJL+GGt/stZ+7AYHoLE6cQzP8iFcwy4mLFsX+gQjoE0UHr0RykKRl3Vw
|
||||
qZST74AhAMlFvEDCfoMxTy9dy5ZjbLTFrH1kvo5CT9MEbyBLpxYrYYFjBAMIZdvV
|
||||
82Mz3JftDLbx7t+96PaGLpDMLk9NgrV1kbUpzck/aCbZ5DgV7enTU33tVIcfMU5e
|
||||
fZii044OkOvFNXibi6tdPORP8TBnrmEZjmDnFAgEijzpIcJ4VVPTrlCuxLUKcSDY
|
||||
wYLro7gbAgMBAAECggEAesztcnoewkCTq0MovdZWo0o2z6wJi1DrC/7oNz+e2/PU
|
||||
YVpwXA3+5AmCfC9cAIXoY+NOVclpbofJBsE89MUQoiQUgPU29GSgCE42VnBO0jVR
|
||||
lWVohLblOcGy3hpcyEyE0VwWj+xQ0lqE9xFFfbIpB/ou7qDxgR/x+oTSu2oG+cmr
|
||||
xv+Dj+k46QW0X2A3XHnlZkk0fQGb+Tf3fIJ4BBwF/UFQTDyZPZrsZfbpoEk1BnIf
|
||||
VPcfM1/ca6h1t/oEnpD66H2TjxYzZC58mqZSZIZ+Pk+wocSsF5UcQLedcM137LDH
|
||||
OR+fdscinr1xgMhV30RmAUP5tug9Km3oq8wf4Xl1IQKBgQDmDtYbwN7V1eA6Dbeq
|
||||
HCk07t/llK+j4mvyuU+pVKfNHmnUUd+NPpDwREFrfoR6ilrZMJ4Obl7z53rja+9s
|
||||
JqECBThhRzHxnUQgsu3DQeY7e2KxBBGGvhQBjRGtVRID3aFTTyMKvaOkTc7kL+x1
|
||||
5DbtoVAA1kbYnjgZJyZosx+bmQKBgQDVW6lXxoLzC7npXdXOTMJifExcCIzXskc9
|
||||
5b+rJTXgkqaPQSQ7nNmRReUY9r9UBsZKLduuYJZ3TLMCaU91/wBwc1ygkQvRz+8A
|
||||
2QGMovlVOqwPAOEc1lMCXzQki9PcqmVX/e0oFmTtUgyF4RKEwL/q8A9fWy587rkQ
|
||||
57MDNJ3h0wKBgQDNJgPFweq0EsGd4yeZuP0B59Wee0VYxgru6lLgM85iujEzFUNd
|
||||
R6KlrqgLvElUoNW8gX8gbUmdBBlwfYqGDbhb/d212W/u/geHhSdCjBxLhI6QPYmH
|
||||
dy6N54cQ4yBqdBNtH8+mv08SsBPDJf0db8GPi960sF+CwSxTObclfD2+WQKBgBgi
|
||||
HxyLmsJNIEFSWN3V9uLW9ngui2fWhZJty2lbcyWs0ORBVQzdKArzof9Z4bhqb8Fy
|
||||
QHgP+tURuunZ6aAKMQ2HLwIGhhS8dWdeJHu474UBdvbXfZ8aaxdIl4hOvK8oIwB5
|
||||
+3peVho1/q6iD8suVkcH0mVR1gdRpWNRIgGJ0RX7AoGBAMzp0u3X3SEMWTlqFR7D
|
||||
BJqmpiMvJJJSqex1i4PJoy+uJpqfl3b1jUMnFIS4GiscVCIEJkPpZaz3eSTt7aVQ
|
||||
+7v0b/Cusv1qYX2c5v9j9x7nmUlfdwT5rY2g6RXI/xlhPD9elb1dxlzmhiO4VQtr
|
||||
AdiFmu0poyPpEGwjpcLEZq6K
|
||||
-----END PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDajCCAlICCQCXmD7IkkDArzANBgkqhkiG9w0BAQsFADB3MQswCQYDVQQGEwJD
|
||||
TjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzEPMA0GA1UECgwG
|
||||
YW4tdGFvMQ8wDQYDVQQLDAZhbi10YW8xIjAgBgkqhkiG9w0BCQEWE2FudGFvMjAw
|
||||
MkBnbWFpbC5jb20wHhcNMTgwNzIxMDEyMjEyWhcNMTkwNzIxMDEyMjEyWjB3MQsw
|
||||
CQYDVQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzEP
|
||||
MA0GA1UECgwGYW4tdGFvMQ8wDQYDVQQLDAZhbi10YW8xIjAgBgkqhkiG9w0BCQEW
|
||||
E2FudGFvMjAwMkBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||
AoIBAQC/vLeZ4B80b22yJSF9WoAEGo5D0GLlzSxL7rRZSd81h3YMKpsgFT4WkZxs
|
||||
9sqoRW/OTaqMGuEzGt6a54RMwycQwJL+GGt/stZ+7AYHoLE6cQzP8iFcwy4mLFsX
|
||||
+gQjoE0UHr0RykKRl3VwqZST74AhAMlFvEDCfoMxTy9dy5ZjbLTFrH1kvo5CT9ME
|
||||
byBLpxYrYYFjBAMIZdvV82Mz3JftDLbx7t+96PaGLpDMLk9NgrV1kbUpzck/aCbZ
|
||||
5DgV7enTU33tVIcfMU5efZii044OkOvFNXibi6tdPORP8TBnrmEZjmDnFAgEijzp
|
||||
IcJ4VVPTrlCuxLUKcSDYwYLro7gbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAClA
|
||||
Z6JHessY1mo0ObCp8Sh83gIiGu9M77xz6Gy2vUpUGE9N1hJwYH3uZPLbpnIuxZU0
|
||||
uDXd9w5vCJmtqo/4X6sedqKA6Upi8DROawtCUJaGrHrb2vJONyL7A3OWpHyYgOAa
|
||||
z+sl4jbrGGv4azSg9ef1mQZTDcEcdsYiKj9U4zoyDXteyt6NQK3x5BtziB0mJfeM
|
||||
gjrltVcc59s8Z2jf3PI3qmWtMuJm7KGHlan2DUz12AZxY3sJrFyew3Vi1V2uvIZB
|
||||
YwgcXbVa3r6PQU3RwFwkazxAe80ZgiQlVsEiBl8mre4zznt5XHF6mzAqvpHyXPCI
|
||||
LfEO3waLkgB94mFha5A=
|
||||
-----END CERTIFICATE-----
|
@ -1,28 +0,0 @@
|
||||
wepoll - epoll for Windows
|
||||
https://github.com/piscisaureus/wepoll
|
||||
|
||||
Copyright 2012-2020, Bert Belder <bertbelder@gmail.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,41 +0,0 @@
|
||||
## wepoll
|
||||
|
||||
This library is based on [wepoll v1.5.8](https://github.com/piscisaureus/wepoll/commit/0598a791bf9cbbf480793d778930fc635b044980).
|
||||
|
||||
An eventfd-like mechanism is added to it. After making the changes, we can wake up `trantor::EventLoop` from the epoll_wait() function.
|
||||
|
||||
## Modifications
|
||||
|
||||
```shell
|
||||
diff wepoll.h Wepoll.h
|
||||
53a54
|
||||
> EPOLLEVENT = (int)(1U << 14),
|
||||
67a69
|
||||
> #define EPOLLEVENT (1U << 14)
|
||||
111a114
|
||||
> WEPOLL_EXPORT void epoll_post_signal(HANDLE ephnd, uint64_t event);
|
||||
```
|
||||
|
||||
```shell
|
||||
diff wepoll.c Wepoll.c
|
||||
50a51
|
||||
> EPOLLEVENT = (int)(1U << 14),
|
||||
64a66
|
||||
> #define EPOLLEVENT (1U << 14)
|
||||
1262a1265,1271
|
||||
> if (iocp_events[i].lpCompletionKey)
|
||||
> {
|
||||
> struct epoll_event* ev = &epoll_events[epoll_event_count++];
|
||||
> ev->data.u64 = (uint64_t)iocp_events[i].lpCompletionKey;
|
||||
> ev->events = EPOLLEVENT;
|
||||
> continue;
|
||||
> }
|
||||
2441a2451,2457
|
||||
> void epoll_post_signal(HANDLE port_handle, uint64_t event)
|
||||
> {
|
||||
> ULONG_PTR ev;
|
||||
> ev = (ULONG_PTR)event;
|
||||
> PostQueuedCompletionStatus(port_handle, 1, ev, NULL);
|
||||
> }
|
||||
>
|
||||
```
|
2491
modules/trantor/trantor/third_party/wepoll/Wepoll.c
vendored
2491
modules/trantor/trantor/third_party/wepoll/Wepoll.c
vendored
File diff suppressed because it is too large
Load Diff
120
modules/trantor/trantor/third_party/wepoll/Wepoll.h
vendored
120
modules/trantor/trantor/third_party/wepoll/Wepoll.h
vendored
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* wepoll - epoll for Windows
|
||||
* https://github.com/piscisaureus/wepoll
|
||||
*
|
||||
* Copyright 2012-2020, Bert Belder <bertbelder@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef WEPOLL_H_
|
||||
#define WEPOLL_H_
|
||||
|
||||
#ifndef WEPOLL_EXPORT
|
||||
#define WEPOLL_EXPORT
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum EPOLL_EVENTS
|
||||
{
|
||||
EPOLLIN = (int)(1U << 0),
|
||||
EPOLLPRI = (int)(1U << 1),
|
||||
EPOLLOUT = (int)(1U << 2),
|
||||
EPOLLERR = (int)(1U << 3),
|
||||
EPOLLHUP = (int)(1U << 4),
|
||||
EPOLLRDNORM = (int)(1U << 6),
|
||||
EPOLLRDBAND = (int)(1U << 7),
|
||||
EPOLLWRNORM = (int)(1U << 8),
|
||||
EPOLLWRBAND = (int)(1U << 9),
|
||||
EPOLLMSG = (int)(1U << 10), /* Never reported. */
|
||||
EPOLLRDHUP = (int)(1U << 13),
|
||||
EPOLLEVENT = (int)(1U << 14),
|
||||
EPOLLONESHOT = (int)(1U << 31)
|
||||
};
|
||||
|
||||
#define EPOLLIN (1U << 0)
|
||||
#define EPOLLPRI (1U << 1)
|
||||
#define EPOLLOUT (1U << 2)
|
||||
#define EPOLLERR (1U << 3)
|
||||
#define EPOLLHUP (1U << 4)
|
||||
#define EPOLLRDNORM (1U << 6)
|
||||
#define EPOLLRDBAND (1U << 7)
|
||||
#define EPOLLWRNORM (1U << 8)
|
||||
#define EPOLLWRBAND (1U << 9)
|
||||
#define EPOLLMSG (1U << 10)
|
||||
#define EPOLLRDHUP (1U << 13)
|
||||
#define EPOLLEVENT (1U << 14)
|
||||
#define EPOLLONESHOT (1U << 31)
|
||||
|
||||
#define EPOLL_CTL_ADD 1
|
||||
#define EPOLL_CTL_MOD 2
|
||||
#define EPOLL_CTL_DEL 3
|
||||
|
||||
typedef void* HANDLE;
|
||||
typedef uintptr_t SOCKET;
|
||||
|
||||
typedef union epoll_data
|
||||
{
|
||||
void* ptr;
|
||||
int fd;
|
||||
uint32_t u32;
|
||||
uint64_t u64;
|
||||
SOCKET sock; /* Windows specific */
|
||||
HANDLE hnd; /* Windows specific */
|
||||
} epoll_data_t;
|
||||
|
||||
struct epoll_event
|
||||
{
|
||||
uint32_t events; /* Epoll events and flags */
|
||||
epoll_data_t data; /* User data variable */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
WEPOLL_EXPORT HANDLE epoll_create(int size);
|
||||
WEPOLL_EXPORT HANDLE epoll_create1(int flags);
|
||||
|
||||
WEPOLL_EXPORT int epoll_close(HANDLE ephnd);
|
||||
|
||||
WEPOLL_EXPORT int epoll_ctl(HANDLE ephnd,
|
||||
int op,
|
||||
SOCKET sock,
|
||||
struct epoll_event* event);
|
||||
|
||||
WEPOLL_EXPORT int epoll_wait(HANDLE ephnd,
|
||||
struct epoll_event* events,
|
||||
int maxevents,
|
||||
int timeout);
|
||||
WEPOLL_EXPORT void epoll_post_signal(HANDLE ephnd, uint64_t event);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* WEPOLL_H_ */
|
@ -1,19 +0,0 @@
|
||||
find_package(GTest REQUIRED)
|
||||
add_executable(msgbuffer_unittest MsgBufferUnittest.cc)
|
||||
add_executable(inetaddress_unittest InetAddressUnittest.cc)
|
||||
add_executable(date_unittest DateUnittest.cc)
|
||||
add_executable(split_string_unittest splitStringUnittest.cc)
|
||||
set(UNITTEST_TARGETS
|
||||
msgbuffer_unittest
|
||||
inetaddress_unittest
|
||||
date_unittest
|
||||
split_string_unittest)
|
||||
set_property(TARGET ${UNITTEST_TARGETS} PROPERTY CXX_STANDARD 14)
|
||||
set_property(TARGET ${UNITTEST_TARGETS} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
set_property(TARGET ${UNITTEST_TARGETS} PROPERTY CXX_EXTENSIONS OFF)
|
||||
|
||||
include(GoogleTest)
|
||||
foreach(T ${UNITTEST_TARGETS})
|
||||
target_link_libraries(${T} PRIVATE trantor GTest::GTest)
|
||||
gtest_discover_tests(${T})
|
||||
endforeach()
|
@ -1,43 +0,0 @@
|
||||
#include <trantor/utils/Date.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
using namespace trantor;
|
||||
TEST(Date, constructorTest)
|
||||
{
|
||||
EXPECT_STREQ("1985-01-01 00:00:00",
|
||||
trantor::Date(1985, 1, 1)
|
||||
.toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S")
|
||||
.c_str());
|
||||
EXPECT_STREQ("2004-02-29 00:00:00.000000",
|
||||
trantor::Date(2004, 2, 29)
|
||||
.toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S", true)
|
||||
.c_str());
|
||||
EXPECT_STRNE("2001-02-29 00:00:00.000000",
|
||||
trantor::Date(2001, 2, 29)
|
||||
.toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S", true)
|
||||
.c_str());
|
||||
EXPECT_STREQ("2018-01-01 00:00:00.000000",
|
||||
trantor::Date(2018, 1, 1, 12, 12, 12, 2321)
|
||||
.roundDay()
|
||||
.toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S", true)
|
||||
.c_str());
|
||||
}
|
||||
TEST(Date, DatabaseStringTest)
|
||||
{
|
||||
auto now = trantor::Date::now();
|
||||
EXPECT_EQ(now, trantor::Date::fromDbStringLocal(now.toDbStringLocal()));
|
||||
std::string dbString = "2018-01-01 00:00:00.123";
|
||||
auto dbDate = trantor::Date::fromDbStringLocal(dbString);
|
||||
auto ms = (dbDate.microSecondsSinceEpoch() % 1000000) / 1000;
|
||||
EXPECT_EQ(ms, 123);
|
||||
dbString = "2018-01-01 00:00:00";
|
||||
dbDate = trantor::Date::fromDbStringLocal(dbString);
|
||||
ms = (dbDate.microSecondsSinceEpoch() % 1000000) / 1000;
|
||||
EXPECT_EQ(ms, 0);
|
||||
}
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
#include <trantor/net/InetAddress.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
using namespace trantor;
|
||||
TEST(InetAddress, innerIpTest)
|
||||
{
|
||||
EXPECT_EQ(true, InetAddress("192.168.0.1", 0).isIntranetIp());
|
||||
EXPECT_EQ(true, InetAddress("192.168.12.1", 0).isIntranetIp());
|
||||
EXPECT_EQ(true, InetAddress("10.168.0.1", 0).isIntranetIp());
|
||||
EXPECT_EQ(true, InetAddress("10.0.0.1", 0).isIntranetIp());
|
||||
EXPECT_EQ(true, InetAddress("172.31.10.1", 0).isIntranetIp());
|
||||
EXPECT_EQ(true, InetAddress("127.0.0.1", 0).isIntranetIp());
|
||||
EXPECT_EQ(true, InetAddress("example.com", 0).isUnspecified());
|
||||
EXPECT_EQ(false, InetAddress("127.0.0.2", 0).isUnspecified());
|
||||
EXPECT_EQ(false, InetAddress("0.0.0.0", 0).isUnspecified());
|
||||
}
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
#include <trantor/utils/MsgBuffer.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
using namespace trantor;
|
||||
TEST(MsgBufferTest, readableTest)
|
||||
{
|
||||
MsgBuffer buffer;
|
||||
|
||||
EXPECT_EQ(0, buffer.readableBytes());
|
||||
buffer.append(std::string(128, 'a'));
|
||||
EXPECT_EQ(128, buffer.readableBytes());
|
||||
buffer.retrieve(100);
|
||||
EXPECT_EQ(28, buffer.readableBytes());
|
||||
EXPECT_EQ('a', buffer.peekInt8());
|
||||
buffer.retrieveAll();
|
||||
EXPECT_EQ(0, buffer.readableBytes());
|
||||
}
|
||||
TEST(MsgBufferTest, writableTest)
|
||||
{
|
||||
MsgBuffer buffer(100);
|
||||
|
||||
EXPECT_EQ(100, buffer.writableBytes());
|
||||
buffer.append("abcde");
|
||||
EXPECT_EQ(95, buffer.writableBytes());
|
||||
buffer.append(std::string(100, 'x'));
|
||||
EXPECT_EQ(111, buffer.writableBytes());
|
||||
buffer.retrieve(100);
|
||||
EXPECT_EQ(111, buffer.writableBytes());
|
||||
buffer.append(std::string(112, 'c'));
|
||||
EXPECT_EQ(99, buffer.writableBytes());
|
||||
buffer.retrieveAll();
|
||||
EXPECT_EQ(216, buffer.writableBytes());
|
||||
}
|
||||
|
||||
TEST(MsgBufferTest, addInFrontTest)
|
||||
{
|
||||
MsgBuffer buffer(100);
|
||||
|
||||
EXPECT_EQ(100, buffer.writableBytes());
|
||||
buffer.addInFrontInt8('a');
|
||||
EXPECT_EQ(100, buffer.writableBytes());
|
||||
buffer.addInFrontInt64(123);
|
||||
EXPECT_EQ(92, buffer.writableBytes());
|
||||
buffer.addInFrontInt64(100);
|
||||
EXPECT_EQ(84, buffer.writableBytes());
|
||||
buffer.addInFrontInt8(1);
|
||||
EXPECT_EQ(84, buffer.writableBytes());
|
||||
}
|
||||
|
||||
TEST(MsgBuffer, MoveContrustor)
|
||||
{
|
||||
MsgBuffer buf1(100);
|
||||
const char *bufptr1 = buf1.peek();
|
||||
MsgBuffer buffnew1 = std::move(buf1);
|
||||
EXPECT_EQ(bufptr1, buffnew1.peek());
|
||||
|
||||
MsgBuffer buf2(100);
|
||||
const char *bufptr2 = buf2.peek();
|
||||
MsgBuffer buffnew2(std::move(buf2));
|
||||
EXPECT_EQ(bufptr2, buffnew2.peek());
|
||||
}
|
||||
|
||||
TEST(Msgbuffer, MoveAssignmentOperator)
|
||||
{
|
||||
MsgBuffer buf(100);
|
||||
const char *bufptr = buf.peek();
|
||||
size_t writable = buf.writableBytes();
|
||||
MsgBuffer buffnew(1000);
|
||||
buffnew = std::move(buf);
|
||||
EXPECT_EQ(bufptr, buffnew.peek());
|
||||
EXPECT_EQ(writable, buffnew.writableBytes());
|
||||
}
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
#include <trantor/utils/Date.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <trantor/utils/Funcs.h>
|
||||
#include <iostream>
|
||||
using namespace trantor;
|
||||
TEST(splitString, ACCEPT_EMPTY_STRING1)
|
||||
{
|
||||
std::string originString = "1,2,3";
|
||||
auto out = splitString(originString, ",", true);
|
||||
EXPECT_EQ(out.size(), 3);
|
||||
}
|
||||
TEST(splitString, ACCEPT_EMPTY_STRING2)
|
||||
{
|
||||
std::string originString = ",1,2,3";
|
||||
auto out = splitString(originString, ",", true);
|
||||
EXPECT_EQ(out.size(), 4);
|
||||
EXPECT_TRUE(out[0].empty());
|
||||
}
|
||||
TEST(splitString, ACCEPT_EMPTY_STRING3)
|
||||
{
|
||||
std::string originString = ",1,2,3,";
|
||||
auto out = splitString(originString, ",", true);
|
||||
EXPECT_EQ(out.size(), 5);
|
||||
EXPECT_TRUE(out[0].empty());
|
||||
}
|
||||
TEST(splitString, ACCEPT_EMPTY_STRING4)
|
||||
{
|
||||
std::string originString = ",1,2,3,";
|
||||
auto out = splitString(originString, ":", true);
|
||||
EXPECT_EQ(out.size(), 1);
|
||||
EXPECT_STREQ(out[0].data(), ",1,2,3,");
|
||||
}
|
||||
TEST(splitString, ACCEPT_EMPTY_STRING5)
|
||||
{
|
||||
std::string originString = "trantor::splitString";
|
||||
auto out = splitString(originString, "::", true);
|
||||
EXPECT_EQ(out.size(), 2);
|
||||
EXPECT_STREQ(out[0].data(), "trantor");
|
||||
EXPECT_STREQ(out[1].data(), "splitString");
|
||||
}
|
||||
TEST(splitString, ACCEPT_EMPTY_STRING6)
|
||||
{
|
||||
std::string originString = "trantor::::splitString";
|
||||
auto out = splitString(originString, "::", true);
|
||||
EXPECT_EQ(out.size(), 3);
|
||||
EXPECT_STREQ(out[0].data(), "trantor");
|
||||
EXPECT_STREQ(out[1].data(), "");
|
||||
EXPECT_STREQ(out[2].data(), "splitString");
|
||||
}
|
||||
TEST(splitString, ACCEPT_EMPTY_STRING7)
|
||||
{
|
||||
std::string originString = "trantor:::splitString";
|
||||
auto out = splitString(originString, "::", true);
|
||||
EXPECT_EQ(out.size(), 2);
|
||||
EXPECT_STREQ(out[0].data(), "trantor");
|
||||
EXPECT_STREQ(out[1].data(), ":splitString");
|
||||
}
|
||||
TEST(splitString, ACCEPT_EMPTY_STRING8)
|
||||
{
|
||||
std::string originString = "trantor:::splitString";
|
||||
auto out = splitString(originString, "trantor:::splitString", true);
|
||||
EXPECT_EQ(out.size(), 2);
|
||||
EXPECT_STREQ(out[0].data(), "");
|
||||
EXPECT_STREQ(out[1].data(), "");
|
||||
}
|
||||
TEST(splitString, ACCEPT_EMPTY_STRING9)
|
||||
{
|
||||
std::string originString = "";
|
||||
auto out = splitString(originString, ",", true);
|
||||
EXPECT_EQ(out.size(), 1);
|
||||
EXPECT_STREQ(out[0].data(), "");
|
||||
}
|
||||
TEST(splitString, ACCEPT_EMPTY_STRING10)
|
||||
{
|
||||
std::string originString = "trantor";
|
||||
auto out = splitString(originString, "", true);
|
||||
EXPECT_EQ(out.size(), 0);
|
||||
}
|
||||
TEST(splitString, ACCEPT_EMPTY_STRING11)
|
||||
{
|
||||
std::string originString = "";
|
||||
auto out = splitString(originString, "", true);
|
||||
EXPECT_EQ(out.size(), 0);
|
||||
}
|
||||
|
||||
TEST(splitString, NO_ACCEPT_EMPTY_STRING1)
|
||||
{
|
||||
std::string originString = ",1,2,3";
|
||||
auto out = splitString(originString, ",");
|
||||
EXPECT_EQ(out.size(), 3);
|
||||
EXPECT_STREQ(out[0].data(), "1");
|
||||
}
|
||||
TEST(splitString, NO_ACCEPT_EMPTY_STRING2)
|
||||
{
|
||||
std::string originString = ",1,2,3,";
|
||||
auto out = splitString(originString, ",");
|
||||
EXPECT_EQ(out.size(), 3);
|
||||
EXPECT_STREQ(out[0].data(), "1");
|
||||
}
|
||||
TEST(splitString, NO_ACCEPT_EMPTY_STRING3)
|
||||
{
|
||||
std::string originString = ",1,2,3,";
|
||||
auto out = splitString(originString, ":");
|
||||
EXPECT_EQ(out.size(), 1);
|
||||
EXPECT_STREQ(out[0].data(), ",1,2,3,");
|
||||
}
|
||||
TEST(splitString, NO_ACCEPT_EMPTY_STRING4)
|
||||
{
|
||||
std::string originString = "trantor::splitString";
|
||||
auto out = splitString(originString, "::");
|
||||
EXPECT_EQ(out.size(), 2);
|
||||
EXPECT_STREQ(out[0].data(), "trantor");
|
||||
EXPECT_STREQ(out[1].data(), "splitString");
|
||||
}
|
||||
TEST(splitString, NO_ACCEPT_EMPTY_STRING5)
|
||||
{
|
||||
std::string originString = "trantor::::splitString";
|
||||
auto out = splitString(originString, "::");
|
||||
EXPECT_EQ(out.size(), 2);
|
||||
EXPECT_STREQ(out[0].data(), "trantor");
|
||||
EXPECT_STREQ(out[1].data(), "splitString");
|
||||
}
|
||||
TEST(splitString, NO_ACCEPT_EMPTY_STRING6)
|
||||
{
|
||||
std::string originString = "trantor:::splitString";
|
||||
auto out = splitString(originString, "::");
|
||||
EXPECT_EQ(out.size(), 2);
|
||||
EXPECT_STREQ(out[0].data(), "trantor");
|
||||
EXPECT_STREQ(out[1].data(), ":splitString");
|
||||
}
|
||||
TEST(splitString, NO_ACCEPT_EMPTY_STRING7)
|
||||
{
|
||||
std::string originString = "trantor:::splitString";
|
||||
auto out = splitString(originString, "trantor:::splitString");
|
||||
EXPECT_EQ(out.size(), 0);
|
||||
}
|
||||
TEST(splitString, NO_ACCEPT_EMPTY_STRING8)
|
||||
{
|
||||
std::string originString = "";
|
||||
auto out = splitString(originString, ",");
|
||||
EXPECT_EQ(out.size(), 0);
|
||||
}
|
||||
TEST(splitString, NO_ACCEPT_EMPTY_STRING9)
|
||||
{
|
||||
std::string originString = "trantor";
|
||||
auto out = splitString(originString, "");
|
||||
EXPECT_EQ(out.size(), 0);
|
||||
}
|
||||
TEST(splitString, NO_ACCEPT_EMPTY_STRING10)
|
||||
{
|
||||
std::string originString = "";
|
||||
auto out = splitString(originString, "");
|
||||
EXPECT_EQ(out.size(), 0);
|
||||
}
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,253 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* AsyncFileLogger.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <trantor/utils/AsyncFileLogger.h>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <chrono>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
static constexpr std::chrono::seconds kLogFlushTimeout{1};
|
||||
static constexpr size_t kMemBufferSize{4 * 1024 * 1024};
|
||||
extern const char *strerror_tl(int savedErrno);
|
||||
} // namespace trantor
|
||||
|
||||
using namespace trantor;
|
||||
|
||||
AsyncFileLogger::AsyncFileLogger()
|
||||
: logBufferPtr_(new std::string), nextBufferPtr_(new std::string)
|
||||
{
|
||||
logBufferPtr_->reserve(kMemBufferSize);
|
||||
nextBufferPtr_->reserve(kMemBufferSize);
|
||||
}
|
||||
|
||||
AsyncFileLogger::~AsyncFileLogger()
|
||||
{
|
||||
// std::cout << "~AsyncFileLogger" << std::endl;
|
||||
stopFlag_ = true;
|
||||
if (threadPtr_)
|
||||
{
|
||||
cond_.notify_all();
|
||||
threadPtr_->join();
|
||||
}
|
||||
// std::cout << "thread exit" << std::endl;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard_(mutex_);
|
||||
if (logBufferPtr_->length() > 0)
|
||||
{
|
||||
writeBuffers_.push(logBufferPtr_);
|
||||
}
|
||||
while (!writeBuffers_.empty())
|
||||
{
|
||||
StringPtr tmpPtr = (StringPtr &&) writeBuffers_.front();
|
||||
writeBuffers_.pop();
|
||||
writeLogToFile(tmpPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncFileLogger::output(const char *msg, const uint64_t len)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard_(mutex_);
|
||||
if (len > kMemBufferSize)
|
||||
return;
|
||||
if (!logBufferPtr_)
|
||||
{
|
||||
logBufferPtr_ = std::make_shared<std::string>();
|
||||
logBufferPtr_->reserve(kMemBufferSize);
|
||||
}
|
||||
if (logBufferPtr_->capacity() - logBufferPtr_->length() < len)
|
||||
{
|
||||
swapBuffer();
|
||||
cond_.notify_one();
|
||||
}
|
||||
if (writeBuffers_.size() > 25) // 100M bytes logs in buffer
|
||||
{
|
||||
++lostCounter_;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lostCounter_ > 0)
|
||||
{
|
||||
char logErr[128];
|
||||
auto strlen =
|
||||
snprintf(logErr,
|
||||
sizeof(logErr),
|
||||
"%llu log information is lost\n",
|
||||
static_cast<long long unsigned int>(lostCounter_));
|
||||
lostCounter_ = 0;
|
||||
logBufferPtr_->append(logErr, strlen);
|
||||
}
|
||||
logBufferPtr_->append(msg, len);
|
||||
}
|
||||
|
||||
void AsyncFileLogger::flush()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard_(mutex_);
|
||||
if (logBufferPtr_->length() > 0)
|
||||
{
|
||||
// std::cout<<"flush log buffer
|
||||
// len:"<<logBufferPtr_->length()<<std::endl;
|
||||
swapBuffer();
|
||||
cond_.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncFileLogger::writeLogToFile(const StringPtr buf)
|
||||
{
|
||||
if (!loggerFilePtr_)
|
||||
{
|
||||
loggerFilePtr_ = std::unique_ptr<LoggerFile>(
|
||||
new LoggerFile(filePath_, fileBaseName_, fileExtName_));
|
||||
}
|
||||
loggerFilePtr_->writeLog(buf);
|
||||
if (loggerFilePtr_->getLength() > sizeLimit_)
|
||||
{
|
||||
loggerFilePtr_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncFileLogger::logThreadFunc()
|
||||
{
|
||||
#ifdef __linux__
|
||||
prctl(PR_SET_NAME, "AsyncFileLogger");
|
||||
#endif
|
||||
while (!stopFlag_)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
while (writeBuffers_.size() == 0 && !stopFlag_)
|
||||
{
|
||||
if (cond_.wait_for(lock, kLogFlushTimeout) ==
|
||||
std::cv_status::timeout)
|
||||
{
|
||||
if (logBufferPtr_->length() > 0)
|
||||
{
|
||||
swapBuffer();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
tmpBuffers_.swap(writeBuffers_);
|
||||
}
|
||||
|
||||
while (!tmpBuffers_.empty())
|
||||
{
|
||||
StringPtr tmpPtr = (StringPtr &&) tmpBuffers_.front();
|
||||
tmpBuffers_.pop();
|
||||
writeLogToFile(tmpPtr);
|
||||
tmpPtr->clear();
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
nextBufferPtr_ = tmpPtr;
|
||||
}
|
||||
}
|
||||
if (loggerFilePtr_)
|
||||
loggerFilePtr_->flush();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncFileLogger::startLogging()
|
||||
{
|
||||
threadPtr_ = std::unique_ptr<std::thread>(
|
||||
new std::thread(std::bind(&AsyncFileLogger::logThreadFunc, this)));
|
||||
}
|
||||
|
||||
AsyncFileLogger::LoggerFile::LoggerFile(const std::string &filePath,
|
||||
const std::string &fileBaseName,
|
||||
const std::string &fileExtName)
|
||||
: creationDate_(Date::date()),
|
||||
filePath_(filePath),
|
||||
fileBaseName_(fileBaseName),
|
||||
fileExtName_(fileExtName)
|
||||
{
|
||||
fileFullName_ = filePath + fileBaseName + fileExtName;
|
||||
#ifndef _MSC_VER
|
||||
fp_ = fopen(fileFullName_.c_str(), "a");
|
||||
#else
|
||||
fp_ = _fsopen(fileFullName_.c_str(), "a+", _SH_DENYWR);
|
||||
#endif
|
||||
if (fp_ == nullptr)
|
||||
{
|
||||
std::cout << strerror_tl(errno) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t AsyncFileLogger::LoggerFile::fileSeq_{0};
|
||||
void AsyncFileLogger::LoggerFile::writeLog(const StringPtr buf)
|
||||
{
|
||||
if (fp_)
|
||||
{
|
||||
// std::cout<<"write "<<buf->length()<<" bytes to file"<<std::endl;
|
||||
fwrite(buf->c_str(), 1, buf->length(), fp_);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncFileLogger::LoggerFile::flush()
|
||||
{
|
||||
if (fp_)
|
||||
{
|
||||
fflush(fp_);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t AsyncFileLogger::LoggerFile::getLength()
|
||||
{
|
||||
if (fp_)
|
||||
return ftell(fp_);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AsyncFileLogger::LoggerFile::~LoggerFile()
|
||||
{
|
||||
if (fp_)
|
||||
{
|
||||
fclose(fp_);
|
||||
char seq[12];
|
||||
snprintf(seq,
|
||||
sizeof(seq),
|
||||
".%06llu",
|
||||
static_cast<long long unsigned int>(fileSeq_ % 1000000));
|
||||
++fileSeq_;
|
||||
std::string newName =
|
||||
filePath_ + fileBaseName_ + "." +
|
||||
creationDate_.toCustomedFormattedString("%y%m%d-%H%M%S") +
|
||||
std::string(seq) + fileExtName_;
|
||||
rename(fileFullName_.c_str(), newName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncFileLogger::swapBuffer()
|
||||
{
|
||||
writeBuffers_.push(logBufferPtr_);
|
||||
if (nextBufferPtr_)
|
||||
{
|
||||
logBufferPtr_ = nextBufferPtr_;
|
||||
nextBufferPtr_.reset();
|
||||
logBufferPtr_->clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
logBufferPtr_ = std::make_shared<std::string>();
|
||||
logBufferPtr_->reserve(kMemBufferSize);
|
||||
}
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file AsyncFileLogger.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/utils/Date.h>
|
||||
#include <trantor/exports.h>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <condition_variable>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
using StringPtr = std::shared_ptr<std::string>;
|
||||
using StringPtrQueue = std::queue<StringPtr>;
|
||||
|
||||
/**
|
||||
* @brief This class implements utility functions for writing logs to files
|
||||
* asynchronously.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT AsyncFileLogger : NonCopyable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Write the message to the log file.
|
||||
*
|
||||
* @param msg
|
||||
* @param len
|
||||
*/
|
||||
void output(const char *msg, const uint64_t len);
|
||||
|
||||
/**
|
||||
* @brief Flush data from memory buffer to the log file.
|
||||
*
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/**
|
||||
* @brief Start writing log files.
|
||||
*
|
||||
*/
|
||||
void startLogging();
|
||||
|
||||
/**
|
||||
* @brief Set the size limit of log files. When the log file size reaches
|
||||
* the limit, the log file is switched.
|
||||
*
|
||||
* @param limit
|
||||
*/
|
||||
void setFileSizeLimit(uint64_t limit)
|
||||
{
|
||||
sizeLimit_ = limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the log file name.
|
||||
*
|
||||
* @param baseName The base name of the log file.
|
||||
* @param extName The extended name of the log file.
|
||||
* @param path The location where the log file is stored.
|
||||
*/
|
||||
void setFileName(const std::string &baseName,
|
||||
const std::string &extName = ".log",
|
||||
const std::string &path = "./")
|
||||
{
|
||||
fileBaseName_ = baseName;
|
||||
extName[0] == '.' ? fileExtName_ = extName
|
||||
: fileExtName_ = std::string(".") + extName;
|
||||
filePath_ = path;
|
||||
if (filePath_.length() == 0)
|
||||
filePath_ = "./";
|
||||
if (filePath_[filePath_.length() - 1] != '/')
|
||||
filePath_ = filePath_ + "/";
|
||||
}
|
||||
~AsyncFileLogger();
|
||||
AsyncFileLogger();
|
||||
|
||||
protected:
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cond_;
|
||||
StringPtr logBufferPtr_;
|
||||
StringPtr nextBufferPtr_;
|
||||
StringPtrQueue writeBuffers_;
|
||||
StringPtrQueue tmpBuffers_;
|
||||
void writeLogToFile(const StringPtr buf);
|
||||
std::unique_ptr<std::thread> threadPtr_;
|
||||
bool stopFlag_{false};
|
||||
void logThreadFunc();
|
||||
std::string filePath_{"./"};
|
||||
std::string fileBaseName_{"trantor"};
|
||||
std::string fileExtName_{".log"};
|
||||
uint64_t sizeLimit_{20 * 1024 * 1024};
|
||||
class LoggerFile : NonCopyable
|
||||
{
|
||||
public:
|
||||
LoggerFile(const std::string &filePath,
|
||||
const std::string &fileBaseName,
|
||||
const std::string &fileExtName);
|
||||
~LoggerFile();
|
||||
void writeLog(const StringPtr buf);
|
||||
uint64_t getLength();
|
||||
explicit operator bool() const
|
||||
{
|
||||
return fp_ != nullptr;
|
||||
}
|
||||
void flush();
|
||||
|
||||
protected:
|
||||
FILE *fp_{nullptr};
|
||||
Date creationDate_;
|
||||
std::string fileFullName_;
|
||||
std::string filePath_;
|
||||
std::string fileBaseName_;
|
||||
std::string fileExtName_;
|
||||
static uint64_t fileSeq_;
|
||||
};
|
||||
std::unique_ptr<LoggerFile> loggerFilePtr_;
|
||||
|
||||
uint64_t lostCounter_{0};
|
||||
void swapBuffer();
|
||||
};
|
||||
|
||||
} // namespace trantor
|
@ -1,95 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* ConcurrentTaskQueue.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <trantor/utils/ConcurrentTaskQueue.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <assert.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
using namespace trantor;
|
||||
ConcurrentTaskQueue::ConcurrentTaskQueue(size_t threadNum,
|
||||
const std::string &name)
|
||||
: queueCount_(threadNum), queueName_(name), stop_(false)
|
||||
{
|
||||
assert(threadNum > 0);
|
||||
for (unsigned int i = 0; i < queueCount_; ++i)
|
||||
{
|
||||
threads_.push_back(
|
||||
std::thread(std::bind(&ConcurrentTaskQueue::queueFunc, this, i)));
|
||||
}
|
||||
}
|
||||
void ConcurrentTaskQueue::runTaskInQueue(const std::function<void()> &task)
|
||||
{
|
||||
LOG_TRACE << "copy task into queue";
|
||||
std::lock_guard<std::mutex> lock(taskMutex_);
|
||||
taskQueue_.push(task);
|
||||
taskCond_.notify_one();
|
||||
}
|
||||
void ConcurrentTaskQueue::runTaskInQueue(std::function<void()> &&task)
|
||||
{
|
||||
LOG_TRACE << "move task into queue";
|
||||
std::lock_guard<std::mutex> lock(taskMutex_);
|
||||
taskQueue_.push(std::move(task));
|
||||
taskCond_.notify_one();
|
||||
}
|
||||
void ConcurrentTaskQueue::queueFunc(int queueNum)
|
||||
{
|
||||
char tmpName[32];
|
||||
snprintf(tmpName, sizeof(tmpName), "%s%d", queueName_.c_str(), queueNum);
|
||||
#ifdef __linux__
|
||||
::prctl(PR_SET_NAME, tmpName);
|
||||
#endif
|
||||
while (!stop_)
|
||||
{
|
||||
std::function<void()> r;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(taskMutex_);
|
||||
while (!stop_ && taskQueue_.size() == 0)
|
||||
{
|
||||
taskCond_.wait(lock);
|
||||
}
|
||||
if (taskQueue_.size() > 0)
|
||||
{
|
||||
LOG_TRACE << "got a new task!";
|
||||
r = std::move(taskQueue_.front());
|
||||
taskQueue_.pop();
|
||||
}
|
||||
else
|
||||
continue;
|
||||
}
|
||||
r();
|
||||
}
|
||||
}
|
||||
|
||||
size_t ConcurrentTaskQueue::getTaskCount()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(taskMutex_);
|
||||
return taskQueue_.size();
|
||||
}
|
||||
|
||||
void ConcurrentTaskQueue::stop()
|
||||
{
|
||||
if (!stop_)
|
||||
{
|
||||
stop_ = true;
|
||||
taskCond_.notify_all();
|
||||
for (auto &t : threads_)
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
ConcurrentTaskQueue::~ConcurrentTaskQueue()
|
||||
{
|
||||
stop();
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file ConcurrentTaskQueue.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/utils/TaskQueue.h>
|
||||
#include <trantor/exports.h>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
/**
|
||||
* @brief This class implements a task queue running in parallel. Basically this
|
||||
* can be called a threads pool.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT ConcurrentTaskQueue : public TaskQueue
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new concurrent task queue instance.
|
||||
*
|
||||
* @param threadNum The number of threads in the queue.
|
||||
* @param name The name of the queue.
|
||||
*/
|
||||
ConcurrentTaskQueue(size_t threadNum, const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Run a task in the queue.
|
||||
*
|
||||
* @param task
|
||||
*/
|
||||
virtual void runTaskInQueue(const std::function<void()> &task);
|
||||
virtual void runTaskInQueue(std::function<void()> &&task);
|
||||
|
||||
/**
|
||||
* @brief Get the name of the queue.
|
||||
*
|
||||
* @return std::string
|
||||
*/
|
||||
virtual std::string getName() const
|
||||
{
|
||||
return queueName_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get the number of tasks to be executed in the queue.
|
||||
*
|
||||
* @return size_t
|
||||
*/
|
||||
size_t getTaskCount();
|
||||
|
||||
/**
|
||||
* @brief Stop all threads in the queue.
|
||||
*
|
||||
*/
|
||||
void stop();
|
||||
|
||||
~ConcurrentTaskQueue();
|
||||
|
||||
private:
|
||||
size_t queueCount_;
|
||||
std::string queueName_;
|
||||
|
||||
std::queue<std::function<void()>> taskQueue_;
|
||||
std::vector<std::thread> threads_;
|
||||
|
||||
std::mutex taskMutex_;
|
||||
std::condition_variable taskCond_;
|
||||
std::atomic_bool stop_;
|
||||
void queueFunc(int queueNum);
|
||||
};
|
||||
|
||||
} // namespace trantor
|
@ -1,359 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Date.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Date.h"
|
||||
#include "Funcs.h"
|
||||
#ifndef _WIN32
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
#include <WinSock2.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int gettimeofday(timeval *tp, void *tzp)
|
||||
{
|
||||
time_t clock;
|
||||
struct tm tm;
|
||||
SYSTEMTIME wtm;
|
||||
|
||||
GetLocalTime(&wtm);
|
||||
tm.tm_year = wtm.wYear - 1900;
|
||||
tm.tm_mon = wtm.wMonth - 1;
|
||||
tm.tm_mday = wtm.wDay;
|
||||
tm.tm_hour = wtm.wHour;
|
||||
tm.tm_min = wtm.wMinute;
|
||||
tm.tm_sec = wtm.wSecond;
|
||||
tm.tm_isdst = -1;
|
||||
clock = mktime(&tm);
|
||||
tp->tv_sec = static_cast<long>(clock);
|
||||
tp->tv_usec = wtm.wMilliseconds * 1000;
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
const Date Date::date()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
int64_t seconds = tv.tv_sec;
|
||||
return Date(seconds * MICRO_SECONDS_PRE_SEC + tv.tv_usec);
|
||||
#else
|
||||
timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
int64_t seconds = tv.tv_sec;
|
||||
return Date(seconds * MICRO_SECONDS_PRE_SEC + tv.tv_usec);
|
||||
#endif
|
||||
}
|
||||
const Date Date::after(double second) const
|
||||
{
|
||||
return Date(static_cast<int64_t>(microSecondsSinceEpoch_ +
|
||||
second * MICRO_SECONDS_PRE_SEC));
|
||||
}
|
||||
const Date Date::roundSecond() const
|
||||
{
|
||||
return Date(microSecondsSinceEpoch_ -
|
||||
(microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC));
|
||||
}
|
||||
const Date Date::roundDay() const
|
||||
{
|
||||
struct tm t;
|
||||
time_t seconds =
|
||||
static_cast<time_t>(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC);
|
||||
#ifndef _WIN32
|
||||
localtime_r(&seconds, &t);
|
||||
#else
|
||||
localtime_s(&t, &seconds);
|
||||
#endif
|
||||
t.tm_hour = 0;
|
||||
t.tm_min = 0;
|
||||
t.tm_sec = 0;
|
||||
return Date(mktime(&t) * MICRO_SECONDS_PRE_SEC);
|
||||
}
|
||||
struct tm Date::tmStruct() const
|
||||
{
|
||||
time_t seconds =
|
||||
static_cast<time_t>(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC);
|
||||
struct tm tm_time;
|
||||
#ifndef _WIN32
|
||||
gmtime_r(&seconds, &tm_time);
|
||||
#else
|
||||
gmtime_s(&tm_time, &seconds);
|
||||
#endif
|
||||
return tm_time;
|
||||
}
|
||||
std::string Date::toFormattedString(bool showMicroseconds) const
|
||||
{
|
||||
// std::cout<<"toFormattedString"<<std::endl;
|
||||
char buf[128] = {0};
|
||||
time_t seconds =
|
||||
static_cast<time_t>(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC);
|
||||
struct tm tm_time;
|
||||
#ifndef _WIN32
|
||||
gmtime_r(&seconds, &tm_time);
|
||||
#else
|
||||
gmtime_s(&tm_time, &seconds);
|
||||
#endif
|
||||
|
||||
if (showMicroseconds)
|
||||
{
|
||||
int microseconds =
|
||||
static_cast<int>(microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC);
|
||||
snprintf(buf,
|
||||
sizeof(buf),
|
||||
"%4d%02d%02d %02d:%02d:%02d.%06d",
|
||||
tm_time.tm_year + 1900,
|
||||
tm_time.tm_mon + 1,
|
||||
tm_time.tm_mday,
|
||||
tm_time.tm_hour,
|
||||
tm_time.tm_min,
|
||||
tm_time.tm_sec,
|
||||
microseconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buf,
|
||||
sizeof(buf),
|
||||
"%4d%02d%02d %02d:%02d:%02d",
|
||||
tm_time.tm_year + 1900,
|
||||
tm_time.tm_mon + 1,
|
||||
tm_time.tm_mday,
|
||||
tm_time.tm_hour,
|
||||
tm_time.tm_min,
|
||||
tm_time.tm_sec);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
std::string Date::toCustomedFormattedString(const std::string &fmtStr,
|
||||
bool showMicroseconds) const
|
||||
{
|
||||
char buf[256] = {0};
|
||||
time_t seconds =
|
||||
static_cast<time_t>(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC);
|
||||
struct tm tm_time;
|
||||
#ifndef _WIN32
|
||||
gmtime_r(&seconds, &tm_time);
|
||||
#else
|
||||
gmtime_s(&tm_time, &seconds);
|
||||
#endif
|
||||
strftime(buf, sizeof(buf), fmtStr.c_str(), &tm_time);
|
||||
if (!showMicroseconds)
|
||||
return std::string(buf);
|
||||
char decimals[12] = {0};
|
||||
int microseconds =
|
||||
static_cast<int>(microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC);
|
||||
snprintf(decimals, sizeof(decimals), ".%06d", microseconds);
|
||||
return std::string(buf) + decimals;
|
||||
}
|
||||
void Date::toCustomedFormattedString(const std::string &fmtStr,
|
||||
char *str,
|
||||
size_t len) const
|
||||
{
|
||||
// not safe
|
||||
time_t seconds =
|
||||
static_cast<time_t>(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC);
|
||||
struct tm tm_time;
|
||||
#ifndef _WIN32
|
||||
gmtime_r(&seconds, &tm_time);
|
||||
#else
|
||||
gmtime_s(&tm_time, &seconds);
|
||||
#endif
|
||||
strftime(str, len, fmtStr.c_str(), &tm_time);
|
||||
}
|
||||
std::string Date::toFormattedStringLocal(bool showMicroseconds) const
|
||||
{
|
||||
// std::cout<<"toFormattedString"<<std::endl;
|
||||
char buf[128] = {0};
|
||||
time_t seconds =
|
||||
static_cast<time_t>(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC);
|
||||
struct tm tm_time;
|
||||
#ifndef _WIN32
|
||||
localtime_r(&seconds, &tm_time);
|
||||
#else
|
||||
localtime_s(&tm_time, &seconds);
|
||||
#endif
|
||||
|
||||
if (showMicroseconds)
|
||||
{
|
||||
int microseconds =
|
||||
static_cast<int>(microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC);
|
||||
snprintf(buf,
|
||||
sizeof(buf),
|
||||
"%4d%02d%02d %02d:%02d:%02d.%06d",
|
||||
tm_time.tm_year + 1900,
|
||||
tm_time.tm_mon + 1,
|
||||
tm_time.tm_mday,
|
||||
tm_time.tm_hour,
|
||||
tm_time.tm_min,
|
||||
tm_time.tm_sec,
|
||||
microseconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buf,
|
||||
sizeof(buf),
|
||||
"%4d%02d%02d %02d:%02d:%02d",
|
||||
tm_time.tm_year + 1900,
|
||||
tm_time.tm_mon + 1,
|
||||
tm_time.tm_mday,
|
||||
tm_time.tm_hour,
|
||||
tm_time.tm_min,
|
||||
tm_time.tm_sec);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
std::string Date::toDbStringLocal() const
|
||||
{
|
||||
char buf[128] = {0};
|
||||
time_t seconds =
|
||||
static_cast<time_t>(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC);
|
||||
struct tm tm_time;
|
||||
#ifndef _WIN32
|
||||
localtime_r(&seconds, &tm_time);
|
||||
#else
|
||||
localtime_s(&tm_time, &seconds);
|
||||
#endif
|
||||
bool showMicroseconds =
|
||||
(microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC != 0);
|
||||
if (showMicroseconds)
|
||||
{
|
||||
int microseconds =
|
||||
static_cast<int>(microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC);
|
||||
snprintf(buf,
|
||||
sizeof(buf),
|
||||
"%4d-%02d-%02d %02d:%02d:%02d.%06d",
|
||||
tm_time.tm_year + 1900,
|
||||
tm_time.tm_mon + 1,
|
||||
tm_time.tm_mday,
|
||||
tm_time.tm_hour,
|
||||
tm_time.tm_min,
|
||||
tm_time.tm_sec,
|
||||
microseconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*this == roundDay())
|
||||
{
|
||||
snprintf(buf,
|
||||
sizeof(buf),
|
||||
"%4d-%02d-%02d",
|
||||
tm_time.tm_year + 1900,
|
||||
tm_time.tm_mon + 1,
|
||||
tm_time.tm_mday);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buf,
|
||||
sizeof(buf),
|
||||
"%4d-%02d-%02d %02d:%02d:%02d",
|
||||
tm_time.tm_year + 1900,
|
||||
tm_time.tm_mon + 1,
|
||||
tm_time.tm_mday,
|
||||
tm_time.tm_hour,
|
||||
tm_time.tm_min,
|
||||
tm_time.tm_sec);
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
Date Date::fromDbStringLocal(const std::string &datetime)
|
||||
{
|
||||
unsigned int year = {0}, month = {0}, day = {0}, hour = {0}, minute = {0},
|
||||
second = {0}, microSecond = {0};
|
||||
std::vector<std::string> &&v = splitString(datetime, " ");
|
||||
if (2 == v.size())
|
||||
{
|
||||
// date
|
||||
std::vector<std::string> date = splitString(v[0], "-");
|
||||
if (3 == date.size())
|
||||
{
|
||||
year = std::stol(date[0]);
|
||||
month = std::stol(date[1]);
|
||||
day = std::stol(date[2]);
|
||||
std::vector<std::string> time = splitString(v[1], ":");
|
||||
if (2 < time.size())
|
||||
{
|
||||
hour = std::stol(time[0]);
|
||||
minute = std::stol(time[1]);
|
||||
auto seconds = splitString(time[2], ".");
|
||||
second = std::stol(seconds[0]);
|
||||
if (1 < seconds.size())
|
||||
{
|
||||
if (seconds[1].length() > 6)
|
||||
{
|
||||
seconds[1].resize(6);
|
||||
}
|
||||
else if (seconds[1].length() < 6)
|
||||
{
|
||||
seconds[1].append(6 - seconds[1].length(), '0');
|
||||
}
|
||||
microSecond = std::stol(seconds[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::move(
|
||||
trantor::Date(year, month, day, hour, minute, second, microSecond));
|
||||
}
|
||||
std::string Date::toCustomedFormattedStringLocal(const std::string &fmtStr,
|
||||
bool showMicroseconds) const
|
||||
{
|
||||
char buf[256] = {0};
|
||||
time_t seconds =
|
||||
static_cast<time_t>(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC);
|
||||
struct tm tm_time;
|
||||
#ifndef _WIN32
|
||||
localtime_r(&seconds, &tm_time);
|
||||
#else
|
||||
localtime_s(&tm_time, &seconds);
|
||||
#endif
|
||||
strftime(buf, sizeof(buf), fmtStr.c_str(), &tm_time);
|
||||
if (!showMicroseconds)
|
||||
return std::string(buf);
|
||||
char decimals[12] = {0};
|
||||
int microseconds =
|
||||
static_cast<int>(microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC);
|
||||
snprintf(decimals, sizeof(decimals), ".%06d", microseconds);
|
||||
return std::string(buf) + decimals;
|
||||
}
|
||||
Date::Date(unsigned int year,
|
||||
unsigned int month,
|
||||
unsigned int day,
|
||||
unsigned int hour,
|
||||
unsigned int minute,
|
||||
unsigned int second,
|
||||
unsigned int microSecond)
|
||||
{
|
||||
struct tm tm;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
tm.tm_isdst = -1;
|
||||
time_t epoch;
|
||||
tm.tm_year = year - 1900;
|
||||
tm.tm_mon = month - 1;
|
||||
tm.tm_mday = day;
|
||||
tm.tm_hour = hour;
|
||||
tm.tm_min = minute;
|
||||
tm.tm_sec = second;
|
||||
epoch = mktime(&tm);
|
||||
microSecondsSinceEpoch_ = epoch * MICRO_SECONDS_PRE_SEC + microSecond;
|
||||
}
|
||||
|
||||
} // namespace trantor
|
@ -1,281 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file Date.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/exports.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
#define MICRO_SECONDS_PRE_SEC 1000000
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
/**
|
||||
* @brief This class represents a time point.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT Date
|
||||
{
|
||||
public:
|
||||
Date() : microSecondsSinceEpoch_(0){};
|
||||
|
||||
/**
|
||||
* @brief Construct a new Date instance.
|
||||
*
|
||||
* @param microSec The microseconds from 1970-01-01 00:00:00.
|
||||
*/
|
||||
explicit Date(int64_t microSec) : microSecondsSinceEpoch_(microSec){};
|
||||
|
||||
/**
|
||||
* @brief Construct a new Date instance.
|
||||
*
|
||||
* @param year
|
||||
* @param month
|
||||
* @param day
|
||||
* @param hour
|
||||
* @param minute
|
||||
* @param second
|
||||
* @param microSecond
|
||||
*/
|
||||
Date(unsigned int year,
|
||||
unsigned int month,
|
||||
unsigned int day,
|
||||
unsigned int hour = 0,
|
||||
unsigned int minute = 0,
|
||||
unsigned int second = 0,
|
||||
unsigned int microSecond = 0);
|
||||
|
||||
/**
|
||||
* @brief Create a Date object that represents the current time.
|
||||
*
|
||||
* @return const Date
|
||||
*/
|
||||
static const Date date();
|
||||
|
||||
/**
|
||||
* @brief Same as the date() method.
|
||||
*
|
||||
* @return const Date
|
||||
*/
|
||||
static const Date now()
|
||||
{
|
||||
return Date::date();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return a new Date instance that represents the time after some
|
||||
* seconds from *this.
|
||||
*
|
||||
* @param second
|
||||
* @return const Date
|
||||
*/
|
||||
const Date after(double second) const;
|
||||
|
||||
/**
|
||||
* @brief Return a new Date instance that equals to *this, but with zero
|
||||
* microseconds.
|
||||
*
|
||||
* @return const Date
|
||||
*/
|
||||
const Date roundSecond() const;
|
||||
|
||||
/// Create a Date object equal to * this, but numbers of hours, minutes,
|
||||
/// seconds and microseconds are zero.
|
||||
|
||||
/**
|
||||
* @brief Return a new Date instance that equals to * this, but with zero
|
||||
* hours, minutes, seconds and microseconds.
|
||||
*
|
||||
* @return const Date
|
||||
*/
|
||||
const Date roundDay() const;
|
||||
|
||||
~Date(){};
|
||||
|
||||
/**
|
||||
* @brief Return true if the time point is equal to another.
|
||||
*
|
||||
*/
|
||||
bool operator==(const Date &date) const
|
||||
{
|
||||
return microSecondsSinceEpoch_ == date.microSecondsSinceEpoch_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return true if the time point is not equal to another.
|
||||
*
|
||||
*/
|
||||
bool operator!=(const Date &date) const
|
||||
{
|
||||
return microSecondsSinceEpoch_ != date.microSecondsSinceEpoch_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return true if the time point is earlier than another.
|
||||
*
|
||||
*/
|
||||
bool operator<(const Date &date) const
|
||||
{
|
||||
return microSecondsSinceEpoch_ < date.microSecondsSinceEpoch_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return true if the time point is later than another.
|
||||
*
|
||||
*/
|
||||
bool operator>(const Date &date) const
|
||||
{
|
||||
return microSecondsSinceEpoch_ > date.microSecondsSinceEpoch_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return true if the time point is not earlier than another.
|
||||
*
|
||||
*/
|
||||
bool operator>=(const Date &date) const
|
||||
{
|
||||
return microSecondsSinceEpoch_ >= date.microSecondsSinceEpoch_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return true if the time point is not later than another.
|
||||
*
|
||||
*/
|
||||
bool operator<=(const Date &date) const
|
||||
{
|
||||
return microSecondsSinceEpoch_ <= date.microSecondsSinceEpoch_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of milliseconds since 1970-01-01 00:00.
|
||||
*
|
||||
* @return int64_t
|
||||
*/
|
||||
int64_t microSecondsSinceEpoch() const
|
||||
{
|
||||
return microSecondsSinceEpoch_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of seconds since 1970-01-01 00:00.
|
||||
*
|
||||
* @return int64_t
|
||||
*/
|
||||
int64_t secondsSinceEpoch() const
|
||||
{
|
||||
return microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the tm struct for the time point.
|
||||
*
|
||||
* @return struct tm
|
||||
*/
|
||||
struct tm tmStruct() const;
|
||||
|
||||
/**
|
||||
* @brief Generate a UTC time string
|
||||
* @example:
|
||||
* 20180101 10:10:25 //If the @param showMicroseconds is false
|
||||
* 20180101 10:10:25:102414 //If the @param showMicroseconds is true
|
||||
*/
|
||||
std::string toFormattedString(bool showMicroseconds) const;
|
||||
|
||||
/**
|
||||
* @brief Generate a UTC time string formated by the @param fmtStr
|
||||
* The @param fmtStr is the format string for the function strftime()
|
||||
* @example:
|
||||
* 2018-01-01 10:10:25 //If the @param fmtStr is "%Y-%m-%d
|
||||
* %H:%M:%S" and the @param showMicroseconds is false 2018-01-01
|
||||
* 10:10:25:102414 //If the @param fmtStr is "%Y-%m-%d %H:%M:%S" and the
|
||||
* @param showMicroseconds is true
|
||||
*/
|
||||
std::string toCustomedFormattedString(const std::string &fmtStr,
|
||||
bool showMicroseconds = false) const;
|
||||
|
||||
/**
|
||||
* @brief Generate a local time zone string, the format of the string is
|
||||
* same as the mothed toFormattedString
|
||||
*
|
||||
* @param showMicroseconds
|
||||
* @return std::string
|
||||
*/
|
||||
std::string toFormattedStringLocal(bool showMicroseconds) const;
|
||||
|
||||
/**
|
||||
* @brief Generate a local time zone string formated by the @param fmtStr
|
||||
*
|
||||
* @param fmtStr
|
||||
* @param showMicroseconds
|
||||
* @return std::string
|
||||
*/
|
||||
std::string toCustomedFormattedStringLocal(
|
||||
const std::string &fmtStr,
|
||||
bool showMicroseconds = false) const;
|
||||
|
||||
/**
|
||||
* @brief Generate a local time zone string for database.
|
||||
* @example:
|
||||
* 2018-01-01 //If hours, minutes, seconds and
|
||||
* microseconds are zero 2018-01-01 10:10:25 //If the microsecond
|
||||
* is zero 2018-01-01 10:10:25:102414 //If the microsecond is not zero
|
||||
*/
|
||||
std::string toDbStringLocal() const;
|
||||
|
||||
/**
|
||||
* @brief From DB string to trantor local time zone.
|
||||
*
|
||||
* Inverse of toDbStringLocal()
|
||||
*/
|
||||
static Date fromDbStringLocal(const std::string &datetime);
|
||||
|
||||
/**
|
||||
* @brief Generate a UTC time string.
|
||||
*
|
||||
* @param fmtStr The format string.
|
||||
* @param str The string buffer for the generated time string.
|
||||
* @param len The length of the string buffer.
|
||||
*/
|
||||
void toCustomedFormattedString(const std::string &fmtStr,
|
||||
char *str,
|
||||
size_t len) const; // UTC
|
||||
|
||||
/**
|
||||
* @brief Return true if the time point is in a same second as another.
|
||||
*
|
||||
* @param date
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isSameSecond(const Date &date) const
|
||||
{
|
||||
return microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC ==
|
||||
date.microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swap the time point with another.
|
||||
*
|
||||
* @param that
|
||||
*/
|
||||
void swap(Date &that)
|
||||
{
|
||||
std::swap(microSecondsSinceEpoch_, that.microSecondsSinceEpoch_);
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t microSecondsSinceEpoch_{0};
|
||||
};
|
||||
} // namespace trantor
|
@ -1,53 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Funcs.h
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
namespace trantor
|
||||
{
|
||||
inline uint64_t hton64(uint64_t n)
|
||||
{
|
||||
static const int one = 1;
|
||||
static const char sig = *(char *)&one;
|
||||
if (sig == 0)
|
||||
return n; // for big endian machine just return the input
|
||||
char *ptr = reinterpret_cast<char *>(&n);
|
||||
std::reverse(ptr, ptr + sizeof(uint64_t));
|
||||
return n;
|
||||
}
|
||||
inline uint64_t ntoh64(uint64_t n)
|
||||
{
|
||||
return hton64(n);
|
||||
}
|
||||
inline std::vector<std::string> splitString(const std::string &s,
|
||||
const std::string &delimiter,
|
||||
bool acceptEmptyString = false)
|
||||
{
|
||||
if (delimiter.empty())
|
||||
return std::vector<std::string>{};
|
||||
std::vector<std::string> v;
|
||||
size_t last = 0;
|
||||
size_t next = 0;
|
||||
while ((next = s.find(delimiter, last)) != std::string::npos)
|
||||
{
|
||||
if (next > last || acceptEmptyString)
|
||||
v.push_back(s.substr(last, next - last));
|
||||
last = next + delimiter.length();
|
||||
}
|
||||
if (s.length() > last || acceptEmptyString)
|
||||
v.push_back(s.substr(last));
|
||||
return v;
|
||||
}
|
||||
} // namespace trantor
|
@ -1,114 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file LockFreeQueue.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <atomic>
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
#include <assert.h>
|
||||
namespace trantor
|
||||
{
|
||||
/**
|
||||
* @brief This class template represents a lock-free multiple producers single
|
||||
* consumer queue
|
||||
*
|
||||
* @tparam T The type of the items in the queue.
|
||||
*/
|
||||
template <typename T>
|
||||
class MpscQueue : public NonCopyable
|
||||
{
|
||||
public:
|
||||
MpscQueue()
|
||||
: head_(new BufferNode), tail_(head_.load(std::memory_order_relaxed))
|
||||
{
|
||||
}
|
||||
~MpscQueue()
|
||||
{
|
||||
T output;
|
||||
while (this->dequeue(output))
|
||||
{
|
||||
}
|
||||
BufferNode *front = head_.load(std::memory_order_relaxed);
|
||||
delete front;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put a item into the queue.
|
||||
*
|
||||
* @param input
|
||||
* @note This method can be called in multiple threads.
|
||||
*/
|
||||
void enqueue(T &&input)
|
||||
{
|
||||
BufferNode *node{new BufferNode(std::move(input))};
|
||||
BufferNode *prevhead{head_.exchange(node, std::memory_order_acq_rel)};
|
||||
prevhead->next_.store(node, std::memory_order_release);
|
||||
}
|
||||
void enqueue(const T &input)
|
||||
{
|
||||
BufferNode *node{new BufferNode(input)};
|
||||
BufferNode *prevhead{head_.exchange(node, std::memory_order_acq_rel)};
|
||||
prevhead->next_.store(node, std::memory_order_release);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a item from the queue.
|
||||
*
|
||||
* @param output
|
||||
* @return false if the queue is empty.
|
||||
* @note This method must be called in a single thread.
|
||||
*/
|
||||
bool dequeue(T &output)
|
||||
{
|
||||
BufferNode *tail = tail_.load(std::memory_order_relaxed);
|
||||
BufferNode *next = tail->next_.load(std::memory_order_acquire);
|
||||
|
||||
if (next == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
output = std::move(*(next->dataPtr_));
|
||||
delete next->dataPtr_;
|
||||
tail_.store(next, std::memory_order_release);
|
||||
delete tail;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool empty()
|
||||
{
|
||||
BufferNode *tail = tail_.load(std::memory_order_relaxed);
|
||||
BufferNode *next = tail->next_.load(std::memory_order_acquire);
|
||||
return next == nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
struct BufferNode
|
||||
{
|
||||
BufferNode() = default;
|
||||
BufferNode(const T &data) : dataPtr_(new T(data))
|
||||
{
|
||||
}
|
||||
BufferNode(T &&data) : dataPtr_(new T(std::move(data)))
|
||||
{
|
||||
}
|
||||
T *dataPtr_;
|
||||
std::atomic<BufferNode *> next_{nullptr};
|
||||
};
|
||||
|
||||
std::atomic<BufferNode *> head_;
|
||||
std::atomic<BufferNode *> tail_;
|
||||
};
|
||||
|
||||
} // namespace trantor
|
@ -1,273 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* LogStream.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
// taken from muduo lib
|
||||
|
||||
#include <trantor/utils/LogStream.h>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace trantor;
|
||||
using namespace trantor::detail;
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
const char digits[] = "9876543210123456789";
|
||||
const char *zero = digits + 9;
|
||||
|
||||
const char digitsHex[] = "0123456789ABCDEF";
|
||||
|
||||
// Efficient Integer to String Conversions, by Matthew Wilson.
|
||||
template <typename T>
|
||||
size_t convert(char buf[], T value)
|
||||
{
|
||||
T i = value;
|
||||
char *p = buf;
|
||||
|
||||
do
|
||||
{
|
||||
int lsd = static_cast<int>(i % 10);
|
||||
i /= 10;
|
||||
*p++ = zero[lsd];
|
||||
} while (i != 0);
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
*p++ = '-';
|
||||
}
|
||||
*p = '\0';
|
||||
std::reverse(buf, p);
|
||||
|
||||
return p - buf;
|
||||
}
|
||||
|
||||
size_t convertHex(char buf[], uintptr_t value)
|
||||
{
|
||||
uintptr_t i = value;
|
||||
char *p = buf;
|
||||
|
||||
do
|
||||
{
|
||||
int lsd = static_cast<int>(i % 16);
|
||||
i /= 16;
|
||||
*p++ = digitsHex[lsd];
|
||||
} while (i != 0);
|
||||
|
||||
*p = '\0';
|
||||
std::reverse(buf, p);
|
||||
|
||||
return p - buf;
|
||||
}
|
||||
|
||||
template class FixedBuffer<kSmallBuffer>;
|
||||
template class FixedBuffer<kLargeBuffer>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace trantor
|
||||
|
||||
template <int SIZE>
|
||||
const char *FixedBuffer<SIZE>::debugString()
|
||||
{
|
||||
*cur_ = '\0';
|
||||
return data_;
|
||||
}
|
||||
|
||||
template <int SIZE>
|
||||
void FixedBuffer<SIZE>::cookieStart()
|
||||
{
|
||||
}
|
||||
|
||||
template <int SIZE>
|
||||
void FixedBuffer<SIZE>::cookieEnd()
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LogStream::formatInteger(T v)
|
||||
{
|
||||
constexpr static int kMaxNumericSize = std::numeric_limits<T>::digits10 + 4;
|
||||
if (exBuffer_.empty())
|
||||
{
|
||||
if (buffer_.avail() >= kMaxNumericSize)
|
||||
{
|
||||
size_t len = convert(buffer_.current(), v);
|
||||
buffer_.add(len);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
exBuffer_.append(buffer_.data(), buffer_.length());
|
||||
}
|
||||
}
|
||||
auto oldLen = exBuffer_.length();
|
||||
exBuffer_.resize(oldLen + kMaxNumericSize);
|
||||
size_t len = convert(&exBuffer_[oldLen], v);
|
||||
exBuffer_.resize(oldLen + len);
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator<<(short v)
|
||||
{
|
||||
*this << static_cast<int>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator<<(unsigned short v)
|
||||
{
|
||||
*this << static_cast<unsigned int>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator<<(int v)
|
||||
{
|
||||
formatInteger(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator<<(unsigned int v)
|
||||
{
|
||||
formatInteger(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator<<(long v)
|
||||
{
|
||||
formatInteger(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator<<(unsigned long v)
|
||||
{
|
||||
formatInteger(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator<<(const long long &v)
|
||||
{
|
||||
formatInteger(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator<<(const unsigned long long &v)
|
||||
{
|
||||
formatInteger(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator<<(const void *p)
|
||||
{
|
||||
uintptr_t v = reinterpret_cast<uintptr_t>(p);
|
||||
constexpr static int kMaxNumericSize =
|
||||
std::numeric_limits<uintptr_t>::digits / 4 + 4;
|
||||
if (exBuffer_.empty())
|
||||
{
|
||||
if (buffer_.avail() >= kMaxNumericSize)
|
||||
{
|
||||
char *buf = buffer_.current();
|
||||
buf[0] = '0';
|
||||
buf[1] = 'x';
|
||||
size_t len = convertHex(buf + 2, v);
|
||||
buffer_.add(len + 2);
|
||||
return *this;
|
||||
}
|
||||
else
|
||||
{
|
||||
exBuffer_.append(buffer_.data(), buffer_.length());
|
||||
}
|
||||
}
|
||||
auto oldLen = exBuffer_.length();
|
||||
exBuffer_.resize(oldLen + kMaxNumericSize);
|
||||
char *buf = &exBuffer_[oldLen];
|
||||
buf[0] = '0';
|
||||
buf[1] = 'x';
|
||||
size_t len = convertHex(buf + 2, v);
|
||||
exBuffer_.resize(oldLen + len + 2);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// TODO: replace this with Grisu3 by Florian Loitsch.
|
||||
LogStream &LogStream::operator<<(const double &v)
|
||||
{
|
||||
constexpr static int kMaxNumericSize = 32;
|
||||
if (exBuffer_.empty())
|
||||
{
|
||||
if (buffer_.avail() >= kMaxNumericSize)
|
||||
{
|
||||
int len = snprintf(buffer_.current(), kMaxNumericSize, "%.12g", v);
|
||||
buffer_.add(len);
|
||||
return *this;
|
||||
}
|
||||
else
|
||||
{
|
||||
exBuffer_.append(buffer_.data(), buffer_.length());
|
||||
}
|
||||
}
|
||||
auto oldLen = exBuffer_.length();
|
||||
exBuffer_.resize(oldLen + kMaxNumericSize);
|
||||
int len = snprintf(&(exBuffer_[oldLen]), kMaxNumericSize, "%.12g", v);
|
||||
exBuffer_.resize(oldLen + len);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogStream &LogStream::operator<<(const long double &v)
|
||||
{
|
||||
constexpr static int kMaxNumericSize = 48;
|
||||
if (exBuffer_.empty())
|
||||
{
|
||||
if (buffer_.avail() >= kMaxNumericSize)
|
||||
{
|
||||
int len = snprintf(buffer_.current(), kMaxNumericSize, "%.12Lg", v);
|
||||
buffer_.add(len);
|
||||
return *this;
|
||||
}
|
||||
else
|
||||
{
|
||||
exBuffer_.append(buffer_.data(), buffer_.length());
|
||||
}
|
||||
}
|
||||
auto oldLen = exBuffer_.length();
|
||||
exBuffer_.resize(oldLen + kMaxNumericSize);
|
||||
int len = snprintf(&(exBuffer_[oldLen]), kMaxNumericSize, "%.12Lg", v);
|
||||
exBuffer_.resize(oldLen + len);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Fmt::Fmt(const char *fmt, T val)
|
||||
{
|
||||
length_ = snprintf(buf_, sizeof buf_, fmt, val);
|
||||
assert(static_cast<size_t>(length_) < sizeof buf_);
|
||||
}
|
||||
|
||||
// Explicit instantiations
|
||||
|
||||
template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, char);
|
||||
|
||||
template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, short);
|
||||
template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, unsigned short);
|
||||
template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, int);
|
||||
template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, unsigned int);
|
||||
template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, long);
|
||||
template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, unsigned long);
|
||||
template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, long long);
|
||||
template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, unsigned long long);
|
||||
|
||||
template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, float);
|
||||
template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, double);
|
@ -1,277 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file LogStream.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Taken from muduo lib and modified. Classes in this file are used internally.
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/exports.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h> // memcpy
|
||||
#include <string>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
static constexpr size_t kSmallBuffer{4000};
|
||||
static constexpr size_t kLargeBuffer{4000 * 1000};
|
||||
|
||||
template <int SIZE>
|
||||
class TRANTOR_EXPORT FixedBuffer : NonCopyable
|
||||
{
|
||||
public:
|
||||
FixedBuffer() : cur_(data_)
|
||||
{
|
||||
setCookie(cookieStart);
|
||||
}
|
||||
|
||||
~FixedBuffer()
|
||||
{
|
||||
setCookie(cookieEnd);
|
||||
}
|
||||
|
||||
bool append(const char * /*restrict*/ buf, size_t len)
|
||||
{
|
||||
if ((size_t)(avail()) > len)
|
||||
{
|
||||
memcpy(cur_, buf, len);
|
||||
cur_ += len;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
int length() const
|
||||
{
|
||||
return static_cast<int>(cur_ - data_);
|
||||
}
|
||||
|
||||
// write to data_ directly
|
||||
char *current()
|
||||
{
|
||||
return cur_;
|
||||
}
|
||||
int avail() const
|
||||
{
|
||||
return static_cast<int>(end() - cur_);
|
||||
}
|
||||
void add(size_t len)
|
||||
{
|
||||
cur_ += len;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
cur_ = data_;
|
||||
}
|
||||
void zeroBuffer()
|
||||
{
|
||||
memset(data_, 0, sizeof(data_));
|
||||
}
|
||||
|
||||
// for used by GDB
|
||||
const char *debugString();
|
||||
void setCookie(void (*cookie)())
|
||||
{
|
||||
cookie_ = cookie;
|
||||
}
|
||||
// for used by unit test
|
||||
std::string toString() const
|
||||
{
|
||||
return std::string(data_, length());
|
||||
}
|
||||
// StringPiece toStringPiece() const { return StringPiece(data_, length());
|
||||
// }
|
||||
|
||||
private:
|
||||
const char *end() const
|
||||
{
|
||||
return data_ + sizeof data_;
|
||||
}
|
||||
// Must be outline function for cookies.
|
||||
static void cookieStart();
|
||||
static void cookieEnd();
|
||||
|
||||
void (*cookie_)();
|
||||
char data_[SIZE];
|
||||
char *cur_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class TRANTOR_EXPORT LogStream : NonCopyable
|
||||
{
|
||||
using self = LogStream;
|
||||
|
||||
public:
|
||||
using Buffer = detail::FixedBuffer<detail::kSmallBuffer>;
|
||||
|
||||
self &operator<<(bool v)
|
||||
{
|
||||
append(v ? "1" : "0", 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
self &operator<<(short);
|
||||
self &operator<<(unsigned short);
|
||||
self &operator<<(int);
|
||||
self &operator<<(unsigned int);
|
||||
self &operator<<(long);
|
||||
self &operator<<(unsigned long);
|
||||
self &operator<<(const long long &);
|
||||
self &operator<<(const unsigned long long &);
|
||||
|
||||
self &operator<<(const void *);
|
||||
|
||||
self &operator<<(float &v)
|
||||
{
|
||||
*this << static_cast<double>(v);
|
||||
return *this;
|
||||
}
|
||||
self &operator<<(const double &);
|
||||
self &operator<<(const long double &v);
|
||||
|
||||
self &operator<<(char v)
|
||||
{
|
||||
append(&v, 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// self& operator<<(signed char);
|
||||
// self& operator<<(unsigned char);
|
||||
template <int N>
|
||||
self &operator<<(const char (&buf)[N])
|
||||
{
|
||||
assert(strnlen(buf, N) == N - 1);
|
||||
append(buf, N - 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
self &operator<<(char *str)
|
||||
{
|
||||
if (str)
|
||||
{
|
||||
append(str, strlen(str));
|
||||
}
|
||||
else
|
||||
{
|
||||
append("(null)", 6);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
self &operator<<(const char *str)
|
||||
{
|
||||
if (str)
|
||||
{
|
||||
append(str, strlen(str));
|
||||
}
|
||||
else
|
||||
{
|
||||
append("(null)", 6);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
self &operator<<(const unsigned char *str)
|
||||
{
|
||||
return operator<<(reinterpret_cast<const char *>(str));
|
||||
}
|
||||
|
||||
self &operator<<(const std::string &v)
|
||||
{
|
||||
append(v.c_str(), v.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
void append(const char *data, size_t len)
|
||||
{
|
||||
if (exBuffer_.empty())
|
||||
{
|
||||
if (!buffer_.append(data, len))
|
||||
{
|
||||
exBuffer_.append(buffer_.data(), buffer_.length());
|
||||
exBuffer_.append(data, len);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
exBuffer_.append(data, len);
|
||||
}
|
||||
}
|
||||
// const Buffer& buffer() const { return buffer_; }
|
||||
const char *bufferData() const
|
||||
{
|
||||
if (!exBuffer_.empty())
|
||||
{
|
||||
return exBuffer_.data();
|
||||
}
|
||||
return buffer_.data();
|
||||
}
|
||||
|
||||
size_t bufferLength() const
|
||||
{
|
||||
if (!exBuffer_.empty())
|
||||
{
|
||||
return exBuffer_.length();
|
||||
}
|
||||
return buffer_.length();
|
||||
}
|
||||
void resetBuffer()
|
||||
{
|
||||
buffer_.reset();
|
||||
exBuffer_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void formatInteger(T);
|
||||
|
||||
Buffer buffer_;
|
||||
std::string exBuffer_;
|
||||
};
|
||||
|
||||
class TRANTOR_EXPORT Fmt // : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
Fmt(const char *fmt, T val);
|
||||
|
||||
const char *data() const
|
||||
{
|
||||
return buf_;
|
||||
}
|
||||
int length() const
|
||||
{
|
||||
return length_;
|
||||
}
|
||||
|
||||
private:
|
||||
char buf_[48];
|
||||
int length_;
|
||||
};
|
||||
|
||||
inline LogStream &operator<<(LogStream &s, const Fmt &fmt)
|
||||
{
|
||||
s.append(fmt.data(), fmt.length());
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace trantor
|
@ -1,212 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Logger.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <stdio.h>
|
||||
#include <thread>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#else
|
||||
#include <sstream>
|
||||
#endif
|
||||
#ifdef __FreeBSD__
|
||||
#include <pthread_np.h>
|
||||
#endif
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
// helper class for known string length at compile time
|
||||
class T
|
||||
{
|
||||
public:
|
||||
T(const char *str, unsigned len) : str_(str), len_(len)
|
||||
{
|
||||
assert(strlen(str) == len_);
|
||||
}
|
||||
|
||||
const char *str_;
|
||||
const unsigned len_;
|
||||
};
|
||||
|
||||
const char *strerror_tl(int savedErrno)
|
||||
{
|
||||
#ifndef _MSC_VER
|
||||
return strerror(savedErrno);
|
||||
#else
|
||||
static thread_local char errMsg[64];
|
||||
(void)strerror_s<sizeof errMsg>(errMsg, savedErrno);
|
||||
return errMsg;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline LogStream &operator<<(LogStream &s, T v)
|
||||
{
|
||||
s.append(v.str_, v.len_);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline LogStream &operator<<(LogStream &s, const Logger::SourceFile &v)
|
||||
{
|
||||
s.append(v.data_, v.size_);
|
||||
return s;
|
||||
}
|
||||
} // namespace trantor
|
||||
using namespace trantor;
|
||||
|
||||
static thread_local uint64_t lastSecond_{0};
|
||||
static thread_local char lastTimeString_[32] = {0};
|
||||
#ifdef __linux__
|
||||
static thread_local pid_t threadId_{0};
|
||||
#else
|
||||
static thread_local uint64_t threadId_{0};
|
||||
#endif
|
||||
// static thread_local LogStream logStream_;
|
||||
|
||||
void Logger::formatTime()
|
||||
{
|
||||
uint64_t now = static_cast<uint64_t>(date_.secondsSinceEpoch());
|
||||
uint64_t microSec =
|
||||
static_cast<uint64_t>(date_.microSecondsSinceEpoch() -
|
||||
date_.roundSecond().microSecondsSinceEpoch());
|
||||
if (now != lastSecond_)
|
||||
{
|
||||
lastSecond_ = now;
|
||||
#ifndef _MSC_VER
|
||||
strncpy(lastTimeString_,
|
||||
date_.toFormattedString(false).c_str(),
|
||||
sizeof(lastTimeString_) - 1);
|
||||
#else
|
||||
strncpy_s<sizeof lastTimeString_>(
|
||||
lastTimeString_,
|
||||
date_.toFormattedString(false).c_str(),
|
||||
sizeof(lastTimeString_) - 1);
|
||||
#endif
|
||||
}
|
||||
logStream_ << T(lastTimeString_, 17);
|
||||
char tmp[32];
|
||||
snprintf(tmp,
|
||||
sizeof(tmp),
|
||||
".%06llu UTC ",
|
||||
static_cast<long long unsigned int>(microSec));
|
||||
logStream_ << T(tmp, 12);
|
||||
#ifdef __linux__
|
||||
if (threadId_ == 0)
|
||||
threadId_ = static_cast<pid_t>(::syscall(SYS_gettid));
|
||||
#elif defined __FreeBSD__
|
||||
if (threadId_ == 0)
|
||||
{
|
||||
threadId_ = pthread_getthreadid_np();
|
||||
}
|
||||
#elif defined __OpenBSD__
|
||||
if (threadId_ == 0)
|
||||
{
|
||||
threadId_ = getthrid();
|
||||
}
|
||||
#elif defined _WIN32
|
||||
if (threadId_ == 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::this_thread::get_id();
|
||||
threadId_ = std::stoull(ss.str());
|
||||
}
|
||||
#else
|
||||
if (threadId_ == 0)
|
||||
{
|
||||
pthread_threadid_np(NULL, &threadId_);
|
||||
}
|
||||
#endif
|
||||
logStream_ << threadId_;
|
||||
}
|
||||
static const char *logLevelStr[Logger::LogLevel::kNumberOfLogLevels] = {
|
||||
" TRACE ",
|
||||
" DEBUG ",
|
||||
" INFO ",
|
||||
" WARN ",
|
||||
" ERROR ",
|
||||
" FATAL ",
|
||||
};
|
||||
Logger::Logger(SourceFile file, int line)
|
||||
: sourceFile_(file), fileLine_(line), level_(kInfo)
|
||||
{
|
||||
formatTime();
|
||||
logStream_ << T(logLevelStr[level_], 7);
|
||||
}
|
||||
Logger::Logger(SourceFile file, int line, LogLevel level)
|
||||
: sourceFile_(file), fileLine_(line), level_(level)
|
||||
{
|
||||
formatTime();
|
||||
logStream_ << T(logLevelStr[level_], 7);
|
||||
}
|
||||
Logger::Logger(SourceFile file, int line, LogLevel level, const char *func)
|
||||
: sourceFile_(file), fileLine_(line), level_(level)
|
||||
{
|
||||
formatTime();
|
||||
logStream_ << T(logLevelStr[level_], 7) << "[" << func << "] ";
|
||||
}
|
||||
Logger::Logger(SourceFile file, int line, bool)
|
||||
: sourceFile_(file), fileLine_(line), level_(kFatal)
|
||||
{
|
||||
formatTime();
|
||||
logStream_ << T(logLevelStr[level_], 7);
|
||||
if (errno != 0)
|
||||
{
|
||||
logStream_ << strerror_tl(errno) << " (errno=" << errno << ") ";
|
||||
}
|
||||
}
|
||||
RawLogger::~RawLogger()
|
||||
{
|
||||
if (index_ < 0)
|
||||
{
|
||||
auto &oFunc = Logger::outputFunc_();
|
||||
if (!oFunc)
|
||||
return;
|
||||
oFunc(logStream_.bufferData(), logStream_.bufferLength());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &oFunc = Logger::outputFunc_(index_);
|
||||
if (!oFunc)
|
||||
return;
|
||||
oFunc(logStream_.bufferData(), logStream_.bufferLength());
|
||||
}
|
||||
}
|
||||
Logger::~Logger()
|
||||
{
|
||||
logStream_ << T(" - ", 3) << sourceFile_ << ':' << fileLine_ << '\n';
|
||||
if (index_ < 0)
|
||||
{
|
||||
auto &oFunc = Logger::outputFunc_();
|
||||
if (!oFunc)
|
||||
return;
|
||||
oFunc(logStream_.bufferData(), logStream_.bufferLength());
|
||||
if (level_ >= kError)
|
||||
Logger::flushFunc_()();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &oFunc = Logger::outputFunc_(index_);
|
||||
if (!oFunc)
|
||||
return;
|
||||
oFunc(logStream_.bufferData(), logStream_.bufferLength());
|
||||
if (level_ >= kError)
|
||||
Logger::flushFunc_(index_)();
|
||||
}
|
||||
|
||||
// logStream_.resetBuffer();
|
||||
}
|
||||
LogStream &Logger::stream()
|
||||
{
|
||||
return logStream_;
|
||||
}
|
@ -1,383 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file Logger.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/utils/Date.h>
|
||||
#include <trantor/utils/LogStream.h>
|
||||
#include <trantor/exports.h>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
/**
|
||||
* @brief This class implements log functions.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT Logger : public NonCopyable
|
||||
{
|
||||
public:
|
||||
enum LogLevel
|
||||
{
|
||||
kTrace = 0,
|
||||
kDebug,
|
||||
kInfo,
|
||||
kWarn,
|
||||
kError,
|
||||
kFatal,
|
||||
kNumberOfLogLevels
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Calculate of basename of source files in compile time.
|
||||
*
|
||||
*/
|
||||
class SourceFile
|
||||
{
|
||||
public:
|
||||
template <int N>
|
||||
inline SourceFile(const char (&arr)[N]) : data_(arr), size_(N - 1)
|
||||
{
|
||||
// std::cout<<data_<<std::endl;
|
||||
const char *slash = strrchr(data_, '/'); // builtin function
|
||||
if (slash)
|
||||
{
|
||||
data_ = slash + 1;
|
||||
size_ -= static_cast<int>(data_ - arr);
|
||||
}
|
||||
}
|
||||
|
||||
explicit SourceFile(const char *filename) : data_(filename)
|
||||
{
|
||||
const char *slash = strrchr(filename, '/');
|
||||
if (slash)
|
||||
{
|
||||
data_ = slash + 1;
|
||||
}
|
||||
size_ = static_cast<int>(strlen(data_));
|
||||
}
|
||||
|
||||
const char *data_;
|
||||
int size_;
|
||||
};
|
||||
Logger(SourceFile file, int line);
|
||||
Logger(SourceFile file, int line, LogLevel level);
|
||||
Logger(SourceFile file, int line, bool isSysErr);
|
||||
Logger(SourceFile file, int line, LogLevel level, const char *func);
|
||||
~Logger();
|
||||
Logger &setIndex(int index)
|
||||
{
|
||||
index_ = index;
|
||||
return *this;
|
||||
}
|
||||
LogStream &stream();
|
||||
|
||||
/**
|
||||
* @brief Set the output function.
|
||||
*
|
||||
* @param outputFunc The function to output a log message.
|
||||
* @param flushFunc The function to flush.
|
||||
* @note Logs are output to the standard output by default.
|
||||
*/
|
||||
static void setOutputFunction(
|
||||
std::function<void(const char *msg, const uint64_t len)> outputFunc,
|
||||
std::function<void()> flushFunc,
|
||||
int index = -1)
|
||||
{
|
||||
if (index < 0)
|
||||
{
|
||||
outputFunc_() = outputFunc;
|
||||
flushFunc_() = flushFunc;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputFunc_(index) = outputFunc;
|
||||
flushFunc_(index) = flushFunc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the log level. Logs below the level are not printed.
|
||||
*
|
||||
* @param level
|
||||
*/
|
||||
static void setLogLevel(LogLevel level)
|
||||
{
|
||||
logLevel_() = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the current log level.
|
||||
*
|
||||
* @return LogLevel
|
||||
*/
|
||||
static LogLevel logLevel()
|
||||
{
|
||||
return logLevel_();
|
||||
}
|
||||
|
||||
protected:
|
||||
static void defaultOutputFunction(const char *msg, const uint64_t len)
|
||||
{
|
||||
fwrite(msg, 1, len, stdout);
|
||||
}
|
||||
static void defaultFlushFunction()
|
||||
{
|
||||
fflush(stdout);
|
||||
}
|
||||
void formatTime();
|
||||
static LogLevel &logLevel_()
|
||||
{
|
||||
#ifdef RELEASE
|
||||
static LogLevel logLevel = LogLevel::kInfo;
|
||||
#else
|
||||
static LogLevel logLevel = LogLevel::kDebug;
|
||||
#endif
|
||||
return logLevel;
|
||||
}
|
||||
static std::function<void(const char *msg, const uint64_t len)>
|
||||
&outputFunc_()
|
||||
{
|
||||
static std::function<void(const char *msg, const uint64_t len)>
|
||||
outputFunc = Logger::defaultOutputFunction;
|
||||
return outputFunc;
|
||||
}
|
||||
static std::function<void()> &flushFunc_()
|
||||
{
|
||||
static std::function<void()> flushFunc = Logger::defaultFlushFunction;
|
||||
return flushFunc;
|
||||
}
|
||||
static std::function<void(const char *msg, const uint64_t len)>
|
||||
&outputFunc_(size_t index)
|
||||
{
|
||||
static std::vector<
|
||||
std::function<void(const char *msg, const uint64_t len)>>
|
||||
outputFuncs;
|
||||
if (index < outputFuncs.size())
|
||||
{
|
||||
return outputFuncs[index];
|
||||
}
|
||||
while (index >= outputFuncs.size())
|
||||
{
|
||||
outputFuncs.emplace_back(outputFunc_());
|
||||
}
|
||||
return outputFuncs[index];
|
||||
}
|
||||
static std::function<void()> &flushFunc_(size_t index)
|
||||
{
|
||||
static std::vector<std::function<void()>> flushFuncs;
|
||||
if (index < flushFuncs.size())
|
||||
{
|
||||
return flushFuncs[index];
|
||||
}
|
||||
while (index >= flushFuncs.size())
|
||||
{
|
||||
flushFuncs.emplace_back(flushFunc_());
|
||||
}
|
||||
return flushFuncs[index];
|
||||
}
|
||||
friend class RawLogger;
|
||||
LogStream logStream_;
|
||||
Date date_{Date::now()};
|
||||
SourceFile sourceFile_;
|
||||
int fileLine_;
|
||||
LogLevel level_;
|
||||
int index_{-1};
|
||||
};
|
||||
class TRANTOR_EXPORT RawLogger : public NonCopyable
|
||||
{
|
||||
public:
|
||||
~RawLogger();
|
||||
RawLogger &setIndex(int index)
|
||||
{
|
||||
index_ = index;
|
||||
return *this;
|
||||
}
|
||||
LogStream &stream()
|
||||
{
|
||||
return logStream_;
|
||||
}
|
||||
|
||||
private:
|
||||
LogStream logStream_;
|
||||
int index_{-1};
|
||||
};
|
||||
#ifdef NDEBUG
|
||||
#define LOG_TRACE \
|
||||
if (0) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \
|
||||
.stream()
|
||||
#else
|
||||
#define LOG_TRACE \
|
||||
if (trantor::Logger::logLevel() <= trantor::Logger::kTrace) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \
|
||||
.stream()
|
||||
#define LOG_TRACE_TO(index) \
|
||||
if (trantor::Logger::logLevel() <= trantor::Logger::kTrace) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \
|
||||
.setIndex(index) \
|
||||
.stream()
|
||||
|
||||
#endif
|
||||
|
||||
#define LOG_DEBUG \
|
||||
if (trantor::Logger::logLevel() <= trantor::Logger::kDebug) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kDebug, __func__) \
|
||||
.stream()
|
||||
#define LOG_DEBUG_TO(index) \
|
||||
if (trantor::Logger::logLevel() <= trantor::Logger::kDebug) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kDebug, __func__) \
|
||||
.setIndex(index) \
|
||||
.stream()
|
||||
#define LOG_INFO \
|
||||
if (trantor::Logger::logLevel() <= trantor::Logger::kInfo) \
|
||||
trantor::Logger(__FILE__, __LINE__).stream()
|
||||
#define LOG_INFO_TO(index) \
|
||||
if (trantor::Logger::logLevel() <= trantor::Logger::kInfo) \
|
||||
trantor::Logger(__FILE__, __LINE__).setIndex(index).stream()
|
||||
#define LOG_WARN \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kWarn).stream()
|
||||
#define LOG_WARN_TO(index) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kWarn) \
|
||||
.setIndex(index) \
|
||||
.stream()
|
||||
#define LOG_ERROR \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kError).stream()
|
||||
#define LOG_ERROR_TO(index) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kError) \
|
||||
.setIndex(index) \
|
||||
.stream()
|
||||
#define LOG_FATAL \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kFatal).stream()
|
||||
#define LOG_FATAL_TO(index) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kFatal) \
|
||||
.setIndex(index) \
|
||||
.stream()
|
||||
#define LOG_SYSERR trantor::Logger(__FILE__, __LINE__, true).stream()
|
||||
#define LOG_SYSERR_TO(index) \
|
||||
trantor::Logger(__FILE__, __LINE__, true).setIndex(index).stream()
|
||||
|
||||
#define LOG_RAW trantor::RawLogger().stream()
|
||||
#define LOG_RAW_TO(index) trantor::RawLogger().setIndex(index).stream()
|
||||
|
||||
#define LOG_TRACE_IF(cond) \
|
||||
if ((trantor::Logger::logLevel() <= trantor::Logger::kTrace) && (cond)) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \
|
||||
.stream()
|
||||
#define LOG_DEBUG_IF(cond) \
|
||||
if ((Tensor::Logger::logLevel() <= Tensor::Logger::kDebug) && (cond)) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kDebug, __func__) \
|
||||
.stream()
|
||||
#define LOG_INFO_IF(cond) \
|
||||
if ((Tensor::Logger::logLevel() <= Tensor::Logger::kInfo) && (cond)) \
|
||||
Tensor::Logger(__FILE__, __LINE__).stream()
|
||||
#define LOG_WARN_IF(cond) \
|
||||
if (cond) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kWarn).stream()
|
||||
#define LOG_ERROR_IF(cond) \
|
||||
if (cond) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kError).stream()
|
||||
#define LOG_FATAL_IF(cond) \
|
||||
if (cond) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kFatal).stream()
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define DLOG_TRACE \
|
||||
if (0) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \
|
||||
.stream()
|
||||
#define DLOG_DEBUG \
|
||||
if (0) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kDebug, __func__) \
|
||||
.stream()
|
||||
#define DLOG_INFO \
|
||||
if (0) \
|
||||
Tensor::Logger(__FILE__, __LINE__).stream()
|
||||
#define DLOG_WARN \
|
||||
if (0) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kWarn).stream()
|
||||
#define DLOG_ERROR \
|
||||
if (0) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kError).stream()
|
||||
#define DLOG_FATAL \
|
||||
if (0) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kFatal).stream()
|
||||
|
||||
#define DLOG_TRACE_IF(cond) \
|
||||
if (0) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \
|
||||
.stream()
|
||||
#define DLOG_DEBUG_IF(cond) \
|
||||
if (0) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kDebug, __func__) \
|
||||
.stream()
|
||||
#define DLOG_INFO_IF(cond) \
|
||||
if (0) \
|
||||
Tensor::Logger(__FILE__, __LINE__).stream()
|
||||
#define DLOG_WARN_IF(cond) \
|
||||
if (0) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kWarn).stream()
|
||||
#define DLOG_ERROR_IF(cond) \
|
||||
if (0) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kError).stream()
|
||||
#define DLOG_FATAL_IF(cond) \
|
||||
if (0) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kFatal).stream()
|
||||
#else
|
||||
#define DLOG_TRACE \
|
||||
if (trantor::Logger::logLevel() <= trantor::Logger::kTrace) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \
|
||||
.stream()
|
||||
#define DLOG_DEBUG \
|
||||
if (Tensor::Logger::logLevel() <= Tensor::Logger::kDebug) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kDebug, __func__) \
|
||||
.stream()
|
||||
#define DLOG_INFO \
|
||||
if (Tensor::Logger::logLevel() <= Tensor::Logger::kInfo) \
|
||||
Tensor::Logger(__FILE__, __LINE__).stream()
|
||||
#define DLOG_WARN \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kWarn).stream()
|
||||
#define DLOG_ERROR \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kError).stream()
|
||||
#define DLOG_FATAL \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kFatal).stream()
|
||||
|
||||
#define DLOG_TRACE_IF(cond) \
|
||||
if ((trantor::Logger::logLevel() <= trantor::Logger::kTrace) && (cond)) \
|
||||
trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \
|
||||
.stream()
|
||||
#define DLOG_DEBUG_IF(cond) \
|
||||
if ((Tensor::Logger::logLevel() <= Tensor::Logger::kDebug) && (cond)) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kDebug, __func__) \
|
||||
.stream()
|
||||
#define DLOG_INFO_IF(cond) \
|
||||
if ((Tensor::Logger::logLevel() <= Tensor::Logger::kInfo) && (cond)) \
|
||||
Tensor::Logger(__FILE__, __LINE__).stream()
|
||||
#define DLOG_WARN_IF(cond) \
|
||||
if (cond) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kWarn).stream()
|
||||
#define DLOG_ERROR_IF(cond) \
|
||||
if (cond) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kError).stream()
|
||||
#define DLOG_FATAL_IF(cond) \
|
||||
if (cond) \
|
||||
Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kFatal).stream()
|
||||
#endif
|
||||
|
||||
const char *strerror_tl(int savedErrno);
|
||||
} // namespace trantor
|
@ -1,232 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* MsgBuffer.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <trantor/utils/MsgBuffer.h>
|
||||
#include <trantor/utils/Funcs.h>
|
||||
#include <string.h>
|
||||
#ifndef _WIN32
|
||||
#include <sys/uio.h>
|
||||
#include <netinet/in.h>
|
||||
#else
|
||||
#include <WindowsSupport.h>
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace trantor;
|
||||
namespace trantor
|
||||
{
|
||||
static constexpr size_t kBufferOffset{8};
|
||||
}
|
||||
|
||||
MsgBuffer::MsgBuffer(size_t len)
|
||||
: head_(kBufferOffset), initCap_(len), buffer_(len + head_), tail_(head_)
|
||||
{
|
||||
}
|
||||
|
||||
void MsgBuffer::ensureWritableBytes(size_t len)
|
||||
{
|
||||
if (writableBytes() >= len)
|
||||
return;
|
||||
if (head_ + writableBytes() >=
|
||||
(len + kBufferOffset)) // move readable bytes
|
||||
{
|
||||
std::copy(begin() + head_, begin() + tail_, begin() + kBufferOffset);
|
||||
tail_ = kBufferOffset + (tail_ - head_);
|
||||
head_ = kBufferOffset;
|
||||
return;
|
||||
}
|
||||
// create new buffer
|
||||
size_t newLen;
|
||||
if ((buffer_.size() * 2) > (kBufferOffset + readableBytes() + len))
|
||||
newLen = buffer_.size() * 2;
|
||||
else
|
||||
newLen = kBufferOffset + readableBytes() + len;
|
||||
MsgBuffer newbuffer(newLen);
|
||||
newbuffer.append(*this);
|
||||
swap(newbuffer);
|
||||
}
|
||||
void MsgBuffer::swap(MsgBuffer &buf) noexcept
|
||||
{
|
||||
buffer_.swap(buf.buffer_);
|
||||
std::swap(head_, buf.head_);
|
||||
std::swap(tail_, buf.tail_);
|
||||
std::swap(initCap_, buf.initCap_);
|
||||
}
|
||||
void MsgBuffer::append(const MsgBuffer &buf)
|
||||
{
|
||||
ensureWritableBytes(buf.readableBytes());
|
||||
memcpy(&buffer_[tail_], buf.peek(), buf.readableBytes());
|
||||
tail_ += buf.readableBytes();
|
||||
}
|
||||
void MsgBuffer::append(const char *buf, size_t len)
|
||||
{
|
||||
ensureWritableBytes(len);
|
||||
memcpy(&buffer_[tail_], buf, len);
|
||||
tail_ += len;
|
||||
}
|
||||
void MsgBuffer::appendInt16(const uint16_t s)
|
||||
{
|
||||
uint16_t ss = htons(s);
|
||||
append(static_cast<const char *>((void *)&ss), 2);
|
||||
}
|
||||
void MsgBuffer::appendInt32(const uint32_t i)
|
||||
{
|
||||
uint32_t ii = htonl(i);
|
||||
append(static_cast<const char *>((void *)&ii), 4);
|
||||
}
|
||||
void MsgBuffer::appendInt64(const uint64_t l)
|
||||
{
|
||||
uint64_t ll = hton64(l);
|
||||
append(static_cast<const char *>((void *)&ll), 8);
|
||||
}
|
||||
|
||||
void MsgBuffer::addInFrontInt16(const uint16_t s)
|
||||
{
|
||||
uint16_t ss = htons(s);
|
||||
addInFront(static_cast<const char *>((void *)&ss), 2);
|
||||
}
|
||||
void MsgBuffer::addInFrontInt32(const uint32_t i)
|
||||
{
|
||||
uint32_t ii = htonl(i);
|
||||
addInFront(static_cast<const char *>((void *)&ii), 4);
|
||||
}
|
||||
void MsgBuffer::addInFrontInt64(const uint64_t l)
|
||||
{
|
||||
uint64_t ll = hton64(l);
|
||||
addInFront(static_cast<const char *>((void *)&ll), 8);
|
||||
}
|
||||
|
||||
uint16_t MsgBuffer::peekInt16() const
|
||||
{
|
||||
assert(readableBytes() >= 2);
|
||||
uint16_t rs = *(static_cast<const uint16_t *>((void *)peek()));
|
||||
return ntohs(rs);
|
||||
}
|
||||
uint32_t MsgBuffer::peekInt32() const
|
||||
{
|
||||
assert(readableBytes() >= 4);
|
||||
uint32_t rl = *(static_cast<const uint32_t *>((void *)peek()));
|
||||
return ntohl(rl);
|
||||
}
|
||||
uint64_t MsgBuffer::peekInt64() const
|
||||
{
|
||||
assert(readableBytes() >= 8);
|
||||
uint64_t rll = *(static_cast<const uint64_t *>((void *)peek()));
|
||||
return ntoh64(rll);
|
||||
}
|
||||
|
||||
void MsgBuffer::retrieve(size_t len)
|
||||
{
|
||||
if (len >= readableBytes())
|
||||
{
|
||||
retrieveAll();
|
||||
return;
|
||||
}
|
||||
head_ += len;
|
||||
}
|
||||
void MsgBuffer::retrieveAll()
|
||||
{
|
||||
if (buffer_.size() > (initCap_ * 2))
|
||||
{
|
||||
buffer_.resize(initCap_);
|
||||
}
|
||||
tail_ = head_ = kBufferOffset;
|
||||
}
|
||||
ssize_t MsgBuffer::readFd(int fd, int *retErrno)
|
||||
{
|
||||
char extBuffer[8192];
|
||||
struct iovec vec[2];
|
||||
size_t writable = writableBytes();
|
||||
vec[0].iov_base = begin() + tail_;
|
||||
vec[0].iov_len = static_cast<int>(writable);
|
||||
vec[1].iov_base = extBuffer;
|
||||
vec[1].iov_len = sizeof(extBuffer);
|
||||
const int iovcnt = (writable < sizeof extBuffer) ? 2 : 1;
|
||||
ssize_t n = ::readv(fd, vec, iovcnt);
|
||||
if (n < 0)
|
||||
{
|
||||
*retErrno = errno;
|
||||
}
|
||||
else if (static_cast<size_t>(n) <= writable)
|
||||
{
|
||||
tail_ += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
tail_ = buffer_.size();
|
||||
append(extBuffer, n - writable);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
std::string MsgBuffer::read(size_t len)
|
||||
{
|
||||
if (len > readableBytes())
|
||||
len = readableBytes();
|
||||
std::string ret(peek(), len);
|
||||
retrieve(len);
|
||||
return ret;
|
||||
}
|
||||
uint8_t MsgBuffer::readInt8()
|
||||
{
|
||||
uint8_t ret = peekInt8();
|
||||
retrieve(1);
|
||||
return ret;
|
||||
}
|
||||
uint16_t MsgBuffer::readInt16()
|
||||
{
|
||||
uint16_t ret = peekInt16();
|
||||
retrieve(2);
|
||||
return ret;
|
||||
}
|
||||
uint32_t MsgBuffer::readInt32()
|
||||
{
|
||||
uint32_t ret = peekInt32();
|
||||
retrieve(4);
|
||||
return ret;
|
||||
}
|
||||
uint64_t MsgBuffer::readInt64()
|
||||
{
|
||||
uint64_t ret = peekInt64();
|
||||
retrieve(8);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MsgBuffer::addInFront(const char *buf, size_t len)
|
||||
{
|
||||
if (head_ >= len)
|
||||
{
|
||||
memcpy(begin() + head_ - len, buf, len);
|
||||
head_ -= len;
|
||||
return;
|
||||
}
|
||||
if (len <= writableBytes())
|
||||
{
|
||||
std::copy(begin() + head_, begin() + tail_, begin() + head_ + len);
|
||||
memcpy(begin() + head_, buf, len);
|
||||
tail_ += len;
|
||||
return;
|
||||
}
|
||||
size_t newLen;
|
||||
if (len + readableBytes() < initCap_)
|
||||
newLen = initCap_;
|
||||
else
|
||||
newLen = len + readableBytes();
|
||||
MsgBuffer newBuf(newLen);
|
||||
newBuf.append(buf, len);
|
||||
newBuf.append(*this);
|
||||
swap(newBuf);
|
||||
}
|
@ -1,376 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file MsgBuffer.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <trantor/exports.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
using ssize_t = long long;
|
||||
#endif
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
static constexpr size_t kBufferDefaultLength{2048};
|
||||
static constexpr char CRLF[]{"\r\n"};
|
||||
|
||||
/**
|
||||
* @brief This class represents a memory buffer used for sending and receiving
|
||||
* data.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT MsgBuffer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new message buffer instance.
|
||||
*
|
||||
* @param len The initial size of the buffer.
|
||||
*/
|
||||
MsgBuffer(size_t len = kBufferDefaultLength);
|
||||
|
||||
/**
|
||||
* @brief Get the beginning of the buffer.
|
||||
*
|
||||
* @return const char*
|
||||
*/
|
||||
const char *peek() const
|
||||
{
|
||||
return begin() + head_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the end of the buffer where new data can be written.
|
||||
*
|
||||
* @return const char*
|
||||
*/
|
||||
const char *beginWrite() const
|
||||
{
|
||||
return begin() + tail_;
|
||||
}
|
||||
char *beginWrite()
|
||||
{
|
||||
return begin() + tail_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a byte value from the buffer.
|
||||
*
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t peekInt8() const
|
||||
{
|
||||
assert(readableBytes() >= 1);
|
||||
return *(static_cast<const uint8_t *>((void *)peek()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a unsigned short value from the buffer.
|
||||
*
|
||||
* @return uint16_t
|
||||
*/
|
||||
uint16_t peekInt16() const;
|
||||
|
||||
/**
|
||||
* @brief Get a unsigned int value from the buffer.
|
||||
*
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t peekInt32() const;
|
||||
|
||||
/**
|
||||
* @brief Get a unsigned int64 value from the buffer.
|
||||
*
|
||||
* @return uint64_t
|
||||
*/
|
||||
uint64_t peekInt64() const;
|
||||
|
||||
/**
|
||||
* @brief Get and remove some bytes from the buffer.
|
||||
*
|
||||
* @param len
|
||||
* @return std::string
|
||||
*/
|
||||
std::string read(size_t len);
|
||||
|
||||
/**
|
||||
* @brief Get the remove a byte value from the buffer.
|
||||
*
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t readInt8();
|
||||
|
||||
/**
|
||||
* @brief Get and remove a unsigned short value from the buffer.
|
||||
*
|
||||
* @return uint16_t
|
||||
*/
|
||||
uint16_t readInt16();
|
||||
|
||||
/**
|
||||
* @brief Get and remove a unsigned int value from the buffer.
|
||||
*
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t readInt32();
|
||||
|
||||
/**
|
||||
* @brief Get and remove a unsigned int64 value from the buffer.
|
||||
*
|
||||
* @return uint64_t
|
||||
*/
|
||||
uint64_t readInt64();
|
||||
|
||||
/**
|
||||
* @brief swap the buffer with another.
|
||||
*
|
||||
* @param buf
|
||||
*/
|
||||
void swap(MsgBuffer &buf) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Return the size of the data in the buffer.
|
||||
*
|
||||
* @return size_t
|
||||
*/
|
||||
size_t readableBytes() const
|
||||
{
|
||||
return tail_ - head_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the size of the empty part in the buffer
|
||||
*
|
||||
* @return size_t
|
||||
*/
|
||||
size_t writableBytes() const
|
||||
{
|
||||
return buffer_.size() - tail_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Append new data to the buffer.
|
||||
*
|
||||
*/
|
||||
void append(const MsgBuffer &buf);
|
||||
template <int N>
|
||||
void append(const char (&buf)[N])
|
||||
{
|
||||
assert(strnlen(buf, N) == N - 1);
|
||||
append(buf, N - 1);
|
||||
}
|
||||
void append(const char *buf, size_t len);
|
||||
void append(const std::string &buf)
|
||||
{
|
||||
append(buf.c_str(), buf.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Append a byte value to the end of the buffer.
|
||||
*
|
||||
* @param b
|
||||
*/
|
||||
void appendInt8(const uint8_t b)
|
||||
{
|
||||
append(static_cast<const char *>((void *)&b), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Append a unsigned short value to the end of the buffer.
|
||||
*
|
||||
* @param s
|
||||
*/
|
||||
void appendInt16(const uint16_t s);
|
||||
|
||||
/**
|
||||
* @brief Append a unsigned int value to the end of the buffer.
|
||||
*
|
||||
* @param i
|
||||
*/
|
||||
void appendInt32(const uint32_t i);
|
||||
|
||||
/**
|
||||
* @brief Appaend a unsigned int64 value to the end of the buffer.
|
||||
*
|
||||
* @param l
|
||||
*/
|
||||
void appendInt64(const uint64_t l);
|
||||
|
||||
/**
|
||||
* @brief Put new data to the beginning of the buffer.
|
||||
*
|
||||
* @param buf
|
||||
* @param len
|
||||
*/
|
||||
void addInFront(const char *buf, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Put a byte value to the beginning of the buffer.
|
||||
*
|
||||
* @param b
|
||||
*/
|
||||
void addInFrontInt8(const uint8_t b)
|
||||
{
|
||||
addInFront(static_cast<const char *>((void *)&b), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put a unsigned short value to the beginning of the buffer.
|
||||
*
|
||||
* @param s
|
||||
*/
|
||||
void addInFrontInt16(const uint16_t s);
|
||||
|
||||
/**
|
||||
* @brief Put a unsigned int value to the beginning of the buffer.
|
||||
*
|
||||
* @param i
|
||||
*/
|
||||
void addInFrontInt32(const uint32_t i);
|
||||
|
||||
/**
|
||||
* @brief Put a unsigned int64 value to the beginning of the buffer.
|
||||
*
|
||||
* @param l
|
||||
*/
|
||||
void addInFrontInt64(const uint64_t l);
|
||||
|
||||
/**
|
||||
* @brief Remove all data in the buffer.
|
||||
*
|
||||
*/
|
||||
void retrieveAll();
|
||||
|
||||
/**
|
||||
* @brief Remove some bytes in the buffer.
|
||||
*
|
||||
* @param len
|
||||
*/
|
||||
void retrieve(size_t len);
|
||||
|
||||
/**
|
||||
* @brief Read data from a file descriptor and put it into the buffer.˝
|
||||
*
|
||||
* @param fd The file descriptor. It is usually a socket.
|
||||
* @param retErrno The error code when reading.
|
||||
* @return ssize_t The number of bytes read from the file descriptor. -1 is
|
||||
* returned when an error occurs.
|
||||
*/
|
||||
ssize_t readFd(int fd, int *retErrno);
|
||||
|
||||
/**
|
||||
* @brief Remove the data before a certain position from the buffer.
|
||||
*
|
||||
* @param end The position.
|
||||
*/
|
||||
void retrieveUntil(const char *end)
|
||||
{
|
||||
assert(peek() <= end);
|
||||
assert(end <= beginWrite());
|
||||
retrieve(end - peek());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find the position of the buffer where the CRLF is found.
|
||||
*
|
||||
* @return const char*
|
||||
*/
|
||||
const char *findCRLF() const
|
||||
{
|
||||
const char *crlf = std::search(peek(), beginWrite(), CRLF, CRLF + 2);
|
||||
return crlf == beginWrite() ? NULL : crlf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make sure the buffer has enough spaces to write data.
|
||||
*
|
||||
* @param len
|
||||
*/
|
||||
void ensureWritableBytes(size_t len);
|
||||
|
||||
/**
|
||||
* @brief Move the write pointer forward when the new data has been written
|
||||
* to the buffer.
|
||||
*
|
||||
* @param len
|
||||
*/
|
||||
void hasWritten(size_t len)
|
||||
{
|
||||
assert(len <= writableBytes());
|
||||
tail_ += len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move the write pointer backward to remove data in the end of the
|
||||
* buffer.
|
||||
*
|
||||
* @param offset
|
||||
*/
|
||||
void unwrite(size_t offset)
|
||||
{
|
||||
assert(readableBytes() >= offset);
|
||||
tail_ -= offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access a byte in the buffer.
|
||||
*
|
||||
* @param offset
|
||||
* @return const char&
|
||||
*/
|
||||
const char &operator[](size_t offset) const
|
||||
{
|
||||
assert(readableBytes() >= offset);
|
||||
return peek()[offset];
|
||||
}
|
||||
char &operator[](size_t offset)
|
||||
{
|
||||
assert(readableBytes() >= offset);
|
||||
return begin()[head_ + offset];
|
||||
}
|
||||
|
||||
private:
|
||||
size_t head_;
|
||||
size_t initCap_;
|
||||
std::vector<char> buffer_;
|
||||
size_t tail_;
|
||||
const char *begin() const
|
||||
{
|
||||
return &buffer_[0];
|
||||
}
|
||||
char *begin()
|
||||
{
|
||||
return &buffer_[0];
|
||||
}
|
||||
};
|
||||
|
||||
inline void swap(MsgBuffer &one, MsgBuffer &two) noexcept
|
||||
{
|
||||
one.swap(two);
|
||||
}
|
||||
} // namespace trantor
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
inline void swap(trantor::MsgBuffer &one, trantor::MsgBuffer &two) noexcept
|
||||
{
|
||||
one.swap(two);
|
||||
}
|
||||
} // namespace std
|
@ -1,41 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file NonCopyable.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/exports.h>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
/**
|
||||
* @brief This class represents a non-copyable object.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT NonCopyable
|
||||
{
|
||||
protected:
|
||||
NonCopyable()
|
||||
{
|
||||
}
|
||||
~NonCopyable()
|
||||
{
|
||||
}
|
||||
NonCopyable(const NonCopyable &) = delete;
|
||||
NonCopyable &operator=(const NonCopyable &) = delete;
|
||||
// some uncopyable classes maybe support move constructor....
|
||||
NonCopyable(NonCopyable &&) noexcept(true) = default;
|
||||
NonCopyable &operator=(NonCopyable &&) noexcept(true) = default;
|
||||
};
|
||||
|
||||
} // namespace trantor
|
@ -1,76 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file ObjectPool.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/utils/NonCopyable.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <mutex>
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
/**
|
||||
* @brief This class template represents a object pool.
|
||||
*
|
||||
* @tparam T
|
||||
*/
|
||||
template <typename T>
|
||||
class ObjectPool : public NonCopyable,
|
||||
public std::enable_shared_from_this<ObjectPool<T>>
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<T> getObject()
|
||||
{
|
||||
static_assert(!std::is_pointer<T>::value,
|
||||
"The parameter type of the ObjectPool template can't be "
|
||||
"pointer type");
|
||||
T *p{nullptr};
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
if (!objs_.empty())
|
||||
{
|
||||
p = objs_.back();
|
||||
objs_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
if (p == nullptr)
|
||||
{
|
||||
p = new T;
|
||||
}
|
||||
|
||||
assert(p);
|
||||
std::weak_ptr<ObjectPool<T>> weakPtr = this->shared_from_this();
|
||||
auto obj = std::shared_ptr<T>(p, [weakPtr](T *ptr) {
|
||||
auto self = weakPtr.lock();
|
||||
if (self)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(self->mtx_);
|
||||
self->objs_.push_back(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<T *> objs_;
|
||||
std::mutex mtx_;
|
||||
};
|
||||
} // namespace trantor
|
@ -1,56 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* SerialTaskQueue.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <trantor/utils/SerialTaskQueue.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
namespace trantor
|
||||
{
|
||||
SerialTaskQueue::SerialTaskQueue(const std::string &name)
|
||||
: queueName_(name.empty() ? "SerailTaskQueue" : name),
|
||||
loopThread_(queueName_)
|
||||
{
|
||||
loopThread_.run();
|
||||
}
|
||||
void SerialTaskQueue::stop()
|
||||
{
|
||||
stop_ = true;
|
||||
loopThread_.getLoop()->quit();
|
||||
loopThread_.wait();
|
||||
}
|
||||
SerialTaskQueue::~SerialTaskQueue()
|
||||
{
|
||||
if (!stop_)
|
||||
stop();
|
||||
LOG_TRACE << "destruct SerialTaskQueue('" << queueName_ << "')";
|
||||
}
|
||||
void SerialTaskQueue::runTaskInQueue(const std::function<void()> &task)
|
||||
{
|
||||
loopThread_.getLoop()->runInLoop(task);
|
||||
}
|
||||
void SerialTaskQueue::runTaskInQueue(std::function<void()> &&task)
|
||||
{
|
||||
loopThread_.getLoop()->runInLoop(std::move(task));
|
||||
}
|
||||
|
||||
void SerialTaskQueue::waitAllTasksFinished()
|
||||
{
|
||||
syncTaskInQueue([]() {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace trantor
|
@ -1,100 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* SerialTaskQueue.h
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TaskQueue.h"
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <trantor/exports.h>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
namespace trantor
|
||||
{
|
||||
/**
|
||||
* @brief This class represents a task queue in which all tasks are executed one
|
||||
* by one.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT SerialTaskQueue : public TaskQueue
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Run a task in the queue.
|
||||
*
|
||||
* @param task
|
||||
*/
|
||||
virtual void runTaskInQueue(const std::function<void()> &task);
|
||||
virtual void runTaskInQueue(std::function<void()> &&task);
|
||||
|
||||
/**
|
||||
* @brief Get the name of the queue.
|
||||
*
|
||||
* @return std::string
|
||||
*/
|
||||
virtual std::string getName() const
|
||||
{
|
||||
return queueName_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Wait until all tasks in the queue are finished.
|
||||
*
|
||||
*/
|
||||
void waitAllTasksFinished();
|
||||
|
||||
SerialTaskQueue() = delete;
|
||||
|
||||
/**
|
||||
* @brief Construct a new serail task queue instance.
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
explicit SerialTaskQueue(const std::string &name);
|
||||
|
||||
virtual ~SerialTaskQueue();
|
||||
|
||||
/**
|
||||
* @brief Check whether a task is running in the queue.
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isRuningTask()
|
||||
{
|
||||
return loopThread_.getLoop()
|
||||
? loopThread_.getLoop()->isCallingFunctions()
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the number of tasks in the queue.
|
||||
*
|
||||
* @return size_t
|
||||
*/
|
||||
size_t getTaskCount();
|
||||
|
||||
/**
|
||||
* @brief Stop the queue.
|
||||
*
|
||||
*/
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
std::string queueName_;
|
||||
EventLoopThread loopThread_;
|
||||
bool stop_{false};
|
||||
};
|
||||
} // namespace trantor
|
@ -1,58 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file TaskQueue.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "NonCopyable.h"
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <string>
|
||||
namespace trantor
|
||||
{
|
||||
/**
|
||||
* @brief This class is a pure virtual class that can be implemented as a
|
||||
* SerialTaskQueue or a ConcurrentTaskQueue.
|
||||
*
|
||||
*/
|
||||
class TaskQueue : public NonCopyable
|
||||
{
|
||||
public:
|
||||
virtual void runTaskInQueue(const std::function<void()> &task) = 0;
|
||||
virtual void runTaskInQueue(std::function<void()> &&task) = 0;
|
||||
virtual std::string getName() const
|
||||
{
|
||||
return "";
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Run a task in the queue sychronously. This means that the task is
|
||||
* executed before the method returns.
|
||||
*
|
||||
* @param task
|
||||
*/
|
||||
void syncTaskInQueue(const std::function<void()> &task)
|
||||
{
|
||||
std::promise<int> prom;
|
||||
std::future<int> fut = prom.get_future();
|
||||
runTaskInQueue([&]() {
|
||||
task();
|
||||
prom.set_value(1);
|
||||
});
|
||||
fut.get();
|
||||
};
|
||||
virtual ~TaskQueue()
|
||||
{
|
||||
}
|
||||
};
|
||||
} // namespace trantor
|
@ -1,126 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* TimingWheel.cc
|
||||
* An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <trantor/utils/TimingWheel.h>
|
||||
|
||||
using namespace trantor;
|
||||
|
||||
TimingWheel::TimingWheel(trantor::EventLoop *loop,
|
||||
size_t maxTimeout,
|
||||
float ticksInterval,
|
||||
size_t bucketsNumPerWheel)
|
||||
: loop_(loop),
|
||||
ticksInterval_(ticksInterval),
|
||||
bucketsNumPerWheel_(bucketsNumPerWheel)
|
||||
{
|
||||
assert(maxTimeout > 1);
|
||||
assert(ticksInterval > 0);
|
||||
assert(bucketsNumPerWheel_ > 1);
|
||||
size_t maxTickNum = static_cast<size_t>(maxTimeout / ticksInterval);
|
||||
auto ticksNum = bucketsNumPerWheel;
|
||||
wheelsNum_ = 1;
|
||||
while (maxTickNum > ticksNum)
|
||||
{
|
||||
++wheelsNum_;
|
||||
ticksNum *= bucketsNumPerWheel_;
|
||||
}
|
||||
wheels_.resize(wheelsNum_);
|
||||
for (size_t i = 0; i < wheelsNum_; ++i)
|
||||
{
|
||||
wheels_[i].resize(bucketsNumPerWheel_);
|
||||
}
|
||||
timerId_ = loop_->runEvery(ticksInterval_, [this]() {
|
||||
++ticksCounter_;
|
||||
size_t t = ticksCounter_;
|
||||
size_t pow = 1;
|
||||
for (size_t i = 0; i < wheelsNum_; ++i)
|
||||
{
|
||||
if ((t % pow) == 0)
|
||||
{
|
||||
EntryBucket tmp;
|
||||
{
|
||||
// use tmp val to make this critical area as short as
|
||||
// possible.
|
||||
wheels_[i].front().swap(tmp);
|
||||
wheels_[i].pop_front();
|
||||
wheels_[i].push_back(EntryBucket());
|
||||
}
|
||||
}
|
||||
pow = pow * bucketsNumPerWheel_;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
TimingWheel::~TimingWheel()
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
loop_->invalidateTimer(timerId_);
|
||||
for (auto iter = wheels_.rbegin(); iter != wheels_.rend(); ++iter)
|
||||
{
|
||||
iter->clear();
|
||||
}
|
||||
LOG_TRACE << "TimingWheel destruct!";
|
||||
}
|
||||
|
||||
void TimingWheel::insertEntry(size_t delay, EntryPtr entryPtr)
|
||||
{
|
||||
if (delay <= 0)
|
||||
return;
|
||||
if (!entryPtr)
|
||||
return;
|
||||
if (loop_->isInLoopThread())
|
||||
{
|
||||
insertEntryInloop(delay, entryPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
loop_->runInLoop(
|
||||
[this, delay, entryPtr]() { insertEntryInloop(delay, entryPtr); });
|
||||
}
|
||||
}
|
||||
|
||||
void TimingWheel::insertEntryInloop(size_t delay, EntryPtr entryPtr)
|
||||
{
|
||||
loop_->assertInLoopThread();
|
||||
|
||||
delay = static_cast<size_t>(delay / ticksInterval_ + 1);
|
||||
size_t t = ticksCounter_;
|
||||
for (size_t i = 0; i < wheelsNum_; ++i)
|
||||
{
|
||||
if (delay <= bucketsNumPerWheel_)
|
||||
{
|
||||
wheels_[i][delay - 1].insert(entryPtr);
|
||||
break;
|
||||
}
|
||||
if (i < (wheelsNum_ - 1))
|
||||
{
|
||||
entryPtr = std::make_shared<CallbackEntry>(
|
||||
[this, delay, i, t, entryPtr]() {
|
||||
if (delay > 0)
|
||||
{
|
||||
wheels_[i][(delay + (t % bucketsNumPerWheel_) - 1) %
|
||||
bucketsNumPerWheel_]
|
||||
.insert(entryPtr);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// delay is too long to put entry at valid position in wheels;
|
||||
wheels_[i][bucketsNumPerWheel_ - 1].insert(entryPtr);
|
||||
}
|
||||
delay = (delay + (t % bucketsNumPerWheel_) - 1) / bucketsNumPerWheel_;
|
||||
t = t / bucketsNumPerWheel_;
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file TimingWheel.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/net/EventLoop.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <trantor/exports.h>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <atomic>
|
||||
#include <assert.h>
|
||||
|
||||
#define TIMING_BUCKET_NUM_PER_WHEEL 100
|
||||
#define TIMING_TICK_INTERVAL 1.0
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
using EntryPtr = std::shared_ptr<void>;
|
||||
|
||||
using EntryBucket = std::unordered_set<EntryPtr>;
|
||||
using BucketQueue = std::deque<EntryBucket>;
|
||||
|
||||
/**
|
||||
* @brief This class implements a timer strategy with high performance and low
|
||||
* accuracy. This is usually used internally.
|
||||
*
|
||||
*/
|
||||
class TRANTOR_EXPORT TimingWheel
|
||||
{
|
||||
public:
|
||||
class CallbackEntry
|
||||
{
|
||||
public:
|
||||
CallbackEntry(std::function<void()> cb) : cb_(std::move(cb))
|
||||
{
|
||||
}
|
||||
~CallbackEntry()
|
||||
{
|
||||
cb_();
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> cb_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Construct a new timing wheel instance.
|
||||
*
|
||||
* @param loop The event loop in which the timing wheel runs.
|
||||
* @param maxTimeout The maximum timeout of the timing wheel.
|
||||
* @param ticksInterval The internal timer tick interval. It affects the
|
||||
* accuracy of the timing wheel.
|
||||
* @param bucketsNumPerWheel The number of buckets per wheel.
|
||||
* @note The max delay of the timing wheel is about
|
||||
* ticksInterval*(bucketsNumPerWheel^wheelsNum) seconds.
|
||||
* @example Four wheels with 200 buckets per wheel means the timing wheel
|
||||
* can work with a timeout up to 200^4 seconds, about 50 years;
|
||||
*/
|
||||
TimingWheel(trantor::EventLoop *loop,
|
||||
size_t maxTimeout,
|
||||
float ticksInterval = TIMING_TICK_INTERVAL,
|
||||
size_t bucketsNumPerWheel = TIMING_BUCKET_NUM_PER_WHEEL);
|
||||
|
||||
void insertEntry(size_t delay, EntryPtr entryPtr);
|
||||
|
||||
void insertEntryInloop(size_t delay, EntryPtr entryPtr);
|
||||
|
||||
EventLoop *getLoop()
|
||||
{
|
||||
return loop_;
|
||||
}
|
||||
|
||||
~TimingWheel();
|
||||
|
||||
private:
|
||||
std::vector<BucketQueue> wheels_;
|
||||
|
||||
std::atomic<size_t> ticksCounter_{0};
|
||||
|
||||
trantor::TimerId timerId_;
|
||||
trantor::EventLoop *loop_;
|
||||
|
||||
float ticksInterval_;
|
||||
size_t wheelsNum_;
|
||||
size_t bucketsNumPerWheel_;
|
||||
};
|
||||
} // namespace trantor
|
@ -1,55 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* WindowsSupport.cc
|
||||
* An Tao
|
||||
*
|
||||
* Implementation of Windows support functions.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <trantor/utils/WindowsSupport.h>
|
||||
#include <winsock2.h>
|
||||
|
||||
// from polipo
|
||||
int win32_read_socket(int fd, void *buf, int n)
|
||||
{
|
||||
int rc = recv(fd, reinterpret_cast<char *>(buf), n, 0);
|
||||
if (rc == SOCKET_ERROR)
|
||||
{
|
||||
_set_errno(WSAGetLastError());
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int readv(int fd, const struct iovec *vector, int count)
|
||||
{
|
||||
int ret = 0; /* Return value */
|
||||
int i;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
int n = vector[i].iov_len;
|
||||
int rc = win32_read_socket(fd, vector[i].iov_base, n);
|
||||
if (rc == n)
|
||||
{
|
||||
ret += rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rc < 0)
|
||||
{
|
||||
ret = (ret == 0 ? rc : ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret += rc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @file WindowsSupport.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Public header file in trantor lib.
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trantor/exports.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct iovec
|
||||
{
|
||||
void *iov_base; /* Starting address */
|
||||
int iov_len; /* Number of bytes */
|
||||
};
|
||||
|
||||
TRANTOR_EXPORT int readv(int fd, const struct iovec *vector, int count);
|
Loading…
Reference in New Issue
Block a user