SWC-DB  v0.5.12 C++ documentations
SWC-DB© (Super Wide Column Database) - High Performance Scalable Database (https://github.com/kashirin-alex/swc-db)
Resource_Mem.h
Go to the documentation of this file.
1 /*
2  * SWC-DB© Copyright since 2019 Alex Kashirin <kashirin.alex@gmail.com>
3  * License details at <https://github.com/kashirin-alex/swc-db/#license>
4  */
5 
6 
7 #ifndef swcdb_common_sys_Resources_Mem_h
8 #define swcdb_common_sys_Resources_Mem_h
9 
10 
11 #if defined(__MINGW64__) || defined(_WIN32)
12 #include <sysinfoapi.h>
13 #else
14 #include <sys/sysinfo.h>
15 #include <fstream>
16 #endif
17 
18 
19 #if defined TCMALLOC_MINIMAL || defined TCMALLOC
20 #include <gperftools/malloc_extension.h>
21 
22 #elif defined MIMALLOC
23 #include <mimalloc.h>
24 
25 #elif defined JEMALLOC
26 #include <jemalloc/jemalloc.h>
27 
28 #else
29 #include <malloc.h>
30 
31 #endif
32 
33 
34 
35 namespace SWC { namespace System {
36 
37 
38 class Mem {
39  static const uint32_t FAIL_MS = 10;
40  static const uint32_t BASE_MS = 250;
41  static const uint32_t URGENT_MS = 100;
42  static const uint32_t MAX_MS = 5000;
43  static const uint32_t RELEASE_NORMAL_MS = 500;
44  static const uint32_t RELEASE_URGENT_MS = 200;
45 
46  public:
47  typedef Mem* Ptr;
48  typedef std::function<size_t(size_t)> ReleaseCall_t;
52 
62 
63  SWC_SHOULD_NOT_INLINE
65  Config::Property::Value_int32_g::Ptr ram_percent_reserved,
66  Config::Property::Value_int32_g::Ptr ram_release_rate,
67  Notifier* a_notifier,
68  ReleaseCall_t&& a_release_call=nullptr) noexcept
69  : cfg_percent_allowed(ram_percent_allowed),
70  cfg_percent_reserved(ram_percent_reserved),
71  cfg_release_rate(ram_release_rate),
72  free(0), used(0), used_reg(0), used_releasable(0), used_future(0),
73  total(0), allowed(0), reserved(0), mem_buff_ms(0),
74  notifier(a_notifier), release_call(std::move(a_release_call)),
76  chk_release_ts(0) {
77 
78  #if !defined(__MINGW64__) && !defined(_WIN32)
79  page_size = sysconf(_SC_PAGE_SIZE);
80  #endif
81 
82  #if defined TCMALLOC_MINIMAL || defined TCMALLOC
83  release_rate_default = MallocExtension::instance()->GetMemoryReleaseRate();
84  if(release_rate_default < 0)
85  release_rate_default = 0;
86  #else
87  (void)cfg_release_rate->get(); // in-case unused
88  #endif
89  }
90 
91  Mem(const Mem&) = delete;
92  Mem(Mem&&) = delete;
93  Mem& operator=(const Mem&) = delete;
94  Mem& operator=(Mem&&) = delete;
95 
96  ~Mem() noexcept { }
97 
98  SWC_SHOULD_NOT_INLINE
99  uint64_t check(uint64_t ts) noexcept {
100  try {
101  if(ts >= (chk_stat_ts + (is_low_mem_state() ? URGENT_MS
102  : MAX_MS))) {
103  _check_malloc(ts);
104  if(!_check_stat())
105  return FAIL_MS;
106  chk_stat_ts = ts;
107  }
108 
109  if(ts >= (chk_statm_ts + (is_low_mem_state() ? URGENT_MS
110  : mem_buff_ms))) {
111  _check_malloc(ts);
112  if(!_check_statm())
113  return FAIL_MS;
114  chk_statm_ts = ts;
115  }
116 
118  : RELEASE_NORMAL_MS))) {
119  _check_malloc(ts);
120  _release();
121  chk_release_ts = ts;
122  }
123 
124  _check_malloc(ts);
125 
126  if(is_low_mem_state() && chk_low_state_ts <= ts) {
127  chk_low_state_ts = ts + 10000;
128  SWC_LOG_OUT(
129  LOG_WARN,
130  print(SWC_LOG_OSTREAM << "Low-Memory state ");
131  );
132  }
133  } catch(...) {
135  return FAIL_MS;
136  }
137  return BASE_MS;
138  }
139 
140 
142  bool is_low_mem_state() const noexcept {
143  return free < reserved;
144  }
145 
147  size_t need_ram() const noexcept {
148  ssize_t need = reserved;
149  need -= free;
150  if(need < 0) {
151  need = used;
152  need -= allowed;
153  if(need < 0) {
154  need = used_reg;
155  need += used_future;
156  need -= allowed;
157  if(need < 0)
158  return 0;
159  }
160  }
161  ssize_t _releasable = used_releasable;
162  return _releasable > need ? need : _releasable;
163  }
164 
166  size_t avail_ram() const noexcept {
167  if(is_low_mem_state() || used > allowed)
168  return 0;
169  ssize_t _allowed = allowed;
170  _allowed -= used_reg;
171  _allowed -= used_future;
172  return _allowed > 0 ? _allowed : 0;
173  }
174 
176  bool need_ram(uint32_t sz) const noexcept {
177  return is_low_mem_state() ||
178  free < (sz * 2) ||
179  (used_reg + used_future + sz) > allowed ||
180  (used + sz) > allowed;
181  }
182 
183 
185  void more_mem_future(size_t sz) noexcept {
187  }
189  void less_mem_future(size_t sz) noexcept {
191  /*
192  size_t was = used_future.fetch_sub(sz);
193  if(sz > was)
194  SWC_LOGF(LOG_WARN,
195  "less_mem_future OVERFLOW was=" SWC_FMT_LU " less=" SWC_FMT_LU,
196  was, sz);
197  */
198  }
199 
200 
202  void more_mem_releasable(size_t sz) noexcept {
204  }
206  void less_mem_releasable(size_t sz) noexcept {
208  /*
209  size_t was = used_releasable.fetch_sub(sz);
210  if(sz > was)
211  SWC_LOGF(LOG_WARN,
212  "less_mem_releasable OVERFLOW was=" SWC_FMT_LU " less=" SWC_FMT_LU,
213  was, sz);
214  */
215  }
217  void adj_mem_releasable(ssize_t sz) noexcept {
219  /*
220  size_t was = used_releasable.fetch_add(sz);
221  if(sz < 0 && size_t(-sz) > was)
222  SWC_LOGF(LOG_WARN,
223  "adj_mem_releasable OVERFLOW was=" SWC_FMT_LU " adj=" SWC_FMT_LD,
224  was, sz);
225  */
226  }
227 
228 
230  uint32_t available_mem_mb() const noexcept {
231  return (total - reserved) / 1024 / 1024;
232  }
233 
234  void print(std::ostream& out, size_t base = 1048576) const {
235  out << "Res(total=" << (total/base)
236  << " free=" << (free/base)
237  << " bytes-used=" << used_releasable << '/' << used_reg << '/' << used
238  << " allowed=" << (allowed/base)
239  << " reserved=" << (reserved/base) << ')';
240  }
241 
242  private:
243 
244  SWC_SHOULD_NOT_INLINE
245  bool _check_stat() {
246  if(notifier)
248 
249  #if defined(__MINGW64__) || defined(_WIN32)
250  MEMORYSTATUSEX statex;
251  statex.dwLength = sizeof (statex);
252  if(!GlobalMemoryStatusEx (&statex) || !statex.ullTotalPhys || !statex.ullAvailPhys)
253  return false;
254  total.store(statex.ullTotalPhys);
255  free.store(statex.ullAvailPhys);
256 
257  #else
258  std::ifstream buffer("/proc/meminfo");
259  if(!buffer.is_open())
260  return false;
261 
262  size_t sz = 0;
263  std::string tmp;
264  uint8_t looking(2);
265  Core::Atomic<size_t>* metric(nullptr);
266  do {
267  buffer >> tmp;
268  if(tmp.length() == 9 &&
269  Condition::str_eq(tmp.c_str(), "MemTotal:", 9)) {
270  metric = &total;
271  } else
272  if(tmp.length() == 13 &&
273  Condition::str_eq(tmp.c_str(), "MemAvailable:", 13)) {
274  metric = &free;
275  }
276  if(metric) {
277  buffer >> sz >> tmp;
278  metric->store(
279  sz * (tmp.front() == 'k'
280  ? 1024
281  : (tmp.front() == 'm' ? 1048576 : 0))
282  );
283  metric = nullptr;
284  --looking;
285  }
286  } while (looking && !buffer.eof());
287  buffer.close();
288  #endif
289 
290  size_t _allowed = (total/100) * cfg_percent_allowed->get();
291  allowed.store(_allowed);
293 
294  _allowed /= 3000;
295  mem_buff_ms.store(_allowed > MAX_MS ? MAX_MS : _allowed);
296 
297  if(notifier)
299  return true;
300  }
301 
302  SWC_SHOULD_NOT_INLINE
303  bool _check_statm() {
304  size_t rss = 0;
305  #if defined(__MINGW64__) || defined(_WIN32)
306  MEMORYSTATUSEX statex;
307  statex.dwLength = sizeof (statex);
308  if(!GlobalMemoryStatusEx (&statex) || !statex.ullTotalPhys || !statex.ullAvailPhys)
309  return false;
310  rss = statex.ullTotalPhys - statex.ullAvailPhys;
311 
312  #else
313  std::ifstream buffer("/proc/self/statm");
314  if(!buffer.is_open())
315  return false;
316  size_t sz = 0;
317  buffer >> sz >> rss;
318  buffer.close();
319  rss *= page_size;
320  #endif
321 
322  used.store(used > allowed || used > rss ? (used + rss) / 2 : rss);
323  if(notifier)
324  notifier->rss_used(rss);
325  return true;
326  }
327 
328  SWC_SHOULD_NOT_INLINE
329  bool _check_malloc(uint64_t ts) {
330  size_t bytes;
331  #if defined TCMALLOC_MINIMAL || defined TCMALLOC
332  // without metadata_bytes
333  if(!MallocExtension::instance()->GetNumericProperty(
334  "generic.current_allocated_bytes", &bytes))
335  return false;
336  /*
337  MallocExtension::TCMallocStats stats;
338  MallocExtension::instance()->ExtractStats(&stats, nullptr, nullptr, nullptr);
339  bytes = stats.pageheap.system_bytes;
340  bytes += stats.metadata_bytes;
341  // bytes =~ rss
342  bytes -= stats.thread_bytes;
343  bytes -= stats.central_bytes;
344  bytes -= stats.transfer_bytes;
345  bytes -= stats.pageheap.free_bytes;
346  bytes -= stats.pageheap.unmapped_bytes;
347  */
348 
349  /*
350  #elif defined MIMALLOC
351  auto h = mi_heap_get_default();
352  stats = h->tld->stats;
353  */
354 
355  #elif defined JEMALLOC
356  if(mallctl("epoch", NULL, NULL, &ts, sizeof(ts)))
357  return false;
358  size_t _bytes;
359  size_t sz = sizeof(size_t);
360  if(mallctl("stats.active", &_bytes, &sz, NULL, 0))
361  return false;
362  bytes = _bytes;
363  if(mallctl("stats.metadata", &_bytes, &sz, NULL, 0))
364  return false;
365  bytes += _bytes;
366 
367  #elif defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 33
368  struct mallinfo2 mi;
369  mi = ::mallinfo2();
370  bytes = mi.uordblks;
371 
372  #elif defined(__MINGW64__) || defined(_WIN32)
373  MEMORYSTATUSEX statex;
374  statex.dwLength = sizeof (statex);
375  if(!GlobalMemoryStatusEx (&statex) || !statex.ullTotalPhys || !statex.ullAvailPhys)
376  return false;
377  bytes = statex.ullTotalPhys - statex.ullAvailPhys;
378 
379  #elif defined(SWC_ENABLE_SANITIZER)
380  bytes = used;
381 
382  #else
383  struct mallinfo mi;
384  static_assert(
385  sizeof(mi.uordblks) == sizeof(size_t),
386  "Unsupported mallinfo size-type"
387  );
388  mi = ::mallinfo();
389  bytes = mi.uordblks;
390  #endif
391 
392  used_reg.store(bytes);
393  (void)ts;
394  return true;
395  }
396 
397  SWC_SHOULD_NOT_INLINE
398  void _release() {
399  size_t bytes = release_call ? need_ram() : 0;
400  if(!bytes && !is_low_mem_state())
401  return;
402 
403  if(bytes) {
404  size_t released = release_call(bytes);
407  SWC_LOG_OSTREAM << " mem-released=" << released << '/' << bytes;
408  );
409  }
410 
411  #if defined TCMALLOC_MINIMAL || defined TCMALLOC
412  if(used > allowed || is_low_mem_state()) {
413  auto inst = MallocExtension::instance();
414  inst->SetMemoryReleaseRate(cfg_release_rate->get());
415  inst->ReleaseFreeMemory();
416  inst->SetMemoryReleaseRate(release_rate_default);
417  }
418 
419  #elif defined MIMALLOC
420  if(used > allowed || is_low_mem_state()) {
421  mi_collect(true);
422  }
423 
424  #elif defined JEMALLOC
425  // ...
426 
427  #elif defined(__MINGW64__) || defined(_WIN32)
428  if(used > allowed || is_low_mem_state())
429  _heapmin();
430 
431  #else
432  ssize_t keep = reserved;
433  keep -= free;
434  if(keep > 0) {
435  keep = allowed - keep;
436  } else if(used > allowed) {
437  keep = allowed;
438  }
439  if(used > allowed || is_low_mem_state())
440  ::malloc_trim(keep > 0 ? keep : 0);
441 
442  #endif
443  }
444 
447 
448  #if !defined(__MINGW64__) && !defined(_WIN32)
449  uint32_t page_size = {};
450  #endif
451 
452  uint64_t chk_stat_ts;
453  uint64_t chk_statm_ts;
455  uint64_t chk_release_ts;
456 
457  #if defined TCMALLOC_MINIMAL || defined TCMALLOC
458  double release_rate_default = {};
459  #endif
460 
461 };
462 
463 
464 }}
465 
466 #endif // swcdb_common_sys_Resources_Mem_h
SWC::System::Mem::cfg_percent_reserved
Config::Property::Value_int32_g::Ptr cfg_percent_reserved
Definition: Resource_Mem.h:50
SWC::System::Mem::chk_statm_ts
uint64_t chk_statm_ts
Definition: Resource_Mem.h:453
SWC::System::Mem::avail_ram
SWC_CAN_INLINE size_t avail_ram() const noexcept
Definition: Resource_Mem.h:166
SWC::System::Mem::ReleaseCall_t
std::function< size_t(size_t)> ReleaseCall_t
Definition: Resource_Mem.h:48
SWC::System::Mem::FAIL_MS
static const uint32_t FAIL_MS
Definition: Resource_Mem.h:39
SWC_LOG_OSTREAM
#define SWC_LOG_OSTREAM
Definition: Logger.h:44
SWC::System::Mem::used
Core::Atomic< size_t > used
Definition: Resource_Mem.h:54
SWC::System::Mem::Mem
Mem(const Mem &)=delete
SWC::System::Notifier::rss_used
virtual void rss_used(size_t) noexcept=0
SWC_LOG_OUT
#define SWC_LOG_OUT(pr, _code_)
Definition: Logger.h:178
SWC::Core::Atomic< size_t >
SWC::System::Notifier
Definition: Resources.h:22
SWC::System::Notifier::rss_used_reg
virtual void rss_used_reg(size_t) noexcept=0
SWC::System::Mem::free
Core::Atomic< size_t > free
Definition: Resource_Mem.h:53
SWC::System::Mem::page_size
uint32_t page_size
Definition: Resource_Mem.h:449
SWC::System::Mem::available_mem_mb
SWC_CAN_INLINE uint32_t available_mem_mb() const noexcept
Definition: Resource_Mem.h:230
SWC::System::Mem::_check_statm
SWC_SHOULD_NOT_INLINE bool _check_statm()
Definition: Resource_Mem.h:303
SWC::System::Mem::operator=
Mem & operator=(Mem &&)=delete
SWC::System::Mem
Definition: Resource_Mem.h:38
SWC::System::Mem::allowed
Core::Atomic< size_t > allowed
Definition: Resource_Mem.h:59
SWC::System::Mem::more_mem_releasable
SWC_CAN_INLINE void more_mem_releasable(size_t sz) noexcept
Definition: Resource_Mem.h:202
SWC::System::Mem::_release
SWC_SHOULD_NOT_INLINE void _release()
Definition: Resource_Mem.h:398
SWC::Config::Property::Value_int32_g::get
SWC_CAN_INLINE int32_t get() const noexcept
Definition: Property.h:610
SWC::System::Mem::operator=
Mem & operator=(const Mem &)=delete
SWC::System::Mem::Mem
SWC_SHOULD_NOT_INLINE Mem(Config::Property::Value_int32_g::Ptr ram_percent_allowed, Config::Property::Value_int32_g::Ptr ram_percent_reserved, Config::Property::Value_int32_g::Ptr ram_release_rate, Notifier *a_notifier, ReleaseCall_t &&a_release_call=nullptr) noexcept
Definition: Resource_Mem.h:64
SWC::System::Mem::used_reg
Core::Atomic< size_t > used_reg
Definition: Resource_Mem.h:55
SWC::System::Mem::BASE_MS
static const uint32_t BASE_MS
Definition: Resource_Mem.h:40
SWC::Condition::str_eq
bool str_eq(const char *s1, const char *s2) noexcept SWC_ATTRIBS((SWC_ATTRIB_O3))
Definition: Comparators_basic.h:237
SWC::System::Mem::RELEASE_URGENT_MS
static const uint32_t RELEASE_URGENT_MS
Definition: Resource_Mem.h:44
SWC::System::Mem::Ptr
Mem * Ptr
Definition: Resource_Mem.h:47
SWC::Core::AtomicBase::store
constexpr SWC_CAN_INLINE void store(T v) noexcept
Definition: Atomic.h:37
SWC::System::Mem::total
Core::Atomic< size_t > total
Definition: Resource_Mem.h:58
SWC_CAN_INLINE
#define SWC_CAN_INLINE
Definition: Compat.h:102
SWC::LOG_DEBUG
@ LOG_DEBUG
Definition: Logger.h:36
SWC::System::Mem::chk_low_state_ts
uint64_t chk_low_state_ts
Definition: Resource_Mem.h:454
SWC
The SWC-DB C++ namespace 'SWC'.
Definition: main.cc:12
SWC::System::Mem::cfg_release_rate
Config::Property::Value_int32_g::Ptr cfg_release_rate
Definition: Resource_Mem.h:51
SWC::System::Mem::_check_malloc
SWC_SHOULD_NOT_INLINE bool _check_malloc(uint64_t ts)
Definition: Resource_Mem.h:329
SWC::System::Mem::less_mem_future
SWC_CAN_INLINE void less_mem_future(size_t sz) noexcept
Definition: Resource_Mem.h:189
SWC::System::Mem::need_ram
SWC_CAN_INLINE bool need_ram(uint32_t sz) const noexcept
Definition: Resource_Mem.h:176
SWC::System::Mem::notifier
Notifier * notifier
Definition: Resource_Mem.h:445
SWC::System::Mem::more_mem_future
SWC_CAN_INLINE void more_mem_future(size_t sz) noexcept
Definition: Resource_Mem.h:185
SWC::System::Mem::used_future
Core::Atomic< size_t > used_future
Definition: Resource_Mem.h:57
SWC::System::Mem::need_ram
SWC_CAN_INLINE size_t need_ram() const noexcept
Definition: Resource_Mem.h:147
SWC::System::Mem::print
void print(std::ostream &out, size_t base=1048576) const
Definition: Resource_Mem.h:234
SWC::System::Mem::~Mem
~Mem() noexcept
Definition: Resource_Mem.h:96
SWC::System::Mem::release_call
const ReleaseCall_t release_call
Definition: Resource_Mem.h:446
SWC::System::Mem::RELEASE_NORMAL_MS
static const uint32_t RELEASE_NORMAL_MS
Definition: Resource_Mem.h:43
SWC::System::Mem::cfg_percent_allowed
Config::Property::Value_int32_g::Ptr cfg_percent_allowed
Definition: Resource_Mem.h:49
SWC::System::Mem::chk_stat_ts
uint64_t chk_stat_ts
Definition: Resource_Mem.h:452
SWC::System::Mem::_check_stat
SWC_SHOULD_NOT_INLINE bool _check_stat()
Definition: Resource_Mem.h:245
SWC::System::Mem::check
SWC_SHOULD_NOT_INLINE uint64_t check(uint64_t ts) noexcept
Definition: Resource_Mem.h:99
SWC::System::Mem::chk_release_ts
uint64_t chk_release_ts
Definition: Resource_Mem.h:455
SWC::System::Mem::mem_buff_ms
Core::Atomic< size_t > mem_buff_ms
Definition: Resource_Mem.h:61
SWC::System::Mem::adj_mem_releasable
SWC_CAN_INLINE void adj_mem_releasable(ssize_t sz) noexcept
Definition: Resource_Mem.h:217
SWC::System::Mem::less_mem_releasable
SWC_CAN_INLINE void less_mem_releasable(size_t sz) noexcept
Definition: Resource_Mem.h:206
SWC::LOG_WARN
@ LOG_WARN
Definition: Logger.h:33
SWC::System::Mem::Mem
Mem(Mem &&)=delete
SWC::System::Mem::is_low_mem_state
SWC_CAN_INLINE bool is_low_mem_state() const noexcept
Definition: Resource_Mem.h:142
SWC::System::Notifier::rss_free
virtual void rss_free(size_t) noexcept=0
SWC::Core::Atomic::fetch_sub
constexpr SWC_CAN_INLINE T fetch_sub(T v) noexcept
Definition: Atomic.h:88
SWC::System::Mem::used_releasable
Core::Atomic< size_t > used_releasable
Definition: Resource_Mem.h:56
SWC::System::Mem::MAX_MS
static const uint32_t MAX_MS
Definition: Resource_Mem.h:42
SWC::Core::Atomic::fetch_add
constexpr SWC_CAN_INLINE T fetch_add(T v) noexcept
Definition: Atomic.h:93
SWC::System::Mem::reserved
Core::Atomic< size_t > reserved
Definition: Resource_Mem.h:60
SWC_LOG_CURRENT_EXCEPTION
#define SWC_LOG_CURRENT_EXCEPTION(_s_)
Definition: Exception.h:144
SWC::System::Mem::URGENT_MS
static const uint32_t URGENT_MS
Definition: Resource_Mem.h:41
SWC::Config::Property::Value_int32_g
Definition: Property.h:586