XCache is a fast, stable PHP opcode cacher that has been proven and is now running on production servers under high load. https://xcache.lighttpd.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

408 lines
8.4 KiB

  1. #ifdef TEST
  2. #include <limits.h>
  3. #include <stdio.h>
  4. # define XCACHE_DEBUG
  5. #else
  6. #include <php.h>
  7. #endif
  8. #ifdef XCACHE_DEBUG
  9. # define ALLOC_DEBUG_BLOCK_CHECK
  10. #endif
  11. #include <assert.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #define XC_MEMBLOCK_IMPL _xc_mem_block_t
  15. #define XC_MEM_IMPL _xc_mem_mem_t
  16. #include "xc_shm.h"
  17. #include "xc_utils.h"
  18. #include "util/xc_align.h"
  19. #include "util/xc_trace.h"
  20. #if 0
  21. #undef ALLOC_DEBUG_BLOCK_CHECK
  22. #endif
  23. #define CHAR_PTR(p) ((char *) (p))
  24. #define PADD(p, a) (CHAR_PTR(p) + a)
  25. #define PSUB(p1, p2) (CHAR_PTR(p1) - CHAR_PTR(p2))
  26. /* {{{ mem */
  27. struct _xc_mem_block_t {
  28. #ifdef ALLOC_DEBUG_BLOCK_CHECK
  29. unsigned int magic;
  30. #endif
  31. xc_memsize_t size; /* reserved even after alloc */
  32. xc_block_t *next; /* not used after alloc */
  33. };
  34. struct _xc_mem_mem_t {
  35. const xc_mem_handlers_t *handlers;
  36. xc_shm_t *shm;
  37. xc_memsize_t size;
  38. xc_memsize_t avail; /* total free */
  39. xc_block_t headblock[1]; /* just as a pointer to first block*/
  40. };
  41. #ifndef XtOffsetOf
  42. # include <linux/stddef.h>
  43. # define XtOffsetOf(s_type, field) offsetof(s_type, field)
  44. #endif
  45. #define SizeOf(type, field) sizeof( ((type *) 0)->field )
  46. #define BLOCK_HEADER_SIZE() (ALIGN( XtOffsetOf(xc_block_t, size) + SizeOf(xc_block_t, size) ))
  47. #define BLOCK_MAGIC ((unsigned int) 0x87655678)
  48. /* }}} */
  49. static inline void xc_block_setup(xc_block_t *b, xc_memsize_t size, xc_block_t *next) /* {{{ */
  50. {
  51. #ifdef ALLOC_DEBUG_BLOCK_CHECK
  52. b->magic = BLOCK_MAGIC;
  53. #endif
  54. b->size = size;
  55. b->next = next;
  56. }
  57. /* }}} */
  58. #ifdef ALLOC_DEBUG_BLOCK_CHECK
  59. static void xc_block_check(xc_block_t *b) /* {{{ */
  60. {
  61. if (b->magic != BLOCK_MAGIC) {
  62. fprintf(stderr, "0x%X != 0x%X magic wrong \n", b->magic, BLOCK_MAGIC);
  63. }
  64. }
  65. /* }}} */
  66. #else
  67. # define xc_block_check(b) do { } while(0)
  68. #endif
  69. static XC_MEM_MALLOC(xc_mem_malloc) /* {{{ */
  70. {
  71. xc_block_t *prev, *cur;
  72. xc_block_t *newb, *b;
  73. xc_memsize_t realsize;
  74. xc_memsize_t minsize;
  75. void *p;
  76. /* [xc_block_t:size|size] */
  77. realsize = BLOCK_HEADER_SIZE() + size;
  78. /* realsize is ALIGNed so next block start at ALIGNed address */
  79. realsize = ALIGN(realsize);
  80. TRACE("avail: %lu (%luKB). Allocate size: %lu realsize: %lu (%luKB)"
  81. , mem->avail, mem->avail / 1024
  82. , size
  83. , realsize, realsize / 1024
  84. );
  85. do {
  86. p = NULL;
  87. if (mem->avail < realsize) {
  88. TRACE("%s", " oom");
  89. break;
  90. }
  91. b = NULL;
  92. minsize = ULONG_MAX;
  93. /* prev|cur */
  94. for (prev = mem->headblock; prev->next; prev = cur) {
  95. /* while (prev->next != 0) { */
  96. cur = prev->next;
  97. xc_block_check(cur);
  98. if (cur->size == realsize) {
  99. /* found a perfect fit, stop searching */
  100. b = prev;
  101. break;
  102. }
  103. /* make sure we can split on the block */
  104. else if (cur->size > (sizeof(xc_block_t) + realsize) &&
  105. cur->size < minsize) {
  106. /* cur is acceptable and memller */
  107. b = prev;
  108. minsize = cur->size;
  109. }
  110. prev = cur;
  111. }
  112. if (b == NULL) {
  113. TRACE("%s", " no fit chunk");
  114. break;
  115. }
  116. prev = b;
  117. cur = prev->next;
  118. p = PADD(cur, BLOCK_HEADER_SIZE());
  119. /* update the block header */
  120. mem->avail -= realsize;
  121. /* perfect fit, just unlink */
  122. if (cur->size == realsize) {
  123. prev->next = cur->next;
  124. TRACE(" perfect fit. Got: %p", p);
  125. break;
  126. }
  127. /* make new free block after alloced space */
  128. /* save, as it might be overwrited by newb (cur->size is ok) */
  129. b = cur->next;
  130. /* prev|cur |next=b */
  131. newb = (xc_block_t *)PADD(cur, realsize);
  132. xc_block_setup(newb, cur->size - realsize, b);
  133. cur->size = realsize;
  134. /* prev|cur|newb|next
  135. * `--^
  136. */
  137. TRACE(" -> avail: %lu (%luKB). new next: %p offset: %lu %luKB. Got: %p"
  138. , mem->avail, mem->avail / 1024
  139. , newb
  140. , PSUB(newb, mem), PSUB(newb, mem) / 1024
  141. , p
  142. );
  143. prev->next = newb;
  144. /* prev|cur|newb|next
  145. * `-----^
  146. */
  147. } while (0);
  148. return p;
  149. }
  150. /* }}} */
  151. static XC_MEM_FREE(xc_mem_free) /* {{{ return block size freed */
  152. {
  153. xc_block_t *cur, *b;
  154. int size;
  155. cur = (xc_block_t *) (CHAR_PTR(p) - BLOCK_HEADER_SIZE());
  156. TRACE("freeing: %p, size=%lu", p, cur->size);
  157. xc_block_check(cur);
  158. assert((char*)mem < (char*)cur && (char*)cur < (char*)mem + mem->size);
  159. /* find free block right before the p */
  160. b = mem->headblock;
  161. while (b->next != 0 && b->next < cur) {
  162. b = b->next;
  163. }
  164. /* restore block */
  165. cur->next = b->next;
  166. b->next = cur;
  167. size = cur->size;
  168. TRACE(" avail %lu (%luKB)", mem->avail, mem->avail / 1024);
  169. mem->avail += size;
  170. /* combine prev|cur */
  171. if (PADD(b, b->size) == (char *)cur) {
  172. b->size += cur->size;
  173. b->next = cur->next;
  174. cur = b;
  175. TRACE("%s", " combine prev");
  176. }
  177. /* combine cur|next */
  178. b = cur->next;
  179. if (PADD(cur, cur->size) == (char *)b) {
  180. cur->size += b->size;
  181. cur->next = b->next;
  182. TRACE("%s", " combine next");
  183. }
  184. TRACE(" -> avail %lu (%luKB)", mem->avail, mem->avail / 1024);
  185. return size;
  186. }
  187. /* }}} */
  188. static XC_MEM_CALLOC(xc_mem_calloc) /* {{{ */
  189. {
  190. xc_memsize_t realsize = memb * size;
  191. void *p = xc_mem_malloc(mem, realsize);
  192. if (p) {
  193. memset(p, 0, realsize);
  194. }
  195. return p;
  196. }
  197. /* }}} */
  198. static XC_MEM_REALLOC(xc_mem_realloc) /* {{{ */
  199. {
  200. void *newp = xc_mem_malloc(mem, size);
  201. if (p && newp) {
  202. memcpy(newp, p, size);
  203. xc_mem_free(mem, p);
  204. }
  205. return newp;
  206. }
  207. /* }}} */
  208. static XC_MEM_STRNDUP(xc_mem_strndup) /* {{{ */
  209. {
  210. void *p = xc_mem_malloc(mem, len + 1);
  211. if (p) {
  212. memcpy(p, str, len + 1);
  213. }
  214. return p;
  215. }
  216. /* }}} */
  217. static XC_MEM_STRDUP(xc_mem_strdup) /* {{{ */
  218. {
  219. return xc_mem_strndup(mem, str, strlen(str));
  220. }
  221. /* }}} */
  222. static XC_MEM_AVAIL(xc_mem_avail) /* {{{ */
  223. {
  224. return mem->avail;
  225. }
  226. /* }}} */
  227. static XC_MEM_SIZE(xc_mem_size) /* {{{ */
  228. {
  229. return mem->size;
  230. }
  231. /* }}} */
  232. static XC_MEM_FREEBLOCK_FIRST(xc_mem_freeblock_first) /* {{{ */
  233. {
  234. return mem->headblock->next;
  235. }
  236. /* }}} */
  237. XC_MEM_FREEBLOCK_NEXT(xc_mem_freeblock_next) /* {{{ */
  238. {
  239. return block->next;
  240. }
  241. /* }}} */
  242. XC_MEM_BLOCK_SIZE(xc_mem_block_size) /* {{{ */
  243. {
  244. return block->size;
  245. }
  246. /* }}} */
  247. XC_MEM_BLOCK_OFFSET(xc_mem_block_offset) /* {{{ */
  248. {
  249. return ((char *) block) - ((char *) mem);
  250. }
  251. /* }}} */
  252. static XC_MEM_INIT(xc_mem_init) /* {{{ */
  253. {
  254. xc_block_t *b;
  255. #define MINSIZE (ALIGN(sizeof(xc_mem_t)) + sizeof(xc_block_t))
  256. /* requires at least the header and 1 tail block */
  257. if (size < MINSIZE) {
  258. fprintf(stderr, "xc_mem_init requires %lu bytes at least\n", (unsigned long) MINSIZE);
  259. return NULL;
  260. }
  261. TRACE("size=%lu", size);
  262. mem->shm = shm;
  263. mem->size = size;
  264. mem->avail = size - MINSIZE;
  265. /* pointer to first block, right after ALIGNed header */
  266. b = mem->headblock;
  267. xc_block_setup(b, 0, (xc_block_t *) PADD(mem, ALIGN(sizeof(xc_mem_t))));
  268. /* first block*/
  269. b = b->next;
  270. xc_block_setup(b, mem->avail, 0);
  271. #undef MINSIZE
  272. return mem;
  273. }
  274. /* }}} */
  275. static XC_MEM_DESTROY(xc_mem_destroy) /* {{{ */
  276. {
  277. }
  278. /* }}} */
  279. #ifdef TEST
  280. /* {{{ */
  281. #undef CHECK
  282. #define CHECK(a, msg) do { if ((a) == NULL) { puts(msg); return -1; } } while (0)
  283. #include <time.h>
  284. int main()
  285. {
  286. int count = 0;
  287. void *p;
  288. void *memory;
  289. xc_mem_t *mem;
  290. void **ptrs;
  291. int size, i;
  292. #if 0
  293. fprintf(stderr, "%s", "Input test size: ");
  294. scanf("%d", &size);
  295. #else
  296. size = 100;
  297. #endif
  298. CHECK(memory = malloc(size), "OOM");
  299. CHECK(ptrs = malloc(size * sizeof(void*)), "OOM");
  300. CHECK(mem = xc_mem_init(memory, size), "Failed init memory allocator");
  301. while ((p = xc_mem_malloc(mem, 1))) {
  302. ptrs[count ++] = p;
  303. }
  304. fprintf(stderr, "count=%d, random freeing\n", count);
  305. srandom(time(NULL));
  306. while (count) {
  307. i = (random() % count);
  308. fprintf(stderr, "freeing %d: ", i);
  309. xc_mem_free(mem, ptrs[i]);
  310. ptrs[i] = ptrs[count - 1];
  311. count --;
  312. }
  313. free(ptrs);
  314. free(memory);
  315. return 0;
  316. }
  317. /* }}} */
  318. #endif
  319. typedef struct {
  320. const char *name;
  321. const xc_mem_handlers_t *handlers;
  322. } xc_mem_scheme_t;
  323. static xc_mem_scheme_t xc_mem_schemes[10];
  324. int xc_mem_scheme_register(const char *name, const xc_mem_handlers_t *handlers) /* {{{ */
  325. {
  326. int i;
  327. for (i = 0; i < 10; i ++) {
  328. if (!xc_mem_schemes[i].name) {
  329. xc_mem_schemes[i].name = name;
  330. xc_mem_schemes[i].handlers = handlers;
  331. return 1;
  332. }
  333. }
  334. return 0;
  335. }
  336. /* }}} */
  337. const xc_mem_handlers_t *xc_mem_scheme_find(const char *name) /* {{{ */
  338. {
  339. int i;
  340. for (i = 0; i < 10 && xc_mem_schemes[i].name; i ++) {
  341. if (strcmp(xc_mem_schemes[i].name, name) == 0) {
  342. return xc_mem_schemes[i].handlers;
  343. }
  344. }
  345. return NULL;
  346. }
  347. /* }}} */
  348. static xc_mem_handlers_t xc_mem_mem_handlers = XC_MEM_HANDLERS(mem);
  349. void xc_shm_mem_init() /* {{{ */
  350. {
  351. memset(xc_mem_schemes, 0, sizeof(xc_mem_schemes));
  352. if (xc_mem_scheme_register("mem", &xc_mem_mem_handlers) == 0) {
  353. zend_error(E_ERROR, "XCache: failed to register mem mem_scheme");
  354. }
  355. }
  356. /* }}} */