home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / heap_7 / mrkrls.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1993-02-19  |  14.2 KB  |  403 lines

  1. {
  2.  
  3. like many others, I am sure, I  had the need to port a real-mode
  4. program to protected mode, and all memory management was done via
  5. MARK-RELEASE. As we all know,  this is not supported in protected
  6. mode, and  re-writting a large  program to use  getmem-freemem is
  7. practically impossible.  Therefore, I came up  with this piece of
  8. code to simulate the operation  of mark-release. I have tested it
  9. on my program  , and it seems to  work. However, I had to  make a
  10. lot  of assumptions  as to  how the  new memory management works,
  11. without any real documentation. Therefore, I dont trust this code
  12. much. The purpose of uploading it here is threefold:
  13.  
  14. 1. I would like to get comments on the code and my rationale from
  15. people that really understand protected mode
  16.  
  17. 2. I would  like the code to be tested  with more programs than I
  18. could ever write!
  19.  
  20. 3. I want others with the same problem to benefit from it.
  21.  
  22. Therefore, please feel free to  use this code with your programs,
  23. no royalties asked. BUT  there is one thing I DO ask  , and it is
  24. this: PLEASE  let me know if  the code works for  you or not, and
  25. any  problems  that  you  may   encounter  with  it.  This  is  a
  26. developer's   forum,  and   we   would   all  benefit   from  any
  27. comments-problems  you  would  report.  I  am  certain there is a
  28. million  or programs  out there  just DYING  to port to protected
  29. mode,  that are  facing the  same problem.  BE SOCIAL. Share your
  30. opinions with others.
  31.  
  32. The preferred method of contact is E-MAIL, or messages posted in
  33. BPASCAL, section 6, protected mode.
  34.  
  35. Mike Cariotoglou,
  36.  
  37. CIS ID : 100012,1767
  38.  
  39.  
  40. DOCUMENTATION (sort of..)
  41. -------------------------
  42.  
  43. The protected mode memory manager works somewhat like this :
  44.  
  45. The system maintains two variables, HEAPLIMIT and HEAPBLOCK
  46. Any request  for memory >=HEAPLIMIT gets  a "normal" allocation ,
  47. ie  a selector-offset  pointer that  is managed  by the  RTM. Any
  48. request smaller than this, is handled as follows:
  49. The  BP  heap  manager  allocates  a  "Large"  memory block, size
  50. HEAPBLOCK,  and then  "carves" or  sub-allocates this  to smaller
  51. chunks  that  it  passes  to  the  application.  Each large block
  52. maintains its  own freelist of deallocated  pointers, and manages
  53. free "small" blocks very much like T6 memory manager.
  54. (By  large  blocks  I  mean  blocks  >=HEAPLIMIT,  that  are  not
  55. sub-allocated. By  the way, a  program can distinguish  between a
  56. large  and  small  block  by  the  fact  that  the  offset of the
  57. corresponding  pointer is  0 for  a large  block, and  >=12 for a
  58. small block).
  59.  
  60. It follows  from this, that  each large block  behaves like a  T6
  61. heap, along with the 8-byte granularity used by the T6 manager to
  62. reserve space for the free  list entries. However, I believe that
  63. here  the granularity  is 4  bytes, not  8, since  only WORDS are
  64. needed to manipulate a heap of  <=64k. This is not important, and
  65. would  affect only  programs that  assumed and  used this  8-byte
  66. granularity.  I am  sure, however,  that such  programs would  be
  67. bound to do pointer arithmetic  also, meaning they would break in
  68. many more places than this.
  69.  
  70. Each large block has an overhead of 12 bytes, which are used to
  71. maintain the block and link to other large blocks.
  72.  
  73. The manual says that the default for heaplimit and heapblock are
  74. 1024  and   8192,  and  suggests  that   heapblock  is  at  least
  75. 4xHeaplimit.  I  dont  really  see  any  reason  for  that,  so I
  76. experimented with the following values :
  77.  
  78. HEAPBLOCK=65532 ( I believe this has to be a multiple of 4)
  79. HEAPLIMIT=heapblock-12; (the overhead)
  80.  
  81. This gives you  a large block that is similar  to a 64k T6 heap,
  82. and its  performance speedwise is  very acceptable, so  these are
  83. the values I used. It is  important that heaplimit is as large as
  84. possible, since any programs using  the technique I will describe
  85. CANNOT allocate a  block of more than HEAPLIMIT  bytes. The above
  86. values give  a maximum block  of 65520, which  happens to be  the
  87. limit in the old T6 heap manager as well.
  88.  
  89. Each "small heap" of 64k belongs to a circular linked list. The
  90. system variable HEAPLIST holds a selector  that can be used as an
  91. entry point to the circular list. This selector does NOT point to
  92. the  head of  the list   (the first  large block  allocated), but
  93. rather at the most recently used  "small" heap. I believe this is
  94. done  to improve  performance, since  the heap  manager traverses
  95. this list when  asked for memory, and this  ensures that the most
  96. recently used block will be used.
  97.  
  98. Now, the basic idea behind my MARK-RELEASE implemenation:
  99.  
  100. Each time MARK is called,  the current HEAPLIST variable is saved
  101. in a stack, and  then it set to 0. This has  the effect of taking
  102. any  memory  allocated  before  the  MARK  "off-line",  making it
  103. unaccesible to  the heap manager. The  first allocation after the
  104. MARK will create a new 64k  "small" heap, that will be managed by
  105. the heap manager normally. When this  is exausted, a new one will
  106. be allocated, and so on. When RELEASE is called it will :
  107.  
  108. a. de-allocate all 64k heaps that belong to the current HEAPLIST
  109. b. de-allocate all heaplists created up to but not including the
  110.    corresponding MARK
  111. c. restore the heaplist saved when MARK was called.
  112.  
  113. This has the  effect of restoring the memory  state exactly as it
  114. was when the MARK was called.  Note that step (b) above allows us
  115. to  use MARK-RELEASE  in nested  form, and  RELEASE to  any point
  116. MARKED. This often happens in real programs. An example:
  117.  
  118. mark(p1);
  119. ...
  120. ...
  121. ...
  122. mark(p2);
  123. ...
  124. ...
  125. release(p1);
  126.  
  127. This would work with the old  heap manager, and it will also work
  128. here, being the equivalent of :
  129.  
  130. mark(p1);
  131. ...
  132. ...
  133. ...
  134. mark(p2);
  135. ...
  136. ...
  137. release(p2);
  138. release(p1);
  139.  
  140. (actually,  this  system  works  BETTER  than  the old, since any
  141. memory in  the freelist before the  MARK is not lost,  as was the
  142. case  with the  old manager,  which always  cleared the free list
  143. upon a RELEASE)
  144.  
  145. This is the  basic idea, the comments in  the code should clarify
  146. more.
  147.  
  148. Limitations and DONT'S
  149. ----------------------
  150.  
  151. 1.  All   allocations  requested  by  the   application  MUST  be
  152. less than HEAPLIMIT, so that they get sub-allocated. If any block
  153. >= heaplimit is allocated, this memory will NOT belong to a small
  154. heap, therefore will NOT be de-allocated by the release, and will
  155. be  PERMANENTLY lost.  My GETMEM  dummy ensures  this ,by halting
  156. the program in such a case. However, I cannot trap calls to NEW
  157. like that, and therefore the potential for loss of memory exists.
  158. I do not expect many programs to be bothered by it, since the
  159. old real mode heap manager would  NOT allow an allocation of more
  160. than 65520 anyway, so existing  programs should NEVER create this
  161. situation. Still,I thought best to mention it.
  162.  
  163. 2.  Do not  expect multiple  allocations to  give you consecutive
  164. memory space. This was also the case with T6, therefore, again, a
  165. program should not rely on  this. (it would involve pointer math,
  166. anyway, which is a DONT in protected mode)
  167.  
  168. 3. It is best that nested MARK-RELEASE calls are matched. Failing
  169. this, it  is acceptable (as shown  above) to do a  RELEASE with a
  170. pointer  that  was  MARKED  before  the  last  mark (any level of
  171. nesting).  What should  NOT be  done, is  to leave dangling MARKS
  172. around, that are not cleared by a RELEASE with this or a previous
  173. MARK. This would lead the program to die soon, since the internal
  174. stack of UHEAP would overflow. In other words:
  175.  
  176. mark(p1)
  177. ...
  178. release(p1)
  179.  
  180. is best
  181.  
  182. OR
  183.  
  184. mark(p1)
  185. ...
  186. mark(p2)
  187. ...
  188. mark(p3)
  189. ...
  190. release(p1)
  191.  
  192. is OK
  193.  
  194. BUT
  195.  
  196. mark(p1)
  197. ...
  198. mark(p2)
  199. ...
  200. release(p2);
  201.  
  202. (missing RELEASE(p1))
  203.  
  204. is NOT allowed.
  205.  
  206. Unfortunately, this  was allowed in  the old heap  manager, since
  207. MARK did not do anything significant, (what it actually was, was
  208. mark(p) :: p:=heapptr;
  209. Therefore, a situation like this MAY exist in old programs.
  210. you will find out by the fact  that the program will crash with a
  211. runtime error if such a thing exists. A good way to test for this
  212. would be  to take the  program through all  its steps, and  right
  213. before exiting check the variable MARKN in uheap. It should be 0.
  214. if not, there is a dangling MARK somewhere.
  215.  
  216. 4. Freemem. Since programs based on mark-release will, sometimes,
  217.    use  freemem  also,  I  have  included  an implementation that
  218.    should do the job. It  should correctly handle the ill-behaved
  219.    case where a program frees a pointer that was allocated before
  220.    the MARK. I  cannot do the same for  DISPOSE, however (for the
  221.    same reasons as NEW) therefore, be warned. It would be a very
  222.    sick program that would do things like that, anyway. Note that
  223.    DISPOSING a  variable that was alloocated  AFTER the innermost
  224.    MARK, is OK. Note also that the implementation of FREEMEM is not
  225.    very efficient, so avoid it if possible
  226.  
  227. 5. Each  MARK that you call,  potentially loses you up  to 64k of
  228. memory, since  it takes the  current heap block  off-line, and is
  229. not accesible  to the heap manager.  This situation is temporary,
  230. and gets fixed  when RELEASE is called. It  means , whoever, that
  231. if  many  nested  MARKS  are  outstanding,  the  amount of memory
  232. available  is less  than you   would expect.  Since PM  gives you
  233. access to unlimited amounts of  memory (especially if you use the
  234. swap  file  technique,  see  messages  section  6 on DPMI virtual
  235. memory), this should not be a problem,  and in any case it is the
  236. price to pay for getiing a MARK-RELEASE that works.
  237.  
  238. 6. the old MARK gave you a real pointer. My MARK will actually
  239. give you an INDEX to a stack. Any programs that actually USED the
  240. value returned  by MARK (they  shouldn't, but...) will  DIE right
  241. away with GP errors.
  242.  
  243. 7.  Once set,  HEAPBLOCK and  HEAPLIMIT should  NEVER be  changed
  244. during the life time of the program. Reasons are obvious
  245.  
  246.  
  247. Using globalxxxx functions
  248. --------------------------
  249.  
  250. Doing memory  management with the WINAPI  and GLOBALxxx functions
  251. is fine, and will not interfere with this scheme
  252.  
  253.  
  254. Ok, here is the code for all that. Please TEST it and let me know
  255. if any problems arise..
  256.  
  257. Good luck!
  258.  
  259. Mike Cariotoglou,
  260. 100012,1767
  261.  
  262. 19-2-93
  263.  
  264. --------------------------------------------------------------------------
  265. }
  266.  
  267. unit uheap;
  268.  
  269. interface
  270.  
  271. procedure mark(var p);
  272. procedure release(p:pointer);
  273. procedure getmem(var p; bytes:word);
  274. procedure Freemem(p:pointer; bytes:word);
  275.  
  276. implementation
  277.  
  278. type
  279.      dword=record {for typecasting pointers}
  280.             o:word;
  281.             s:word
  282.            end;
  283.  
  284.      theapheader=record {12 bytes, header of large block}
  285.                   hsSignature:word;
  286.                   hsReserved:word;
  287.                   hsFreeList:pointer;
  288.                   hsMemFree:word;
  289.                   hsNextHeap:word;
  290.                  end;
  291.  
  292.      pheapheader=^theapheader;
  293.  
  294. const HpOvrHead=sizeof(theapheader); {suballocator overhead}
  295.       MaxMark=64;    {allows for 64 outstanding MARKS
  296.                      which should be enough}
  297.  
  298.       markn:integer=0;  {stack pointer into array of HEAPLIST values}
  299.  
  300. var  markl:array[1..maxmark] of word;
  301. {keeps the HEAPLIST values at the time of the MARK}
  302.  
  303. procedure mark(var p);
  304.  begin
  305.   if markn=maxmark then runerror(203);  {exceeded the limit for outstanding marks}
  306.   inc(markn);
  307.   markl[markn]:=heaplist;
  308.   heaplist:=0; {remove the current heaplist from the heap manager}
  309.   longint(p):=markn;   {uses the pointer passed to store the stack top}
  310.  end;
  311.  
  312. {this routine will de-allocate a whole HEAPLIST, given an entry to any
  313. point in it}
  314. procedure killList(base:word);
  315.  var p:word;
  316.      pp:pheapheader;
  317.  begin
  318.   if base=0 then exit;
  319.   p:=base;
  320.   repeat
  321.    pp:=ptr(p,0);
  322.    p:=pp^.HsNextHeap;
  323.    system.freemem(pp,heapBlock); {deallocates a LARGE block, since offset is 0}
  324.   until p=base;   {circular list proccessing terminates when entry point is reached again}
  325.  end;
  326.  
  327. procedure release(p:pointer);
  328.  var i,j:integer;
  329.  begin
  330.   i:=dword(p).o;
  331.   if i<=0 then runerror(203); {illegal stack index passed to release}
  332.   killList(heaplist);         {kill the current heaplist}
  333.   for j:=markn downto i+1 do killlist(markL[j]); {this is in case the release NOT for the last MARK}
  334.   heaplist:=markl[i]; {restore the heaplist at the time of the MARK}
  335.   markn:=i-1;         {pop values from the stack}
  336.  end;
  337.  
  338. {this routine is used for FREEMEM support. It will try to find
  339. which "small" heap a pointer belongs to, by searching a heaplist for
  340. a selector that matches the pointer's. If you plan to use FREEMEM
  341. a lot, this routine should be coded in assembly for performance}
  342.  
  343. function FindOnList(pp:pointer; base:word ):boolean;
  344.  var p,bs:word;
  345.  begin
  346.   findonlist:=false;
  347.   if base=0 then exit;
  348.   bs:=dword(pp).s;    {selector of pointer}
  349.   p:=base;            {used for list traversal}
  350.   repeat
  351.    if p=bs then
  352.     begin
  353.      findonlist:=true; {yes, this pointer was allocated from this small heap}
  354.      exit
  355.     end;
  356.    p:=pheapheader(ptr(p,0))^.hsNextheap; {link to next heap}
  357.   until p=base;
  358.  end;
  359.  
  360. procedure getmem(var p; bytes:word);
  361.  begin
  362.   if bytes>=heaplimit then runerror(203); {kill programs that allocate blocks >=65520}
  363.   system.getmem(pointer(p),bytes)
  364.  end;
  365.  
  366. {this is the freemem implementation. it can handle pointers allocated
  367. before the last MARK, but is not very efficient, so use with care}
  368.  
  369. procedure Freemem(p:pointer; bytes:word);
  370.  var i:integer;
  371.      tmp:word;
  372.  begin
  373. {if this is a large block (offset=0) or it belongs to the current HEAPLIST,
  374. just do normal freemem}
  375.   if (dword(p).o=0) or findonlist(p,heaplist) then
  376.    begin
  377.     system.freemem(p,bytes);
  378.     exit
  379.    end;
  380. {search the stacked heaplists to find which one it belongs to}
  381.   for i:=markn downto 1 do if FindOnList(p,markL[i]) then
  382.    begin
  383. {if found, then :
  384.  restore the heaplist.
  385.  do the freemem
  386.  put back the current heaplist.
  387.  this has the effect of returning the memory to the correct heaplist,
  388.  so it is not lost}
  389.     tmp:=heaplist;
  390.     heaplist:=markl[i];
  391.     system.freemem(p,bytes);
  392.     markL[i]:=heaplist;
  393.     heaplist:=tmp;
  394.     exit;
  395.    end;
  396.   runerror(203); {should never get here}
  397.  end;
  398.  
  399. begin
  400.  heapblock:=65532; {I saw somewhere that this must be a multiple of 4}
  401.  heaplimit:=heapblock-HpOvrHead; {max block allowed}
  402. end.
  403.