home *** CD-ROM | disk | FTP | other *** search
/ back2roots/padua / padua.7z / padua / audio / MultiPlayer132sr.lha / Bovs / MemMan / MemMan.doc < prev    next >
Encoding:
Text File  |  1992-02-23  |  24.0 KB  |  510 lines

  1.                                   MemMan
  2.                                 Version 2.1
  3.                             Low-Memory manager
  4.                          Copyright 1992 Bryan FORD
  5.  
  6. Warning:  DO NOT try to use MemMan without FULLY reading this documentation!
  7.  
  8.  
  9.                                 Disclaimer
  10.                                 ~~~~~~~~~~
  11.  
  12.     This program is provided "as is" without warranty of any kind, either
  13. expressed or implied, including, but not limited to, the implied warranty
  14. of fitness for a particular purpose.  The entire risk as to the results,
  15. reliability and performance of this program is assumed by you.
  16.  
  17.  
  18.  
  19.                                Distribution
  20.                                ~~~~~~~~~~~~
  21.  
  22.     MemMan may be freely distributed as long as it is complete (none of the
  23. files listed below are left out), all of the files are unmodified, and no
  24. charge is made for such distribution other than a small fee to cover the
  25. cost of copying.  (In effect, no profit may be made through distribution of
  26. this program).  You may modify this code for your own personal use, but you
  27. may not distribute modified versions.  (If you improve MemMan, just send
  28. your modifications back to me - I'll incorporate them into the master,
  29. giving appropriate credit to you, and we won't have hundreds of different
  30. versions of MemMan floating around.)
  31.  
  32.     You may use the object code derived from this source code, or your own
  33. modified version of this source code, in your own public domain, freeware,
  34. shareware, or otherwise freely distributable programs, as long as you state
  35. that you used MemMan and mention the name of its author (Bryan Ford)
  36. somewhere in your documentation.  You may also distribute the
  37. memman.library with your freely distributable programs.  You do not need to
  38. obtain any kind of license to use it in freely distributable software.
  39.  
  40.     If you want to distribute MemMan, the memman.library, or any modified
  41. version of MemMan, in either source or object form, with a commercial
  42. package, you must first obtain a license from me (Bryan Ford).  The cost of
  43. the license will be equal to the retail price of the commercial program in
  44. which MemMan is used.  Additionally, you must send me a copy of the program
  45. as soon as it is finished and released.  The license fee covers all future
  46. versions of your program; however you must send me copies of any major
  47. updates you release afterwards, if they still use Bovs.  This license cost
  48. will never increase, but I reserve the right to decrease or eliminate it.
  49.  
  50.     The MemMan distribution must contain the following files, complete and
  51. unmodified:
  52.  
  53.         MemMan.doc      MemMan documentation (this file)
  54.         memman.library  MemMan runtime library
  55.         MemMan.o        Linkable version of MemMan
  56.         MemMan.fd       Entrypoints for memman.library
  57.         MemMan.h        Header for C programs using MemMan
  58.         MemMan.i        Same, for assembly language
  59.         MemMan.asm      Source to MemMan (A68k only)
  60.         MemManLib.asm   Source to memman.library interface (A68k only)
  61.         Macros.i        Macros you'll need for the above two files
  62.         LMKfile         SAS/C makefile for everything
  63.         test            Program to test memman.library
  64.         test.c          Source for test program
  65.  
  66.  
  67.  
  68.                               Acknowledgments
  69.                               ~~~~~~~~~~~~~~~
  70.  
  71.     Michael Sinz of Commodore for his valuable suggestions for improving
  72. this code and making it more compatible with future versions of the OS.
  73.  
  74.     David Le Blanc for the library version and the test program.
  75.  
  76.  
  77.  
  78.                                Introduction
  79.                                ~~~~~~~~~~~~
  80.  
  81.     MemMan is a short assembly language module which allows your
  82. application to be called whenever there is a memory shortage during an
  83. AllocMem() call.  This allows your application to free up any memory it can
  84. do without so another program can use the memory.  Resident libraries and
  85. devices use a similar system to automatically unload them if the system
  86. runs out of memory.  However, MemMan provides two very important features
  87. that the standard expunging system doesn't.  First, MemMan doesn't require
  88. an application to free everything it possibly can all at once.  The
  89. application can free a little bit of memory, then MemMan will try the
  90. allocation again, and if the allocation still fails, the application can
  91. free more memory, and so on.  Second, to go along with this, the
  92. application can tell MemMan how "important" a given piece of memory is.
  93. This allows the application to free some types of buffers or caches first
  94. while keeping other more valuable items until the last minute.
  95.  
  96.     To use MemMan, you will either need to link the "MemMan.o" file into
  97. your program, or use the runtime "memman.library".  Either way, MemMan
  98. should work with either SAS/C 5.10 or Manx.  In program modules that use
  99. MemMan, you will need to include the header file "MemMan.h" or MemMan.i" as
  100. appropriate.  If you're using the runtime library from a C program, you
  101. must #define the symbol MM_RUNLIB before including MemMan.h.
  102.  
  103.     At the very beginning of your program you must call the MMInit()
  104. function to initialize MemMan.  If you're using the runtime library, you
  105. don't need to do this, but you need to open memman.library and put the
  106. library base in MMBase.  At the very end of your program, if you're using
  107. the linked library, you must call MMFinish() to shut it down.  (If you're
  108. using the memman.library, you just need to close the library.)  During your
  109. program, you use the MMAddNode() and MMRemNode() functions to tell MemMan
  110. about things that can be freed during a memory crunch.  All of these
  111. functions are described in detail later in the document.
  112.  
  113.     If you would like to make your own modifications to MemMan, you will
  114. probably need the A68k assembler by Charlie Gibbs.  It will only work with
  115. a version LATER than version 2.71.  This is because MemMan makes use of
  116. some enhancements to A68k which I have made just recently, and as of this
  117. writing, the new version of A68k that can assemble it has not been released
  118. yet.  Also, MemMan.asm references the include files "memman.i" and
  119. "macros.i" in a subdirectory called "bry".  You will either need to create
  120. this subdirectory on your system and put these files into that directory,
  121. or change MemMan.asm to reference them where you want them.  ("macros.i" is
  122. full of strange and interesting macros that I commonly use.)
  123.  
  124.  
  125.  
  126.                                  Functions
  127.                                  ~~~~~~~~~
  128.  
  129.     This section describes in detail the functions that MemMan makes
  130. available to the the application.  These functions are externally defined
  131. by MemMan.o as both assembly-language-style functions without any name
  132. prefix, as well as prefixed with '_' to allow calls from SAS/C and Manx C.
  133. To call these functions from assembly language, all you need to do is
  134. 'xref' the names and put the appropriate arguments in the appropriate
  135. registers before 'bsr'- or 'jsr'ing to the function.  For SAS/C and Manx C,
  136. you just need to include the header file "MemMan.h" which prototypes these
  137. functions and tells the compiler which registers to use for arguments.  (To
  138. use the runtime library, you must define MM_RUNLIB before including this
  139. file.) For other compilers or languages, you may have to do a little
  140. adaptation.
  141.  
  142.  
  143.     int Success = MMInit(void)
  144.            D0
  145.  
  146.     Initializes MemMan.  (This function does not exist in the runtime
  147. library.)  You must call this before calling any other MemMan function
  148. except for MMFinish().  You must test the return code - if it is zero, it
  149. means that there wasn't enough memory to initialize.  (The first time
  150. MemMan is initialized in the system, some code is made permanently
  151. resident.  Other invocations of programs that use MemMan then use this code
  152. without allocating anything else.)  You must NEVER call MMInit() more than
  153. once in your program!
  154.  
  155.  
  156.     void MMFinish(void)
  157.  
  158.     Closes down MemMan.  (This function does not exist in the runtime
  159. library.)  This never actually causes memory to be freed (the resident code
  160. remains in memory until reboot), but it puts the AllocMem() patch "to
  161. sleep" so it uses a minimum of CPU resources while MemMan is not in use. It
  162. is not dangerous to call MMFinish() more than once, to call it without ever
  163. calling MMInit(), or to call it after MMInit() has failed.  However, after
  164. you call MMFinish(), you may not call ANY other MemMan functions before
  165. your program terminates.
  166.  
  167.     Before calling MMFinish() (or closing MMBase, in the case of the
  168. runtime library), ALL MMNodes you own MUST have been taken off MemMan's
  169. list!  Remember that the MMList is not private to your application - it is
  170. used by other applications that are running MemMan at the same time, and it
  171. remains in memory even after all applications using MemMan have terminated.
  172. (The same list will be used again the next time some program starts using
  173. MemMan.)
  174.  
  175.  
  176.     void MMAddNode(struct MMNode *node)
  177.                        A1
  178.  
  179.     Adds an MMNode to the systemwide MMList.  An MMNode represents an item
  180. that can be freed on demand.  (See the 'MMNodes' section below for details
  181. on creating these structures.) This function has protection against adding
  182. a node twice, so as long as you don't muck with the ln_Type field in the
  183. Node, you can call MMAddNode() any time you want to make sure a particular
  184. MMNode is on the list.  This function is very simple and quick, especially
  185. if the node is already on the list, so there's no problem with calling this
  186. function often.  You may call this function from any interrupt code except
  187. the Non-Maskable Interrupt (NMI).
  188.  
  189.  
  190.     void MMRemNode(struct MMNode *node)
  191.                        A1
  192.  
  193.     Removes an MMNode from the MMList.  As with MMAddNode(), you don't have
  194. to worry about calling this function with an MMNode that wasn't already on
  195. the list (as long as you call it with a valid MMNode).  Also like
  196. MMAddNode(), this function executes very quickly, so you can call it often,
  197. and you may call this function from any interrupt code except NMI.
  198.  
  199.  
  200.  
  201.                                   MMNodes
  202.                                   ~~~~~~~
  203.  
  204.     To use MemMan, you will need to create structures called MMNodes.  This
  205. structure is defined in C as follows:
  206.  
  207. struct MMNode
  208.   {
  209.     struct Node Node;
  210.     long (*GetRidFunc)(long size,long memtype,void *data);
  211.     void *GetRidData;
  212.   };
  213.  
  214.     Each MMNode structure is generally associated with one particular piece
  215. of memory (or several pieces closely tied together) that may be freed on
  216. demand when the system needs more memory.  MMNodes are added to a single
  217. system-wide list (the same for all applications currently using MemMan) by
  218. MMAddNode(), in order of priority.
  219.  
  220.     When a memory crunch occurs and some AllocMem() call is about to fail,
  221. MemMan starts traversing the system MMList.  It first calls the routine
  222. associated with the MMNode of highest priority, then retries the AllocMem()
  223. call once, then if it still fails, it finds the next lower MMNode in
  224. priority and tries again, and so on.  If one of the calls frees up enough
  225. memory to allow the AllocMem() to succeed, MemMan will stop traversing the
  226. list immediately, so the lower-priority nodes aren't affected.  If MemMan
  227. gets all the way through the MMList and still can't get enough memory to
  228. satisfy the AllocMem() request, it will finally fail and return NULL to the
  229. original caller of AllocMem().
  230.  
  231.     You can create an MMNode as either a static/global variable in your
  232. program, or allocate it dynamically with AllocMem().  If you use
  233. AllocMem(), you MUST either use the MEMF_CLEAR flag, or explicitly clear
  234. the ln_Type field in the Node structure to zero before calling MMAddNode()
  235. or MMRemNode() with it.  (Static/global variables get initialized to zero
  236. before the program starts, so you don't have to worry about it in this
  237. case.)
  238.  
  239.     You should set the ln_Pri field in the Node to an appropriate priority
  240. level.  (See the 'Priorities' section for guidelines on selecting a
  241. priority level.) The GetRidFunc pointer should point to the function in
  242. your application which will be called during a memory crunch, in order to
  243. free some memory. (See the 'GetRidFuncs' section below for information on
  244. creating these functions.)  The GetRidData pointer can be anything you
  245. want, and is simply passed to the GetRidFunc whenever it is called.
  246.  
  247.     You should (though you don't have to) set the ln_Name field in the Node
  248. to the name of your program or some other descriptive string.  This may be
  249. helpful for debugging programs that use MemMan.
  250.  
  251.     Once you have created and initialized an MMNode, you can add it to the
  252. system list at any time with MMAddNode(), and remove it later with
  253. MMRemNode().  To emphasize again what I said before, ALL nodes you add MUST
  254. be removed from the system list before your program ends.
  255.  
  256.  
  257.  
  258.                                 GetRidFuncs
  259.                                 ~~~~~~~~~~~
  260.  
  261.     The GetRidFunc pointed to by a MMNode is the function that MemMan will
  262. call whenever there is a memory crunch and it needs you to free some
  263. memory.  Note that it takes register arguments, NOT standard C-style stack
  264. arguments.  In SAS/C, you must define the function as __regargs; in Manx,
  265. I'm not sure what you do, but I know it's possible.  The GetRidFunc will be
  266. called with the following arguments:
  267.  
  268.     long GetRidFunc(long MemSize, long MemType, void *GetRidData)
  269.      DO         D0        D1          A0
  270.  
  271.     The first two arguments are simply the arguments from the AllocMem()
  272. call that is causing the memory crunch.  In general, you shouldn't need to
  273. look at MemSize, but it's there just in case you find some use for it.  You
  274. can look at the MemType parameter to see if giving up your memory will
  275. actually benefit the caller at all.  For example, if the caller specifies
  276. MEMF_CHIP and you know for sure that the memory you can free is not chip
  277. memory, then you don't need to free your memory.  (Remember, though, that
  278. AllocMem() may return chip memory even if you didn't specifically ask for
  279. it.)
  280.  
  281.     The GetRidData argument is simply a copy of the GetRidData pointer you
  282. supplied in the MMNode.  You can use it as a pointer back to the MMNode, or
  283. to some other related data structure, or whatever.
  284.  
  285.     The GetRidFunc must return nonzero if any memory at all was freed, or
  286. zero if it couldn't free anything.  If a GetRidFunc returns zero,
  287.  
  288.     Since your GetRidFunc may be called by any Task or Process in the
  289. system, at any time AllocMem() can be called, you must be very careful when
  290. writing it.  Here are some specific rules you must observe:
  291.  
  292.     You must not depend on any registers, except for the specified
  293. arguments.  For SAS/C, this means you must use the __saveds qualifier when
  294. defining the function.
  295.  
  296.     Be sure to disable stack checking for the GetRidFunc. For SAS/C, you
  297. can use __interrupt, or you can supply -v on the command line to disable
  298. stack checking for the whole program.
  299.  
  300.     Standard Amiga register saving conventions must be used - your function
  301. must save all registers except D0, D1, A0, and A1.  Most compilers should
  302. automatically comply with this requirement.
  303.  
  304.     The GetRidFunc must NEVER break the Forbid() state by calling Wait() or
  305. any function that might indirectly call it.  If another task is permitted
  306. to execute while MemMan is in the middle of the MMList, things could get
  307. very interesting.
  308.  
  309.     Since it can be called from Tasks as well as Processes, it must NEVER
  310. call the dos.library, or any functions that might call the dos.library.
  311. (Sorry, no swapping memory out to disk from a GetRidFunc, although you may
  312. send a signal or message to a process to do it later.)
  313.  
  314.     The GetRidFunc must use VERY LITTLE stack space.  This means no big
  315. Un*x-style auto arrays!  Ideally, the GetRidFunc should use no more
  316. (actually a little less) stack space than the actual AllocMem() function
  317. uses, which is very small.  (I haven't checked to see exactly what it is.)
  318.  
  319.     The GetRidFunc must NEVER call MMAddNode() or anything that could call
  320. this function.  This could cause the MMList to be changed while MemMan is
  321. traversing it.
  322.  
  323.     The GetRidFunc MAY call MMRemNode() on the CURRENT node (the MMNode
  324. that caused this call to be made) but NEVER on any OTHER node.
  325.  
  326.     The GetRidFunc may call AllocMem() and FreeMem().  The general
  327. intention, of course, is to call FreeMem() at least once.  It is possible
  328. to swap chip-memory data into fast memory in a GetRidFunc by allocating a
  329. block of MEMF_FAST memory, copying the chip memory data into the new block,
  330. and freeing the old block.
  331.  
  332.     To summarize, you shoul make sure your GetRidFunc is defined correctly
  333. if you're using a high-level language, and keep your GetRidFunc as simple
  334. and direct as possible.  If possible, limit your calls to AllocMem(),
  335. FreeMem(), and MMRemNode() only.
  336.  
  337.  
  338.  
  339.                              Choosing Priority
  340.                              ~~~~~~~~~~~~~~~~~
  341.  
  342.     MemMan traverses the MMList in order of priority from highest priority
  343. to lowest priority.  Therefore, MMNodes with highest priority will be
  344. called first.  You should assign your MMNodes with high priorities for
  345. pieces of memory that aren't needed very much or can be recovered easily,
  346. and assign lower priorities for those that you'd really like to keep if at
  347. all possible.  Although this may be the opposite of what you would normally
  348. expect (having nodes of the lowest priority being the most "important" to
  349. keep in memory), there is a good reason for this:  When several MMNodes
  350. have the same priority, the first one to be added will be called first,
  351. creating a kind of least-recently-used (LRU) caching system.  This results
  352. from how Exec's Enqueue() function operates.  If the list were to be
  353. traversed from the tail to the head, as might seem more natural, then the
  354. most recently added nodes of the same priority would be called first, which
  355. is not generally a good algorithm for memory management.
  356.  
  357.     It is a good idea to choose priority for your nodes carefully, since
  358. priority not only affects order of calls to your application, but also
  359. affects the order of calls to different applications.  Remember that your
  360. MMNodes may be mixed in with the MMNodes of many other applications using
  361. MemMan at the same time.
  362.  
  363.     Priority should be chosen according to the penalty incurred for freeing
  364. that memory (time delays for restoring the data, disk reloads, etc.).  Here
  365. are some general guidelines for selecting priority:
  366.  
  367.     100 and up:  Use these for pieces of memory that are mostly unnecessary
  368. or that can be recovered with almost no effort or time delay.  You might
  369. create large speed-up buffers and such with this priority: things that
  370. benefit system performance if enough memory is available, but are not
  371. necessary to correct functioning, and can be easily recovered later if
  372. needed.
  373.  
  374.     50 to 100:  Use these priority levels for things that may require time
  375. delays or cause other small nuisances in order to restore them later.  For
  376. example, precalculated data tables and such would fit into this category -
  377. things that aren't really needed at the moment, but may be a slight pain to
  378. get back later when they are needed.
  379.  
  380.     -50 to 50:  These priority levels are intended for memory that isn't
  381. needed immediately, but contains cached data that will need to be reloaded
  382. from disk (as opposed to simply recalculated) if needed later.  My overlay
  383. supervisor "Bovs" uses priority 0 for all overlay nodes not currently in
  384. use.
  385.  
  386.     -100 to -50:  This range is for data that can still be recovered, but
  387. only at great expense.  For example, large data tables which take a while
  388. to recaulculate, or data that must be reloaded and decompressed if needed
  389. again.
  390.  
  391.  
  392.     Below -100:  These levels should be used for data that CANNOT be
  393. recovered at all.  For example, you could put an application's undo buffers
  394. at this level.  It's better to lose some undo capability than to not be
  395. able to use the program at all, but the undo buffers should be retained as
  396. long as possible.
  397.  
  398.  
  399.  
  400.                               Final Warnings
  401.                               ~~~~~~~~~~~~~~
  402.  
  403.     Always MMInit() before using MMAddNode() or MMRemNode().  (You may call
  404. MMFinish() without calling MMInit().)
  405.  
  406.     Never call MMInit() more than once in your program.
  407.  
  408.     Never call MMAddNode() or the dos.library from a GetRidFunc.
  409.  
  410.     Never call MMRemNode() on any MMNode OTHER than the one that caused
  411. this particular GetRidFunc call.
  412.  
  413.     Never allow a GetRidFunc to break the Forbid() state by calling
  414. anything that might cause a Wait().
  415.  
  416.     Use as little stack space as possible in your GetRidFunc.
  417.  
  418.     Finally, one more time:  ALWAYS remove ALL MMNodes you added BEFORE
  419. calling MMFinish() or closing the library!  This can't be stressed enough.
  420. If you fail to do this, you will most likely NOT see any immediate
  421. problems.  The problems will only show up the next time you run out of
  422. memory, and even then only if the MemMan patch is currently "awake" (when
  423. an application is using MemMan).  You should test your program VERY
  424. thoroughly for bugs in this area.
  425.  
  426.     Be VERY careful to follow these rules.  Test your program thoroughly,
  427. bang it to its limits, and subject it to every known program terror.
  428. MemMan and the operating system are not very forgiving of mistakes when it
  429. comes to low-level programming like this.
  430.  
  431.  
  432.  
  433.                                    Hints
  434.                                    ~~~~~
  435.  
  436.     Don't keep MMNodes on the system list unless they actually represent
  437. free-able data.  When you want to lock something in memory, use MMRemNode()
  438. to prevent it from being expunged.  Then when you unlock it, use
  439. MMAddNode() to put it back on the list.  A GetRidFunc should ideally be
  440. able to simply FreeMem() and return, without having to check locks or
  441. anything.  MMAddNode() and MMRemNode() are completely atomic, and may be
  442. called from any task or interrupt except the NMI.  They are also very
  443. quick, so adding and removing MMNodes won't put a great burden on the
  444. system.
  445.  
  446.     When debugging your program, use the 'Avail FLUSH' command in 2.0 (if
  447. you have 2.0; if you don't, now would be a good time to get it) to test
  448. your program's handling of memory crunches.  Play around with your program
  449. a little, get some caches loaded, etc., then use 'Avail FLUSH' to force
  450. MemMan into action.  Make sure the system doesn't crash, and make sure
  451. memory is being freed as expected.  Run your program several times in a
  452. row, in each case "exercising" MemMan, to make sure your program is not
  453. leaving "stale" MMNodes on the global list.  Finally, run several copies of
  454. your program simultaneously to make sure they don't interfere with each
  455. other.  If you happen to have another program that also uses MemMan (such
  456. as MultiPlayer or another program that uses Bovs), load up a copy or two of
  457. that alongside as well.  Another good program to test with is FragIt by
  458. Justin V. McCormick.  Really bash it out.
  459.  
  460.  
  461.  
  462.                                   History
  463.                                   ~~~~~~~
  464.  
  465. 2.1 (18-Feb-92)
  466.         GetRidFunc() calling convention changed - it now returns nonzero if
  467.             any memory was freed, zero if it couldn't free anything.
  468.         MMAddNode() and MMRemNode() may now be called from interrupt code,
  469.             or from different tasks or processes.
  470.         A GetRidFunc may now call AllocMem().
  471.         Changed semaphore name to MemMan21.
  472.         Other small reworkings - MemMan should be completely bulletproof now.
  473.  
  474. 2.0 (4-Dec-91)
  475.         Added runtime library and test program (originally by David Le Blanc).
  476.         Changed format of MMNode to use a standard Node structure.
  477.         Changed semaphore name to MemMan2 to avoid version conflicts.
  478.  
  479.  
  480.  
  481.                               Contact Address
  482.                               ~~~~~~~~~~~~~~~
  483.  
  484.     I tend to move around a great deal, so mail sent directly to me
  485. sometimes has a hard time catching up.  If you want mail to reach me (it
  486. may take a while, but it WILL reach me), send it to this address:
  487.  
  488.         Bryan Ford
  489.         8749 Alta Hills Circle
  490.         Sandy, UT 84093
  491.  
  492.     I can be reached more quickly (for the time being anyway) on the phone
  493. or through one of the electronic mail addresses below:
  494.  
  495.         (801) 585-4619
  496.         bryan.ford@m.cc.utah.edu
  497.         baf0863@cc.utah.edu
  498.         baf0863@utahcca.bitnet
  499.  
  500.     If you want to get something to me through the mail more quickly, FIRST
  501. call or E-mail me to make sure I'm still here, then send it to this
  502. address:
  503.  
  504.         Bryan Ford
  505.         27104 Ballif Hall
  506.         University of Utah
  507.         Salt Lake City, UT 84112
  508.  
  509.  
  510.