home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / FakeStart 1.0 / FS_Source / FS_Memory.c < prev   
Encoding:
C/C++ Source or Header  |  1996-05-01  |  16.2 KB  |  529 lines  |  [TEXT/CWIE]

  1. /****
  2.     File.....:     FS_Memory.c
  3.     Date.....:     Wednesday, April 10, 1996
  4.     By.......:     Ken Earle. No rights reserved.
  5.     Status...:     tested
  6.     Spec.....:     GrabUpperSystemMemory(): allocate memory in the system heap, leaving
  7.                     enough room for the "real" application.
  8.                  ReleaseUpperSystemMemory(): release same memory.
  9.     Revisions:     
  10. ****/
  11.  
  12. #include "FakeStart.h"
  13. #include "FS_Memory.h"
  14.  
  15. // Define this to debug GrabUpperSystemMemory()
  16. //#define Debug_Memory
  17.  
  18.  
  19. // Memory sizes, using the Finder's units of 1024-byte chunks
  20. #define MINIMUM_FOR_APP         (2700*1024L)        // default size of app
  21. #define ONE_MEG                    (1024*1024L)
  22. #define ABOUT_THIRTEEN_MEG        ( (13*1024*1024L) + (250*1024L) )
  23. #define SIXTEEN_MEG                (16*1024*1024L)
  24.  
  25. // CHANGE this to determine how high in memory your application can go.
  26. // Note this is the limit for the top of your app's heap, not the bottom.
  27. #define APPLICATION_LIMIT        ABOUT_THIRTEEN_MEG
  28.  
  29. #define MINIMUM_FREE_INC        (100*1024L)            // minimum increment for free heap
  30. #define MINIMUM_FREE            (MINIMUM_FREE_INC * 3) // initial minimum
  31.  
  32. // Memory error codes, for debugging only
  33. typedef enum DBMemoryError
  34.     {
  35.     eAppLeftTooHigh_DBMem = 1,
  36.     eTooManyFragments_DBMEM = 2,
  37.     eUnknown_DBMem = 3,
  38.     eReleaseLowestFailed_DBMem = 4,
  39.     eCouldNotGetLastHandle_DBMem = 5,
  40.     eCouldNotGetAppChunk_DBMem = 6,
  41.     eCouldNotLeaveJustApp_DBMem = 7,
  42.     eCouldNotFindFreeHandle_DBMem = 8
  43.     } DBMemoryError;
  44.  
  45.  
  46. // Functions used here only
  47. // Memory-related support functions
  48. static OSErr AllocateSysMemUntilBelowLimit
  49.     (Handle        *bigChunkHA,            // array of Handle
  50.     short        topNumHandles,            // maximum Handles in array
  51.     long        leaveThisMuch,            // for application, or 0, plus...
  52.     Size        *safetyMarginP            // <->...a few 100K, set as we go
  53.     );
  54. static OSErr ReleaseLowestHandle
  55.     (Handle        *bigChunkHA,            // array of Handle
  56.     short        topNumHandles,            // maximum Handles in array
  57.     long        leaveThisMuch,            // for application, or 0, plus...
  58.     Size        safetyMargin,            // ->...a few 100K, set as we go
  59.     short        *lowestWhichHP            // <- which handle was released
  60.     );
  61. static OSErr VerifyThereIsRoomForApp
  62.     (Handle        *bigChunkHA,            // array of Handle
  63.     long        leaveThisMuch,            // for application, or 0, plus...
  64.     short        lowestWhichH,            // index of lowest bigChunkHA[]
  65.     Size        *safetyMarginP            // <->...a few 100K, set as we go
  66.     );
  67. static OSErr AllocateContiguousChunk
  68.     (Size        availableContigSysMem,    // biggest single piece from sys
  69.     long        leaveThisMuch,            // for application, or 0, plus...
  70.     Size        *safetyMarginP,            // <->...a few 100K, set as we go
  71.     Handle        *tempMemHP                // <- address of Handle
  72.     );
  73. // Resource-related support
  74. static OSErr PreferredAndMinimumForApplication
  75.     (FSSpecPtr     appSpecP,                // ->for the application
  76.     long        *preferredSizeP,        // <-from SIZE rsrc
  77.     long        *minimumSizeP            // <-ditto
  78.     );
  79.  
  80.  
  81. /* Grab system memory up high, force application to be loaded low. Verify that real
  82. app can be loaded low. Error here implies that memory is fragmented, or not enough
  83. memory is currently free to allow running the app. The codes in DBMemoryError are for
  84. debugging, only a generic "out of memory" error is returned from here.
  85.  
  86. A general algorithm (especially good if memory is fragmented):
  87. 1-allocate until contiguous system memory < leaveThisMuch
  88. 2-release lowest Handle (if any)
  89. 3-allocate all but room for app out of the lowest contiguous chunk
  90. 4-allocate for app, verify it is low, release its Handle
  91. 5-return with all memory locked up except low spot for the app.
  92.  
  93. Note temp mem is allocated high by TempNewHandle() within a particular chunk. There
  94. is often difficulty allocating the very last (lowest) handle: to get around this, a
  95. "safetyMargin" (300-400k) is used to leave a little bit free at the heap bottom.
  96. Regular systems require about 2-300K, those using RamDoubler seem to need more like
  97. 400K. If there's a problem with the last handle, we increase the amount of slop
  98. (safetyMargin) and try repeatedly to clear the error.
  99. */
  100. OSErr GrabUpperSystemMemory
  101.     (FSSpecPtr    appSpecP,                // ->"real" application, for SIZE rsrc
  102.     Handle        *bigChunkHA,            // <-array of Handle
  103.     short        topNumHandles            // maximum Handles in array
  104.     )
  105.     {
  106.     Size                    availableContigSysMem;     // biggest single piece from sys
  107.     Size                    grow;                    // value not used (always 0)
  108.     Size                    safetyMargin;            // a few 100K, varies per system
  109.     long                    preferredSize;            // from SIZE for real app
  110.     long                    minimumSize;            // ditto
  111.     long                    leaveThisMuch;            // preferred size (or?)
  112.     short                    lowestWhichH = -1;        // index of lowest bigChunkHA[]
  113.     OSErr                    theErr = noErr;
  114.  
  115.     // Consult SIZE rsrc for app to determine memory needed.
  116.     // Decide on amount to leave for real app, "leaveThisMuch".
  117.     theErr = PreferredAndMinimumForApplication( appSpecP, &preferredSize,
  118.                                                 &minimumSize );
  119.     if (theErr != noErr)
  120.         {
  121.         // If no SIZE, use some defaults and attempt to continue.
  122.         theErr = noErr;
  123.         preferredSize = MINIMUM_FOR_APP;
  124.         minimumSize = MINIMUM_FOR_APP;
  125.         }
  126.     
  127.     // We ignore the minimumSize, but it's there if you need it....
  128.     leaveThisMuch = preferredSize;
  129.     
  130.     // Is there room at all?
  131.     availableContigSysMem = TempMaxMem(&grow);
  132.     if (availableContigSysMem < leaveThisMuch)
  133.         theErr = memFullErr;
  134.     
  135.     // Start with a reasonably small safety margin.
  136.     safetyMargin = MINIMUM_FREE;
  137.     
  138.     // 1-allocate until contiguous system memory < leaveThisMuch
  139.     if (theErr == noErr)
  140.         {
  141.         theErr = AllocateSysMemUntilBelowLimit( bigChunkHA, topNumHandles,
  142.                                                 leaveThisMuch, &safetyMargin );
  143.         }
  144.     
  145.     // If allocation just above failed on last handle, try to proceed anyway.
  146.     if (theErr == eCouldNotGetLastHandle_DBMem)
  147.         theErr = noErr;
  148.     
  149.     // 2-release lowest handle (if any and not enough left free).
  150.     if (theErr == noErr)
  151.         {
  152.         theErr = ReleaseLowestHandle( bigChunkHA, topNumHandles, leaveThisMuch,
  153.                                       safetyMargin, &lowestWhichH );
  154.         }
  155.     
  156.     // 3-allocate all but room for app-plus-safety out of the lowest contiguous chunk.
  157.     if (theErr == noErr)
  158.         {
  159.         availableContigSysMem = TempMaxMem(&grow);
  160.         theErr = AllocateContiguousChunk( availableContigSysMem, leaveThisMuch,
  161.                     &safetyMargin, &bigChunkHA[lowestWhichH] );
  162.         if (theErr != noErr)
  163.             theErr = eCouldNotLeaveJustApp_DBMem;
  164.         }
  165.     
  166.     // 4-allocate for app, verify it is low, release its Handle.
  167.     if (theErr == noErr)
  168.         {
  169.         theErr = VerifyThereIsRoomForApp( bigChunkHA, leaveThisMuch,
  170.                                           lowestWhichH, &safetyMargin );
  171.         }
  172.     
  173.     // Clean up if there was an error.
  174.     if (theErr != noErr)
  175.         ReleaseUpperSystemMemory(bigChunkHA, topNumHandles);
  176.  
  177. #ifdef Debug_Memory
  178.     // Debug only, sound a few beeps (see DBMemoryError above).
  179.     if (theErr > 0 && theErr < 10)
  180.         {
  181.         short    i;
  182.         
  183.         for (i = 1; i <= theErr; ++i)
  184.             SysBeep(2);
  185.         }
  186. #endif
  187.     
  188.     // Return general error
  189.     if (theErr != noErr)
  190.         theErr = memFullErr;
  191.     // 5-return with all memory locked up except low spot for the app.
  192.     return theErr;
  193.     }
  194.  
  195. /* Release temporary memory. Not really needed, just nice to do.
  196. Otherwise, system memory would be released automatically when this app quits.
  197. */
  198. void ReleaseUpperSystemMemory
  199.     (Handle        *bigChunkHA,            // <->array of Handle
  200.     short        topNumHandles            // maximum Handles in array
  201.     )
  202.     {
  203.     short        i;
  204.  
  205.     // Deallocate system memory.
  206.     for (i = 0; i < topNumHandles; ++i)
  207.         {
  208.         if (bigChunkHA[i] != NULL)
  209.             {
  210.             DisposeHandle(bigChunkHA[i]);
  211.             bigChunkHA[i] = NULL;
  212.             }
  213.         }
  214.     }
  215.  
  216.  
  217. // Functions used here only
  218.  
  219. // Memory-related support functions
  220.  
  221. /* Allocate handles out of system memory to bigChunkHA[] until contig memory left is
  222. less than leaveThisMuch. Start at index 0 in bigChunkHA[]. Returns error if array
  223. overflow or cannot allocate handle. May set *safetyMarginP to a larger value at end.
  224. */
  225. static OSErr AllocateSysMemUntilBelowLimit
  226.     (Handle        *bigChunkHA,            // array of Handle
  227.     short        topNumHandles,            // maximum Handles in array
  228.     long        leaveThisMuch,            // for application, or 0, plus...
  229.     Size        *safetyMarginP            // <->...a few 100K, set as we go
  230.     )
  231.     {
  232.     Size                    availableContigSysMem;     // biggest single piece from sys
  233.     Size                    grow;                    // value not used (always 0)
  234.     Size                    safetyMargin;            // a few 100K, varies per system
  235.     short                    whichH = 0;                // into bigChunkHA[]
  236.     OSErr                    theErr = noErr;
  237.     
  238.     // Determine amount of sys mem available, get the safety margin.
  239.     availableContigSysMem = TempMaxMem(&grow);
  240.     safetyMargin = *safetyMarginP;
  241.     
  242.     // Allocate until contig < leaveThisMuch.
  243.     while ( availableContigSysMem >= leaveThisMuch
  244.         &&  theErr == noErr )
  245.         {
  246.         // Avoid array overflow
  247.         if (whichH >= topNumHandles)
  248.             {
  249.             theErr = eTooManyFragments_DBMEM;
  250.             break;
  251.             }
  252.         // Allocate the chunk, leaving safety margin free.
  253.         theErr = AllocateContiguousChunk( availableContigSysMem,    0,
  254.                                          &safetyMargin, &bigChunkHA[whichH] );
  255.         if (theErr == noErr)
  256.             {
  257.             // Move to next handle, see if there's another free chunk.
  258.             ++whichH;
  259.             availableContigSysMem = TempMaxMem(&grow);
  260.             }
  261.         else
  262.             {
  263.             // Will attempt to proceed (may have been a small handle at end).
  264.             bigChunkHA[whichH] = NULL;
  265.             theErr = eCouldNotGetLastHandle_DBMem;
  266.             }
  267.         }
  268.     *safetyMarginP = safetyMargin;
  269.     return theErr;
  270.     }
  271.  
  272. /* If there is not enough system memory free for the application, release
  273. the lowest handle allocated in bigChunkA[] that is large enough for the app.
  274. Set *lowestWhichHP to the index for that handle or to a free handle in
  275. bigChunkA[] if no handle is freed.
  276. Error if no handle big enough, or if can't find a free handle.
  277. */
  278. static OSErr ReleaseLowestHandle
  279.     (Handle        *bigChunkHA,            // array of Handle
  280.     short        topNumHandles,            // maximum Handles in array
  281.     long        leaveThisMuch,            // for application, or 0, plus...
  282.     Size        safetyMargin,            // ->...a few 100K, set as we go
  283.     short        *lowestWhichHP            // <- which handle was released
  284.     )
  285.     {
  286.     Size                    availableContigSysMem;     // biggest single piece from sys
  287.     Size                    grow;                    // value not used (always 0)
  288.     short                    lowestWhichH = -1;        // index of lowest bigChunkHA[]
  289.     long                    lowestAddress;            // the chunk holding real app
  290.     short                    i;
  291.     OSErr                    theErr = noErr;
  292.     
  293.     // How much memory is left?
  294.     availableContigSysMem = TempMaxMem(&grow);
  295.     
  296.     // Release lowest handle, if any and not enough memory left free.
  297.     if (availableContigSysMem < leaveThisMuch + safetyMargin)
  298.         {
  299.         // Find lowest handle, check it is big enough for app plus safety margin.
  300.         lowestAddress = 0;
  301.         for (i = 0; i < topNumHandles; ++i)
  302.             {
  303.             if (bigChunkHA[i] != NULL)
  304.                 {
  305.                 if ( (lowestAddress == 0
  306.                   || (long)*(bigChunkHA[i]) < lowestAddress)
  307.                   && GetHandleSize(bigChunkHA[i]) >= leaveThisMuch + safetyMargin )
  308.                     {
  309.                     lowestAddress = (long)*(bigChunkHA[i]);
  310.                     lowestWhichH = i;
  311.                     }
  312.                 }
  313.             }
  314.         // Release lowest handle.
  315.         if (lowestWhichH >= 0)
  316.             {
  317.             DisposeHandle(bigChunkHA[lowestWhichH]);
  318.             bigChunkHA[lowestWhichH] = NULL;
  319.             }
  320.         else
  321.             {
  322.             // Serious error, no chunk big enough for application.
  323.             theErr = eReleaseLowestFailed_DBMem;
  324.             }
  325.         }
  326.     
  327.     // If we didn't free a handle, adopt an unused one.
  328.     if ( theErr == noErr
  329.       && lowestWhichH < 0 )
  330.         {
  331.         for (i = 0; i < topNumHandles; ++i)
  332.             {
  333.             if (bigChunkHA[i] == NULL)
  334.                 {
  335.                 lowestWhichH = i;
  336.                 break;
  337.                 }
  338.             }
  339.         // Bizarre error, shouldn't happen since we have lots of handles.
  340.         if (lowestWhichH < 0)
  341.             theErr = eCouldNotFindFreeHandle_DBMem;
  342.         }
  343.     
  344.     // Set index of handle freed, return error code
  345.     *lowestWhichHP = lowestWhichH;
  346.     return theErr;
  347.     }
  348.  
  349. /* Allocate a handle out of system memory for the "real" application, and
  350. verify it is low enough in memory. Always release this handle if allocated.
  351. If necessary, shrink the allocated handle just above the spot for the real
  352. app to make room.
  353. Error if cannot allocate handle for app or resulting handle is too
  354. high in memory.
  355. */
  356. static OSErr VerifyThereIsRoomForApp
  357.     (Handle        *bigChunkHA,            // array of Handle
  358.     long        leaveThisMuch,            // for application, or 0, plus...
  359.     short        lowestWhichH,            // index of lowest bigChunkHA[]
  360.     Size        *safetyMarginP            // <->...a few 100K, set as we go
  361.     )
  362.     {
  363.     Handle                    appHandle = NULL;        // to preflight app
  364.     Size                    safetyMargin;            // a few 100K, varies per system
  365.     Size                    amountToAllocate;        // available less amount to keep
  366.     OSErr                    theErr = noErr;
  367.     
  368.     // Get the safety margin.
  369.     safetyMargin = *safetyMarginP;
  370.     
  371.     // -allocate for app, verify it is low.
  372.     appHandle = TempNewHandle(leaveThisMuch, &theErr);
  373.     if (theErr == noErr)
  374.         {
  375.         ; // Handle was allocated for app.
  376.         }
  377.     else if (bigChunkHA[lowestWhichH] != NULL)
  378.         {
  379.         // The handle just above appHandle's spot may be too large
  380.         amountToAllocate = GetHandleSize(bigChunkHA[lowestWhichH]);
  381.         amountToAllocate -= MINIMUM_FREE_INC;
  382.         safetyMargin += MINIMUM_FREE_INC;
  383.         if (amountToAllocate > 0)
  384.             {
  385.             do
  386.                 {
  387.                 DisposeHandle(bigChunkHA[lowestWhichH]);
  388.                 bigChunkHA[lowestWhichH] = TempNewHandle( amountToAllocate,
  389.                                                          &theErr );
  390.                 if (theErr == noErr)
  391.                     {
  392.                     // Attempt to reallocate for the application.
  393.                     HLock(bigChunkHA[lowestWhichH]);
  394.                     appHandle = TempNewHandle(leaveThisMuch, &theErr);
  395.                     }
  396.                 else
  397.                     {
  398.                     // Try to clear the error by allocating less above the app.
  399.                     amountToAllocate -= MINIMUM_FREE_INC;
  400.                     safetyMargin += MINIMUM_FREE_INC;
  401.                     theErr = eCouldNotGetAppChunk_DBMem;
  402.                     }
  403.                 } while ( theErr != noErr
  404.                     &&       amountToAllocate > 0);
  405.             }
  406.         }
  407.     // Verify the application's handle is low enough.
  408.     if (theErr == noErr)
  409.         {
  410.         if ( (long)*appHandle + leaveThisMuch + MINIMUM_FREE_INC >= APPLICATION_LIMIT )
  411.             {
  412.             theErr = eAppLeftTooHigh_DBMem;
  413.             }
  414.         // else handle is properly low
  415.         }
  416.     
  417.     // -release the spot for the real application
  418.     if (appHandle != NULL)
  419.         DisposeHandle(appHandle);
  420.     
  421.     *safetyMarginP = safetyMargin;
  422.     return theErr;
  423.     }
  424.  
  425. /* Get size of next chunk of contiguous system memory, deduct leaveThisMuch and
  426. safety margin, allocate and lock that. safetyMarginP may be adjusted upwards in an
  427. attempt to clear out-of-memory error.
  428. Error if try to allocate and TempNewHandle fails on all tries.
  429. */
  430. static OSErr AllocateContiguousChunk
  431.     (Size        availableContigSysMem,    // biggest single piece from sys
  432.     long        leaveThisMuch,            // for application, or 0, plus...
  433.     Size        *safetyMarginP,            // <->...a few 100K, set as we go
  434.     Handle        *tempMemHP                // <- address of Handle
  435.     )
  436.     {
  437.     Size                    amountToAllocate;        // available less amount to leave
  438.     short                    count = 0;                // limit the number of tries
  439.     short                    topCount = 10;            // ditto
  440.     Size                    safetyMargin;
  441.     OSErr                    theErr = noErr;
  442.     
  443.     // Get the safety margin.
  444.     safetyMargin = *safetyMarginP;
  445.     // Deduct application size and safety margin from amount free.
  446.     amountToAllocate = availableContigSysMem - leaveThisMuch - safetyMargin;
  447.     // Clear handle just to be safe.
  448.     *tempMemHP = NULL;
  449.     
  450.     // Allocate handle, leaving a bit free for the system to play with.
  451.     if (amountToAllocate > 0)
  452.         {
  453.         do
  454.             {
  455.             *tempMemHP = TempNewHandle(amountToAllocate, &theErr);
  456.             if (theErr != noErr)
  457.                 {
  458.                 // Try to clear the error (in practice it happens on the last handle)
  459.                 amountToAllocate -= MINIMUM_FREE_INC;
  460.                 safetyMargin += MINIMUM_FREE_INC;
  461.                 }
  462.             } while ( theErr != noErr
  463.                 &&      amountToAllocate > 0
  464.                 &&      ++count < topCount );
  465.         // Lock down the handle if we succeeded.
  466.         if (theErr == noErr)
  467.             {
  468.             HLock(*tempMemHP);
  469.             }
  470.         }
  471.     
  472.     // Report safety margin used and error code.
  473.     *safetyMarginP = safetyMargin;
  474.     return theErr;
  475.     }
  476.  
  477.  
  478. // Resource-related support
  479.  
  480. /* Retrieve preferred and minimum sizes for an app.
  481. Error if cannot open resource fork.
  482. */
  483. static OSErr PreferredAndMinimumForApplication
  484.     (FSSpecPtr     appSpecP,                // ->for the application
  485.     long        *preferredSizeP,        // <-from SIZE rsrc
  486.     long        *minimumSizeP            // <-ditto
  487.     )
  488.     {
  489.     short        curResFile;            // current resource fork ref
  490.     short        appResReference;    // for the application
  491.     short        resErr;                // typ. out of memory
  492.     SIZEPtr        sizeP;                // see FakeStart.h
  493.     Handle        h;                    // for the SIZE rsrc
  494.     
  495.     // Remember current resource file.
  496.     curResFile = CurResFile();
  497.     // Open and use application's resource fork.
  498.     appResReference = FSpOpenResFile(appSpecP, fsRdPerm);
  499.     resErr = ResError();
  500.     if (resErr != noErr || appResReference == -1)
  501.         return fnfErr; // open error
  502.     UseResFile(appResReference);
  503.     
  504.     // Retrieve SIZE 0 or -1 (preferred in that order).
  505.     h = Get1Resource('SIZE', 0);
  506.     if (h == NULL)
  507.         h = Get1Resource('SIZE', -1);
  508.     
  509.     // Set preferred and minimum values.
  510.     if (h != NULL)
  511.         {
  512.         sizeP = (SIZEPtr)*h;
  513.         *preferredSizeP = sizeP->preferredSize;
  514.         *minimumSizeP = sizeP->minimumSize;
  515.         ReleaseResource(h);
  516.         }
  517.     else
  518.         {
  519.         *preferredSizeP = MINIMUM_FOR_APP;
  520.         *minimumSizeP = MINIMUM_FOR_APP;
  521.         }
  522.     
  523.     // Close up, use original resource file.
  524.     CloseResFile(appResReference);
  525.     UseResFile(curResFile);
  526.     // Return sucess.
  527.     return noErr;
  528.     }
  529.