-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
261 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
#include "core/cache.h" | ||
#include <cassert> | ||
#include <chrono> | ||
#include <utility> | ||
|
||
namespace Next { | ||
|
||
auto GetTimeUtc() noexcept -> uint64_t { | ||
auto mill_since_epoch = | ||
std::chrono::duration_cast<std::chrono::milliseconds>( | ||
std::chrono::system_clock::now().time_since_epoch()) | ||
.count(); | ||
return mill_since_epoch; | ||
} | ||
Cache::CacheNode::CacheNode() noexcept { UpdataTimeStamp(); } | ||
Cache::CacheNode::CacheNode(std::string identifier, | ||
const std::vector<unsigned char> &data) | ||
: identifier_(std::move(identifier)), data_(data) { | ||
UpdataTimeStamp(); | ||
} | ||
void Cache::CacheNode::SetIdentifier(const std::string &identifier) { | ||
identifier_ = identifier; | ||
} | ||
void Cache::CacheNode::SetData(const std::vector<unsigned char> &data) { | ||
data_ = data; | ||
} | ||
void Cache::CacheNode::Serialize(std::vector<unsigned char> &destination) { | ||
size_t resource_size = data_.size(); | ||
size_t buffer_old_size = destination.size(); | ||
destination.reserve(resource_size + buffer_old_size); | ||
destination.insert(destination.end(), data_.begin(), data_.end()); | ||
} | ||
auto Cache::CacheNode::Size() const noexcept -> size_t { return data_.size(); } | ||
void Cache::CacheNode::UpdataTimeStamp() noexcept { | ||
last_access_ = GetTimeUtc(); | ||
} | ||
auto Cache::CacheNode::GetTimeStamp() const noexcept -> uint64_t { | ||
return last_access_; | ||
} | ||
|
||
Cache::Cache(size_t capacity = DEFAULT_CACHE_CAPACITY) noexcept | ||
: capacity_(capacity), header_(std::make_unique<CacheNode>()), | ||
tailer_(std::make_unique<CacheNode>()) { | ||
header_->next_ = tailer_.get(); | ||
tailer_->prev_ = header_.get(); | ||
} | ||
|
||
auto Cache::GetOccupancy() const noexcept -> size_t { return occupancy_; } | ||
auto Cache::GetCapacity() const noexcept -> size_t { return capacity_; } | ||
|
||
auto Cache::TryLoad(const std::string &resource_url, | ||
std::vector<unsigned char> &destination) -> bool { | ||
std::shared_lock<std::shared_mutex> lock(mtx_); | ||
auto iter = mapping_.find(resource_url); | ||
if (iter != mapping_.end()) { | ||
iter->second->Serialize(destination); | ||
RemoveFromList(iter->second); | ||
AppendToListTail(iter->second); | ||
iter->second->UpdataTimeStamp(); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
auto Cache::TryInsert(const std::string &resource_url, | ||
const std::vector<unsigned char> &source) -> bool { | ||
std::shared_lock<std::shared_mutex> lock(mtx_); | ||
auto iter = mapping_.find(resource_url); | ||
if (iter != mapping_.end()) { | ||
return false; | ||
} | ||
auto source_size = source.size(); | ||
if (source_size > capacity_) { | ||
return false; | ||
} | ||
while (!mapping_.empty() && (capacity_ - occupancy_) < source_size) { | ||
EvictOne(); | ||
} | ||
auto node = std::make_shared<CacheNode>(resource_url, source); | ||
AppendToListTail(node); | ||
occupancy_ += source_size; | ||
mapping_.emplace(resource_url, node); | ||
return true; | ||
} | ||
|
||
void Cache::Clear() { | ||
header_->next_ = tailer_.get(); | ||
tailer_->prev_ = header_.get(); | ||
mapping_.clear(); | ||
occupancy_ = 0; | ||
} | ||
|
||
void Cache::EvictOne() noexcept { | ||
auto *first_node = header_->next_; | ||
auto resource_size = first_node->Size(); | ||
auto iter = mapping_.find(first_node->identifier_); // friend | ||
assert(iter != mapping_.end()); | ||
RemoveFromList(iter->second); | ||
mapping_.erase(iter); | ||
occupancy_ -= resource_size; | ||
} | ||
void Cache::RemoveFromList(const std::shared_ptr<CacheNode> &node) noexcept { | ||
auto *node_ptr = node.get(); | ||
auto *node_prev = node_ptr->prev_; | ||
auto *node_next = node_ptr->next_; | ||
node_prev->next_ = node_next; | ||
node_next->prev_ = node_prev; | ||
} | ||
void Cache::AppendToListTail(const std::shared_ptr<CacheNode> &node) noexcept { | ||
auto *node_ptr = node.get(); | ||
auto *node_prev = tailer_->prev_; | ||
node_prev->next_ = node_ptr; | ||
tailer_->prev_ = node_ptr; | ||
node_ptr->prev_ = node_prev; | ||
node_ptr->next_ = tailer_.get(); | ||
} | ||
} // namespace Next |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
#ifndef NEXT_CACHE_H | ||
#define NEXT_CACHE_H | ||
|
||
#include "core/utils.h" | ||
#include <memory> | ||
#include <mutex> | ||
#include <shared_mutex> | ||
#include <string> | ||
#include <unordered_map> | ||
#include <vector> | ||
|
||
namespace Next { | ||
static constexpr size_t DEFAULT_CACHE_CAPACITY = 10 * 1024 * 1024; | ||
auto GetTimeUtc() noexcept -> uint64_t; | ||
|
||
class Cache { | ||
public: | ||
class CacheNode { | ||
friend class Cache; | ||
|
||
public: | ||
CacheNode() noexcept; | ||
CacheNode(std::string identifier, const std::vector<unsigned char> &data); | ||
void SetIdentifier(const std::string &identifier); | ||
void SetData(const std::vector<unsigned char> &data); | ||
void Serialize(std::vector<unsigned char> &destination); | ||
auto Size() const noexcept -> size_t; | ||
void UpdataTimeStamp() noexcept; | ||
auto GetTimeStamp() const noexcept -> uint64_t; | ||
|
||
private: | ||
std::string identifier_; | ||
std::vector<unsigned char> data_; | ||
uint64_t last_access_{0}; | ||
CacheNode *prev_{nullptr}; | ||
CacheNode *next_{nullptr}; | ||
}; | ||
|
||
explicit Cache(size_t capacity = DEFAULT_CACHE_CAPACITY) noexcept; | ||
NON_MOVE_AND_COPYABLE(Cache); | ||
|
||
auto GetOccupancy() const noexcept -> size_t; | ||
auto GetCapacity() const noexcept -> size_t; | ||
|
||
auto TryLoad(const std::string &resource_url, | ||
std::vector<unsigned char> &destination) -> bool; | ||
|
||
auto TryInsert(const std::string &resource_url, | ||
const std::vector<unsigned char> &source) -> bool; | ||
|
||
void Clear(); | ||
|
||
private: | ||
void EvictOne() noexcept; | ||
void RemoveFromList(const std::shared_ptr<CacheNode> &node) noexcept; | ||
void AppendToListTail(const std::shared_ptr<CacheNode> &node) noexcept; | ||
|
||
// shared_mutex to implement read/write lock | ||
std::shared_mutex mtx_; | ||
// use map to fast find ptr pos | ||
std::unordered_map<std::string, std::shared_ptr<CacheNode>> mapping_; | ||
const size_t capacity_{0}; | ||
size_t occupancy_{0}; | ||
const std::shared_ptr<CacheNode> header_; | ||
const std::shared_ptr<CacheNode> tailer_; | ||
}; | ||
|
||
} // namespace Next | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
#include <algorithm> | ||
#include <functional> | ||
#include <memory> | ||
#include <stdexcept> | ||
#include <utility> | ||
#include <vector> | ||
/* all header files included */ | ||
#include "core/acceptor.h" | ||
#include "core/buffer.h" | ||
#include "core/cache.h" | ||
#include "core/connection.h" | ||
#include "core/looper.h" | ||
#include "core/net_addr.h" | ||
#include "core/poller.h" | ||
#include "core/socket.h" | ||
#include "core/thread_pool.h" | ||
#include "core/utils.h" | ||
|
||
#ifndef NEXT_SERVER_H_ | ||
#define NEXT_SERVER_H_ | ||
|
||
namespace Next { | ||
class NextServer { | ||
public: | ||
NextServer(NetAddress server_address, | ||
int concurrency = | ||
static_cast<int>(std::thread::hardware_concurrency()) - 1) | ||
: pool_(std::make_unique<ThreadPool>(concurrency)), | ||
listener_(std::make_unique<Looper>()) { | ||
for (size_t i = 0; i < pool_->GetSize(); i++) { | ||
reactors_.push_back(std::make_unique<Looper>(TIMER_EXPIRATION)) | ||
} | ||
for (auto &reactor : reactors_) { | ||
pool_->SubmitTask([capture0 = reactor.get()]() { capture0->Loop(); }); | ||
} | ||
std::vector<Looper *> raw_reactors; | ||
raw_reactors.reserve(reactors_.size()); | ||
|
||
std::transform(reactors_.begin(), reactors_.end(), | ||
std::back_inserter(raw_reactors), | ||
[](auto &uni_ptr) { return uni_ptr.get(); }); | ||
acceptor_ = std::make_unique<Acceptor>(listener_.get(), raw_reactors, | ||
server_address); | ||
} | ||
|
||
~NextServer() = default; | ||
|
||
auto OnAccept(std::function<void(Connection *)> on_accept) -> NextServer & { | ||
acceptor_->SetCustomAcceptCallback(std::move(on_accept)); | ||
return *this; | ||
} | ||
|
||
auto OnHandle(std::function<void(Connection *)> on_handle) -> NextServer & { | ||
acceptor_->SetCustomHandleCallback(std::move(on_handle)); | ||
on_handle_set_ = true; | ||
return *this; | ||
} | ||
|
||
void Begin() { | ||
if (!on_handle_set_) { | ||
throw std::logic_error( | ||
"Please specify OnHandle callback function before starts"); | ||
} | ||
listener_->Loop(); | ||
} | ||
|
||
private: | ||
bool on_handle_set_{false}; | ||
std::unique_ptr<Acceptor> acceptor_; | ||
std::vector<std::unique_ptr<Looper>> reactors_; | ||
std::unique_ptr<ThreadPool> pool_; | ||
std::unique_ptr<Looper> listener_; | ||
} | ||
} // namespace Next | ||
#endif |