z_zone.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. /* Emacs style mode select -*- C++ -*-
  2. *-----------------------------------------------------------------------------
  3. *
  4. *
  5. * PrBoom: a Doom port merged with LxDoom and LSDLDoom
  6. * based on BOOM, a modified and improved DOOM engine
  7. * Copyright (C) 1999 by
  8. * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
  9. * Copyright (C) 1999-2000 by
  10. * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
  11. * Copyright 2005, 2006 by
  12. * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
  13. *
  14. * This program is free software; you can redistribute it and/or
  15. * modify it under the terms of the GNU General Public License
  16. * as published by the Free Software Foundation; either version 2
  17. * of the License, or (at your option) any later version.
  18. *
  19. * This program is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU General Public License
  25. * along with this program; if not, write to the Free Software
  26. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  27. * 02111-1307, USA.
  28. *
  29. * DESCRIPTION:
  30. * Zone Memory Allocation. Neat.
  31. *
  32. * Neat enough to be rewritten by Lee Killough...
  33. *
  34. * Must not have been real neat :)
  35. *
  36. * Made faster and more general, and added wrappers for all of Doom's
  37. * memory allocation functions, including malloc() and similar functions.
  38. * Added line and file numbers, in case of error. Added performance
  39. * statistics and tunables.
  40. *-----------------------------------------------------------------------------
  41. */
  42. // use config.h if autoconf made one -- josh
  43. #ifdef HAVE_CONFIG_H
  44. #include "config.h"
  45. #endif
  46. #include <stdlib.h>
  47. #include <stdio.h>
  48. #include "z_zone.h"
  49. #include "doomstat.h"
  50. #include "m_argv.h"
  51. #include "v_video.h"
  52. #include "g_game.h"
  53. #include "lprintf.h"
  54. #ifdef DJGPP
  55. #include <dpmi.h>
  56. #endif
  57. // Tunables
  58. // Alignment of zone memory (benefit may be negated by HEADER_SIZE, CHUNK_SIZE)
  59. #define CACHE_ALIGN 32
  60. // Minimum chunk size at which blocks are allocated
  61. #define CHUNK_SIZE 32
  62. // Minimum size a block must be to become part of a split
  63. #define MIN_BLOCK_SPLIT (1024)
  64. // How much RAM to leave aside for other libraries
  65. #define LEAVE_ASIDE (128*1024)
  66. // Amount to subtract when retrying failed attempts to allocate initial pool
  67. #define RETRY_AMOUNT (256*1024)
  68. // signature for block header
  69. #define ZONEID 0x931d4a11
  70. // Number of mallocs & frees kept in history buffer (must be a power of 2)
  71. #define ZONE_HISTORY 4
  72. // End Tunables
  73. typedef struct memblock {
  74. #ifdef ZONEIDCHECK
  75. unsigned id;
  76. #endif
  77. struct memblock *next,*prev;
  78. size_t size;
  79. void **user;
  80. unsigned char tag;
  81. #ifdef INSTRUMENTED
  82. const char *file;
  83. int line;
  84. #endif
  85. } memblock_t;
  86. /* size of block header
  87. * cph - base on sizeof(memblock_t), which can be larger than CHUNK_SIZE on
  88. * 64bit architectures */
  89. static const size_t HEADER_SIZE = (sizeof(memblock_t)+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1);
  90. static memblock_t *blockbytag[PU_MAX];
  91. // 0 means unlimited, any other value is a hard limit
  92. //static int memory_size = 8192*1024;
  93. static int memory_size = 0;
  94. static int free_memory = 0;
  95. #ifdef INSTRUMENTED
  96. // statistics for evaluating performance
  97. static int active_memory = 0;
  98. static int purgable_memory = 0;
  99. static void Z_DrawStats(void) // Print allocation statistics
  100. {
  101. if (gamestate != GS_LEVEL)
  102. return;
  103. if (memory_size > 0) {
  104. unsigned long total_memory = free_memory + memory_size + active_memory + purgable_memory;
  105. double s = 100.0 / total_memory;
  106. doom_printf("%-5i\t%6.01f%%\tstatic\n"
  107. "%-5i\t%6.01f%%\tpurgable\n"
  108. "%-5i\t%6.01f%%\tfree\n"
  109. "%-5li\t\ttotal\n",
  110. active_memory,
  111. active_memory*s,
  112. purgable_memory,
  113. purgable_memory*s,
  114. (free_memory + memory_size),
  115. (free_memory + memory_size)*s,
  116. total_memory
  117. );
  118. } else {
  119. unsigned long total_memory = active_memory + purgable_memory;
  120. double s = 100.0 / total_memory;
  121. doom_printf("%-5i\t%6.01f%%\tstatic\n"
  122. "%-5i\t%6.01f%%\tpurgable\n"
  123. "%-5li\t\ttotal\n",
  124. active_memory,
  125. active_memory*s,
  126. purgable_memory,
  127. purgable_memory*s,
  128. total_memory
  129. );
  130. }
  131. }
  132. #ifdef HEAPDUMP
  133. #ifndef HEAPDUMP_DIR
  134. #define HEAPDUMP_DIR "."
  135. #endif
  136. void W_PrintLump(FILE* fp, void* p);
  137. void Z_DumpMemory(void)
  138. {
  139. static int dump;
  140. char buf[PATH_MAX + 1];
  141. FILE* fp;
  142. size_t total_cache = 0, total_free = 0, total_malloc = 0;
  143. int tag;
  144. sprintf(buf, "%s/memdump.%d", HEAPDUMP_DIR, dump++);
  145. fp = fopen(buf, "w");
  146. for (tag = PU_FREE; tag < PU_MAX; tag++)
  147. {
  148. memblock_t* end_block, *block;
  149. block = blockbytag[tag];
  150. if (!block)
  151. continue;
  152. end_block = block->prev;
  153. while (1)
  154. {
  155. switch (block->tag) {
  156. case PU_FREE:
  157. fprintf(fp, "free %d\n", block->size);
  158. total_free += block->size;
  159. break;
  160. case PU_CACHE:
  161. fprintf(fp, "cache %s:%d:%d\n", block->file, block->line, block->size);
  162. total_cache += block->size;
  163. break;
  164. case PU_LEVEL:
  165. fprintf(fp, "level %s:%d:%d\n", block->file, block->line, block->size);
  166. total_malloc += block->size;
  167. break;
  168. default:
  169. fprintf(fp, "malloc %s:%d:%d", block->file, block->line, block->size);
  170. total_malloc += block->size;
  171. if (block->file)
  172. if (strstr(block->file,"w_memcache.c"))
  173. W_PrintLump(fp, (char*)block + HEADER_SIZE);
  174. fputc('\n', fp);
  175. break;
  176. }
  177. if (block == end_block)
  178. break;
  179. block=block->next;
  180. }
  181. }
  182. fprintf(fp, "malloc %d, cache %d, free %d, total %d\n",
  183. total_malloc, total_cache, total_free,
  184. total_malloc + total_cache + total_free);
  185. fclose(fp);
  186. }
  187. #endif
  188. #endif
  189. #ifdef INSTRUMENTED
  190. // killough 4/26/98: Add history information
  191. enum {malloc_history, free_history, NUM_HISTORY_TYPES};
  192. static const char *file_history[NUM_HISTORY_TYPES][ZONE_HISTORY];
  193. static int line_history[NUM_HISTORY_TYPES][ZONE_HISTORY];
  194. static int history_index[NUM_HISTORY_TYPES];
  195. static const char *const desc[NUM_HISTORY_TYPES] = {"malloc()'s", "free()'s"};
  196. void Z_DumpHistory(char *buf)
  197. {
  198. int i,j;
  199. char s[1024];
  200. strcat(buf,"\n");
  201. for (i=0;i<NUM_HISTORY_TYPES;i++)
  202. {
  203. sprintf(s,"\nLast several %s:\n\n", desc[i]);
  204. strcat(buf,s);
  205. for (j=0; j<ZONE_HISTORY; j++)
  206. {
  207. int k = (history_index[i]-j-1) & (ZONE_HISTORY-1);
  208. if (file_history[i][k])
  209. {
  210. sprintf(s, "File: %s, Line: %d\n", file_history[i][k],
  211. line_history[i][k]);
  212. strcat(buf,s);
  213. }
  214. }
  215. }
  216. }
  217. #else
  218. void Z_DumpHistory(char *buf)
  219. {
  220. }
  221. #endif
  222. void Z_Close(void)
  223. {
  224. #if 0
  225. (free)(zonebase);
  226. zone = rover = zonebase = NULL;
  227. #endif
  228. }
  229. void Z_Init(void)
  230. {
  231. #if 0
  232. size_t size = zone_size*1000;
  233. #ifdef HAVE_MMAP
  234. return; /* cphipps - if we have mmap, we don't need our own heap */
  235. #endif
  236. #ifdef INSTRUMENTED
  237. if (!(HEADER_SIZE >= sizeof(memblock_t) && size > HEADER_SIZE))
  238. I_Error("Z_Init: Sanity check failed");
  239. #endif
  240. size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size
  241. size += HEADER_SIZE + CACHE_ALIGN;
  242. // Allocate the memory
  243. zonebase=(malloc)(size);
  244. if (!zonebase)
  245. I_Error("Z_Init: Failed on allocation of %lu bytes", (unsigned long)size);
  246. lprintf(LO_INFO,"Z_Init : Allocated %lukb zone memory\n",
  247. (long unsigned)size / 1000);
  248. // Align on cache boundary
  249. zone = (memblock_t *) ((char *) zonebase + CACHE_ALIGN -
  250. ((unsigned) zonebase & (CACHE_ALIGN-1)));
  251. rover = zone; // Rover points to base of zone mem
  252. zone->next = zone->prev = zone; // Single node
  253. zone->size = size; // All memory in one block
  254. zone->tag = PU_FREE; // A free block
  255. zone->vm = 0;
  256. #ifdef ZONEIDCHECK
  257. zone->id = 0;
  258. #endif
  259. #ifdef INSTRUMENTED
  260. free_memory = size;
  261. /* cph - remove unnecessary initialisations to 0 */
  262. #endif
  263. #ifdef HEAPDUMP
  264. atexit(Z_DumpMemory);
  265. #endif
  266. #endif
  267. }
  268. /* Z_Malloc
  269. * You can pass a NULL user if the tag is < PU_PURGELEVEL.
  270. *
  271. * cph - the algorithm here was a very simple first-fit round-robin
  272. * one - just keep looping around, freeing everything we can until
  273. * we get a large enough space
  274. *
  275. * This has been changed now; we still do the round-robin first-fit,
  276. * but we only free the blocks we actually end up using; we don't
  277. * free all the stuff we just pass on the way.
  278. */
  279. void *(Z_Malloc)(size_t size, int tag, void **user
  280. #ifdef INSTRUMENTED
  281. , const char *file, int line
  282. #endif
  283. )
  284. {
  285. memblock_t *block = NULL;
  286. #ifdef INSTRUMENTED
  287. #ifdef CHECKHEAP
  288. Z_CheckHeap();
  289. #endif
  290. file_history[malloc_history][history_index[malloc_history]] = file;
  291. line_history[malloc_history][history_index[malloc_history]++] = line;
  292. history_index[malloc_history] &= ZONE_HISTORY-1;
  293. #endif
  294. #ifdef ZONEIDCHECK
  295. if (tag >= PU_PURGELEVEL && !user)
  296. I_Error ("Z_Malloc: An owner is required for purgable blocks"
  297. #ifdef INSTRUMENTED
  298. "Source: %s:%d", file, line
  299. #endif
  300. );
  301. #endif
  302. if (!size)
  303. return user ? *user = NULL : NULL; // malloc(0) returns NULL
  304. size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size
  305. if (memory_size > 0 && ((free_memory + memory_size) < (int)(size + HEADER_SIZE)))
  306. {
  307. memblock_t *end_block;
  308. block = blockbytag[PU_CACHE];
  309. if (block)
  310. {
  311. end_block = block->prev;
  312. while (1)
  313. {
  314. memblock_t *next = block->next;
  315. #ifdef INSTRUMENTED
  316. (Z_Free)((char *) block + HEADER_SIZE, file, line);
  317. #else
  318. (Z_Free)((char *) block + HEADER_SIZE);
  319. #endif
  320. if (((free_memory + memory_size) >= (int)(size + HEADER_SIZE)) || (block == end_block))
  321. break;
  322. block = next; // Advance to next block
  323. }
  324. }
  325. block = NULL;
  326. }
  327. #ifdef HAVE_LIBDMALLOC
  328. while (!(block = dmalloc_malloc(file,line,size + HEADER_SIZE,DMALLOC_FUNC_MALLOC,0,0))) {
  329. #else
  330. while (!(block = (malloc)(size + HEADER_SIZE))) {
  331. #endif
  332. if (!blockbytag[PU_CACHE])
  333. I_Error ("Z_Malloc: Failure trying to allocate %lu bytes"
  334. #ifdef INSTRUMENTED
  335. "\nSource: %s:%d"
  336. #endif
  337. ,(unsigned long) size
  338. #ifdef INSTRUMENTED
  339. , file, line
  340. #endif
  341. );
  342. Z_FreeTags(PU_CACHE,PU_CACHE);
  343. }
  344. if (!blockbytag[tag])
  345. {
  346. blockbytag[tag] = block;
  347. block->next = block->prev = block;
  348. }
  349. else
  350. {
  351. blockbytag[tag]->prev->next = block;
  352. block->prev = blockbytag[tag]->prev;
  353. block->next = blockbytag[tag];
  354. blockbytag[tag]->prev = block;
  355. }
  356. block->size = size;
  357. #ifdef INSTRUMENTED
  358. if (tag >= PU_PURGELEVEL)
  359. purgable_memory += block->size;
  360. else
  361. active_memory += block->size;
  362. #endif
  363. free_memory -= block->size;
  364. #ifdef INSTRUMENTED
  365. block->file = file;
  366. block->line = line;
  367. #endif
  368. #ifdef ZONEIDCHECK
  369. block->id = ZONEID; // signature required in block header
  370. #endif
  371. block->tag = tag; // tag
  372. block->user = user; // user
  373. block = (memblock_t *)((char *) block + HEADER_SIZE);
  374. if (user) // if there is a user
  375. *user = block; // set user to point to new block
  376. #ifdef INSTRUMENTED
  377. Z_DrawStats(); // print memory allocation stats
  378. // scramble memory -- weed out any bugs
  379. memset(block, gametic & 0xff, size);
  380. #endif
  381. return block;
  382. }
  383. void (Z_Free)(void *p
  384. #ifdef INSTRUMENTED
  385. , const char *file, int line
  386. #endif
  387. )
  388. {
  389. memblock_t *block = (memblock_t *)((char *) p - HEADER_SIZE);
  390. #ifdef INSTRUMENTED
  391. #ifdef CHECKHEAP
  392. Z_CheckHeap();
  393. #endif
  394. file_history[free_history][history_index[free_history]] = file;
  395. line_history[free_history][history_index[free_history]++] = line;
  396. history_index[free_history] &= ZONE_HISTORY-1;
  397. #endif
  398. if (!p)
  399. return;
  400. #ifdef ZONEIDCHECK
  401. if (block->id != ZONEID)
  402. I_Error("Z_Free: freed a pointer without ZONEID"
  403. #ifdef INSTRUMENTED
  404. "\nSource: %s:%d"
  405. "\nSource of malloc: %s:%d"
  406. , file, line, block->file, block->line
  407. #endif
  408. );
  409. block->id = 0; // Nullify id so another free fails
  410. #endif
  411. if (block->user) // Nullify user if one exists
  412. *block->user = NULL;
  413. if (block == block->next)
  414. blockbytag[block->tag] = NULL;
  415. else
  416. if (blockbytag[block->tag] == block)
  417. blockbytag[block->tag] = block->next;
  418. block->prev->next = block->next;
  419. block->next->prev = block->prev;
  420. free_memory += block->size;
  421. #ifdef INSTRUMENTED
  422. if (block->tag >= PU_PURGELEVEL)
  423. purgable_memory -= block->size;
  424. else
  425. active_memory -= block->size;
  426. /* scramble memory -- weed out any bugs */
  427. memset(block, gametic & 0xff, block->size + HEADER_SIZE);
  428. #endif
  429. #ifdef HAVE_LIBDMALLOC
  430. dmalloc_free(file,line,block,DMALLOC_FUNC_MALLOC);
  431. #else
  432. (free)(block);
  433. #endif
  434. #ifdef INSTRUMENTED
  435. Z_DrawStats(); // print memory allocation stats
  436. #endif
  437. }
  438. void (Z_FreeTags)(int lowtag, int hightag
  439. #ifdef INSTRUMENTED
  440. , const char *file, int line
  441. #endif
  442. )
  443. {
  444. #ifdef HEAPDUMP
  445. Z_DumpMemory();
  446. #endif
  447. if (lowtag <= PU_FREE)
  448. lowtag = PU_FREE+1;
  449. if (hightag > PU_CACHE)
  450. hightag = PU_CACHE;
  451. for (;lowtag <= hightag; lowtag++)
  452. {
  453. memblock_t *block, *end_block;
  454. block = blockbytag[lowtag];
  455. if (!block)
  456. continue;
  457. end_block = block->prev;
  458. while (1)
  459. {
  460. memblock_t *next = block->next;
  461. #ifdef INSTRUMENTED
  462. (Z_Free)((char *) block + HEADER_SIZE, file, line);
  463. #else
  464. (Z_Free)((char *) block + HEADER_SIZE);
  465. #endif
  466. if (block == end_block)
  467. break;
  468. block = next; // Advance to next block
  469. }
  470. }
  471. }
  472. void (Z_ChangeTag)(void *ptr, int tag
  473. #ifdef INSTRUMENTED
  474. , const char *file, int line
  475. #endif
  476. )
  477. {
  478. memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE);
  479. // proff - added sanity check, this can happen when an empty lump is locked
  480. if (!ptr)
  481. return;
  482. // proff - do nothing if tag doesn't differ
  483. if (tag == block->tag)
  484. return;
  485. #ifdef INSTRUMENTED
  486. #ifdef CHECKHEAP
  487. Z_CheckHeap();
  488. #endif
  489. #endif
  490. #ifdef ZONEIDCHECK
  491. if (block->id != ZONEID)
  492. I_Error ("Z_ChangeTag: freed a pointer without ZONEID"
  493. #ifdef INSTRUMENTED
  494. "\nSource: %s:%d"
  495. "\nSource of malloc: %s:%d"
  496. , file, line, block->file, block->line
  497. #endif
  498. );
  499. if (tag >= PU_PURGELEVEL && !block->user)
  500. I_Error ("Z_ChangeTag: an owner is required for purgable blocks\n"
  501. #ifdef INSTRUMENTED
  502. "Source: %s:%d"
  503. "\nSource of malloc: %s:%d"
  504. , file, line, block->file, block->line
  505. #endif
  506. );
  507. #endif // ZONEIDCHECK
  508. if (block == block->next)
  509. blockbytag[block->tag] = NULL;
  510. else
  511. if (blockbytag[block->tag] == block)
  512. blockbytag[block->tag] = block->next;
  513. block->prev->next = block->next;
  514. block->next->prev = block->prev;
  515. if (!blockbytag[tag])
  516. {
  517. blockbytag[tag] = block;
  518. block->next = block->prev = block;
  519. }
  520. else
  521. {
  522. blockbytag[tag]->prev->next = block;
  523. block->prev = blockbytag[tag]->prev;
  524. block->next = blockbytag[tag];
  525. blockbytag[tag]->prev = block;
  526. }
  527. #ifdef INSTRUMENTED
  528. if (block->tag < PU_PURGELEVEL && tag >= PU_PURGELEVEL)
  529. {
  530. active_memory -= block->size;
  531. purgable_memory += block->size;
  532. }
  533. else
  534. if (block->tag >= PU_PURGELEVEL && tag < PU_PURGELEVEL)
  535. {
  536. active_memory += block->size;
  537. purgable_memory -= block->size;
  538. }
  539. #endif
  540. block->tag = tag;
  541. }
  542. void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user
  543. #ifdef INSTRUMENTED
  544. , const char *file, int line
  545. #endif
  546. )
  547. {
  548. void *p = (Z_Malloc)(n, tag, user DA(file, line));
  549. if (ptr)
  550. {
  551. memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE);
  552. memcpy(p, ptr, n <= block->size ? n : block->size);
  553. (Z_Free)(ptr DA(file, line));
  554. if (user) // in case Z_Free nullified same user
  555. *user=p;
  556. }
  557. return p;
  558. }
  559. void *(Z_Calloc)(size_t n1, size_t n2, int tag, void **user
  560. #ifdef INSTRUMENTED
  561. , const char *file, int line
  562. #endif
  563. )
  564. {
  565. return
  566. (n1*=n2) ? memset((Z_Malloc)(n1, tag, user DA(file, line)), 0, n1) : NULL;
  567. }
  568. char *(Z_Strdup)(const char *s, int tag, void **user
  569. #ifdef INSTRUMENTED
  570. , const char *file, int line
  571. #endif
  572. )
  573. {
  574. return strcpy((Z_Malloc)(strlen(s)+1, tag, user DA(file, line)), s);
  575. }
  576. void (Z_CheckHeap)(
  577. #ifdef INSTRUMENTED
  578. const char *file, int line
  579. #else
  580. void
  581. #endif
  582. )
  583. {
  584. #if 0
  585. memblock_t *block; // Start at base of zone mem
  586. if (block)
  587. do { // Consistency check (last node treated special)
  588. if ((block->next != zone &&
  589. (memblock_t *)((char *) block+HEADER_SIZE+block->size) != block->next)
  590. || block->next->prev != block || block->prev->next != block)
  591. I_Error("Z_CheckHeap: Block size does not touch the next block\n"
  592. #ifdef INSTRUMENTED
  593. "Source: %s:%d"
  594. "\nSource of offending block: %s:%d"
  595. , file, line, block->file, block->line
  596. #endif
  597. );
  598. //#ifdef INSTRUMENTED
  599. // shouldn't be needed anymore, was just for testing
  600. #if 0
  601. if (((int)block->file < 0x00001000) && (block->file != NULL) && (block->tag != 0)) {
  602. block->file = NULL;
  603. }
  604. #endif
  605. } while ((block=block->next) != zone);
  606. #endif
  607. }