pool_alloc.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. #include "rsync.h"
  2. #define POOL_DEF_EXTENT (32 * 1024)
  3. struct alloc_pool
  4. {
  5. size_t size; /* extent size */
  6. size_t quantum; /* allocation quantum */
  7. struct pool_extent *extents; /* top extent is "live" */
  8. void (*bomb)(); /* function to call if
  9. * malloc fails */
  10. int flags;
  11. /* statistical data */
  12. unsigned long e_created; /* extents created */
  13. unsigned long e_freed; /* extents detroyed */
  14. int64 n_allocated; /* calls to alloc */
  15. int64 n_freed; /* calls to free */
  16. int64 b_allocated; /* cum. bytes allocated */
  17. int64 b_freed; /* cum. bytes freed */
  18. };
  19. struct pool_extent
  20. {
  21. void *start; /* starting address */
  22. size_t free; /* free bytecount */
  23. size_t bound; /* bytes bound by padding,
  24. * overhead and freed */
  25. struct pool_extent *next;
  26. };
  27. struct align_test {
  28. void *foo;
  29. int64 bar;
  30. };
  31. #define MINALIGN offsetof(struct align_test, bar)
  32. /* Temporarily cast a void* var into a char* var when adding an offset (to
  33. * keep some compilers from complaining about the pointer arithmetic). */
  34. #define PTR_ADD(b,o) ( (void*) ((char*)(b) + (o)) )
  35. alloc_pool_t
  36. pool_create(size_t size, size_t quantum, void (*bomb)(const char *), int flags)
  37. {
  38. struct alloc_pool *pool;
  39. if (!(pool = new(struct alloc_pool)))
  40. return pool;
  41. memset(pool, 0, sizeof (struct alloc_pool));
  42. pool->size = size /* round extent size to min alignment reqs */
  43. ? (size + MINALIGN - 1) & ~(MINALIGN - 1)
  44. : POOL_DEF_EXTENT;
  45. if (flags & POOL_INTERN) {
  46. pool->size -= sizeof (struct pool_extent);
  47. flags |= POOL_APPEND;
  48. }
  49. pool->quantum = quantum ? quantum : MINALIGN;
  50. pool->bomb = bomb;
  51. pool->flags = flags;
  52. return pool;
  53. }
  54. void
  55. pool_destroy(alloc_pool_t p)
  56. {
  57. struct alloc_pool *pool = (struct alloc_pool *) p;
  58. struct pool_extent *cur, *next;
  59. if (!pool)
  60. return;
  61. for (cur = pool->extents; cur; cur = next) {
  62. next = cur->next;
  63. free(cur->start);
  64. if (!(pool->flags & POOL_APPEND))
  65. free(cur);
  66. }
  67. free(pool);
  68. }
  69. void *
  70. pool_alloc(alloc_pool_t p, size_t len, const char *bomb_msg)
  71. {
  72. struct alloc_pool *pool = (struct alloc_pool *) p;
  73. if (!pool)
  74. return NULL;
  75. if (!len)
  76. len = pool->quantum;
  77. else if (pool->quantum > 1 && len % pool->quantum)
  78. len += pool->quantum - len % pool->quantum;
  79. if (len > pool->size)
  80. goto bomb_out;
  81. if (!pool->extents || len > pool->extents->free) {
  82. void *start;
  83. size_t free;
  84. size_t bound;
  85. size_t skew;
  86. size_t asize;
  87. struct pool_extent *ext;
  88. free = pool->size;
  89. bound = 0;
  90. asize = pool->size;
  91. if (pool->flags & POOL_APPEND)
  92. asize += sizeof (struct pool_extent);
  93. if (!(start = new_array(char, asize)))
  94. goto bomb_out;
  95. if (pool->flags & POOL_CLEAR)
  96. memset(start, 0, free);
  97. if (pool->flags & POOL_APPEND)
  98. ext = PTR_ADD(start, free);
  99. else if (!(ext = new(struct pool_extent)))
  100. goto bomb_out;
  101. if (pool->flags & POOL_QALIGN && pool->quantum > 1
  102. && (skew = (size_t)PTR_ADD(start, free) % pool->quantum)) {
  103. bound += skew;
  104. free -= skew;
  105. }
  106. ext->start = start;
  107. ext->free = free;
  108. ext->bound = bound;
  109. ext->next = pool->extents;
  110. pool->extents = ext;
  111. pool->e_created++;
  112. }
  113. pool->n_allocated++;
  114. pool->b_allocated += len;
  115. pool->extents->free -= len;
  116. return PTR_ADD(pool->extents->start, pool->extents->free);
  117. bomb_out:
  118. if (pool->bomb)
  119. (*pool->bomb)(bomb_msg);
  120. return NULL;
  121. }
  122. /* This function allows you to declare memory in the pool that you are done
  123. * using. If you free all the memory in a pool's extent, that extent will
  124. * be freed. */
  125. void
  126. pool_free(alloc_pool_t p, size_t len, void *addr)
  127. {
  128. struct alloc_pool *pool = (struct alloc_pool *)p;
  129. struct pool_extent *cur, *prev;
  130. if (!pool)
  131. return;
  132. if (!len)
  133. len = pool->quantum;
  134. else if (pool->quantum > 1 && len % pool->quantum)
  135. len += pool->quantum - len % pool->quantum;
  136. pool->n_freed++;
  137. pool->b_freed += len;
  138. for (prev = NULL, cur = pool->extents; cur; prev = cur, cur = cur->next) {
  139. if (addr >= cur->start
  140. && addr < PTR_ADD(cur->start, pool->size))
  141. break;
  142. }
  143. if (!cur)
  144. return;
  145. if (!prev) {
  146. /* The "live" extent is kept ready for more allocations. */
  147. if (cur->free + cur->bound + len >= pool->size) {
  148. size_t skew;
  149. if (pool->flags & POOL_CLEAR) {
  150. memset(PTR_ADD(cur->start, cur->free), 0,
  151. pool->size - cur->free);
  152. }
  153. cur->free = pool->size;
  154. cur->bound = 0;
  155. if (pool->flags & POOL_QALIGN && pool->quantum > 1
  156. && (skew = (size_t)PTR_ADD(cur->start, cur->free) % pool->quantum)) {
  157. cur->bound += skew;
  158. cur->free -= skew;
  159. }
  160. } else if (addr == PTR_ADD(cur->start, cur->free)) {
  161. if (pool->flags & POOL_CLEAR)
  162. memset(addr, 0, len);
  163. cur->free += len;
  164. } else
  165. cur->bound += len;
  166. } else {
  167. cur->bound += len;
  168. if (cur->free + cur->bound >= pool->size) {
  169. prev->next = cur->next;
  170. free(cur->start);
  171. if (!(pool->flags & POOL_APPEND))
  172. free(cur);
  173. pool->e_freed++;
  174. } else if (prev != pool->extents) {
  175. /* Move the extent to be the first non-live extent. */
  176. prev->next = cur->next;
  177. cur->next = pool->extents->next;
  178. pool->extents->next = cur;
  179. }
  180. }
  181. }
  182. /* This allows you to declare that the given address marks the edge of some
  183. * pool memory that is no longer needed. Any extents that hold only data
  184. * older than the boundary address are freed. NOTE: You MUST NOT USE BOTH
  185. * pool_free() and pool_free_old() on the same pool!! */
  186. void
  187. pool_free_old(alloc_pool_t p, void *addr)
  188. {
  189. struct alloc_pool *pool = (struct alloc_pool *)p;
  190. struct pool_extent *cur, *prev, *next;
  191. if (!pool || !addr)
  192. return;
  193. for (prev = NULL, cur = pool->extents; cur; prev = cur, cur = cur->next) {
  194. if (addr >= cur->start
  195. && addr < PTR_ADD(cur->start, pool->size))
  196. break;
  197. }
  198. if (!cur)
  199. return;
  200. if (addr == PTR_ADD(cur->start, cur->free)) {
  201. if (prev) {
  202. prev->next = NULL;
  203. next = cur;
  204. } else {
  205. size_t skew;
  206. /* The most recent live extent can just be reset. */
  207. if (pool->flags & POOL_CLEAR)
  208. memset(addr, 0, pool->size - cur->free);
  209. cur->free = pool->size;
  210. cur->bound = 0;
  211. if (pool->flags & POOL_QALIGN && pool->quantum > 1
  212. && (skew = (size_t)PTR_ADD(cur->start, cur->free) % pool->quantum)) {
  213. cur->bound += skew;
  214. cur->free -= skew;
  215. }
  216. next = cur->next;
  217. cur->next = NULL;
  218. }
  219. } else {
  220. next = cur->next;
  221. cur->next = NULL;
  222. }
  223. while ((cur = next) != NULL) {
  224. next = cur->next;
  225. free(cur->start);
  226. if (!(pool->flags & POOL_APPEND))
  227. free(cur);
  228. pool->e_freed++;
  229. }
  230. }
  231. /* If the current extent doesn't have "len" free space in it, mark it as full
  232. * so that the next alloc will start a new extent. If len is (size_t)-1, this
  233. * bump will always occur. The function returns a boundary address that can
  234. * be used with pool_free_old(), or a NULL if no memory is allocated. */
  235. void *
  236. pool_boundary(alloc_pool_t p, size_t len)
  237. {
  238. struct alloc_pool *pool = (struct alloc_pool *)p;
  239. struct pool_extent *cur;
  240. if (!pool || !pool->extents)
  241. return NULL;
  242. cur = pool->extents;
  243. if (cur->free < len) {
  244. cur->bound += cur->free;
  245. cur->free = 0;
  246. }
  247. return PTR_ADD(cur->start, cur->free);
  248. }
  249. #define FDPRINT(label, value) \
  250. do { \
  251. int len = snprintf(buf, sizeof buf, label, value); \
  252. if (write(fd, buf, len) != len) \
  253. ret = -1; \
  254. } while (0)
  255. #define FDEXTSTAT(ext) \
  256. do { \
  257. int len = snprintf(buf, sizeof buf, " %12ld %5ld\n", \
  258. (long)ext->free, (long)ext->bound); \
  259. if (write(fd, buf, len) != len) \
  260. ret = -1; \
  261. } while (0)
  262. int
  263. pool_stats(alloc_pool_t p, int fd, int summarize)
  264. {
  265. struct alloc_pool *pool = (struct alloc_pool *) p;
  266. struct pool_extent *cur;
  267. char buf[BUFSIZ];
  268. int ret = 0;
  269. if (!pool)
  270. return ret;
  271. FDPRINT(" Extent size: %12ld\n", (long) pool->size);
  272. FDPRINT(" Alloc quantum: %12ld\n", (long) pool->quantum);
  273. FDPRINT(" Extents created: %12ld\n", pool->e_created);
  274. FDPRINT(" Extents freed: %12ld\n", pool->e_freed);
  275. FDPRINT(" Alloc count: %12.0f\n", (double) pool->n_allocated);
  276. FDPRINT(" Free Count: %12.0f\n", (double) pool->n_freed);
  277. FDPRINT(" Bytes allocated: %12.0f\n", (double) pool->b_allocated);
  278. FDPRINT(" Bytes freed: %12.0f\n", (double) pool->b_freed);
  279. if (summarize)
  280. return ret;
  281. if (!pool->extents)
  282. return ret;
  283. if (write(fd, "\n", 1) != 1)
  284. ret = -1;
  285. for (cur = pool->extents; cur; cur = cur->next)
  286. FDEXTSTAT(cur);
  287. return ret;
  288. }