home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 568a.lha / MemMan / MemMan.doc.pp / MemMan.doc
Encoding:
Text File  |  1991-11-10  |  19.3 KB  |  413 lines

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