home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 581a.lha / MemMan_v2.0 / MemMan.doc.pp / MemMan.doc
Text File  |  1991-12-05  |  22KB  |  452 lines

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