Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
OTMemoryReserve.c
/* |
File: OTMemoryReserve.c |
Contains: Wrapper for OTAllocMem to enable lots of interrupt-time memory allocations. |
Written by: Quinn "The Eskimo!" |
Copyright: © 1998-2000 by Apple Computer, Inc., all rights reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Change History (most recent first): |
*/ |
///////////////////////////////////////////////////////////////// |
// MIB Setup |
#include "MoreSetup.h" |
// Mac OS Interfaces |
#include <MacTypes.h> |
#include <MacMemory.h> |
#include <OpenTransport.h> |
// Our prototypes |
#include "OTMemoryReserve.h" |
///////////////////////////////////////////////////////////////////// |
static OTLIFO gMemoryReserve = {nil}; |
#if MORE_DEBUG |
static ByteCount gFreeHeapSpaceRequired; |
static ByteCount gChunkSize; |
static ItemCount gMinChunks; |
static ItemCount gMaxChunks; |
static OTClientContextPtr gClientContext; |
static ItemCount gChunksAllocated; |
static ItemCount CountReserveChunks(void) |
{ |
ItemCount count; |
OTLink *cursor; |
count = 0; |
cursor = gMemoryReserve.fHead; |
while ( cursor != nil ) { |
count += 1; |
cursor = cursor->fNext; |
} |
return count; |
} |
#endif |
extern pascal OSStatus InitOTMemoryReserve(ByteCount freeHeapSpaceRequired, ByteCount chunkSize, |
ItemCount minChunks, ItemCount maxChunks, |
OTClientContextPtr clientContext) |
// See comment in interface part. |
{ |
OSStatus err; |
ItemCount chunksAllocated; |
Boolean done; |
OTLink * thisChunk; |
MoreAssertQ(freeHeapSpaceRequired >= 32768); // Leave less than this in your app heap is extremely dangerous! |
MoreAssertQ(chunkSize >= 8192); // Using this for smaller blocks is pretty silly. |
MoreAssertQ(minChunks != 0); // Passing 0 is silly, and loop below doesnÕt work properly. |
MoreAssertQ(maxChunks != 0); // Passing 0 is silly, and loop below doesnÕt work properly. |
MoreAssertQ(maxChunks != 0xFFFFFFFF); // See comment in header file. |
MoreAssertQ(minChunks <= maxChunks); |
MoreAssert(gMemoryReserve.fHead == nil); // YouÕve probably initialised the module twice. |
err = noErr; |
done = false; |
chunksAllocated = 0; |
do { |
// Only try allocating this chunk if doing so will not |
// break the minimum free space requirement. |
thisChunk = nil; |
if ( FreeMem() >= (freeHeapSpaceRequired + chunkSize) ) { |
thisChunk = OTAllocMemInContext(chunkSize, clientContext); |
} |
if (thisChunk == nil) { |
// Either the next block is going to push us below the free |
// space requirement, or we tried to get the block and failed. |
if (chunksAllocated < minChunks) { |
// Because weÕre still trying to get our minimum chunks, |
// this is a fatal error. |
err = memFullErr; |
} else { |
// The block was optional so we donÕt need to error. |
// We set done and leave the loop. |
done = true; |
} |
} else { |
// We got the block. Put it into the reserve. If we have |
// all of our chunks, set done so that we leave the loop. |
OTLIFOEnqueue(&gMemoryReserve, (OTLink *) thisChunk); |
chunksAllocated += 1; |
if (chunksAllocated == maxChunks) { |
done = true; |
} |
} |
} while (err == noErr && !done); |
if (err == noErr) { |
// Check some post conditions. |
MoreAssertQ( (chunksAllocated >= minChunks) && (chunksAllocated <= maxChunks) ); |
MoreAssertQ( FreeMem() >= freeHeapSpaceRequired ); |
MoreAssertQ( chunksAllocated == CountReserveChunks() ); |
#if MORE_DEBUG |
gFreeHeapSpaceRequired; |
gChunkSize = chunkSize; |
gMinChunks = minChunks; |
gMaxChunks = maxChunks; |
gClientContext = clientContext; |
gChunksAllocated = chunksAllocated; |
#endif |
} else { |
// Free up any chunks that we allocated. |
TermOTMemoryReserve(); |
} |
return err; |
} |
extern pascal void TermOTMemoryReserve(void) |
// See comment in interface part. |
{ |
do { |
OTLink *thisChunk; |
thisChunk = OTLIFODequeue(&gMemoryReserve); |
if (thisChunk != nil) { |
OTFreeMem(thisChunk); |
} |
} while ( gMemoryReserve.fHead != nil ); |
} |
extern pascal void *OTAllocMemFromReserveInContext(OTByteCount size, OTClientContextPtr clientContext) |
// See comment in interface part. |
{ |
void *result; |
// First try to allocate the memory from the OT client pool. |
// If this succeeds, weÕre done. If it fails, the failure |
// will trigger OT to try and grow the client pool the next |
// time SystemTask is called. However, if the memory reserve |
// still has memory we free a block of the reserve and retry |
// the allocation. Hopefully that will allow the allocation to |
// succeed. |
// |
// Note that I originally wrapped this in a do {} while loop, |
// however I deleted that because I figured that all of the |
// blocks are the same size, so if freeing one block didnÕt |
// help then freeing another wonÕt help either. Except |
// OT might try to coallesce the memory blocks if they live |
// in the same Mac OS Memory Manager memory block. However, |
// they probably wonÕt live in the same Memory Manager block |
// because the blocks are large. Regardless, freeing all of |
// our reserve blocks at once will cause the entire reserve |
// to go the first time if the client tries to allocate a |
// block large than our block size, which is not good. |
result = OTAllocMemInContext(size, clientContext); |
if (result == nil) { |
OTLink *thisChunk; |
thisChunk = OTLIFODequeue(&gMemoryReserve); |
if (thisChunk != nil) { |
OTFreeMem(thisChunk); |
result = OTAllocMemInContext(size, clientContext); |
} |
} |
return result; |
} |
#if MORE_DEBUG |
extern pascal void OTMemoryReserveTest(void) |
// This routine is designed to help test OTMemoryReserve |
// by allocating all the memory in the reserve and checking |
// that it roughly matches the expected size of the reserve. |
{ |
OTLink *thisBlock; |
OTList blockList; |
ByteCount bytesAllocated; |
MoreAssertQ(gChunkSize != 0); // You must init the module before doing the test. |
// Tell OT that weÕre at interrupt time so that it wonÕt |
// grow the client pool. |
OTEnterInterrupt(); |
// Allocate all of the memory that OT will give us. |
blockList.fHead = nil; |
bytesAllocated = 0; |
do { |
thisBlock = OTAllocMemFromReserveInContext(1024, gClientContext); |
if (thisBlock != nil) { |
OTAddFirst(&blockList, thisBlock); |
bytesAllocated += 1024; |
} |
} while (thisBlock != nil); |
// Check that itÕs approproximately what the client asked for. |
// Note that the * 9 / 10 wonÕt work properly for very large client pools. |
MoreAssertQ( bytesAllocated > (((gChunksAllocated * gChunkSize) * 9) / 10) ); |
// Free the memory we allocated. |
do { |
thisBlock = OTRemoveFirst(&blockList); |
if (thisBlock != nil) { |
OTFreeMem(thisBlock); |
} |
} while (thisBlock != nil); |
OTLeaveInterrupt(); |
} |
#endif |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-07-22