hmbdc
simplify-high-performance-messaging-programming
SendServer.hpp
1 #include "hmbdc/Copyright.hpp"
2 #pragma once
3 #include "hmbdc/app/Logger.hpp"
4 #include "hmbdc/tips/tcpcast/Transport.hpp"
5 #include "hmbdc/tips/tcpcast/Messages.hpp"
6 #include "hmbdc/tips/tcpcast/SendSession.hpp"
7 #include "hmbdc/time/Time.hpp"
8 #include "hmbdc/pattern/MonoLockFreeBuffer.hpp"
9 #include "hmbdc/comm/inet/Misc.hpp"
10 
11 #include <unordered_set>
12 #include <memory>
13 #include <utility>
14 #include <iostream>
15 
16 #include <netinet/tcp.h>
17 
18 namespace hmbdc { namespace tips { namespace tcpcast {
19 
20 namespace sendserver_detail {
21 
22 struct SendServer {
24  , size_t toSendQueueMaxSize
25  , TypeTagSet& outboundSubscriptions)
26  : config_(cfg)
27  , serverFd_(cfg)
28  , serverAddr_(serverFd_.localAddr)
29  , maxSendBatch_(config_.getExt<size_t>("maxSendBatch"))
30  , outboundSubscriptions_(outboundSubscriptions) {
31  if (listen(serverFd_.fd, 10) < 0) {
32  HMBDC_THROW(std::runtime_error, "failed to listen, errno=" << errno);
33  }
34  hmbdc::app::utils::EpollTask::instance().add(
35  hmbdc::app::utils::EpollTask::EPOLLIN
36  | hmbdc::app::utils::EpollTask::EPOLLET, serverFd_);
37  toSendQueue_.set_capacity(toSendQueueMaxSize);
38  }
39 
40  void advertisingMessages(TypeTagSetST& tts) {
41  decltype(advertisingMessages_) newAds;
42  tts.exportTo([&](uint16_t tag, auto&& subCount) {
43  if (newAds.size() == 0
44  || !newAds.rbegin()->addTypeTag(tag)) {
45  newAds.emplace_back(serverFd_.localIp
46  , serverFd_.localPort
47  , config_.getExt<bool>("loopback"));
48  newAds.rbegin()->addTypeTag(tag);
49  }
50  });
51  std::swap(advertisingMessages_, newAds);
52  for (auto& m : advertisingMessages_) {
53  HMBDC_LOG_N("advertise at ", m);
54  }
55  }
56 
57  auto const& advertisingMessages() const {
58  return advertisingMessages_;
59  }
60 
61  void queue(uint16_t tag
62  , ToSend && toSend
63  , size_t toSendByteSize
65  toSendQueue_.push_back(std::forward_as_tuple(tag, std::forward<ToSend>(toSend), toSendByteSize, it));
66  }
67 
68  /**
69  * @brief run the server's async send function and decide which items in buffer
70  * can be release
71  * @details called all the time
72  *
73  * @param begin if nothing can be released in buffer return this
74  * @param end if all can be releases return this
75  *
76  * @return iterator in buffer poing to new start (not released)
77  */
79  , hmbdc::pattern::MonoLockFreeBuffer::iterator end) HMBDC_RESTRICT {
80  doAccept();
81  if (sessions_.size() == 0) {
82  toSendQueue_.clear();
83  return end;
84  }
85  //nothing to retire by default
86  auto newStartIt = begin;
87 
88  auto minIndex = toSendQueue_.size();
89  for (auto it = sessions_.begin(); it != sessions_.end();) {
90  if (minIndex > (*it)->toSendQueueIndex_) {
91  minIndex = (*it)->toSendQueueIndex_;
92  }
93  if (hmbdc_unlikely(!(*it)->runOnce())) {
94  sessions_.erase(it++);
95  } else {
96  it++;
97  }
98  }
99  if (minIndex) {
100  newStartIt = std::get<3>(toSendQueue_[minIndex - 1]);
101  toSendQueue_.erase_begin(minIndex);
102  for (auto it = sessions_.begin(); it != sessions_.end(); ++it) {
103  (*it)->toSendQueueIndex_ -= minIndex;
104  }
105  }
106 
107  return newStartIt;
108  }
109 
110  size_t readySessionCount() const {
111  size_t res = 0;
112  for (auto const& s : sessions_) {
113  if (s->ready()) res++;
114  }
115  return res;
116  }
117 
118  void killSlowestSession() {
119  for (auto it = sessions_.begin(); it != sessions_.end(); ++it) {
120  if (0 == (*it)->toSendQueueIndex_) {
121  HMBDC_LOG_C((*it)->id(), " too slow, dropping");
122  sessions_.erase(it);
123  break;
124  }
125  }
126  }
127 
128 private:
129  void doAccept() HMBDC_RESTRICT {
130  if (hmbdc_unlikely(serverFd_.isFdReady())) {
131  auto addrlen = sizeof(serverAddr_);
132  auto conn = accept(serverFd_.fd, (struct sockaddr *)&serverAddr_, (socklen_t*)&addrlen);
133  if (conn == -1) {
134  if (!serverFd_.checkErr()) {
135  HMBDC_LOG_C("accept failure, errno=", errno);
136  }
137  return;
138  }
139  auto sz = config_.getExt<int>("tcpSendBufferBytes");
140  if (sz) {
141  if (setsockopt(conn, SOL_SOCKET, SO_SNDBUF, &sz, sizeof(sz)) < 0) {
142  HMBDC_LOG_C("failed to set send buffer size=", sz, " errno=", errno);
143  }
144  }
145  int flag = config_.getExt<bool>("nagling")?0:1;
146  if (setsockopt(conn, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag)) < 0) {
147  HMBDC_LOG_C("failed to set TCP_NODELAY, errno=", errno);
148  }
149  try {
150  auto s = std::make_shared<SendSession>(
151  conn, toSendQueue_, maxSendBatch_, outboundSubscriptions_);
152  sessions_.insert(s);
153  } catch (std::exception const& e) {
154  HMBDC_LOG_C(e.what());
155  }
156  }
157  }
158 
159  hmbdc::app::Config const& config_;
160  using Sessions = std::unordered_set<SendSession::ptr>;
161  Sessions sessions_;
162  ToSendQueue toSendQueue_;
163  EpollFd serverFd_;
164  sockaddr_in& serverAddr_;
165  mutable std::vector<TypeTagSource> advertisingMessages_;
166 
167  size_t maxSendBatch_;
168  TypeTagSet& outboundSubscriptions_;
169 };
170 } //sendserver_detail
171 using SendServer = sendserver_detail::SendServer;
172 }}}
T getExt(const path_type &param, bool throwIfMissing=true) const
get a value from the config
Definition: Config.hpp:238
class to hold an hmbdc configuration
Definition: Config.hpp:44
Definition: TypeTagSet.hpp:138
Definition: TypeTagSet.hpp:198
hmbdc::pattern::MonoLockFreeBuffer::iterator runOnce(hmbdc::pattern::MonoLockFreeBuffer::iterator begin, hmbdc::pattern::MonoLockFreeBuffer::iterator end) HMBDC_RESTRICT
run the server&#39;s async send function and decide which items in buffer can be release ...
Definition: SendServer.hpp:78
Definition: Base.hpp:12
Definition: LockFreeBufferMisc.hpp:89