onewayalloc.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. #include "onewayalloc.h"
  2. // https://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html
  3. #define OWA_NATURAL_ALIGNMENT (sizeof(uintptr_t) * 2)
  4. typedef struct owa_page {
  5. size_t stats_pages;
  6. size_t stats_pages_size;
  7. size_t stats_mallocs_made;
  8. size_t stats_mallocs_size;
  9. size_t size; // the total size of the page
  10. size_t offset; // the first free byte of the page
  11. struct owa_page *next; // the next page on the list
  12. struct owa_page *last; // the last page on the list - we currently allocate on this
  13. } OWA_PAGE;
  14. // allocations need to be aligned to CPU register width
  15. // https://en.wikipedia.org/wiki/Data_structure_alignment
  16. static inline size_t natural_alignment(size_t size) {
  17. if(unlikely(size % OWA_NATURAL_ALIGNMENT))
  18. size = size + OWA_NATURAL_ALIGNMENT - (size % OWA_NATURAL_ALIGNMENT);
  19. return size;
  20. }
  21. // Create an OWA
  22. // Once it is created, the called may call the onewayalloc_mallocz()
  23. // any number of times, for any amount of memory.
  24. static OWA_PAGE *onewayalloc_create_internal(OWA_PAGE *head, size_t size_hint) {
  25. static size_t OWA_NATURAL_PAGE_SIZE = 0;
  26. if(unlikely(!OWA_NATURAL_PAGE_SIZE)) {
  27. long int page_size = sysconf(_SC_PAGE_SIZE);
  28. if (unlikely(page_size == -1))
  29. OWA_NATURAL_PAGE_SIZE = 4096;
  30. else
  31. OWA_NATURAL_PAGE_SIZE = page_size;
  32. }
  33. // our default page size
  34. size_t size = OWA_NATURAL_PAGE_SIZE;
  35. // make sure the new page will fit both the requested size
  36. // and the OWA_PAGE structure at its beginning
  37. size_hint += natural_alignment(sizeof(OWA_PAGE));
  38. // prefer the user size if it is bigger than our size
  39. if(size_hint > size) size = size_hint;
  40. // try to allocate half of the total we have allocated already
  41. if(likely(head)) {
  42. size_t optimal_size = head->stats_pages_size / 2;
  43. if(optimal_size > size) size = optimal_size;
  44. }
  45. // Make sure our allocations are always a multiple of the hardware page size
  46. if(size % OWA_NATURAL_PAGE_SIZE) size = size + OWA_NATURAL_PAGE_SIZE - (size % OWA_NATURAL_PAGE_SIZE);
  47. // OWA_PAGE *page = (OWA_PAGE *)netdata_mmap(NULL, size, MAP_ANONYMOUS|MAP_PRIVATE, 0);
  48. // if(unlikely(!page)) fatal("Cannot allocate onewayalloc buffer of size %zu", size);
  49. OWA_PAGE *page = (OWA_PAGE *)mallocz(size);
  50. page->size = size;
  51. page->offset = natural_alignment(sizeof(OWA_PAGE));
  52. page->next = page->last = NULL;
  53. if(unlikely(!head)) {
  54. // this is the first time we are called
  55. head = page;
  56. head->stats_pages = 0;
  57. head->stats_pages_size = 0;
  58. head->stats_mallocs_made = 0;
  59. head->stats_mallocs_size = 0;
  60. }
  61. else {
  62. // link this page into our existing linked list
  63. head->last->next = page;
  64. }
  65. head->last = page;
  66. head->stats_pages++;
  67. head->stats_pages_size += size;
  68. return page;
  69. }
  70. ONEWAYALLOC *onewayalloc_create(size_t size_hint) {
  71. return (ONEWAYALLOC *)onewayalloc_create_internal(NULL, size_hint);
  72. }
  73. void *onewayalloc_mallocz(ONEWAYALLOC *owa, size_t size) {
  74. OWA_PAGE *head = (OWA_PAGE *)owa;
  75. OWA_PAGE *page = head->last;
  76. // update stats
  77. head->stats_mallocs_made++;
  78. head->stats_mallocs_size += size;
  79. // make sure the size is aligned
  80. size = natural_alignment(size);
  81. if(unlikely(page->size - page->offset < size)) {
  82. // we don't have enough space to fit the data
  83. // let's get another page
  84. page = onewayalloc_create_internal(head, (size > page->size)?size:page->size);
  85. }
  86. char *mem = (char *)page;
  87. mem = &mem[page->offset];
  88. page->offset += size;
  89. return (void *)mem;
  90. }
  91. void *onewayalloc_callocz(ONEWAYALLOC *owa, size_t nmemb, size_t size) {
  92. size_t total = nmemb * size;
  93. void *mem = onewayalloc_mallocz(owa, total);
  94. memset(mem, 0, total);
  95. return mem;
  96. }
  97. char *onewayalloc_strdupz(ONEWAYALLOC *owa, const char *s) {
  98. size_t size = strlen(s) + 1;
  99. char *d = onewayalloc_mallocz((OWA_PAGE *)owa, size);
  100. memcpy(d, s, size);
  101. return d;
  102. }
  103. void *onewayalloc_memdupz(ONEWAYALLOC *owa, const void *src, size_t size) {
  104. void *mem = onewayalloc_mallocz((OWA_PAGE *)owa, size);
  105. // memcpy() is way faster than strcpy() since it does not check for '\0'
  106. memcpy(mem, src, size);
  107. return mem;
  108. }
  109. void onewayalloc_freez(ONEWAYALLOC *owa __maybe_unused, const void *ptr __maybe_unused) {
  110. #ifdef NETDATA_INTERNAL_CHECKS
  111. // allow the caller to call us for a mallocz() allocation
  112. // so try to find it in our memory and if it is not there
  113. // log an error
  114. if (unlikely(!ptr))
  115. return;
  116. OWA_PAGE *head = (OWA_PAGE *)owa;
  117. OWA_PAGE *page;
  118. uintptr_t seeking = (uintptr_t)ptr;
  119. for(page = head; page ;page = page->next) {
  120. uintptr_t start = (uintptr_t)page;
  121. uintptr_t end = start + page->size;
  122. if(seeking >= start && seeking <= end) {
  123. // found it - it is ours
  124. // just return to let the caller think we actually did something
  125. return;
  126. }
  127. }
  128. // not found - it is not ours
  129. // let's free it with the system allocator
  130. error("ONEWAYALLOC: request to free address 0x%p that is not allocated by this OWA", ptr);
  131. #endif
  132. return;
  133. }
  134. void *onewayalloc_doublesize(ONEWAYALLOC *owa, const void *src, size_t oldsize) {
  135. size_t newsize = oldsize * 2;
  136. void *dst = onewayalloc_mallocz(owa, newsize);
  137. memcpy(dst, src, oldsize);
  138. onewayalloc_freez(owa, src);
  139. return dst;
  140. }
  141. void onewayalloc_destroy(ONEWAYALLOC *owa) {
  142. if(!owa) return;
  143. OWA_PAGE *head = (OWA_PAGE *)owa;
  144. //info("OWA: %zu allocations of %zu total bytes, in %zu pages of %zu total bytes",
  145. // head->stats_mallocs_made, head->stats_mallocs_size,
  146. // head->stats_pages, head->stats_pages_size);
  147. OWA_PAGE *page = head;
  148. while(page) {
  149. OWA_PAGE *p = page;
  150. page = page->next;
  151. // munmap(p, p->size);
  152. freez(p);
  153. }
  154. }