jmalloc.cpp 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052
  1. /*##############################################################################
  2. Copyright (C) 2011 HPCC Systems.
  3. All rights reserved. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ############################################################################## */
  14. #include "platform.h"
  15. #include "jlib.hpp"
  16. #include "jlog.hpp"
  17. #include "jbuff.hpp"
  18. #ifndef WIN32
  19. #include <sys/mman.h>
  20. #endif
  21. #include "jmalloc.hpp"
  22. #ifdef _DEBUG
  23. #define ASSERTEX(e) assertex(e)
  24. #else
  25. #define ASSERTEX(e)
  26. #endif
  27. static PointerArray osblkptrs;
  28. // Super (large block) manager
  29. #define NoCheck 0
  30. #define InitCheck 1
  31. #define PtrCheck 2
  32. /*
  33. * Checking mode of the heap manager
  34. *
  35. * NoCheck - No checking!
  36. * InitCheck - Initialise allocated and freed blocks of memory
  37. * PtrCheck - Check validity of each pointer that is freed. This
  38. * should catch invalid frees, multiple frees and
  39. * blocks which have been changed past the end of the
  40. * allocated size.
  41. */
  42. //#define HeapChecking NoCheck
  43. #define HeapChecking InitCheck
  44. //#define HeapChecking PtrCheck
  45. // Maximum number of bytes that will be non-OS allocated
  46. #define MaxSuperAllocSize 0xD000
  47. #define InitialiseMemory 0 // initialises malloc & free'd blocks
  48. #ifndef _DEBUG
  49. #undef HeapChecking
  50. #undef InitialiseMemory
  51. #define HeapChecking NoCheck
  52. #endif
  53. #if HeapChecking == NoCheck
  54. #undef InitialiseMemory
  55. #endif
  56. interface IWalkMem
  57. {
  58. virtual void inuse(const void *a,size32_t sz)=0;
  59. virtual void osblock(const void *a,size32_t sz)=0;
  60. };
  61. /*
  62. * The alignment that each memory block should have.
  63. * MUST be a power of 2
  64. */
  65. #define Alignment 8
  66. #define OSCHUNKSIZE 0x100000U // 1MB
  67. #define OSPAGESIZE (0x1000U)
  68. #define OSPAGEMASK (OSPAGESIZE-1)
  69. #define OSPAGEROUND(s) (((s)+OSPAGEMASK)&~OSPAGEMASK)
  70. #define MAXALLOCATESIZE 0x80000000U // 2GB (uses top bits for flag so don't increase!)
  71. #define MAXUSERALLOCATESIZE (MAXALLOCATESIZE-OSCHUNKSIZE-OSPAGESIZE)
  72. #define ISSUBALLOCATED(sz) (sz>MAXALLOCATESIZE)
  73. /* Get the aligned size of the information */
  74. #define DoAlign(size, align) ((size + align - 1) & (~(align - 1)))
  75. #define Align(Size) (DoAlign(Size, Alignment))
  76. /*
  77. * The minimum sensible size for a free block.
  78. * MUST be less than 256 - Alignment.
  79. * It must be at least Alignment(4 + CheckHeadSize + CheckTailSize
  80. */
  81. #define _MinFreeSize 32 // Should be Align(32)
  82. #if HeapChecking >= PtrCheck
  83. #define _MINFREESIZE (_MinFreeSize + 4)
  84. #undef MinFreeSize
  85. #define MinFreeSize (_MINFREESIZE)
  86. #else
  87. #define MinFreeSize _MinFreeSize
  88. #endif
  89. /******************** End of configurable constants ******************/
  90. /*
  91. * Check the constants are OK.
  92. */
  93. #if MaxSuperAllocSize > OSCHUNKSIZE
  94. #error "MaxSuperAllocSize is too large"
  95. #endif
  96. #if MinFreeSize >= (256 - Alignment)
  97. #error "MinFreeSize too large" MinFreeSize Alignment
  98. #endif
  99. /**************** Various constants and type definitions ***************/
  100. /* Following are macros so they can be used in #defines */
  101. #define BestFit 0
  102. #define FirstFit 1
  103. #define LargestFit 2
  104. #if HeapChecking < PtrCheck
  105. #define CheckHeadSize 0
  106. #define CheckTailSize 0
  107. #else
  108. #define CheckHeadSize sizeof(byte)
  109. #define CheckTailSize sizeof(byte)
  110. #endif
  111. /*
  112. * Tags which tag where each block came from - unusual values so we don't
  113. * normally hit on them by accident.
  114. */
  115. #define FreeBlockTag 0x34
  116. #define SuperAllocTag 0xa3
  117. #define OSAllocTag 0xe7
  118. #define BlockBoundTag 0x17
  119. #define HeadCheckVal 0x76
  120. #define CheckFillVal 0x4f
  121. #define FreeFillChar 0x66 // 'f'
  122. #define MallocFillChar 0x4d // 'M'
  123. #define SubFillChar 0x53 // 'S'
  124. /*
  125. * The types used to hold information about the heap....
  126. */
  127. typedef struct FreeListTag * FreeListPtr;
  128. typedef struct FreeListTag
  129. {
  130. FreeListPtr Prev;
  131. FreeListPtr Next;
  132. size32_t Size;
  133. byte * BlockPtr;
  134. } FreeListEnt;
  135. /*
  136. * The 'structures' used at the start and end of the allocated/free
  137. * blocks of memory. (Not actually used - here for information).
  138. */
  139. typedef struct BlockHeadTag
  140. {
  141. byte SizeChk;
  142. byte Tag;
  143. size32_t Size;
  144. } BlockHeadRec;
  145. typedef struct BlockTailTag
  146. {
  147. byte WriteChk;
  148. byte Tag;
  149. } BlockTailRec;
  150. /*****************************************************************
  151. ****************Set up several derived constants ****************
  152. *****************************************************************/
  153. #define HeadTagOff (sizeof(size32_t) + sizeof(byte))
  154. #define TailTagOff (CheckTailSize)
  155. #define HeadChkOff (HeadTagOff + CheckHeadSize)
  156. #define TailChkOff 0
  157. /* Number of extra bytes that occur at the start and end of the record */
  158. #define HeadExtraBytes (sizeof(size32_t) + sizeof(byte) + sizeof(unsigned short) + CheckHeadSize)
  159. #define TailExtraBytes (CheckTailSize + sizeof(byte))
  160. /* Total number of extra bytes for each allocated block. */
  161. #define BlockExtraSize (Align(HeadExtraBytes + TailExtraBytes))
  162. #define OSBlockExtra Align(BlockExtraSize + sizeof(FreeListPtr)) // this looks excessive to me
  163. #define OSPtrExtra Align(sizeof(FreeListPtr))
  164. /*
  165. ****************** Helpful macros to get at fields *****************
  166. */
  167. #define BlockSize(ptr) (*((size32_t *)((byte *)ptr - sizeof(size32_t))))
  168. #define UserLinkCount(ptr) (*((unsigned short *)((byte *)ptr - HeadTagOff-sizeof(unsigned short))))
  169. #define OwnerOffset(size) ((size) - sizeof(FreeListPtr))
  170. #define ListOwner(ptr, size) *((FreeListPtr *)((byte *)ptr + OwnerOffset(size)))
  171. #define e_corrupt_owner 1
  172. #define e_mangled_free_list 2
  173. #define e_bad_block_ptr 3
  174. #define e_size_inconsitancy 4
  175. #define e_tag_mismatch 5
  176. #define e_free_block_tag_corrupt 6
  177. #define e_unknown_os_block 7
  178. #define e_free_block_twice 8
  179. #define e_corrupt_unknown_block 9
  180. #define e_illegal_size_field 10
  181. #define e_alloc_too_big 11
  182. #define e_out_free_space 12
  183. #define e_inconsist_block_tag 13
  184. #define e_end_heap_overwritten 14
  185. #define e_corrupt_sub_block 15
  186. #define e_out_of_memory 28 // coincides with ENOSPC
  187. class jlib_thrown_decl CAllocatorOutOfMemException: public CInterface, implements IOutOfMemException
  188. {
  189. int errcode;
  190. memsize_t wanted;
  191. memsize_t got;
  192. public:
  193. IMPLEMENT_IINTERFACE;
  194. CAllocatorOutOfMemException(int _errcode,memsize_t _wanted,memsize_t _got)
  195. {
  196. errcode = _errcode;
  197. wanted = _wanted;
  198. got = _got;
  199. };
  200. int errorCode() const { return errcode; }
  201. StringBuffer & errorMessage(StringBuffer &str) const
  202. {
  203. str.append("JMalloc: Out of Memory (").append((offset_t)wanted);
  204. if (got)
  205. str.append(',').append((offset_t)got);
  206. return str.append(')');
  207. }
  208. MessageAudience errorAudience() const { return MSGAUD_user; }
  209. };
  210. static void HeapError(int errnum, const void * BlockPtr=NULL) // probably fatal
  211. {
  212. StringBuffer tmp;
  213. tmp.appendf("JMalloc Heap error %d (%p)",errnum, BlockPtr);
  214. ERRLOG("%s",tmp.str());
  215. PrintStackReport();
  216. throw MakeStringException(errnum,"%s",tmp.str());
  217. }
  218. //--------------------------------------------------------------------------------
  219. // Sub heap manager
  220. //--------------------------------------------------------------------------------
  221. #define SBOVERHEAD sizeof(short)
  222. #define MINSUBBLOCKSIZE sizeof(void *)
  223. #define SBPAGESIZE 4050 // default block size
  224. class CSubAllocatorBlock;
  225. struct CSubAllocatorBlockLinkHead
  226. {
  227. CSubAllocatorBlock *next; // next/prev must match CSubAllocatorBlockLink
  228. CSubAllocatorBlock *prev;
  229. };
  230. struct CSubAllocatorBlockData: public CSubAllocatorBlockLinkHead
  231. {
  232. void *freelist; // points to free chain (note data i.e. past startadj)
  233. unsigned short recsize; // doesn't includes startadj
  234. unsigned short usedcount;
  235. // { short startadj; byte[recsize] data; } startadj+&startadj = block begin
  236. };
  237. #define SBHEADERSIZE (sizeof(CSubAllocatorBlockData))
  238. #define FIRSTSUBELEM(blk) ((byte *)(blk))+SBHEADERSIZE;
  239. #define NEXTSUBELEMFREE(fl) (*(void **)(fl))
  240. class CSubAllocatorBlock : public CSubAllocatorBlockData
  241. {
  242. public:
  243. static inline CSubAllocatorBlock *base(const void * p)
  244. {
  245. byte *b = (byte *)p-sizeof(short);
  246. b += *(short *)b;
  247. return (CSubAllocatorBlock *)b;
  248. }
  249. bool subfree(void *p) // returns true if entire block gone
  250. {
  251. #if HeapChecking >= PtrCheck
  252. assertex(usedcount);
  253. #endif
  254. #if InitialiseMemory
  255. memset((byte *)p+sizeof(freelist), FreeFillChar, recsize-sizeof(freelist));
  256. #endif
  257. NEXTSUBELEMFREE(p) = freelist;
  258. freelist = p;
  259. return --usedcount==0;
  260. }
  261. inline bool full()
  262. {
  263. return freelist==NULL;
  264. }
  265. inline void *suballoc()
  266. {
  267. void *ret = freelist;
  268. if (ret) {
  269. freelist = NEXTSUBELEMFREE(freelist);
  270. usedcount++;
  271. #if InitialiseMemory
  272. memset(ret, SubFillChar, recsize);
  273. #endif
  274. }
  275. return ret;
  276. }
  277. void init(size32_t _recsize,unsigned num) // recs
  278. {
  279. assertex(SBHEADERSIZE==sizeof(CSubAllocatorBlock));
  280. recsize = (unsigned short)_recsize; // don't include startadj
  281. usedcount = 0;
  282. byte *p = FIRSTSUBELEM(this);
  283. freelist = p+sizeof(short);
  284. loop {
  285. *(short *)p = (short)((byte *)this-p);
  286. p += SBOVERHEAD;
  287. if (--num) { // not last
  288. NEXTSUBELEMFREE(p) = p+recsize+SBOVERHEAD; // forward chain
  289. p += recsize;
  290. }
  291. else {
  292. NEXTSUBELEMFREE(p) = NULL;
  293. break;
  294. }
  295. }
  296. }
  297. inline void unlink()
  298. {
  299. next->prev = prev;
  300. prev->next = next;
  301. }
  302. bool isFree(const void *a)
  303. {
  304. const void *p = freelist;
  305. while (p) {
  306. if (p==a)
  307. return true;
  308. p = NEXTSUBELEMFREE(p);
  309. }
  310. return false;
  311. }
  312. void walk(IWalkMem &walker)
  313. {
  314. byte *p = FIRSTSUBELEM(this);
  315. unsigned n = usedcount;
  316. while (n) {
  317. p += SBOVERHEAD;
  318. if (!isFree(p)) {
  319. n--;
  320. walker.inuse(p,recsize);
  321. }
  322. p += recsize;
  323. }
  324. }
  325. };
  326. struct CSubAllocatorBlockLink: public CSubAllocatorBlockLinkHead // for head of chain
  327. {
  328. inline CSubAllocatorBlockLink()
  329. {
  330. next = (CSubAllocatorBlock *)this;
  331. prev = (CSubAllocatorBlock *)this;
  332. }
  333. inline bool isempty() { return next==(CSubAllocatorBlock *)this; }
  334. inline void prepend(CSubAllocatorBlock &link)
  335. {
  336. link.next = next;
  337. link.prev = (CSubAllocatorBlock *)this;
  338. link.next->prev = &link;
  339. next = &link;
  340. }
  341. inline void append(CSubAllocatorBlock &link)
  342. {
  343. link.next = (CSubAllocatorBlock *)this;
  344. link.prev = (CSubAllocatorBlock *)prev;
  345. link.prev->next = &link;
  346. prev = &link;
  347. }
  348. };
  349. class CSuperAllocator;
  350. class CSubAllocator: public CInterface
  351. {
  352. CSubAllocatorBlockLink *blks;
  353. NonReentrantSpinLock sublock;
  354. unsigned nblks;
  355. CSuperAllocator &superallocator;
  356. size32_t maxrecsize; // even
  357. inline size32_t sbroundsz(size32_t recsize)
  358. {
  359. return (recsize<MINSUBBLOCKSIZE)?MINSUBBLOCKSIZE:((recsize+1)&0xfffffffe);
  360. }
  361. inline CSubAllocatorBlock *item(size32_t recsize, unsigned &i)
  362. {
  363. i = recsize/2-1;
  364. ASSERTEX(i<nblks);
  365. return blks[i].isempty()?NULL:blks[i].next;
  366. }
  367. public:
  368. CSubAllocator(CSuperAllocator &_superallocator,size32_t _maxrecsize)
  369. : superallocator(_superallocator)
  370. {
  371. size32_t ms = (SBPAGESIZE-SBHEADERSIZE)/16-SBOVERHEAD; // fit at least 16 per page
  372. if (_maxrecsize>ms)
  373. _maxrecsize = ms;
  374. maxrecsize = sbroundsz(_maxrecsize);
  375. nblks = maxrecsize/2;
  376. blks = new CSubAllocatorBlockLink[nblks];
  377. }
  378. ~CSubAllocator();
  379. inline size32_t maxRecSize() const { return maxrecsize; }
  380. inline size32_t roundupSize(size32_t sz)
  381. {
  382. if (sz>maxrecsize)
  383. return 0;
  384. return sbroundsz(sz);
  385. }
  386. void *allocMem(size32_t sz, size32_t &usablesize) // returns NULL when htab full
  387. {
  388. if (sz>maxrecsize)
  389. return NULL;
  390. size32_t recsize = sbroundsz(sz);
  391. {
  392. NonReentrantSpinBlock block(sublock);
  393. unsigned i;
  394. CSubAllocatorBlock *b = item(recsize,i);
  395. if (b) {
  396. void *ret = b->suballoc();
  397. if (ret) {
  398. if (b->full()) {
  399. b->unlink();
  400. blks[i].append(*b); // move to end
  401. }
  402. usablesize = recsize;
  403. return ret;
  404. }
  405. }
  406. }
  407. unsigned n;
  408. CSubAllocatorBlock *b = (CSubAllocatorBlock *)getMem(recsize,n);
  409. b->init(recsize,n);
  410. NonReentrantSpinBlock block(sublock);
  411. blks[recsize/2-1].prepend(*b);
  412. usablesize = recsize;
  413. return b->suballoc(); // should work
  414. }
  415. void freeMem(void * p);
  416. void *reallocMem(void *p, size32_t sz, size32_t &usablesize)
  417. {
  418. ASSERTEX(p);
  419. ASSERTEX(sz);
  420. CSubAllocatorBlock *b = CSubAllocatorBlock::base(p); // don't need lock here
  421. size32_t oldsz = b->recsize;
  422. size32_t newsz = sbroundsz(sz);
  423. if (oldsz==newsz) {
  424. usablesize = newsz;
  425. return p;
  426. }
  427. void *ret = allocMem(sz,usablesize);
  428. if (!ret)
  429. return ret; // sligtly odd - indicates could not reallocate
  430. memcpy(ret,p,(oldsz<sz)?oldsz:sz);
  431. freeMem(p);
  432. return ret;
  433. }
  434. void *getMem(size32_t recsz,unsigned &n);
  435. size32_t usableSize(const void *p)
  436. {
  437. CSubAllocatorBlock *b = CSubAllocatorBlock::base(p); // don't need lock here
  438. return b->recsize;
  439. }
  440. void checkPtrValid(const void *p)
  441. {
  442. CSubAllocatorBlock *b = CSubAllocatorBlock::base(p);
  443. if ((memsize_t)p>=SBHEADERSIZE+(memsize_t)b) {
  444. NonReentrantSpinBlock block(sublock);
  445. unsigned i;
  446. CSubAllocatorBlock * f = item(b->recsize,i);
  447. while (f) {
  448. if (f==b)
  449. return;
  450. f = f->next;
  451. }
  452. }
  453. HeapError(e_corrupt_sub_block,p);
  454. }
  455. void walk(IWalkMem &walker)
  456. {
  457. NonReentrantSpinBlock block(sublock);
  458. for (unsigned i=0;i<nblks;i++) {
  459. CSubAllocatorBlock *head = (CSubAllocatorBlock *)&blks[i];
  460. CSubAllocatorBlock *p = head->next;
  461. while (p!=head) {
  462. CSubAllocatorBlock *n = p->next;
  463. p->walk(walker);
  464. p = n;
  465. }
  466. }
  467. }
  468. bool isBlock(const void *blk)
  469. {
  470. NonReentrantSpinBlock block(sublock);
  471. for (unsigned i=0;i<nblks;i++) {
  472. CSubAllocatorBlock *head = (CSubAllocatorBlock *)&blks[i];
  473. CSubAllocatorBlock *p = head->next;
  474. while (p!=head) {
  475. if (p==blk)
  476. return true;
  477. p = p->next;
  478. }
  479. }
  480. return false;
  481. }
  482. };
  483. /*
  484. ***************************************************************************
  485. * *
  486. * Implementation of the superheap manager: *
  487. * *
  488. * Designed with the following design goals: *
  489. * *
  490. * 1. Good operation within a paged memory environment *
  491. * 2. Quick location of free memory *
  492. * 3. Low fragmentation. *
  493. * *
  494. * This is achieved as follows: *
  495. * *
  496. * The housekeeping information is kept separate from the allocated *
  497. * memory. This means that searching for a free block doesn't swap in *
  498. * each of the allocated pages - just the small amount of house keeping *
  499. * information. *
  500. * *
  501. * The routine is based on the Boundary Tag Method (described in *
  502. * Fundamentals of Data Structures by Horowitz and Sahni). *
  503. * *
  504. * The housekeeping information consists of a circular doubly linked list *
  505. * holding the following information about free blocks: *
  506. * *
  507. * LeftPtr -> Next Block Lower in memory. *
  508. * RightPtr -> Next Block following this one in memory. *
  509. * Size -> Number of bytes (including extra bytes we might need). *
  510. * BlockPtr -> Pointer to the allocated block. *
  511. * *
  512. * Unused nodes on this free list are also linked on another linked list. *
  513. * *
  514. * *
  515. * The blocks of memory have the following format: *
  516. * *
  517. * Allocated: Free: *
  518. * *
  519. * +-------------------------------+ +-------------------------------+ *
  520. * | | ! | 1 | S I Z E | | | | ! | 0 | S I Z E | *
  521. * +-------------------------------+ +-------------------------------+ *
  522. * | | | x x x x x x x x | *
  523. * | | | x x x x x x x x | *
  524. * | | | x x x x +---------------+ *
  525. * | $ $ $ | | x x x x | ^HouseKeeping | *
  526. * +-------------------------------+ +-------------------------------+ *
  527. * | ? | 1 | | | | | | | | | 0 | | | | | | | *
  528. * +-------------------------------+ +-------------------------------+ *
  529. * *
  530. * The block of memory allocated when malloc() is called, is actually 8 *
  531. * bytes larger than required size. 6 bytes of house keeping information *
  532. * are tagged on the front of the block, and 2 on the end. *
  533. *
  534. * The 0 and 1 tags (together with the pointer to the housekeeping *
  535. * information are used as in a quick method of checking whether a *
  536. * block that is being free()'d should be concatenated with blocks to the *
  537. * left and right, and if so where those blocks are. *
  538. * *
  539. * The !, ? and the $ characters mark bytes that are used in the checking *
  540. * version of the heap system. *
  541. * *
  542. * ? is a byte containing the difference between the number of bytes *
  543. * requested, and the number of bytes in the block. This byte is then *
  544. * exclusive OR'd with a mask so the common error of storing a zero byte *
  545. * past the end of memory is detected. *
  546. * *
  547. * i.e. *TailCheck = (SizeBlock - SizeNeeded) ^ CheckMask. *
  548. * *
  549. * The (SizeBlock - SizeNeeded) bytes at the end of the block are filled *
  550. * with a special value - which can be checked that they're not modified. *
  551. * *
  552. * The ! is similar, except it contains the last byte of the size, so *
  553. * we can check that the front of the block hasn't been overwritten. *
  554. * *
  555. * *
  556. * The normal system of allocation is to ask the operating system for *
  557. * large chunks of memory, and then use our routines to sub-divide the *
  558. * memory up. *
  559. * *
  560. * Any requests for large chunks of memory are passed straight through to *
  561. * the OS, and the allocated block is tagged with the number 2, to *
  562. * distinguish it from blocks that have been sub-allocated from large *
  563. * blocks. *
  564. * *
  565. * *
  566. * The block pointers in the free list point just after the size field - *
  567. * at the first byte of the memory that would be used when allocated. *
  568. * Free->Size includes the size of the extra housekeeping information *
  569. * *
  570. * It would be possible to *
  571. * i. Only use one tag (at the end of the block), to reduce the *
  572. * housekeeping information by one byte at the expense of extra *
  573. * processing. *
  574. * ii. Could have different sizes, since most blocks are less than *
  575. * 64K or even 256 bytes. Probably introduces unnecessary *
  576. * complexity in the block processing routines. *
  577. * *
  578. ***************************************************************************
  579. */
  580. class CSuperAllocator: public CInterface, implements IAllocator
  581. {
  582. SpinLock lock;
  583. CFixedSizeAllocator FreeListAllocator;
  584. FreeListEnt HeadFreeList;
  585. FreeListEnt HeadOsBlockList;
  586. memsize_t OsTotal;
  587. memsize_t OsMax;
  588. memsize_t OsMin;
  589. FreeListPtr LastFreeEnt;
  590. Owned<CSubAllocator> suballocator;
  591. bool avoidReallocFragmentation; // avoids reallocMem leaving gap
  592. inline FreeListPtr newFreeListEnt()
  593. {
  594. return (FreeListPtr)FreeListAllocator.alloc();
  595. }
  596. inline void InsertNode(FreeListEnt &head, FreeListPtr NewNode)
  597. {
  598. NewNode->Prev = &head;
  599. NewNode->Next = head.Next;
  600. head.Next = NewNode;
  601. NewNode->Next->Prev = NewNode;
  602. // Always HeadFreeList?? NH
  603. }
  604. inline void InsertFreeNode(FreeListPtr NewNode)
  605. {
  606. InsertNode(HeadFreeList,NewNode);
  607. LastFreeEnt = NewNode->Prev; // Point at the next block to look at
  608. }
  609. inline void UnLinkNode(FreeListPtr CurNode)
  610. {
  611. /*
  612. * Remove the node from the free list - and add it to the list of unused
  613. * free blocks.
  614. */
  615. CurNode->Prev->Next = CurNode->Next;
  616. CurNode->Next->Prev = CurNode->Prev;
  617. FreeListAllocator.dealloc(CurNode);
  618. }
  619. #if InitialiseMemory
  620. inline void InitFreeBlock(void * ptr)
  621. {
  622. memset(ptr, FreeFillChar, BlockSize(ptr) - BlockExtraSize);
  623. }
  624. #else
  625. #define InitFreeBlock(ptr)
  626. #endif
  627. void checkPtrValid(const void * ptr)
  628. {
  629. /*
  630. * Perform several checks on a pointer that is being freed.
  631. *
  632. * o That the tags at either end of the block are valid.
  633. * o That the size is sensible
  634. * o That the check bytes are OK.
  635. * o That the padding bytes on the end of the block haven't been
  636. * overwritten.
  637. */
  638. #if HeapChecking >= PtrCheck
  639. /*
  640. * Check that the tags at the head and tail of the block hold valid numbers.
  641. */
  642. if (!ptr)
  643. return;
  644. size32_t bs = BlockSize(ptr);
  645. if (ISSUBALLOCATED(bs)) {
  646. suballocator->checkPtrValid(ptr);
  647. return;
  648. }
  649. byte HeadTagVal = *((byte *)ptr - HeadTagOff);
  650. if ((HeadTagVal != SuperAllocTag) && (HeadTagVal != OSAllocTag)) {
  651. if (HeadTagVal == FreeBlockTag)
  652. HeapError(e_free_block_twice,ptr);
  653. else
  654. HeapError(e_corrupt_unknown_block,ptr);
  655. return;
  656. }
  657. size32_t SourceSize = bs - BlockExtraSize;
  658. byte *EndPtr = (byte *)ptr + SourceSize;
  659. if ((SourceSize == 0) || ((SourceSize & (Alignment - 1)) != 0)) {
  660. HeapError(e_illegal_size_field);
  661. return;
  662. }
  663. byte TailTagVal = *(EndPtr + TailTagOff);
  664. if ((TailTagVal != SuperAllocTag) && (TailTagVal != OSAllocTag)) {
  665. if (TailTagVal == FreeBlockTag)
  666. HeapError(e_free_block_twice,ptr);
  667. else
  668. HeapError(e_corrupt_unknown_block,ptr);
  669. return;
  670. }
  671. if (HeadTagVal != TailTagVal) {
  672. HeapError(e_inconsist_block_tag);
  673. return;
  674. }
  675. if (*(EndPtr + TailChkOff) != CheckFillVal)
  676. HeapError(e_end_heap_overwritten,ptr);
  677. #endif
  678. }
  679. #if HeapChecking < PtrCheck
  680. #define SetCheckInfo(StartAllocPtr, ActualSize, AllocSize)
  681. #else
  682. void SetCheckInfo(byte * StartAllocPtr, size32_t ActualSize, size32_t AllocSize)
  683. {
  684. /*
  685. * Add checking info into the block.
  686. *
  687. * Fill the extra bytes that were allocated, but not requested
  688. * with special characters, so we can tell if they were overwritten.
  689. */
  690. byte * EndPtr = (byte *)StartAllocPtr + AllocSize;
  691. *((byte *)StartAllocPtr - HeadChkOff) = HeadCheckVal;
  692. *(EndPtr + TailChkOff) = CheckFillVal;
  693. }
  694. #endif
  695. memsize_t freeListMemRemaining(bool trace)
  696. {
  697. memsize_t sz = 0;
  698. unsigned num = 0;
  699. size32_t max = 0;
  700. size32_t min = (size32_t)-1;
  701. FreeListPtr CurNode = LastFreeEnt;
  702. do {
  703. sz += CurNode->Size;
  704. if (CurNode->Size > max) max = CurNode->Size;
  705. if (CurNode->Size < min) min = CurNode->Size;
  706. num++;
  707. CurNode = CurNode->Next;
  708. }
  709. while (CurNode != LastFreeEnt);
  710. if (trace)
  711. {
  712. size32_t avg = (size32_t) (sz / num);
  713. StringBuffer report("FreeMem");
  714. report.newline();
  715. report.append("total free : ").append((unsigned __int64)sz).newline();
  716. report.append("free ptr count : ").append(num).newline();
  717. report.append("largest : ").append(max).newline();
  718. report.append("smallest : ").append(min).newline();
  719. report.append("average : ").append(avg).newline();
  720. PROGLOG("%s", report.str());
  721. }
  722. return sz;
  723. }
  724. void * OsAllocMem(size32_t sz)
  725. {
  726. ASSERTEX(sz==OSPAGEROUND(sz));
  727. if (OsTotal+sz>OsMax)
  728. {
  729. PrintStackReport();
  730. DBGLOG("Free list mem = %"I64F"d", (unsigned __int64)freeListMemRemaining(true));
  731. throw new CAllocatorOutOfMemException(e_out_of_memory,sz,OsTotal);
  732. }
  733. #ifdef _WIN32
  734. void * ret = VirtualAlloc(NULL, sz, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
  735. if (ret == NULL) {
  736. #else
  737. void * ret = mmap(NULL,sz,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_NORESERVE|MAP_ANONYMOUS,-1,0);
  738. if (ret == (void *)MAP_FAILED) {
  739. #endif
  740. PrintStackReport();
  741. DBGLOG("Free list mem = %"I64F"d", (unsigned __int64)freeListMemRemaining(true));
  742. throw new CAllocatorOutOfMemException(e_out_of_memory,sz,OsTotal);
  743. return NULL;
  744. }
  745. OsTotal += sz;
  746. return ret;
  747. }
  748. void OsFreeMem(void *ptr, size32_t sz)
  749. {
  750. if (!ptr)
  751. return;
  752. ASSERTEX(OsTotal>=sz);
  753. ASSERTEX(sz==OSPAGEROUND(sz));
  754. OsTotal -= sz;
  755. #ifdef _WIN32
  756. if (!VirtualFree(ptr,0,MEM_RELEASE))
  757. HeapError(GetLastError(),ptr);
  758. #else
  759. if (munmap(ptr,sz)<0)
  760. HeapError(errno ,ptr);
  761. #endif
  762. }
  763. void * AllocOSChunk(size32_t SizeRequired)
  764. {
  765. /*
  766. * Allocate a block of memory directly from the operating system.
  767. * Set up the housekeeping information that we need.
  768. */
  769. size32_t UserSize = Align(SizeRequired) + BlockExtraSize;
  770. size32_t TotalSize = OSPAGEROUND(UserSize + OSBlockExtra);
  771. byte *BlockPtr = (byte *) OsAllocMem((size32_t)TotalSize);
  772. byte * StartAllocPtr = BlockPtr + BlockExtraSize;
  773. /*
  774. * Set up the housekeeping information - the size, and tag it as a block
  775. * directly allocated from the OS.
  776. */
  777. BlockSize(StartAllocPtr) = UserSize;
  778. *(StartAllocPtr - HeadTagOff) = OSAllocTag;
  779. *(StartAllocPtr + Align(SizeRequired) + TailTagOff) = OSAllocTag;
  780. FreeListPtr CurNode = newFreeListEnt();
  781. CurNode->BlockPtr = BlockPtr;
  782. CurNode->Size = TotalSize;
  783. InsertNode(HeadOsBlockList,CurNode);
  784. ListOwner(BlockPtr, TotalSize) = CurNode;
  785. SetCheckInfo(StartAllocPtr, SizeRequired, Align(SizeRequired));
  786. return StartAllocPtr;
  787. }
  788. void * AllocBlock(FreeListPtr CurBlock, size32_t SizeRequired, size32_t TotalSize)
  789. {
  790. /*
  791. * Allocate the required amount of memory from the block, and
  792. * either allocate the whole block, or allocate a chunk of the block.
  793. */
  794. byte * StartAllocPtr;
  795. if (CurBlock->Size - TotalSize < MinFreeSize) {
  796. /*
  797. * There isn't a sensible amount of memory left in the remainder of
  798. * the block, so allocate
  799. * the whole block.
  800. */
  801. /*
  802. * Unlink the block from the free block list, and add it to the
  803. * unused free node list.
  804. */
  805. LastFreeEnt = CurBlock->Prev; /* Point at the next block to look at */
  806. StartAllocPtr = CurBlock->BlockPtr;
  807. TotalSize = CurBlock->Size;
  808. UnLinkNode(CurBlock);
  809. }
  810. else {
  811. /*
  812. * Only use part of the block - use the top half, so that a subsequent
  813. * call to realloc() is more likely to be able to expand the block.
  814. */
  815. StartAllocPtr = CurBlock->BlockPtr;
  816. CurBlock->BlockPtr = StartAllocPtr + TotalSize;
  817. CurBlock->Size -= TotalSize;
  818. BlockSize(CurBlock->BlockPtr) = CurBlock->Size;
  819. /*
  820. * The housekeeping information, and the
  821. * pointer to the housekeeping information remains in the same place, so
  822. * they don't need updating. We do need to set the HeadBlockTag.
  823. */
  824. *(CurBlock->BlockPtr - HeadTagOff) = FreeBlockTag;
  825. }
  826. /*
  827. * Then set up the housekeeping information.
  828. */
  829. BlockSize(StartAllocPtr) = TotalSize;
  830. *(StartAllocPtr - HeadTagOff) = SuperAllocTag;
  831. *(StartAllocPtr + TotalSize - BlockExtraSize + TailTagOff) = SuperAllocTag;
  832. SetCheckInfo(StartAllocPtr, SizeRequired, TotalSize - BlockExtraSize);
  833. return (void *) StartAllocPtr;
  834. }
  835. FreeListPtr AllocMoreMemory()
  836. {
  837. /*
  838. * Get a new chunk of memory from the operating system, and add it onto
  839. * the free list.
  840. * return a pointer to the free list information.
  841. */
  842. byte * StartOSBlock = (byte *) OsAllocMem((size32_t)OSCHUNKSIZE);
  843. FreeListPtr OsNode = newFreeListEnt();
  844. /*
  845. * We get BlockExtraSize less bytes than were allocated, in order to store
  846. * the housekeeping information required at the end of the last block.
  847. */
  848. OsNode->BlockPtr = StartOSBlock;
  849. OsNode->Size = OSCHUNKSIZE;
  850. InsertNode(HeadOsBlockList,OsNode);
  851. ListOwner(StartOSBlock, OSCHUNKSIZE) = OsNode;
  852. FreeListPtr CurNode = newFreeListEnt();
  853. CurNode->BlockPtr = (StartOSBlock + BlockExtraSize);
  854. CurNode->Size = OSCHUNKSIZE - OSBlockExtra;
  855. BlockSize(CurNode->BlockPtr) = CurNode->Size;
  856. /*
  857. * Set up the special house keeping information at the start, and end
  858. * of the memory block. This means we don't merge the current block with
  859. * anything before or after it.
  860. */
  861. *(StartOSBlock + TailTagOff) = BlockBoundTag;
  862. *(StartOSBlock + (OSCHUNKSIZE - OSPtrExtra) - HeadTagOff) = BlockBoundTag;
  863. /*
  864. * Tag the bounds of the new free block.
  865. */
  866. *(CurNode->BlockPtr - HeadTagOff) = FreeBlockTag;
  867. *(StartOSBlock + (OSCHUNKSIZE - OSBlockExtra) + TailTagOff) = FreeBlockTag;
  868. /*
  869. * Add a pointer to the free list entry at the end of the block.
  870. */
  871. ListOwner(StartOSBlock, (OSCHUNKSIZE - OSBlockExtra)) = CurNode;
  872. InsertFreeNode(CurNode);
  873. return CurNode;
  874. }
  875. void * AllocChunk(size32_t SizeRequired)
  876. {
  877. // Allocate a chunk of memory - and return a pointer to the base.
  878. // Include the size of the housekeeping information...
  879. size32_t TotalSize = Align(SizeRequired) + BlockExtraSize;
  880. if (TotalSize >= MaxSuperAllocSize)
  881. return AllocOSChunk(SizeRequired);
  882. FreeListPtr CurNode = LastFreeEnt;
  883. do {
  884. if (CurNode->Size >= TotalSize)
  885. return AllocBlock(CurNode, SizeRequired, TotalSize);
  886. CurNode = CurNode->Next;
  887. }
  888. while (CurNode != LastFreeEnt);
  889. CurNode = AllocMoreMemory();
  890. return AllocBlock(CurNode, SizeRequired, TotalSize);
  891. }
  892. void FreeOSBlock(void * ptr, size32_t size)
  893. {
  894. byte *BlockPtr = (byte *)ptr - BlockExtraSize;
  895. size = OSPAGEROUND(size+OSBlockExtra);
  896. FreeListPtr CurNode = ListOwner(BlockPtr, size);
  897. ASSERTEX(CurNode->Size==size);
  898. UnLinkNode(CurNode);
  899. OsFreeMem(BlockPtr,size);
  900. }
  901. void FreeOSChunk(FreeListPtr fp)
  902. {
  903. if (OsTotal<=OsMin)
  904. return;
  905. byte *ptr = fp->BlockPtr;
  906. UnLinkNode(fp);
  907. byte *BlockPtr = (byte *)ptr - BlockExtraSize;
  908. FreeListPtr CurNode = ListOwner(BlockPtr, OSCHUNKSIZE);
  909. ASSERTEX(CurNode->Size==OSCHUNKSIZE);
  910. UnLinkNode(CurNode);
  911. OsFreeMem(BlockPtr,OSCHUNKSIZE);
  912. }
  913. void FreeSubBlock(void * ptr, const size32_t CurBlockSize)
  914. {
  915. FreeListPtr PrevFreeEnt;
  916. FreeListPtr NextFreeEnt;
  917. byte * NextBlockBase;
  918. size32_t NextSize;
  919. #if HeapChecking >= PtrCheck
  920. /*** Make sure a second free gets recorded as such ***/
  921. *((byte *)ptr - HeadTagOff) = FreeBlockTag;
  922. *((byte *)ptr + CurBlockSize - BlockExtraSize + TailTagOff) = FreeBlockTag;
  923. #endif
  924. /*
  925. * Check for a possible merge on the left and right hand sides:
  926. */
  927. byte PrevTag = *((byte *)ptr - BlockExtraSize + TailTagOff);
  928. byte NextTag = *((byte *)ptr + CurBlockSize - HeadTagOff);
  929. /*
  930. * Merge with previous and subsequent blocks...
  931. */
  932. if (PrevTag == FreeBlockTag)
  933. {
  934. /*
  935. * Get the free list entry of the previous block.
  936. */
  937. byte *TempPtr = (byte *)ptr - BlockExtraSize - sizeof(FreeListPtr);
  938. PrevFreeEnt = *((FreeListPtr *)TempPtr);
  939. }
  940. if (NextTag == FreeBlockTag)
  941. {
  942. /* The free list entry of the next block */
  943. NextBlockBase = (byte *)ptr + CurBlockSize;
  944. NextSize = BlockSize(NextBlockBase);
  945. NextFreeEnt = ListOwner(NextBlockBase, NextSize - BlockExtraSize);
  946. }
  947. /*
  948. * Initialise the data from the free'd block. We don't clear the whole
  949. * new entry (which would be best), but this is most efficient.
  950. */
  951. InitFreeBlock(ptr);
  952. if ((PrevTag == FreeBlockTag) && (NextTag == FreeBlockTag))
  953. {
  954. /*
  955. * Merge the new block into both the previous and next free list entries.
  956. * 1. Increase the size of the previous block.
  957. * 2. A a pointer at the end of the block to point at the relevant
  958. * free list entry.
  959. * 3. Set the size recorded in the block.
  960. *
  961. * The tags at the start and end of the block are already set up correctly.
  962. */
  963. PrevFreeEnt->Size += CurBlockSize + NextFreeEnt->Size;
  964. ListOwner(NextBlockBase, NextSize - BlockExtraSize) = PrevFreeEnt;
  965. BlockSize(PrevFreeEnt->BlockPtr) = PrevFreeEnt->Size;
  966. /* Now unlink the second block from the free list... */
  967. UnLinkNode(NextFreeEnt);
  968. LastFreeEnt = PrevFreeEnt->Prev; /* Point at the next block to look at */
  969. if (PrevFreeEnt->Size==OSCHUNKSIZE-OSBlockExtra)
  970. FreeOSChunk(PrevFreeEnt);
  971. }
  972. else if (PrevTag == FreeBlockTag)
  973. {
  974. /*
  975. * Merge with the block in front of it.
  976. * 1. Increase size of free block
  977. * 2. Point at the (new) beginning of the free block.
  978. * 3. Put a 'free' tag on the end.
  979. * 4. Change the size recorded in the block.
  980. */
  981. PrevFreeEnt->Size += CurBlockSize;
  982. ListOwner(ptr, CurBlockSize - BlockExtraSize) = PrevFreeEnt;
  983. /* Change the last tag to indicate the block is free */
  984. *((byte *)ptr + CurBlockSize - BlockExtraSize + TailTagOff) = FreeBlockTag;
  985. BlockSize(PrevFreeEnt->BlockPtr) = PrevFreeEnt->Size;
  986. LastFreeEnt = PrevFreeEnt->Prev; /* Point at the next block to look at */
  987. if (PrevFreeEnt->Size==OSCHUNKSIZE-OSBlockExtra)
  988. FreeOSChunk(PrevFreeEnt);
  989. }
  990. else if (NextTag == FreeBlockTag)
  991. {
  992. /*
  993. * Merge with next block:
  994. * 1. Increase size of free block
  995. * 2. Point at the (new) beginning of the free block.
  996. * 3. Put a 'free' tag on the start.
  997. * 4. Change the size recorded in the block.
  998. *
  999. * (The owning free block is already set up)
  1000. */
  1001. NextFreeEnt->Size += CurBlockSize;
  1002. NextFreeEnt->BlockPtr = (byte *) ptr;
  1003. *((byte *)ptr - HeadTagOff) = FreeBlockTag;
  1004. BlockSize(ptr) = NextFreeEnt->Size;
  1005. LastFreeEnt = NextFreeEnt->Prev; /* Point at the next block to look at */
  1006. if (NextFreeEnt->Size==OSCHUNKSIZE-OSBlockExtra)
  1007. FreeOSChunk(NextFreeEnt);
  1008. }
  1009. else
  1010. {
  1011. /*
  1012. * The free()'d block doesn't merge with any other block => create
  1013. * a new item on the free list.
  1014. *
  1015. * First, allocate a new free node to point at the new memory area.
  1016. */
  1017. FreeListPtr CurFreeEnt = newFreeListEnt();
  1018. /*
  1019. * Set up
  1020. * 1. the fields in the free-list-entry,
  1021. * 2. The tags at either end of the new free block.
  1022. * 3. The pointer to the owning free-node.
  1023. */
  1024. CurFreeEnt->BlockPtr = (byte *)ptr;
  1025. CurFreeEnt->Size = CurBlockSize;
  1026. *((byte *)ptr - HeadTagOff) = FreeBlockTag;
  1027. *((byte *)ptr + CurBlockSize - BlockExtraSize + TailTagOff) = FreeBlockTag;
  1028. ListOwner(ptr, CurBlockSize - BlockExtraSize) = CurFreeEnt;
  1029. InsertFreeNode(CurFreeEnt);
  1030. }
  1031. }
  1032. void * ExpandOSBlock(void * ptr, size32_t NewSize)
  1033. {
  1034. /*
  1035. * Expand a block that was allocated directly from the operating system.
  1036. */
  1037. const size32_t OldBlockSize = OSPAGEROUND(BlockSize(ptr) + OSBlockExtra);
  1038. size32_t NewUserSize = Align(NewSize) + BlockExtraSize;
  1039. size32_t NewTotalSize = OSPAGEROUND(NewUserSize + OSBlockExtra);
  1040. if (NewTotalSize!=OldBlockSize)
  1041. return NULL;
  1042. byte * StartOSBlock = (byte *)ptr - BlockExtraSize;
  1043. FreeListPtr BlockInfo = ListOwner(StartOSBlock, OldBlockSize);
  1044. ASSERTEX(BlockInfo->Size==NewTotalSize);
  1045. /*
  1046. * We could expand the block....
  1047. * NOTE- this means the value of ptr is still valid.
  1048. *
  1049. * 1. Change the block size.
  1050. * 2. Update the trailing tags.
  1051. * 3. Update the pointer to the owning-block
  1052. */
  1053. BlockSize(ptr) = NewUserSize;
  1054. *((byte *)ptr + NewUserSize - HeadTagOff) = BlockBoundTag;
  1055. *((byte *)ptr + (NewUserSize - BlockExtraSize) + TailTagOff) = OSAllocTag;
  1056. ListOwner(StartOSBlock, NewTotalSize) = BlockInfo;
  1057. SetCheckInfo((byte *)ptr, NewSize, NewUserSize - BlockExtraSize);
  1058. return ptr;
  1059. }
  1060. void * ExpandSubBlock(void * ptr, size32_t NewSize)
  1061. {
  1062. const size32_t OldBlockSize = BlockSize(ptr);
  1063. size32_t TotalNewSize = Align(NewSize) + BlockExtraSize;
  1064. size32_t SizeDiff;
  1065. size32_t NextSize;
  1066. FreeListPtr NextNode;
  1067. FreeListPtr CurFreeEnt;
  1068. byte * StartNewBlock;
  1069. byte * NextBlock = (byte *)ptr + OldBlockSize;
  1070. if (TotalNewSize < OldBlockSize)
  1071. {
  1072. if (avoidReallocFragmentation)
  1073. return NULL;
  1074. SizeDiff = OldBlockSize - TotalNewSize;
  1075. /*
  1076. * The block of memory is being contracted - we can always do this - unless
  1077. * we run our of free nodes.
  1078. */
  1079. if (*(NextBlock - HeadTagOff) == FreeBlockTag)
  1080. {
  1081. /*
  1082. * Easy... just cut down the size of this block, and increase the
  1083. * size of the next free block.
  1084. */
  1085. NextSize = BlockSize(NextBlock);
  1086. NextNode = ListOwner(NextBlock, NextSize - BlockExtraSize);
  1087. /*
  1088. * 1. Change the size in the free list.
  1089. * 2. Change the pointer to the block.
  1090. * 3. Add the leading free list tag.
  1091. * 4. Set the size in the block.
  1092. */
  1093. NextNode->Size += SizeDiff;
  1094. NextBlock -= SizeDiff;
  1095. NextNode->BlockPtr = NextBlock;
  1096. *(NextBlock - HeadTagOff) = FreeBlockTag;
  1097. BlockSize(NextBlock) = NextNode->Size;
  1098. }
  1099. else
  1100. {
  1101. /*
  1102. * May need to create a new free entry - only do this if the change in
  1103. * size is sufficient to warrant it.
  1104. */
  1105. if (SizeDiff >= MinFreeSize)
  1106. {
  1107. CurFreeEnt = newFreeListEnt();
  1108. /*
  1109. * Set up
  1110. * 1. the fields in the free-list-entry,
  1111. * 2. The tags at either end of the new free block.
  1112. * 3. The size held within the block.
  1113. * 4. The pointer to the owning free-node.
  1114. */
  1115. StartNewBlock = (byte *)ptr + TotalNewSize;
  1116. CurFreeEnt->BlockPtr = StartNewBlock;
  1117. CurFreeEnt->Size = SizeDiff;
  1118. *(StartNewBlock - HeadTagOff) = FreeBlockTag;
  1119. *(StartNewBlock + SizeDiff - BlockExtraSize + TailTagOff) = FreeBlockTag;
  1120. BlockSize(StartNewBlock) = SizeDiff;
  1121. ListOwner(StartNewBlock, SizeDiff - BlockExtraSize) = CurFreeEnt;
  1122. InsertFreeNode(CurFreeEnt);
  1123. }
  1124. else
  1125. TotalNewSize = OldBlockSize;
  1126. }
  1127. }
  1128. else if (TotalNewSize != OldBlockSize)
  1129. {
  1130. SizeDiff = TotalNewSize - OldBlockSize;
  1131. /*
  1132. * We can't expand this block if the next section of memory isn't free
  1133. * or if the next free section of memory isn't big enough to give us
  1134. * the extra space we need.
  1135. */
  1136. if (*(NextBlock - HeadTagOff) != FreeBlockTag)
  1137. return NULL;
  1138. NextSize = BlockSize(NextBlock);
  1139. if (NextSize < SizeDiff)
  1140. return NULL;
  1141. if (NextSize - SizeDiff >= MinFreeSize)
  1142. {
  1143. /*
  1144. * The next free block is large enough to just have a chunk taken out
  1145. * of it.
  1146. */
  1147. CurFreeEnt = ListOwner(NextBlock, NextSize - BlockExtraSize);
  1148. /*
  1149. * First update the information in the free space list
  1150. * 1. Size
  1151. * 2. Base address.
  1152. */
  1153. CurFreeEnt->Size -= SizeDiff;
  1154. CurFreeEnt->BlockPtr += SizeDiff;
  1155. /*
  1156. * Now update the free block's information
  1157. * 1. Size
  1158. * 2. Lower Tag.
  1159. */
  1160. BlockSize(CurFreeEnt->BlockPtr) = CurFreeEnt->Size;
  1161. *(CurFreeEnt->BlockPtr - HeadTagOff) = FreeBlockTag;
  1162. }
  1163. else
  1164. {
  1165. TotalNewSize = NextSize + OldBlockSize;
  1166. /*
  1167. * Eat the whole of the next free space chunk...
  1168. */
  1169. CurFreeEnt = ListOwner(NextBlock, NextSize - BlockExtraSize);
  1170. /*
  1171. * Remove the entry from the freespace list...
  1172. */
  1173. LastFreeEnt = CurFreeEnt->Prev;
  1174. UnLinkNode(CurFreeEnt);
  1175. }
  1176. }
  1177. /*
  1178. * Change the information stored in the allocated block:
  1179. * 1. The block size.
  1180. * 2. The trailing tag.
  1181. */
  1182. BlockSize(ptr) = TotalNewSize;
  1183. *((byte *)ptr + TotalNewSize - BlockExtraSize + TailTagOff) = SuperAllocTag;
  1184. SetCheckInfo((byte *)ptr, NewSize, TotalNewSize - BlockExtraSize);
  1185. return ptr;
  1186. }
  1187. public:
  1188. IMPLEMENT_IINTERFACE;
  1189. CSuperAllocator(memsize_t maxtotal,unsigned _maxsubrecsize, memsize_t mintotal,bool _avoidReallocFragmentation)
  1190. : FreeListAllocator(sizeof(FreeListEnt),0x10000)
  1191. {
  1192. ASSERTEX ((BlockExtraSize == 8) || (HeapChecking >= PtrCheck));
  1193. HeadFreeList.BlockPtr = NULL;
  1194. HeadFreeList.Next = &HeadFreeList;
  1195. HeadFreeList.Prev = &HeadFreeList;
  1196. HeadFreeList.Size = 0;
  1197. LastFreeEnt = &HeadFreeList;
  1198. HeadOsBlockList.BlockPtr = NULL;
  1199. HeadOsBlockList.Next = &HeadOsBlockList;
  1200. HeadOsBlockList.Prev = &HeadOsBlockList;
  1201. HeadOsBlockList.Size = 0;
  1202. OsTotal = 0;
  1203. OsMax = maxtotal ? maxtotal : ((memsize_t)-1); // unbound if not set
  1204. OsMin = mintotal;
  1205. if (_maxsubrecsize)
  1206. suballocator.setown(new CSubAllocator(*this,_maxsubrecsize));
  1207. avoidReallocFragmentation = _avoidReallocFragmentation;
  1208. }
  1209. ~CSuperAllocator()
  1210. {
  1211. suballocator.clear();
  1212. while (HeadOsBlockList.Next!=&HeadOsBlockList) {
  1213. OsFreeMem(HeadOsBlockList.Next->BlockPtr,HeadOsBlockList.Next->Size);
  1214. UnLinkNode(HeadOsBlockList.Next);
  1215. }
  1216. }
  1217. size32_t roundupSize(size32_t sz)
  1218. {
  1219. size32_t ret;
  1220. if (suballocator) {
  1221. ret = suballocator->roundupSize(sz);
  1222. if (ret)
  1223. return ret;
  1224. }
  1225. return Align(sz);
  1226. }
  1227. void *allocMem2(size32_t size,size32_t &usablesize)
  1228. {
  1229. if (size == 0)
  1230. return NULL;
  1231. if (size>MAXUSERALLOCATESIZE) // too big!
  1232. HeapError(e_alloc_too_big);
  1233. void *ptr;
  1234. if (suballocator) {
  1235. ptr = suballocator->allocMem(size,usablesize);
  1236. if (ptr) {
  1237. ASSERTEX(roundupSize(size)<=usablesize);
  1238. return ptr;
  1239. }
  1240. }
  1241. SpinBlock block(lock);
  1242. ptr = AllocChunk(size);
  1243. #if InitialiseMemory
  1244. memset(ptr, MallocFillChar, size);
  1245. #endif
  1246. usablesize = BlockSize(ptr)-BlockExtraSize;
  1247. ASSERTEX(roundupSize(size)<=usablesize);
  1248. return ptr;
  1249. }
  1250. void *allocMem(size32_t size)
  1251. {
  1252. size32_t usablesize;
  1253. return allocMem2(size,usablesize);
  1254. }
  1255. void freeMem(void *ptr)
  1256. {
  1257. if (!ptr)
  1258. return;
  1259. #if HeapChecking >= PtrCheck
  1260. checkPtrValid(ptr);
  1261. #endif
  1262. size32_t CurBlockSize = BlockSize(ptr);
  1263. if (!ISSUBALLOCATED(CurBlockSize)) {
  1264. SpinBlock block(lock);
  1265. if (*((byte *)ptr - HeadTagOff) == OSAllocTag)
  1266. FreeOSBlock(ptr,CurBlockSize);
  1267. else {
  1268. #if InitialiseMemory
  1269. memset(ptr, FreeFillChar, CurBlockSize-BlockExtraSize);
  1270. #endif
  1271. FreeSubBlock(ptr,CurBlockSize);
  1272. }
  1273. }
  1274. else if (suballocator)
  1275. suballocator->freeMem(ptr);
  1276. else
  1277. HeapError(e_inconsist_block_tag,ptr);
  1278. }
  1279. void * reallocMem2 (void * ptr, size32_t NewSize,size32_t &usablesize)
  1280. {
  1281. if (ptr == NULL)
  1282. return allocMem2(NewSize,usablesize);
  1283. if (NewSize == 0) {
  1284. freeMem(ptr);
  1285. usablesize = 0;
  1286. return NULL;
  1287. }
  1288. if (NewSize>MAXUSERALLOCATESIZE) // too big!
  1289. HeapError(e_alloc_too_big);
  1290. #if HeapChecking >= PtrCheck
  1291. checkPtrValid(ptr);
  1292. #endif
  1293. /*
  1294. * Check for an allocation request that exceeds the amount we can
  1295. * allocate in one go.
  1296. */
  1297. size32_t CurBlockSize = BlockSize(ptr);
  1298. void * NewBlock=NULL;
  1299. if (!ISSUBALLOCATED(CurBlockSize)) {
  1300. if (suballocator) // want to use suballocator if can
  1301. NewBlock = suballocator->allocMem(NewSize,usablesize);
  1302. if (!NewBlock) {
  1303. {
  1304. SpinBlock block(lock);
  1305. if (*((byte *)ptr - HeadTagOff) == OSAllocTag)
  1306. NewBlock = ExpandOSBlock(ptr, NewSize);
  1307. else
  1308. NewBlock = ExpandSubBlock(ptr, NewSize);
  1309. if (NewBlock) {
  1310. usablesize = BlockSize(ptr)-BlockExtraSize;
  1311. return NewBlock;
  1312. }
  1313. }
  1314. NewBlock = allocMem2(NewSize,usablesize);
  1315. }
  1316. memcpy(NewBlock,ptr,(NewSize<CurBlockSize-BlockExtraSize)?NewSize:(CurBlockSize-BlockExtraSize));
  1317. freeMem(ptr);
  1318. }
  1319. else if (suballocator) {
  1320. NewBlock = suballocator->reallocMem(ptr,NewSize,usablesize); // returns NULL if can't do
  1321. if (NewBlock) {
  1322. ASSERTEX(roundupSize(NewSize)<=usablesize);
  1323. return NewBlock;
  1324. }
  1325. NewBlock = allocMem2(NewSize,usablesize);
  1326. size32_t oldsize = suballocator->usableSize(ptr);
  1327. memcpy(NewBlock,ptr,(NewSize<oldsize)?NewSize:oldsize);
  1328. freeMem(ptr);
  1329. }
  1330. else
  1331. HeapError(e_inconsist_block_tag,ptr);
  1332. ASSERTEX(roundupSize(NewSize)<=usablesize);
  1333. return NewBlock;
  1334. }
  1335. void * reallocMem (void * ptr, size32_t NewSize)
  1336. {
  1337. size32_t usablesize;
  1338. return reallocMem2(ptr,NewSize,usablesize);
  1339. }
  1340. size32_t usableSize(const void * ptr)
  1341. {
  1342. if (!ptr)
  1343. return 0;
  1344. // SpinBlock block(lock); // lock shouldn't be needed
  1345. size32_t CurBlockSize = BlockSize(ptr);
  1346. if (!ISSUBALLOCATED(CurBlockSize))
  1347. return CurBlockSize-BlockExtraSize;
  1348. if (suballocator)
  1349. return suballocator->usableSize(ptr);
  1350. HeapError(e_inconsist_block_tag,ptr);
  1351. return 0;
  1352. }
  1353. memsize_t totalAllocated()
  1354. {
  1355. SpinBlock block(lock);
  1356. return OsTotal;
  1357. }
  1358. memsize_t totalMax()
  1359. {
  1360. SpinBlock block(lock);
  1361. return OsMax;
  1362. }
  1363. memsize_t totalRemaining()
  1364. {
  1365. SpinBlock block(lock);
  1366. if (OsTotal<OsMax) // JCS, don't see how OsTotal could be > OsMax, but code elsewhere implied it could
  1367. return OsMax-OsTotal;
  1368. return 0;
  1369. }
  1370. void walkBlock(IWalkMem &walker,const byte * BlockPtr, size32_t OSBlockSize)
  1371. {
  1372. if (!OSBlockSize)
  1373. return;
  1374. byte BlockHeadTag = *(BlockPtr - HeadTagOff);
  1375. do {
  1376. size32_t CurBlockSize = BlockSize(BlockPtr);
  1377. byte HeadTag = *(BlockPtr - HeadTagOff);
  1378. if (HeadTag == SuperAllocTag) {
  1379. if (!suballocator.get()||!suballocator->isBlock(BlockPtr))
  1380. walker.inuse(BlockPtr,CurBlockSize);
  1381. }
  1382. else if (HeadTag == OSAllocTag) {
  1383. walker.inuse(BlockPtr,CurBlockSize);
  1384. }
  1385. BlockPtr += CurBlockSize;
  1386. OSBlockSize -= CurBlockSize;
  1387. } while ((BlockHeadTag != OSAllocTag) && (*(BlockPtr - HeadTagOff) != BlockBoundTag));
  1388. }
  1389. void walk(IWalkMem &walker)
  1390. {
  1391. if (suballocator.get())
  1392. suballocator->walk(walker);
  1393. FreeListEnt *CurOsBlock = HeadOsBlockList.Next;
  1394. while (CurOsBlock!=&HeadOsBlockList) {
  1395. walker.osblock(CurOsBlock->BlockPtr,CurOsBlock->Size);
  1396. walkBlock(walker,(const byte *)CurOsBlock->BlockPtr+BlockExtraSize,CurOsBlock->Size);
  1397. CurOsBlock = CurOsBlock->Next;
  1398. }
  1399. }
  1400. void logMemLeaks(bool logdata)
  1401. {
  1402. struct cwalker: public IWalkMem
  1403. {
  1404. memsize_t tot;
  1405. unsigned num;
  1406. memsize_t ostot;
  1407. unsigned osnum;
  1408. bool logdata;
  1409. cwalker(bool _logdata)
  1410. {
  1411. tot = 0;
  1412. num = 0;
  1413. ostot = 0;
  1414. osnum = 0;
  1415. logdata = _logdata;
  1416. }
  1417. void inuse(const void *mem,size32_t sz)
  1418. {
  1419. num++;
  1420. if (logdata) {
  1421. StringBuffer line;
  1422. for (unsigned i=0;(i<sz)&&(i<16);i++) {
  1423. if (i)
  1424. line.append(", ");
  1425. line.appendf("%02x",(unsigned)(*((byte *)mem+i)));
  1426. }
  1427. PROGLOG("LEAK(%d,%u,%p) : %s",num,sz,mem,line.str());
  1428. }
  1429. tot += sz;
  1430. }
  1431. void osblock(const void *mem,size32_t sz)
  1432. {
  1433. osnum++;
  1434. if (logdata)
  1435. PROGLOG("OSBLOCK(%d,%u,%p)",osnum,sz,mem);
  1436. ostot += sz;
  1437. }
  1438. } walker(logdata);
  1439. walk(walker);
  1440. if (walker.num)
  1441. PROGLOG("JMALLOC LEAKCHECKING: %d leaks, total memory %"I64F"d",walker.num,(__int64)walker.tot);
  1442. PROGLOG("JMALLOC OSBLOCKS: %d, total memory %"I64F"d",walker.osnum,(__int64)walker.ostot);
  1443. }
  1444. };
  1445. CSubAllocator::~CSubAllocator()
  1446. {
  1447. for (unsigned i=0;i<nblks;i++) {
  1448. CSubAllocatorBlock *head = (CSubAllocatorBlock *)&blks[i];
  1449. CSubAllocatorBlock *p = head->next;
  1450. while (p!=head) {
  1451. CSubAllocatorBlock *n = p->next;
  1452. superallocator.freeMem(p);
  1453. p = n;
  1454. }
  1455. }
  1456. delete [] blks;
  1457. }
  1458. void CSubAllocator::freeMem(void * p)
  1459. {
  1460. NonReentrantSpinBlock block(sublock);
  1461. ASSERTEX(p);
  1462. CSubAllocatorBlock *b = CSubAllocatorBlock::base(p);
  1463. bool wasfull = b->full();
  1464. if (b->subfree(p)) { // empty so free block from chain
  1465. b->unlink();
  1466. superallocator.freeMem(b);
  1467. }
  1468. else if (wasfull) {
  1469. unsigned i;
  1470. CSubAllocatorBlock * f = item(b->recsize,i);
  1471. #ifdef _DEBUG
  1472. if (!f)
  1473. HeapError(e_corrupt_sub_block,p);
  1474. #endif
  1475. b->unlink();
  1476. blks[i].prepend(*b);
  1477. }
  1478. }
  1479. void *CSubAllocator::getMem(size32_t recsz,unsigned &n)
  1480. {
  1481. ASSERTEX(recsz<=maxrecsize);
  1482. n = (SBPAGESIZE-SBHEADERSIZE)/(recsz+SBOVERHEAD);
  1483. ASSERTEX(n>=15);
  1484. size32_t wanted = SBHEADERSIZE+(recsz+SBOVERHEAD)*n;
  1485. ASSERTEX(wanted<=SBPAGESIZE);
  1486. void *ret = superallocator.allocMem(wanted);
  1487. n = (superallocator.usableSize(ret)-SBHEADERSIZE)/(recsz+SBOVERHEAD);
  1488. return ret;
  1489. }
  1490. IAllocator *createMemoryAllocator(memsize_t maxtotal, unsigned suballoc_htabsize, memsize_t mintotal,bool avoidReallocFragmentation)
  1491. {
  1492. return new CSuperAllocator(maxtotal,suballoc_htabsize,mintotal,avoidReallocFragmentation);
  1493. }
  1494. class CGuardedSuperAllocator: public CInterface, implements IAllocator
  1495. {
  1496. CSuperAllocator allocator;
  1497. size32_t guardsize;
  1498. CriticalSection crit;
  1499. __int64 numallocs;
  1500. unsigned guarderror(const void *ptr,size32_t sz,unsigned n)
  1501. {
  1502. const byte * p = (const byte *)ptr;
  1503. p += sizeof(size32_t)+sz;
  1504. size32_t szn = *(const size32_t *)p;
  1505. ERRLOG("CGuardedSuperAllocator guard error(%d) %p len %u %d",n,ptr,sz,(int)szn);
  1506. PrintStackReport();
  1507. #ifdef _WIN32
  1508. DebugBreak();
  1509. #endif
  1510. throw MakeStringException(-1,"CGuardedSuperAllocator guard error(%d) %p len %u",n,ptr,sz);
  1511. }
  1512. void *fillguard(void *_p,size32_t sz )
  1513. {
  1514. // format sz,data,-sz,e0,e1,...
  1515. byte * p = (byte *)_p;
  1516. *(size32_t *)p = sz;
  1517. p += sizeof(size32_t);
  1518. void * ret = p;
  1519. p += sz;
  1520. *(size32_t *)p = 0-sz;
  1521. p += sizeof(size32_t);
  1522. unsigned rest = guardsize-sizeof(size32_t)*2;
  1523. byte b = 0xe0;
  1524. while (rest--)
  1525. *(p++) = b++;
  1526. return ret;
  1527. }
  1528. void checkguard(const void *_p,size32_t &sz)
  1529. {
  1530. const byte * p = (const byte *)_p;
  1531. sz = *(const size32_t *)p;
  1532. p += sizeof(size32_t);
  1533. p += sz;
  1534. size32_t szn = *(const size32_t *)p;
  1535. if (szn!=0-sz)
  1536. guarderror(_p,sz,1);
  1537. p += sizeof(size32_t);
  1538. unsigned rest = guardsize-sizeof(size32_t)*2;
  1539. byte b = 0xe0;
  1540. while (rest--)
  1541. if (*(p++) != b++)
  1542. guarderror(_p,sz,2);
  1543. }
  1544. public:
  1545. IMPLEMENT_IINTERFACE;
  1546. CGuardedSuperAllocator(memsize_t maxtotal, unsigned suballoc_htabsize, memsize_t mintotal,bool avoidReallocFragmentation, size32_t _guardsize)
  1547. : allocator(maxtotal,suballoc_htabsize,mintotal,avoidReallocFragmentation)
  1548. {
  1549. guardsize = (_guardsize<sizeof(size32_t)*2)?(sizeof(size32_t)*2):_guardsize;
  1550. numallocs = 0;
  1551. }
  1552. virtual void * allocMem(size32_t sz)
  1553. {
  1554. if (!sz)
  1555. return NULL;
  1556. void *ret = allocator.allocMem(sz+guardsize);
  1557. ret = fillguard(ret,sz);
  1558. CriticalBlock block(crit);
  1559. numallocs++;
  1560. return ret;
  1561. }
  1562. virtual void freeMem(void *_ptr)
  1563. {
  1564. if (_ptr) {
  1565. size32_t sz;
  1566. void *ptr = (byte *)_ptr-sizeof(size32_t);
  1567. checkguard(ptr,sz);
  1568. memset(ptr,0xff,sz+guardsize);
  1569. allocator.freeMem(ptr);
  1570. CriticalBlock block(crit);
  1571. numallocs--;
  1572. }
  1573. }
  1574. virtual void * reallocMem(void *_prev,size32_t sz)
  1575. {
  1576. size32_t oldsz;
  1577. checkguard((byte *)_prev-sizeof(size32_t),oldsz);
  1578. if (oldsz==sz)
  1579. return _prev;
  1580. void *ret = allocMem(sz);
  1581. memcpy(ret,_prev,oldsz<sz?oldsz:sz);
  1582. freeMem(_prev);
  1583. return ret;
  1584. }
  1585. virtual size32_t usableSize(const void *ptr)
  1586. {
  1587. if (!ptr)
  1588. return 0;
  1589. size32_t ret;
  1590. checkguard((byte *)ptr-sizeof(size32_t),ret);
  1591. return ret;
  1592. }
  1593. virtual memsize_t totalAllocated()
  1594. {
  1595. return allocator.totalAllocated();
  1596. }
  1597. virtual memsize_t totalMax()
  1598. {
  1599. return allocator.totalMax();
  1600. }
  1601. virtual memsize_t totalRemaining()
  1602. {
  1603. return allocator.totalRemaining();
  1604. }
  1605. virtual void checkPtrValid(const void * ptr)
  1606. {
  1607. size32_t sz;
  1608. checkguard((byte *)ptr-sizeof(size32_t),sz);
  1609. }
  1610. virtual void logMemLeaks(bool logdata)
  1611. {
  1612. //allocator.logMemLeaks(logdata);
  1613. }
  1614. virtual void * allocMem2(size32_t sz, size32_t &usablesz)
  1615. {
  1616. usablesz = sz;
  1617. return allocMem(sz);
  1618. }
  1619. virtual void * reallocMem2(void *prev,size32_t sz, size32_t &usablesz)
  1620. {
  1621. usablesz = sz;
  1622. return reallocMem(prev,sz);
  1623. }
  1624. virtual size32_t roundupSize(size32_t sz)
  1625. {
  1626. return sz;
  1627. }
  1628. };
  1629. IAllocator *createGuardedMemoryAllocator(memsize_t maxtotal, unsigned suballoc_htabsize, memsize_t mintotal,bool avoidReallocFragmentation, size32_t guardsize)
  1630. {
  1631. return new CGuardedSuperAllocator(maxtotal,suballoc_htabsize,mintotal,avoidReallocFragmentation,guardsize);
  1632. }
  1633. void fillPtr(IAllocator *a,void *p,size32_t sz)
  1634. {
  1635. size32_t us = a->usableSize(p);
  1636. ASSERTEX(us>=sz);
  1637. memset(p,sz^0xc7,us);
  1638. }
  1639. void testFillPtr(IAllocator *a,void *p,size32_t sz)
  1640. {
  1641. size32_t us = a->usableSize(p);
  1642. byte *b = (byte *)p;
  1643. ASSERTEX(us>=sz);
  1644. ASSERTEX(us<sz+OSPAGESIZE);
  1645. byte t = sz^0xc7;
  1646. while (us--)
  1647. ASSERTEX(b[us]==t);
  1648. }
  1649. void testAllocator()
  1650. {
  1651. printf("HeadTagOff=%x\n",(unsigned) HeadTagOff);
  1652. printf("TailTagOff=%x\n",(unsigned) TailTagOff);
  1653. printf("HeadChkOff=%x\n",(unsigned) HeadChkOff);
  1654. printf("TailChkOff=%x\n",(unsigned) TailChkOff);
  1655. printf("HeadExtraBytes=%x\n",(unsigned) HeadExtraBytes);
  1656. printf("TailExtraBytes=%x\n",(unsigned) TailExtraBytes);
  1657. printf("BlockExtraSize=%x\n",(unsigned) BlockExtraSize);
  1658. printf("OSBlockExtra=%x\n",(unsigned) OSBlockExtra);
  1659. printf("OSPtrExtra=%x\n",(unsigned) OSPtrExtra);
  1660. printf("SBHEADERSIZE=%x\n",(unsigned) SBHEADERSIZE);
  1661. printf("MINSUBBLOCKSIZE=%x\n",(unsigned) MINSUBBLOCKSIZE);
  1662. Owned<IAllocator> a;
  1663. a.setown(createMemoryAllocator(0x100000*100));
  1664. byte *ptrs[8192];
  1665. unsigned i;
  1666. unsigned j;
  1667. size32_t us;
  1668. for (i=0;i<8192;i++) {
  1669. ptrs[i] = (byte *)a->allocMem(i);
  1670. us = a->usableSize(ptrs[i]);
  1671. assertex(us>=i);
  1672. memset(ptrs[i],(byte)i,us);
  1673. }
  1674. for (i=0;i<8192;i++) {
  1675. a->checkPtrValid(ptrs[i]);
  1676. us = a->usableSize(ptrs[i]);
  1677. assertex(us>=i);
  1678. for (j=0;j<us;j++)
  1679. assertex(ptrs[i][j]==(byte)i);
  1680. }
  1681. for (i=0;i<8192;i++) {
  1682. for (j=0;j<i;j++)
  1683. assertex(ptrs[i][j]==(byte)i);
  1684. memset(ptrs[i],255-(byte)i,i);
  1685. a->freeMem(ptrs[i]);
  1686. }
  1687. //assertex(a->totalAllocated()==0);
  1688. memsize_t fibs[100];
  1689. seedRandom(1234);
  1690. unsigned f = 2;
  1691. fibs[0] = 1;
  1692. fibs[1] = 1;
  1693. for (f=2;f<100;f++) {
  1694. unsigned nf = fibs[f-1]+fibs[f-2];
  1695. if (nf>0x100000*10)
  1696. break;
  1697. fibs[f] = nf;
  1698. }
  1699. size32_t sizes[8192];
  1700. memset(sizes,0,sizeof(sizes));
  1701. size32_t total = 0;
  1702. size32_t max = 0x100000*50;
  1703. for (unsigned iter=0;iter<10000000;iter++) {
  1704. unsigned r = getRandom();
  1705. i = r%8192;
  1706. r /= 8192;
  1707. size32_t sz = sizes[i];
  1708. if (sz) {
  1709. if (r%8==0) {
  1710. r/=8;
  1711. size32_t nsz = fibs[r%f];
  1712. r/=f;
  1713. if (r%16==0)
  1714. nsz = getRandom()%nsz+1;
  1715. if (total+nsz-sz>max)
  1716. continue;
  1717. testFillPtr(a,ptrs[i],sz);
  1718. ptrs[i] = (byte *)a->reallocMem(ptrs[i],nsz);
  1719. fillPtr(a,ptrs[i],nsz);
  1720. a->checkPtrValid(ptrs[i]);
  1721. sizes[i] = nsz;
  1722. total += nsz-sz;
  1723. }
  1724. else {
  1725. testFillPtr(a,ptrs[i],sz);
  1726. a->freeMem(ptrs[i]);
  1727. sizes[i] = 0;
  1728. assertex(total>=sz);
  1729. total -= sz;
  1730. }
  1731. }
  1732. else {
  1733. sz = fibs[r%f];
  1734. r/=f;
  1735. if (r%16==0)
  1736. sz = getRandom()%sz+1;
  1737. if (total+sz>max)
  1738. continue;
  1739. ptrs[i] = (byte *)a->allocMem(sz);
  1740. fillPtr(a,ptrs[i],sz);
  1741. a->checkPtrValid(ptrs[i]);
  1742. sizes[i] = sz;
  1743. total += sz;
  1744. }
  1745. if (iter%10000==0)
  1746. printf("iter = %d, total=%d, allocated=%d\n",iter,total, (unsigned) a->totalAllocated());
  1747. }
  1748. for (i=0;i<8192;i++)
  1749. if (sizes[i])
  1750. a->freeMem(ptrs[i]);
  1751. //assertex(a->totalAllocated()==0);
  1752. a.clear();
  1753. }