diff --git a/ChangeLog b/ChangeLog index 9ec981727..b923a7da7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,12 @@ +Mon Mar 3 14:41:01 1997 Andrew McCallum + + * src/NSHost.m, src/include/NSHost.h: New files from Luke Howard + . + Sun Jan 12 13:46:38 1997 Andrew McCallum * Version (SUBMINOR_VERSION): Version 0.2.12. There seems to be - a problem with Richard and my patches to distributed objects, but + a problem with Richard's and my patches to distributed objects, but many other bug fixes have been added, NSZone has a complete re-write, -awakeAfterUsingCoder: is now called, and NSString is all cleaned up and ready to go. Richard and I will work on fixing diff --git a/Headers/gnustep/base/NSException.h b/Headers/gnustep/base/NSException.h index bd6a3f553..3fe8a2543 100644 --- a/Headers/gnustep/base/NSException.h +++ b/Headers/gnustep/base/NSException.h @@ -109,7 +109,8 @@ extern void _NSRemoveHandler( NSHandler *handler ); if( !setjmp(NSLocalHandler.jumpState) ) { #define NS_HANDLER _NSRemoveHandler(&NSLocalHandler); } else { \ - NSException *localException = NSLocalHandler.exception; + NSException *localException; \ + localException = NSLocalHandler.exception; #define NS_ENDHANDLER }} diff --git a/Headers/gnustep/base/NSZone.h b/Headers/gnustep/base/NSZone.h index e006f46b7..64c0aaaa3 100644 --- a/Headers/gnustep/base/NSZone.h +++ b/Headers/gnustep/base/NSZone.h @@ -1,113 +1,80 @@ -/* NSZone memory management. - Copyright (C) 1996, 1997 Free Software Foundation, Inc. +/* Zone memory management. -*- Mode: ObjC -*- + Copyright (C) 1997 Free Software Foundation, Inc. Written by: Yoo C. Chung - Date: September 1996 - + Date: January 1997 + This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. - + You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - See NSZone.c for additional information. */ - #ifndef __NSZone_h_GNUSTEP_BASE_INCLUDE #define __NSZone_h_GNUSTEP_BASE_INCLUDE #include -#include @class NSString; -typedef objc_mutex_t ZoneLock; typedef struct _NSZone NSZone; struct _NSZone { - unsigned granularity; - void *(*malloc)(struct _NSZone *zonep, unsigned size); - void *(*realloc)(struct _NSZone *zonep, void *ptr, unsigned size); - void (*free)(struct _NSZone *zonep, void *ptr); - void (*recycle)(struct _NSZone *zonep); - ZoneLock lock; - NSString *name; - void *table, *bblocks; - void *sblocks; /* Block with highest address comes first. */ + /* Functions for zone. */ + void *(*malloc)(struct _NSZone *zone, size_t size); + void *(*realloc)(struct _NSZone *zone, void *ptr, size_t size); + void (*free)(struct _NSZone *zone, void *ptr); + void (*recycle)(struct _NSZone *zone); + + size_t gran; // Zone granularity + objc_mutex_t lock; // Mutex for zone + NSString *name; // Name of zone (default is 'nil') }; -/* Create a new zone with its own memory pool. - The library will automatically set the start size and/or the - granularity if STARTSIZE and/or GRANULARITY are zero. Also, there - is no advantage in setting startSize or granularity to multiples of - NSPageSize(). */ -extern NSZone* -NSCreateZone(unsigned startSize, unsigned granularity, BOOL canFree); -extern NSZone *NSDefaultMallocZone(void); +/* Default zone. Name is hopelessly long so that no one will ever + want to use it. ;) Private variable. */ +extern NSZone* __nszone_private_hidden_default_zone; -extern NSZone *NSZoneFromPointer(void *pointer); +extern NSZone* NSCreateZone (size_t start, size_t gran, BOOL canFree); -extern inline void *NSZoneMalloc(NSZone *zone, unsigned size) -{ - return (zone->malloc)(zone, size); -} +extern inline NSZone* NSDefaultMallocZone (void) +{ return __nszone_private_hidden_default_zone; } -extern void *NSZoneCalloc(NSZone *zone, unsigned numElems, unsigned numBytes); +extern inline NSZone* NSZoneFromPointer (void *ptr) +{ return *((NSZone**)ptr-1); } -extern inline void *NSZoneRealloc(NSZone *zone, void *pointer, unsigned size) -{ - return (zone->realloc)(zone, pointer, size); -} +extern inline void* NSZoneMalloc (NSZone *zone, size_t size) +{ return (zone->malloc)(zone, size); } -/* For a non-freeable zone, ALL memory will be returned, regardless - of whether there are objects in it that are still in use. */ -extern inline void NSRecycleZone(NSZone *zone) -{ - (zone->recycle)(zone); -} +extern void* NSZoneCalloc (NSZone *zone, size_t elems, size_t bytes); -/* Will do nothing if pointer == NULL. */ -extern inline void NSZoneFree(NSZone *zone, void *pointer) -{ - (zone->free)(zone, pointer); -} +extern inline void* NSZoneRealloc (NSZone *zone, void *ptr, size_t size) +{ return (zone->realloc)(zone, ptr, size); } + +extern inline void NSRecycleZone (NSZone *zone) +{ (zone->recycle)(zone); } + +extern inline void NSZoneFree (NSZone *zone, void *ptr) +{ (zone->free)(zone, ptr); } extern void NSSetZoneName (NSZone *zone, NSString *name); -extern NSString *NSZoneName (NSZone *zone); +extern inline NSString* NSZoneName (NSZone *zone) +{ return zone->name; } -/* Debugging Helpers. */ - -/* Will print to stdout if this pointer is in the malloc heap, free - status, and size. */ -extern void NSZonePtrInfo(void *ptr); +/* Functions not in OpenStep. */ +extern BOOL NSZoneMemInUse (void *ptr); -/* Will verify all internal malloc information. - This is what malloc_debug calls. */ -extern BOOL NSMallocCheck(void); - -/* Memory-Page-related functions. */ - -extern unsigned NSPageSize(void); -extern unsigned NSLogPageSize(void); -extern unsigned NSRoundUpToMultipleOfPageSize(unsigned bytes); -extern unsigned NSRoundDownToMultipleOfPageSize(unsigned bytes); - -extern unsigned NSRealMemoryAvailable(void); - -extern void *NSAllocateMemoryPages(unsigned bytes); -extern void NSDeallocateMemoryPages(void *ptr, unsigned bytes); -extern void NSCopyMemoryPages(const void *source, void *dest, unsigned bytes); - -#endif /* __NSZone_h_GNUSTEP_BASE_INCLUDE */ +#endif /* not __NSZone_h_GNUSTEP_BASE_INCLUDE */ diff --git a/Headers/gnustep/base/o_array.h b/Headers/gnustep/base/o_array.h index 98ef3b770..e9ff38ea2 100644 --- a/Headers/gnustep/base/o_array.h +++ b/Headers/gnustep/base/o_array.h @@ -53,7 +53,6 @@ struct _o_array /* Identifying information. */ int magic_number; size_t serial_number; - NSZone *zone; NSString *name; const void *extra; o_callbacks_t extra_callbacks; diff --git a/Headers/gnustep/base/o_hash.h b/Headers/gnustep/base/o_hash.h index e2d61e6ac..5d4c129bb 100644 --- a/Headers/gnustep/base/o_hash.h +++ b/Headers/gnustep/base/o_hash.h @@ -82,7 +82,6 @@ struct _o_hash * And all structures have them in the same order. */ int magic_number; size_t serial_number; - NSZone *zone; NSString *name; const void *extra; o_callbacks_t extra_callbacks; diff --git a/Headers/gnustep/base/o_list.h b/Headers/gnustep/base/o_list.h index 1ccfb489f..1503447cc 100644 --- a/Headers/gnustep/base/o_list.h +++ b/Headers/gnustep/base/o_list.h @@ -53,7 +53,6 @@ struct _o_list /* Container identifiers */ int magic_number; size_t serial_number; - NSZone *zone; NSString *name; const void *extra; o_callbacks_t extra_callbacks; diff --git a/Headers/gnustep/base/o_map.h b/Headers/gnustep/base/o_map.h index 11080951e..281f6ab8f 100644 --- a/Headers/gnustep/base/o_map.h +++ b/Headers/gnustep/base/o_map.h @@ -84,7 +84,6 @@ struct _o_map * And all structures have them in the same order. */ int magic_number; size_t serial_number; - NSZone *zone; NSString *name; const void *extra; o_callbacks_t extra_callbacks; diff --git a/Source/Makefile.in b/Source/Makefile.in index d69a9e93f..22ec65360 100644 --- a/Source/Makefile.in +++ b/Source/Makefile.in @@ -79,7 +79,8 @@ FILE_AUTHORS = \ "Georg Tuparev" \ "Peter Burka" \ "Albin L. Jones" \ -"Scott Christley" +"Scott Christley" \ +"Luke Howard" DYNAMIC_LINKER=@DYNAMIC_LINKER@ @@ -457,6 +458,7 @@ include/NSMethodSignature.h \ include/NSNotification.h \ include/NSObjCRuntime.h \ include/NSObject.h \ +include/NSPage.h \ include/NSPathUtilities.h \ include/NSProcessInfo.h \ include/NSRange.h \ diff --git a/Source/NSAutoreleasePool.m b/Source/NSAutoreleasePool.m index abe064492..2aafe1c1d 100644 --- a/Source/NSAutoreleasePool.m +++ b/Source/NSAutoreleasePool.m @@ -25,6 +25,7 @@ #include #include #include +#include #include /* TODO: @@ -299,7 +300,7 @@ pop_pool_from_cache (struct autorelease_thread_vars *tv) for (i = 0; i < released->count; i++) { id anObject = released->objects[i]; - if (object_get_class(anObject) == (void*) 0xdeadface) + if (!NSZoneMemInUse(anObject)) [NSException raise: NSGenericException format: @"Autoreleasing deallocated object.\n" diff --git a/Source/NSDeallocateObject.m b/Source/NSDeallocateObject.m index 2bb96079c..ef715248d 100644 --- a/Source/NSDeallocateObject.m +++ b/Source/NSDeallocateObject.m @@ -26,10 +26,6 @@ void NSDeallocateObject(NSObject *anObject) { if ((anObject!=nil) && CLS_ISCLASS(((id)anObject)->class_pointer)) - { - NSZone *z = [anObject zone]; - ((id)anObject)->class_pointer = (void*) 0xdeadface; - NSZoneFree (z, anObject); - } + NSZoneFree (NSZoneFromPointer(anObject), anObject); return; } diff --git a/Source/NSZone.m b/Source/NSZone.m index 5a7fe0931..62d2102c4 100644 --- a/Source/NSZone.m +++ b/Source/NSZone.m @@ -1,1007 +1,978 @@ -/* Zone memory management. - Copyright (C) 1996, 1997 Free Software Foundation, Inc. - - Author: Yoo C. Chung - Date: September 1996 - +/* Zone memory management. -*- Mode: ObjC -*- + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by: Yoo C. Chung + Date: January 1997 + This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. - + You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* This uses some GCC specific extensions. But since the library is +/* Design goals: + + - Allocation and deallocation should be reasonably effecient. + + - Finding the zone containing a given pointer should be very + effecient, since objects in Objective-C use that information to + deallocate themselves. */ + + +/* Actual design: + + - All memory chunks allocated in a zone is preceded by a pointer to + the zone. This makes locating the zone containing the memory chunk + extermemely fast. However, this creates an additional 4 byte + overhead for 32 bit machines (8 bytes on 64 bit machines!). + + - For freeable zones, a small linear buffer is used for + deallocating and allocating. Anything that can't go into the + buffer then uses a more general purpose segregated fit algorithm + after flushing the buffer. + + - For memory chunks in freeable zones, the pointer to the zone is + preceded by the size, which also contains other information for + boundary tags. This adds 4 bytes for freeable zones, for a total + of a minimum of 8 byte overhead for every memory chunk in the zone. + + - For nonfreeable zones, worst-like fit is used. This is OK since + we don't have to worry about memory fragmentation. */ + +/* Other information: + + - This uses some GCC specific extensions. But since the library is supposed to compile on GCC 2.7.2 (patched) or higher, and the only other Objective-C compiler I know of (other than NeXT's) is the StepStone compiler, which I haven't the foggiest idea why anyone would prefer it to GCC ;), it should be OK. - - This uses it's own routines with NSDefaultMallocZone() instead of - using malloc() and friends. But if that's a problem, then it's a - trivial problem to fix (at least it should be). - THe NSZone functions should be thread-safe. But I haven't actually - tested them in a multi-threaded environment. - - In a small block, every chunk has a size that is a multiple of CHUNK. - A free chunk in a freeable zone looks like this: + - This uses its own routines for NSDefaultMallocZone() instead of + using malloc() and friends. Making it use them would be a somewhat + intractable problem if we want to have a fast NSZoneFromPointer(), + since we would have to search all the zones to see if they + contained the pointer. - unsigned : front : size of chunk - back : 0 - unsigned : front : position of next free chunk (0 if none) - back : position of previous free chunk (0 if none) - Unused memory - unsigned : front : position of previous free chunk (0 if none) - back : position of next free chunk (0 if none) - unsigned : front : size of chunk - back : 0 - - A used chunk in a freeable zone looks like this. + - These functions should be thread safe, but I haven't really + tested them extensively in multithreaded cases. */ - unsigned : front : size of chunk - back : position of this chunk in block - Memory that is actually used - unsigned : front : size of chunk - back : position of this chunk in block - - All sizes and positions are in units of bytes. - - The use of unsigned is probably a Bad Thing (tm). This should - still work on machines where sizeof(void*) != sizof(unsigned), but - you wouldn't be able to allocate as much memory as you might be - able to in one chunk, and it's kind of unelegant. The DEC Alpha is - such a machine (though in this case, a program that needs memory - whose size can't fit in a 32 bit integer should really think about - cutting down on its size, or at least divide the problem up). - - This assumes that sizeof(unsigned) is a multiple of two. I don't - think I'll have to worry too much about this assumption. */ - -#define NDEBUG /* Comment this out to turn on assertions. */ +/* Define to turn off assertions. */ +#define NDEBUG +#include #include -#include -#include +#include #include #include #include +#include +#include #include -#define ONES (~0U) -#define BACK (ONES << (sizeof(unsigned)*4)) -#define FRONT (ONES >> (sizeof(unsigned)*4)) -#define FREEOVERHEAD (4*sizeof(unsigned)) -#define USEDOVERHEAD (2*sizeof(unsigned)) -#define CHUNK FREEOVERHEAD /* Minimum size of chunk. */ -#define BLOCKHEAD roundupto(sizeof(BlockHeader), CHUNK) +#define ALIGN 8 /* Alignment. FIXME: Make this portable. */ +#define MINGRAN 256 /* Minimum granularity. */ +#define DEFBLOCK 16384 /* Default granularity. */ +#define BUFFER 16 /* Buffer size */ +#define MAX_SEG 16 /* Segregated list size. */ +#define ZPTRSZ sizeof(NSZone*) /* Size of zone pointers. */ +#define SZSZ sizeof(size_t) /* Size of size_t. */ -typedef struct _ZoneTable ZoneTable; -typedef struct _BlockHeader BlockHeader; +/* Information bits in size. */ +#define INUSE 0x01 /* Current chunk in use. */ +#define PREVUSE 0x02 /* Previous chunk in use. */ -struct _ZoneTable +/* Bits to mask off to get size. */ +#define SIZE_BITS (INUSE | PREVUSE) + +/* Minimum chunk size for freeable zones. */ +#define MINCHUNK roundupto(2*(SZSZ+ZPTRSZ), ALIGN) + +/* Size of block headers in freeable zones. */ +#define FF_HEAD (roundupto(sizeof(ff_block)+ZPTRSZ+SZSZ, MINCHUNK)-ZPTRSZ-SZSZ) + +/* Size of block headers in nonfreeable zones. */ +#define NF_HEAD (roundupto(sizeof(nf_block)+ZPTRSZ, ALIGN)-ZPTRSZ) + +#define CLTOSZ(n) ((n)*MINCHUNK) /* Converts classes to sizes. */ + +typedef struct _ffree_free_link ff_link; +typedef struct _nfree_block_struct nf_block; +typedef struct _ffree_block_struct ff_block; +typedef struct _ffree_NSZone_struct ffree_NSZone; +typedef struct _nfree_NSZone_struct nfree_NSZone; + + +/* Links for free lists. */ +struct _ffree_free_link { - struct _ZoneTable *next; - unsigned ident; /* Identifier for zone table, starts at 1. */ - unsigned count, size; - NSZone zones[0]; + size_t *prev, *next; }; -struct _BlockHeader +/* Header for blocks in nonfreeable zones. */ +struct _nfree_block_struct { - struct _BlockHeader *previous, *next; - - /* For small block, front is size, back is zone identifier, for big - block and free block, the whole thing is size. The size includes - the header. */ - unsigned size; - - /* For freeable zone, front is size of biggest free chunk, back is - position of biggest free chunk (it's size of the block if none - available). For non-freeable zone, position of free chunk (it's - size of the block if none available). It's 0 for a big block. */ - unsigned free; + struct _nfree_block_struct *next; + size_t size; // Size of block + size_t top; // Position of next memory chunk to allocate }; -static unsigned bsize = 0; /* Minimum block size. */ -static unsigned zunit; /* Number of zones in a zone table. */ -static ZoneLock zonelock; /* Lock for zone tables. */ -static ZoneLock blocklock; /* Lock for blocks. */ -static BlockHeader *freeBlocks = NULL; /* Higher blocks come first. */ -static BlockHeader *lastFree = NULL; -static NSZone defaultZone; -static ZoneTable *zones = NULL; -static ZoneTable *endzones = NULL; - -/* Gets a block with size SIZE. SIZE must be a multiple of bsize. The - size given includes overhead. Returns NULL if no block can be - returned. */ -static BlockHeader *getBlock(unsigned size); - -static void initialize(void) __attribute((constructor)); - -static void releaseBlock(BlockHeader *block); -static void *getMemInBlock(BlockHeader *block, unsigned size); -static void insertFreeChunk(BlockHeader *block, void *chunk); - -/* Get previously unused zone slot. Return NULL if no zone can be returned. */ -static NSZone *getZone(void); - -static void releaseZone(NSZone *zone); /* Mark zone slot as unused. */ - -/* Memory functions for freeable zones. */ -static void *fmalloc(NSZone *zone, unsigned size); -static void *frealloc(NSZone *zone, void *ptr, unsigned size); -static void ffree(NSZone *zone, void *ptr); -static void frecycle(NSZone *zone); - -/* Memory functions for non-freeable zones. */ -static void *nmalloc(NSZone *zone, unsigned size); -static void *nrealloc(NSZone *zone, void *ptr, unsigned size); -static void nfree(NSZone *zone, void *ptr); -static void nrecycle(NSZone *zone); - -/* Rounds N up to a multiple of BASE. */ -static inline unsigned -roundupto(unsigned n, unsigned base) +/* Header for blocks in freeable zones. */ +struct _ffree_block_struct { - unsigned a = (n/base)*base; + struct _ffree_block_struct *next; + size_t size; +}; - return (n-a)? a+base: n; +/* NSZone structure for freeable zones. */ +struct _ffree_NSZone_struct +{ + NSZone common; + ff_block *blocks; // Linked list of blocks + size_t *segheadlist[MAX_SEG]; // Segregated list, holds heads + size_t *segtaillist[MAX_SEG]; // Segregated list, holds tails + size_t bufsize; // Buffer size + size_t size_buf[BUFFER]; // Buffer holding sizes + size_t *ptr_buf[BUFFER]; // Buffer holding pointers to chunks +}; + +/* NSZone structure for nonfreeable zones. */ +struct _nfree_NSZone_struct +{ + NSZone common; + /* Linked list of blocks in decreasing order of free space, + except maybe for the first block. */ + nf_block *blocks; +}; + +/* Initializing functions. */ +static void initialize (void) __attribute__ ((constructor)); +static void become_threaded (void); + +/* Rounds up N to nearest multiple of BASE. */ +static inline size_t roundupto (size_t n, size_t base); + +/* Dummy lock, unlock function. */ +static void dummy_lock (objc_mutex_t mutex); + +/* Memory management functions for freeable zones. */ +static void* fmalloc (NSZone *zone, size_t size); +static void* frealloc (NSZone *zone, void *ptr, size_t size); +static void ffree (NSZone *zone, void *ptr); +static void frecycle (NSZone *zone); + +static inline size_t segindex (size_t size); +static void* get_chunk (ffree_NSZone *zone, size_t size); +static void take_chunk (ffree_NSZone *zone, size_t *chunk); +static void put_chunk (ffree_NSZone *zone, size_t *chunk); +static inline void add_buf (ffree_NSZone *zone, size_t *chunk); +static void flush_buf (ffree_NSZone *zone); + +/* Memory management functions for nonfreeable zones. */ +static void* nmalloc (NSZone *zone, size_t size); +static void nrecycle (NSZone *zone); + +/* Saves callback when mulithreading starts. */ +static objc_thread_callback thread_callback; + +/* Mutex function pointers. */ +static void (*lock)(objc_mutex_t mutex) = dummy_lock; +static void (*unlock)(objc_mutex_t mutex) = dummy_lock; + +/* Error message. */ +static NSString *outmemstr = @"Out of memory"; + +static ffree_NSZone default_zone = +{ + { fmalloc, frealloc, ffree, NULL, DEFBLOCK, (objc_mutex_t)0, nil }, + NULL, + { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL + }, + { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL + }, + 0, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL + } +}; + +/* Default zone. Name is hopelessly long so that no one will ever + want to use it. ;) */ +NSZone* __nszone_private_hidden_default_zone = (NSZone*)(&default_zone); + +/* It would be nice if the Objective-C runtime had something like + pthread's PTHREAD_MUTEX_INITIALIZER macro. Then we wouldn't need + to do any initialization at all. Oh well. */ +static void +initialize (void) +{ + thread_callback = objc_set_thread_callback(become_threaded); } -/* Return front half of N. */ -static inline unsigned -splitfront(unsigned n) +static void +become_threaded (void) { - return n & FRONT; + if (thread_callback != NULL) + thread_callback(); + default_zone.common.lock = objc_mutex_allocate(); } -/* Return back half of N. Expect this to be slower that splitfront(). */ -static inline unsigned -splitback(unsigned n) +static inline size_t +roundupto (size_t n, size_t base) { - return (n & BACK) >> (sizeof(unsigned)*4); + size_t a = (n/base)*base; + + return (n-a)? (a+base): n; } -/* Check that back half of N is not zero. If so, return non-zero number. */ -static inline unsigned -backnonzero(unsigned n) +static void +dummy_lock (objc_mutex_t mutex) { - return n & BACK; + // Do nothing. } -/* Set front half of n. Front must fit within half of an unsigned. */ -static inline void -setfront(unsigned *n, unsigned front) +/* Search the buffer to see if there is any memory chunks large enough + to satisfy request using first fit. If the memory chunk found has + a size exactly equal to the one requested, remove it from the buffer + and return it. If not, cut off a chunk that does match the size + and return it. If there is no chunk large enough in the buffer, + get a chunk from the general purpose allocator that uses segregated + fit. Since a chunk in the buffer is not freed in the general purpose + allocator, the headers are as if it is still in use. */ +static void* +fmalloc (NSZone *zone, size_t size) { - assert(front <= FRONT); - - *n = (*n & BACK) | front; + size_t i = 0; + size_t chunksize = roundupto(size+SZSZ+ZPTRSZ, MINCHUNK); + ffree_NSZone *zptr = (ffree_NSZone*)zone; + size_t bufsize; + size_t *size_buf = zptr->size_buf; + size_t **ptr_buf = zptr->ptr_buf; + size_t *chunkhead; - assert(splitfront(*n) == front); -} - -/* Set back half of n. Back half must fit in half of an unsigned. - Expect this to be slower than setfront(). */ -static inline void -setback(unsigned *n, unsigned back) -{ - assert(back <= FRONT); - - *n = (*n & FRONT) | (back << (sizeof(unsigned)*4)); - - assert(splitback(*n) == back); -} - -/* Return unsigned integer such that front and back are set to the - given numbers. The given numbers must fit within half of an - unsigned. */ -static inline unsigned -setfrontback(unsigned front, unsigned back) -{ - assert(front <= FRONT); - assert(back <= FRONT); - - return front | (back << (sizeof(unsigned)*4)); -} - -/* Maximum size for blocks containing small chunks. */ -static inline unsigned -maxsblock(void) -{ - return 1U << (sizeof(unsigned)*4-1); -} - -/* Create mutex. */ -static inline ZoneLock -makelock(void) -{ - return objc_mutex_allocate(); -} - -/* Destroy mutex. */ -static inline void -destroylock(ZoneLock mutex) -{ - objc_mutex_deallocate(mutex); -} - -/* Lock with MUTEX. */ -static inline void -lock(ZoneLock mutex) -{ - /* The thought of a probable system call is rather unappealing, but - what else can I do? */ - objc_mutex_lock(mutex); -} - -/* Release the lock on MUTEX. */ -static inline void -unlock(ZoneLock mutex) -{ - /* Like lock(), a probable system call is rather unappealing. */ - objc_mutex_unlock(mutex); -} - -/* Get zone identifier for given zone. */ -static inline unsigned -getZoneIdent(NSZone *zone) -{ - ZoneTable *table = zone->table; - - if (zone->table == NULL) - return 0; - return table->ident*zunit+(zone-table->zones); -} - -static inline NSZone* -zoneWithIdent(unsigned ident) -{ - if (ident) + if (size == 0) + return NULL; + lock(zone->lock); + bufsize = zptr->bufsize; + while ((i < bufsize) && (chunksize > size_buf[i])) + i++; + if (i < bufsize) + /* Use memory chunk in buffer. */ { - int i, a; - ZoneTable *table = zones; + if (size_buf[i] == chunksize) + /* Exact fit. */ + { + zptr->bufsize--; + bufsize = zptr->bufsize; + chunkhead = ptr_buf[i]; + size_buf[i] = size_buf[bufsize]; + ptr_buf[i] = ptr_buf[bufsize]; - for (i = a = ident/zunit; i > 1; i--) - table = table->next; - return table->zones+(ident-a*zunit); + assert(*chunkhead & INUSE); + assert((*chunkhead & ~SIZE_BITS)%MINCHUNK == 0); + } + else + /* Break off chunk. */ + { + NSZone **zoneptr; // Pointer to zone preceding memory chunk + + chunkhead = ptr_buf[i]; + + assert(*chunkhead & INUSE); + assert((*chunkhead & ~SIZE_BITS)%MINCHUNK == 0); + assert(chunksize < size_buf[i]); + + size_buf[i] -= chunksize; + ptr_buf[i] = (void*)chunkhead+chunksize; + *(ptr_buf[i]) = size_buf[i] | PREVUSE | INUSE; + zoneptr = (NSZone**)(ptr_buf[i]+1); + *zoneptr = zone; + *chunkhead = chunksize | (*chunkhead & PREVUSE) | INUSE; + } } - return &defaultZone; + else + /* Get memory from segregate fit allocator. */ + { + flush_buf(zptr); + chunkhead = get_chunk(zptr, chunksize); + if (chunkhead == NULL) + { + unlock(zone->lock); + [NSException raise: NSMallocException format: outmemstr]; + } + + assert(*chunkhead & INUSE); + assert((*chunkhead & ~SIZE_BITS)%MINCHUNK == 0); + } + unlock(zone->lock); + return (void*)chunkhead+(SZSZ+ZPTRSZ); } -static inline BlockHeader* -addBBlock(BlockHeader *list, BlockHeader *block) +/* If PTR == NULL, then it's the same as ordinary memory allocation. + If a smaller size than it originally had is requested, shrink the + chunk. If a larger size is requested, check if there is enough + space after it. If there isn't enough space, get a new chunk and + move it there, releasing the original. The space before the chunk + should also be checked, but I'll leave this to a later date. */ +static void* +frealloc (NSZone *zone, void *ptr, size_t size) { - block->previous = NULL; - block->next = list; - if (list != NULL) - list->previous = block; - return block; -} + size_t realsize; + size_t chunksize = roundupto(size+SZSZ+ZPTRSZ, MINCHUNK); + ffree_NSZone *zptr = (ffree_NSZone*)zone; + size_t *chunkhead, *slack; + NSZone **zoneptr; // Zone pointer preceding memory chunk. -static inline BlockHeader* -addSBlock(BlockHeader *list, BlockHeader *block) -{ - block->previous = NULL; - block->next = list; - list->previous = block; - return block; -} + if (size == 0) + { + ffree(zone, ptr); + return NULL; + } + if (ptr == NULL) + return fmalloc(zone, size); + lock(zone->lock); + chunkhead = ptr-(SZSZ+ZPTRSZ); + realsize = *chunkhead & ~SIZE_BITS; -static inline void -releaseSBlock(BlockHeader *block) -{ - if (block->next != NULL) - block->next->previous = block->previous; - if (block->previous != NULL) - block->previous->next = block->next; - block->size = splitfront(block->size); - releaseBlock(block); -} - -static inline void -releaseBBlock(BlockHeader *block) -{ - if (block->next != NULL) - block->next->previous = block->previous; - if (block->previous != NULL) - block->previous->next = block->next; - releaseBlock(block); -} - -/* SIZE includes the overhead. */ -static inline void -setFreeChunk(void *chunk, unsigned size, unsigned prev, unsigned next) -{ - unsigned tmp = setfrontback(size-USEDOVERHEAD, 0); - unsigned *intp = chunk; - - assert(size%CHUNK == 0); + assert(*chunkhead & INUSE); + assert(realsize%MINCHUNK == 0); - *intp = tmp; - *(intp+1) = setfrontback(next, prev); - intp = (void*)intp+size; - *(intp-1) = tmp; - *(intp-2) = setfrontback(prev, next); + if (chunksize < realsize) + /* Make chunk smaller. */ + { + slack = (void*)chunkhead+chunksize; + *slack = (realsize-chunksize) | PREVUSE | INUSE; + zoneptr = (NSZone**)(slack+1); + *zoneptr = zone; + add_buf(zptr, slack); + *chunkhead = chunksize | (*chunkhead & PREVUSE) | INUSE; + } + else if (chunksize > realsize) + { + size_t nextsize; + size_t *nextchunk, *farchunk; + + nextchunk = (void*)chunkhead+realsize; + nextsize = *nextchunk & ~SIZE_BITS; + + assert(nextsize%MINCHUNK == 0); + + if (!(*nextchunk & INUSE) && (nextsize+realsize >= chunksize)) + /* Expand to next chunk. */ + { + take_chunk(zptr, nextchunk); + if (nextsize+realsize == chunksize) + { + farchunk = (void*)nextchunk+nextsize; + *farchunk |= PREVUSE; + } + else + { + slack = (void*)chunkhead+chunksize; + *slack = ((nextsize+realsize)-chunksize) | PREVUSE; + put_chunk(zptr, slack); + } + *chunkhead = chunksize | (*chunkhead & PREVUSE) | INUSE; + } + else + /* Get new chunk and copy. */ + { + size_t *newchunk; + + newchunk = get_chunk(zptr, chunksize); + if (newchunk == NULL) + { + unlock(zone->lock); + [NSException raise: NSMallocException format: outmemstr]; + } + memcpy((void*)newchunk+SZSZ+ZPTRSZ, (void*)chunkhead+SZSZ+ZPTRSZ, + chunksize-SZSZ-ZPTRSZ); + add_buf(zptr, chunkhead); + chunkhead = newchunk; + } + /* FIXME: consider other cases where we can get more memory. */ + } + unlock(zone->lock); + return (void*)chunkhead+(SZSZ+ZPTRSZ); } -/* SIZE includes the overhead. */ -static inline void -setUsedChunk(void *chunk, unsigned size, unsigned pos) +/* Frees memory chunk by simply adding it to the buffer. */ +static void +ffree (NSZone *zone, void *ptr) { - unsigned n = setfrontback(size-USEDOVERHEAD, pos); - unsigned *intp = chunk; + lock(zone->lock); + add_buf((ffree_NSZone*)zone, ptr-(SZSZ+ZPTRSZ)); + unlock(zone->lock); +} - assert(size%CHUNK == 0); +/* Recycle the zone. We should give objects that are still alive to + the default zone by looking into each block and resetting the zone + pointers in each used memory chunks and adding the free chunks to + the default zone's buffer, but I'm lazy, so I'll do it later. For + now, we just release all the blocks. + + No locking is done. Making sure that nothing tries to use this + zone when and after it's recycled should be the programmer's + responsibility. */ +static void +frecycle (NSZone *zone) +{ + ffree_NSZone *zptr = (ffree_NSZone*)zone; + ff_block *block = zptr->blocks; + ff_block *nextblock; + + objc_mutex_deallocate(zone->lock); + while (block != NULL) + { + nextblock = block->next; + ffree(NSDefaultMallocZone(), block); + block = nextblock; + /* FIXME: should return live objects to default zone. */ + } + if (zone->name != nil) + [zone->name release]; + ffree(NSDefaultMallocZone(), zptr); +} + +static inline size_t +segindex (size_t size) +{ + assert(size%MINCHUNK == 0); + + + if (size < CLTOSZ(8)) + return size/MINCHUNK; + else if (size < CLTOSZ(16)) + return 7; + else if (size < CLTOSZ(32)) + return 8; + else if (size < CLTOSZ(64)) + return 9; + else if (size < CLTOSZ(128)) + return 10; + else if (size < CLTOSZ(256)) + return 11; + else if (size < CLTOSZ(512)) + return 12; + else if (size < CLTOSZ(1024)) + return 13; + else if (size < CLTOSZ(2048)) + return 14; + else + return 15; +} + +/* Look through the segregated list with first fit to find a memory + chunk. If one is not found, get more memory. */ +static void* +get_chunk (ffree_NSZone *zone, size_t size) +{ + size_t class = segindex(size); + size_t *chunk = zone->segheadlist[class]; + NSZone **zoneptr; // Zone pointer preceding memory chunk + + assert(size%MINCHUNK == 0); - *intp = n; - intp = (void*)intp+size; - *(intp-1) = n; - return; + while ((chunk != NULL) && ((*chunk & ~SIZE_BITS) < size)) + chunk = ((ff_link*)(chunk+1))->next; + if (chunk == NULL) + /* Get more memory. */ + { + class++; + while ((class < MAX_SEG) && (zone->segheadlist[class] == NULL)) + class++; + if (class == MAX_SEG) + /* Absolutely no memory in segregated list. */ + { + size_t blocksize; + ff_block *block; + + blocksize = roundupto(size+FF_HEAD+SZSZ+ZPTRSZ, zone->common.gran); + if (zone == &default_zone) + { + block = NSAllocateMemoryPages(blocksize); + if (block == NULL) + return NULL; + } + else + { + /* Any clean way to make gcc shut up here? */ + NS_DURING + block = fmalloc(NSDefaultMallocZone(), blocksize); + NS_HANDLER + return NULL; + NS_ENDHANDLER + } + assert(block != NULL); + + block->size = blocksize; + block->next = zone->blocks; + zone->blocks = block; + chunk = (void*)block+(blocksize-SZSZ-ZPTRSZ); + if (FF_HEAD+size+SZSZ+ZPTRSZ < blocksize) + { + *chunk = INUSE; + chunk = (void*)block+(FF_HEAD+size); + *chunk = (blocksize-size-FF_HEAD-SZSZ-ZPTRSZ) | PREVUSE; + put_chunk(zone, chunk); + + assert((*chunk & ~SIZE_BITS)%MINCHUNK == 0); + } + else + *chunk = PREVUSE | INUSE; + chunk = (void*)block+FF_HEAD; + } + else + { + size_t *slack; + + chunk = zone->segheadlist[class]; + + assert(class < MAX_SEG); + assert(!(*chunk & INUSE)); + assert(*chunk & PREVUSE); + assert(size < (*chunk & ~SIZE_BITS)); + assert((*chunk & ~SIZE_BITS)%MINCHUNK == 0); + + take_chunk(zone, chunk); + slack = (void*)chunk+size; + *slack = ((*chunk & ~SIZE_BITS)-size) | PREVUSE; + put_chunk(zone, slack); + } + } + else + { + size_t chunksize = *chunk & ~SIZE_BITS; + + assert(chunksize%MINCHUNK == 0); + assert(!(*chunk & INUSE)); + assert(*chunk & PREVUSE); + assert(*(size_t*)((void*)chunk+chunksize) & INUSE); + + take_chunk(zone, chunk); + if (chunksize > size) + { + size_t *slack; + + slack = (void*)chunk+size; + *slack = (chunksize-size) | PREVUSE; + put_chunk(zone, slack); + } + else + { + size_t *nextchunk = (void*)chunk+chunksize; + + assert(!(*nextchunk & PREVUSE)); + assert(chunksize == size); + + *nextchunk |= PREVUSE; + } + } + *chunk = size | PREVUSE | INUSE; + zoneptr = (NSZone**)(chunk+1); + *zoneptr = (NSZone*)zone; + return chunk; +} + +/* Take the given chunk out of the free list. No headers are set. */ +static void +take_chunk (ffree_NSZone *zone, size_t *chunk) +{ + size_t size = *chunk & ~SIZE_BITS; + size_t class = segindex(size); + ff_link *otherlink; + ff_link *links = (ff_link*)(chunk+1); + + assert(size%MINCHUNK == 0); + assert(!(*chunk & INUSE)); + assert(*chunk & PREVUSE); + + if (links->prev == NULL) + zone->segheadlist[class] = links->next; + else + { + otherlink = (ff_link*)(links->prev+1); + otherlink->next = links->next; + } + if (links->next == NULL) + zone->segtaillist[class] = links->prev; + else + { + otherlink = (ff_link*)(links->next+1); + otherlink->prev = links->prev; + } +} + +/* Add the given chunk to the segregated list. The header to the + chunk must be set appropriately, but the tailer is set here. */ +static void +put_chunk (ffree_NSZone *zone, size_t *chunk) +{ + size_t size = *chunk & ~SIZE_BITS; + size_t class = segindex(size); + size_t *tailer = (void*)chunk+(size-SZSZ); + ff_link *links = (ff_link*)(chunk+1); + + assert(size%MINCHUNK == 0); + assert(!(*chunk & INUSE)); + assert(*chunk & PREVUSE); + + *tailer = size; + if (zone->segtaillist[class] == NULL) + { + assert(zone->segheadlist[class] == NULL); + + zone->segheadlist[class] = zone->segtaillist[class] = chunk; + links->prev = links->next = NULL; + } + else + { + ff_link *prevlink = (ff_link*)(zone->segtaillist[class]+1); + + assert(zone->segheadlist[class] != NULL); + + links->next = NULL; + links->prev = zone->segtaillist[class]; + prevlink->next = chunk; + zone->segtaillist[class] = chunk; + } +} + +/* Add the given pointer to the buffer. If the buffer becomes full, + flush it. The given pointer must always be one that points to used + memory. */ +static inline void +add_buf (ffree_NSZone *zone, size_t *chunk) +{ + size_t bufsize = zone->bufsize; + + assert(bufsize < BUFFER); + assert(*chunk & INUSE); + assert((*chunk & ~SIZE_BITS)%MINCHUNK == 0); + + zone->bufsize++; + zone->size_buf[bufsize] = *chunk & ~SIZE_BITS; + zone->ptr_buf[bufsize] = chunk; + if (bufsize == BUFFER-1) + flush_buf(zone); +} + +/* Flush buffers. All coalescing is done here. */ +static void +flush_buf (ffree_NSZone *zone) +{ + size_t i, size; + size_t bufsize = zone->bufsize; + size_t *chunk, *nextchunk; + size_t *size_buf = zone->size_buf; + size_t **ptr_buf = zone->ptr_buf; + + assert(bufsize <= BUFFER); + + for (i = 0; i < bufsize; i++) + { + size = size_buf[i]; + chunk = ptr_buf[i]; + + assert((*chunk & ~SIZE_BITS) == size); + assert(*chunk & INUSE); + + nextchunk = (void*)chunk+size; + if (!(*chunk & PREVUSE)) + /* Coalesce with previous chunk. */ + { + size_t prevsize = *(chunk-1); + + assert(prevsize%MINCHUNK == 0); + + size += prevsize; + chunk = (void*)chunk-prevsize; + + assert(!(*chunk & INUSE)); + assert(*chunk & PREVUSE); + assert((*chunk & ~SIZE_BITS) == prevsize); + + take_chunk(zone, chunk); + } + if (!(*nextchunk & INUSE)) + /* Coalesce with next chunk. */ + { + size_t nextsize = *nextchunk & ~SIZE_BITS; + + assert(chunksize%MINCHUNK == 0); + assert(*nextchunk & PREVUSE); + assert(!(*nextchunk & INUSE)); + assert((void*)chunk+chunksize == nextchunk); + + take_chunk(zone, nextchunk); + size += nextsize; + } + *chunk = size | PREVUSE; + put_chunk(zone, chunk); + nextchunk = (void*)chunk+size; + *nextchunk &= ~PREVUSE; + + assert((*chunk & ~SIZE_BITS)%MINCHUNK == 0); + assert(!(*chunk & INUSE)); + assert(*chunk & PREVUSE); + assert(*nextchunk & INUSE); + assert(!(*nextchunk & PREVUSE)); + } + zone->bufsize = 0; +} + +/* If the first block in block list has enough space, use that space. + Otherwise, sort the block list in decreasing free space order (only + the first block needs to be put in its appropriate place since + the rest of the list is already sorted). Then check if the first + block has enough space for the request. If it does, use it. If it + doesn't, get more memory from the default zone, since none of the + other blocks in the block list could have enough memory. */ +static void* +nmalloc (NSZone *zone, size_t size) +{ + nfree_NSZone *zptr = (nfree_NSZone*)zone; + size_t top; + size_t chunksize = roundupto(size+ZPTRSZ, ALIGN); + NSZone **chunkhead; + + lock(zone->lock); + top = zptr->blocks->top; + /* No need to worry about (block == NULL), since a nonfreeable zone + always starts with a block. */ + if (zptr->blocks->size-top >= chunksize) + { + chunkhead = (void*)(zptr->blocks)+top; + *chunkhead = zone; + zptr->blocks->top += chunksize; + } + else + { + size_t freesize = zptr->blocks->size-top; + nf_block *block, *preblock; + + /* First, get the block list in decreasing free size order. */ + preblock = NULL; + block = zptr->blocks; + while ((block->next != NULL) + && (freesize < block->next->size-block->next->top)) + { + preblock = block; + block = block->next; + } + if (preblock != NULL) + { + preblock->next = zptr->blocks; + zptr->blocks = zptr->blocks->next; + preblock->next->next = block; + } + if (zptr->blocks->size-zptr->blocks->top < chunksize) + /* Get new block. */ + { + size_t blocksize = roundupto(size+NF_HEAD, zone->gran); + nf_block *block; + + /* Any clean way to make gcc shut up here? */ + NS_DURING + block = fmalloc(NSDefaultMallocZone(), blocksize); + NS_HANDLER + unlock(zone->lock); + [localException raise]; + NS_ENDHANDLER + block->next = zptr->blocks; + block->size = blocksize; + block->top = NF_HEAD; + zptr->blocks = block; + } + chunkhead = (void*)block+zptr->blocks->top; + *chunkhead = zone; + zptr->blocks->top += chunksize; + } + unlock(zone->lock); + return chunkhead+1; +} + +/* Return the blocks to the default zone, then deallocate mutex, and + then release zone name if it exists. + + No locking is done because I don't think someone would use a zone + when they're going to recycle it. If they do that, it's a + programming error. */ +static void +nrecycle (NSZone *zone) +{ + nf_block *nextblock; + nf_block *block = ((nfree_NSZone*)zone)->blocks; + + objc_mutex_deallocate(zone->lock); + while (block != NULL) + { + nextblock = block->next; + ffree(NSDefaultMallocZone(), block); + block = nextblock; + } + if (zone->name != nil) + [zone->name release]; + ffree(NSDefaultMallocZone(), zone); } NSZone* -NSCreateZone(unsigned startSize, unsigned granularity, BOOL canFree) +NSCreateZone (size_t start, size_t gran, BOOL canFree) { - NSZone *zone; - BlockHeader *block; + size_t i, startsize, granularity; - initialize (); - if ((startSize == 0) || (startSize > maxsblock())) - startSize = bsize; + if (start > 0) + startsize = roundupto(start, MINGRAN); else - startSize = roundupto(startSize, bsize); - if ((granularity == 0) || (granularity > maxsblock())) - granularity = bsize; - zone = getZone(); - if (zone == NULL) - [NSException raise: NSMallocException - format: @"NSCreateZone(): Unable to obtain zone"]; - zone->sblocks = block = getBlock(startSize); - if (block == NULL) - { - releaseZone(zone); - [NSException raise: NSMallocException - format: @"NSCreateZone(): More memory unattainable"]; - } - zone->granularity = roundupto(granularity, bsize); - zone->name = nil; - zone->bblocks = NULL; - zone->lock = makelock(); - block->previous = block->next = NULL; - block->size = setfrontback(startSize, getZoneIdent(zone)); + startsize = MINGRAN; + if (gran > 0) + granularity = roundupto(gran, MINGRAN); + else + granularity = MINGRAN; if (canFree) { - zone->malloc = fmalloc; - zone->realloc = frealloc; - zone->free = ffree; - zone->recycle = frecycle; - block->free = - setfrontback(startSize-(BLOCKHEAD+USEDOVERHEAD), BLOCKHEAD); - setFreeChunk((void*)block+BLOCKHEAD, startSize-BLOCKHEAD, 0, 0); + ff_block *block; + ffree_NSZone *zone; + size_t *header, *tailer; + NSZone **zoneptr; + + zone = fmalloc(NSDefaultMallocZone(), sizeof(ffree_NSZone)); + zone->common.malloc = fmalloc; + zone->common.realloc = frealloc; + zone->common.free = ffree; + zone->common.recycle = frecycle; + zone->common.gran = granularity; + zone->common.lock = objc_mutex_allocate(); + zone->common.name = nil; + for (i = 0; i < MAX_SEG; i++) + { + zone->segheadlist[i] = NULL; + zone->segtaillist[i] = NULL; + } + zone->bufsize = 0; + NS_DURING + zone->blocks = block = fmalloc(NSDefaultMallocZone(), startsize); + NS_HANDLER + objc_mutex_dealloc(zone->common.lock); + ffree(NSDefaultMallocZone(), zone); + [localException raise]; + NS_ENDHANDLER + block->next = NULL; + block->size = startsize; + header = (void*)block+FF_HEAD; + *header = (startsize-FF_HEAD-SZSZ-ZPTRSZ) | PREVUSE | INUSE; + zoneptr = (NSZone**)(header+1); + *zoneptr = (NSZone*)zone; + tailer = (void*)block+(startsize-SZSZ-ZPTRSZ); + *tailer = INUSE | PREVUSE; + add_buf(zone, header); + return (NSZone*)zone; } else { - zone->malloc = nmalloc; - zone->realloc = nrealloc; - zone->free = nfree; - zone->recycle = nrecycle; - block->free = BLOCKHEAD; + nf_block *block; + nfree_NSZone *zone; + + zone = fmalloc(NSDefaultMallocZone(), sizeof(nfree_NSZone)); + zone->common.malloc = nmalloc; + zone->common.realloc = NULL; + zone->common.free = NULL; + zone->common.recycle = nrecycle; + zone->common.gran = granularity; + zone->common.lock = objc_mutex_allocate(); + zone->common.name = nil; + NS_DURING + zone->blocks = block = fmalloc(NSDefaultMallocZone(), startsize); + NS_HANDLER + objc_mutex_deallocate(zone->common.lock); + ffree(NSDefaultMallocZone(), zone); + [localException raise]; + NS_ENDHANDLER + block->next = NULL; + block->size = startsize; + block->top = NF_HEAD; + return (NSZone*)zone; } - return zone; } -NSZone* -NSDefaultMallocZone(void) +inline NSZone* +NSDefaultMallocZone (void) { - initialize (); - return &defaultZone; + return __nszone_private_hidden_default_zone; } -NSZone* -NSZoneFromPointer(void *pointer) +inline NSZone* +NSZoneFromPointer (void *ptr) { - unsigned *intp; - BlockHeader *block; - - intp = pointer-sizeof(unsigned); - block = (void*)intp-splitback(*intp); - if (block->free) - return zoneWithIdent(splitback(block->size)); - else - { - BlockHeader *aBlock = defaultZone.bblocks; - NSZone *zone, *endzone; - ZoneTable *table; - - while (aBlock != NULL) - { - if (aBlock == block) - return &defaultZone; - aBlock = aBlock->next; - } - table = zones; - while (table != NULL) - { - zone = table->zones; - endzone = zone+table->size; - while (zone < endzone) - { - if (zone->table != NULL) - { - aBlock = zone->bblocks; - while (aBlock != NULL) - { - if (aBlock == block) - return zone; - aBlock = aBlock->next; - } - } - zone++; - } - table = table->next; - } - } - return NULL; /* No zone containing pointer found. */ + return *((NSZone**)ptr-1); } - + inline void* -NSZoneMalloc(NSZone *zone, unsigned size) +NSZoneMalloc (NSZone *zone, size_t size) { return (zone->malloc)(zone, size); } void* -NSZoneCalloc(NSZone *zone, unsigned numElems, unsigned numBytes) +NSZoneCalloc (NSZone *zone, size_t elems, size_t bytes) { - return memset((zone->malloc)(zone, numElems*numBytes), 0, numElems*numBytes); + return memset(NSZoneMalloc(zone, elems*bytes), 0, elems*bytes); } inline void* -NSZoneRealloc(NSZone *zone, void *pointer, unsigned size) +NSZoneRealloc (NSZone *zone, void *ptr, size_t size) { - return (zone->realloc)(zone, pointer, size); + return (zone->realloc)(zone, ptr, size); } inline void -NSRecycleZone(NSZone *zone) +NSRecycleZone (NSZone *zone) { (zone->recycle)(zone); } inline void -NSZoneFree(NSZone *zone, void *pointer) +NSZoneFree (NSZone *zone, void *ptr) { - (zone->free)(zone, pointer); + (zone->free)(zone, ptr); } void NSSetZoneName (NSZone *zone, NSString *name) { - zone->name = [name copy]; + lock(zone->lock); + if (zone->name != nil) + [zone->name release]; + if (name == nil) + zone->name = nil; + else + zone->name = [name copy]; + unlock(zone->lock); } -NSString* +inline NSString* NSZoneName (NSZone *zone) { return zone->name; } -void -NSZonePtrInfo(void *ptr) -{ - /* FIXME: Implement this. */ - fprintf(stderr, "NSZonePtrInfo() not implemented yet!\n"); -} - BOOL -NSMallocCheck(void) +NSZoneMemInUse (void *ptr) { - /* FIXME: Implement this. */ - fprintf(stderr, "NSMallocCheck() not implemented yet!\n"); - abort(); - return NO; -} - -static void -initialize(void) -{ - static int initialize_done = 0; - BlockHeader *block; - - if (initialize_done) - return; - initialize_done = 1; - - bsize = NSPageSize(); - zunit = (bsize-sizeof(ZoneTable))/sizeof(NSZone); - zonelock = makelock(); - blocklock = makelock(); - defaultZone.lock = makelock(); - defaultZone.granularity = bsize; - defaultZone.malloc = fmalloc; - defaultZone.realloc = frealloc; - defaultZone.free = ffree; - defaultZone.recycle = NULL; - defaultZone.name = nil; - defaultZone.table = NULL; - defaultZone.bblocks = NULL; - block = defaultZone.sblocks = getBlock(bsize); - if (block == NULL) - { - fprintf(stderr, "Unable to allocate memory for default zone.\n"); - abort(); /* No point surviving if we can't even use the default zone. */ - } - block->previous = block->next = NULL; - block->size = setfrontback(bsize, 0); - block->free = setfrontback(bsize-BLOCKHEAD-USEDOVERHEAD, BLOCKHEAD); - setFreeChunk((void*)block+BLOCKHEAD, bsize-BLOCKHEAD, 0, 0); -} - -static BlockHeader* -getBlock(unsigned size) -{ - BlockHeader *block; - - assert(size%bsize == 0); - - lock(blocklock); - block = freeBlocks; - while ((block != NULL) && (block->size < size)) - block = block->next; - if (block == NULL) - block = NSAllocateMemoryPages(size); - else if (block->size != size) - { - BlockHeader *splitblock; - - splitblock = (void*)block+size; - splitblock->previous = block->previous; - splitblock->next = block->next; - splitblock->size = block->size-size; - if (block->next != NULL) - block->next->previous = splitblock; - if (block->previous != NULL) - block->previous->next = splitblock; - } - unlock(blocklock); - return block; -} - -static void -releaseBlock(BlockHeader *block) -{ - BlockHeader *aBlock; - - lock(blocklock); - aBlock = freeBlocks; - while ((aBlock != NULL) && (aBlock > block)) - aBlock = aBlock->next; - if (aBlock == NULL) - { - if (lastFree == NULL) - { - lastFree = freeBlocks = block; - block->previous = aBlock->next = NULL; - } - else if ((void*)block+block->size == (void*)lastFree) - { - block->size += lastFree->size; - block->previous = lastFree->previous; - block->next = NULL; - if (block->previous != NULL) - block->previous->next = block; - lastFree = block; - } - else - { - block->previous = lastFree; - block->next = NULL; - lastFree->next = block; - lastFree = block; - } - } - else - { - if (aBlock->previous == NULL) - { - freeBlocks = block; - block->next = aBlock; - block->previous = NULL; - aBlock->previous = block; - } - else if ((void*)block+block->size == aBlock->previous) - { - block->size += aBlock->previous->size; - block->previous = aBlock->previous->previous; - block->next = aBlock; - if (block->previous != NULL) - block->previous->next = block; - aBlock->previous = block; - } - if ((void*)aBlock+aBlock->size == block) - aBlock->size += block->size; - } - unlock(blocklock); -} - -static void* -getMemInBlock(BlockHeader *block, unsigned size) -{ - unsigned chunksize = roundupto(size+USEDOVERHEAD, CHUNK); - unsigned *intp, *intp2; - - assert(splitfront(block->free) >= size+USEDOVERHEAD); - - intp = (void*)block+splitback(block->free); - intp2 = (void*)block+splitfront(*(intp+1)); - if ((void*)intp2 != (void*)block) - { - setFreeChunk(intp2, splitfront(*intp2)+USEDOVERHEAD, - 0, splitfront(*(intp2+1))); - block->free = - setfrontback(splitfront(*intp2), (void*)intp2-(void*)block); - } - else - block->free = setfrontback(0, splitfront(block->size)); - if (splitfront(*intp)+USEDOVERHEAD != chunksize) - { - setFreeChunk((void*)intp+chunksize, - (splitfront(*intp)+USEDOVERHEAD)-chunksize, 0, 0); - insertFreeChunk(block, (void*)intp+chunksize); - } - setUsedChunk(intp, chunksize, (void*)intp-(void*)block); - return intp+1; -} - -static void -insertFreeChunk(BlockHeader *block, void *chunk) -{ - unsigned *intp = chunk; - - assert((void*)chunk < (void*)block+splitfront(block->size)); - - if (splitfront(block->free) == 0) - { - block->free = setfrontback(splitfront(*intp), chunk-(void*)block); - setFreeChunk(chunk, splitfront(*intp)+USEDOVERHEAD, 0, 0); - } - else - { - unsigned *intp2 = (void*)block+splitback(block->free); - unsigned *intp3 = NULL; - - while (((void*)intp2 != (void*)block) - && (splitfront(*intp) < splitfront(*intp2))) - { - intp3 = intp2; - intp2 = (void*)block+splitfront(*(intp2+1)); - } - if (intp3 == NULL) - { - unsigned pos = chunk-(void*)block; - - setFreeChunk(intp2, splitfront(*intp2)+USEDOVERHEAD, - pos, splitfront(*(intp2+1))); - setFreeChunk(chunk, splitfront(*intp)+USEDOVERHEAD, - 0, (void*)intp2-(void*)block); - block->free = setfrontback(splitfront(*intp), pos); - } - else if ((void*)intp2 == (void*)block) - { - setFreeChunk(intp3, splitfront(*intp3)+USEDOVERHEAD, - splitback(*(intp3+1)), chunk-(void*)block); - setFreeChunk(chunk, splitfront(*intp)+USEDOVERHEAD, - (void*)intp3-(void*)block, 0); - } - else - { - unsigned pos = chunk-(void*)block; - - setFreeChunk(intp2, splitfront(*intp2)+USEDOVERHEAD, - pos, splitfront(*(intp2+1))); - setFreeChunk(intp3, splitfront(*intp3)+USEDOVERHEAD, - splitback(*(intp3+1)), pos); - setFreeChunk(chunk, splitfront(*intp)+USEDOVERHEAD, - (void*)intp3-(void*)block, (void*)intp2-(void*)block); - } - } -} - -static NSZone* -getZone(void) -{ - NSZone *zone; - ZoneTable *table = zones; - - lock(zonelock); - while ((table != NULL) && (table->count == zunit)) - table = table->next; - if (table == NULL) - { - table = NSAllocateMemoryPages(bsize); - if (table == NULL) - zone = NULL; - else - { - table->size = table->count = 1; - table->next = zones; - if (zones == NULL) - { - zones = table; - table->ident = 1; - } - else - { - endzones->next = table; - table->ident = endzones->ident+1; - } - endzones = table; - table->next = NULL; - zone = table->zones; - } - } - else - { - if (table->size == zunit) - { - zone = table->zones; - while (zone->table != NULL) - zone++; - } - else - { - zone = table->zones+table->size; - table->size++; - } - table->count++; - } - zone->table = table; - unlock(zonelock); - return zone; -} - -static void -releaseZone(NSZone *zone) -{ - lock(zonelock); - ((ZoneTable*)zone->table)->count--; - zone->table = NULL; - unlock(zonelock); - return; -} - -static void* -fmalloc(NSZone *zone, unsigned size) -{ - unsigned *intp; - BlockHeader *block; - void *ptr; - - lock(zone->lock); - if (size+BLOCKHEAD+USEDOVERHEAD > maxsblock()) - { - unsigned realSize = roundupto(size+BLOCKHEAD+USEDOVERHEAD, bsize); - - block = getBlock(realSize); - if (block == NULL) - { - unlock(zone->lock); - [NSException raise: NSMallocException - format: @"NSZoneMalloc(): Unable to get memory"]; - } - block->size = realSize; - block->free = 0; - zone->bblocks = addBBlock(zone->bblocks, block); - intp = (void*)block+BLOCKHEAD; - *intp = setfrontback(0, BLOCKHEAD); - ptr = intp+1; - } - else - { - block = zone->sblocks; - while ((block != NULL) && (splitfront(block->free) < size+USEDOVERHEAD)) - block = block->next; - if (block == NULL) - { - unsigned chunk = roundupto(size+USEDOVERHEAD, CHUNK); - unsigned tmp, total; - - total = roundupto(size+BLOCKHEAD+USEDOVERHEAD, zone->granularity); - block = getBlock(total); - if (block == NULL) - { - unlock(zone->lock); - [NSException raise: NSMallocException - format: @"NSZoneMalloc(): Unable to get memory"]; - } - zone->sblocks = addSBlock(zone->sblocks, block); - tmp = total-chunk-BLOCKHEAD-USEDOVERHEAD; - block->size = setfrontback(total, getZoneIdent(zone)); - block->free = setfrontback(tmp, chunk+BLOCKHEAD); - setUsedChunk((void*)block+BLOCKHEAD, chunk, BLOCKHEAD); - setFreeChunk((void*)block+(chunk+BLOCKHEAD), tmp+USEDOVERHEAD, 0, 0); - ptr = (void*)block+(BLOCKHEAD+sizeof(unsigned)); - } - else - ptr = getMemInBlock(block, size); - } - unlock(zone->lock); - return ptr; -} - -static void* -frealloc(NSZone *zone, void *ptr, unsigned size) -{ - /* FIXME: Implement this properly! */ - void *newptr; - - newptr = fmalloc(zone, size); - memcpy(newptr, ptr, size); - ffree(zone, ptr); - return newptr; -} - -static void -ffree(NSZone *zone, void *ptr) -{ - unsigned *intp; - BlockHeader *block; - - if (ptr == NULL) - return; - lock(zone->lock); - intp = ptr-sizeof(unsigned); - block = (void*)intp-splitback(*intp); - if (block->free) - insertFreeChunk(block, intp); - else - { - if (block->previous == NULL) - zone->bblocks = block->next; - else - block->previous->next = block->next; - if (block->next != NULL) - block->next->previous = block->previous; - releaseBBlock(block); - } - unlock(zone->lock); - return; -} - -static void -frecycle(NSZone *zone) -{ - BlockHeader *block, *nextblock; - - block = zone->bblocks; - while (block != NULL) - { - nextblock = block->next; - defaultZone.bblocks = addBBlock(defaultZone.bblocks, block); - block = nextblock; - } - block = zone->sblocks; - while (block != NULL) - { - nextblock = block->next; - if (splitfront(block->size) - == splitfront(block->free)+BLOCKHEAD+USEDOVERHEAD) - releaseSBlock(block); - else - defaultZone.sblocks = addSBlock(defaultZone.sblocks, block); - block = nextblock; - } - [zone->name release]; - destroylock(zone->lock); - releaseZone(zone); - return; -} - -static void* -nmalloc(NSZone *zone, unsigned size) -{ - unsigned *intp; - BlockHeader *block; - - lock(zone->lock); - if (size+BLOCKHEAD+USEDOVERHEAD > maxsblock()) - { - unsigned realSize = roundupto(size+BLOCKHEAD+USEDOVERHEAD, bsize); - - block = getBlock(realSize); - if (block == NULL) - { - unlock(zone->lock); - [NSException raise: NSMallocException - format: @"NSZoneMalloc(): Unable to get memory"]; - } - block->size = realSize; - block->free = 0; - zone->bblocks = addBBlock(zone->bblocks, block); - intp = (void*)block+BLOCKHEAD; - *intp = setfrontback(0, BLOCKHEAD); - } - else - { - block = zone->sblocks; - if (size+sizeof(unsigned) > splitfront(block->size)-block->free) - { - unsigned newsize; - BlockHeader *newblock; - - newsize = - roundupto(size+USEDOVERHEAD+BLOCKHEAD, zone->granularity); - newblock = getBlock(newsize); - if (newblock == NULL) - { - unlock(zone->lock); - [NSException raise: NSMallocException - format: @"NSZoneMalloc(): Unable to get memory"]; - } - newblock->size = setfrontback(newsize, getZoneIdent(zone)); - newblock->free = roundupto(size+sizeof(unsigned), CHUNK)+BLOCKHEAD; - zone->sblocks = addSBlock(zone->sblocks, newblock); - intp = (void*)newblock+BLOCKHEAD; - *intp = setfrontback(0, BLOCKHEAD); - } - else - { - intp = (void*)block+block->free; - *intp = (void*)intp-(void*)block; - block->free += roundupto(size+sizeof(unsigned), CHUNK); - } - } - unlock(zone->lock); - return intp+1; -} - -static void* -nrealloc(NSZone *zone, void *ptr, unsigned size) -{ - [NSException raise: NSGenericException - format: @"Trying to reallocate memory in non-freeable zone"]; - return NULL; -} - -static void -nfree(NSZone *zone, void *ptr) -{ - [NSException raise: NSGenericException - format: @"Trying to free memory in non-freeable zone"]; - return; -} - -static void -nrecycle(NSZone *zone) -{ - BlockHeader *block; - - block = zone->sblocks; - while (block != NULL) - { - releaseSBlock(block); - block = block->next; - } - block = zone->bblocks; - while (block != NULL) - { - releaseBBlock(block); - block = block->next; - } - destroylock(zone->lock); - [zone->name release]; - releaseZone(zone); - return; + return (*(size_t*)(ptr-ZPTRSZ-SZSZ)) & INUSE; } diff --git a/Source/StdioStream.m b/Source/StdioStream.m index 85b1d6d5d..c255a3d6f 100644 --- a/Source/StdioStream.m +++ b/Source/StdioStream.m @@ -281,7 +281,6 @@ stdio_unchar_func(void *s, int c) - (void) dealloc { - fclose(fp); [super dealloc]; } diff --git a/Source/o_array.m b/Source/o_array.m index af8cabd0e..8f450e9d6 100644 --- a/Source/o_array.m +++ b/Source/o_array.m @@ -415,7 +415,7 @@ o_array_alloc_with_zone(NSZone *zone) o_array_t * o_array_alloc(void) { - return o_array_alloc_with_zone(0); + return o_array_alloc_with_zone(NSDefaultMallocZone()); } o_array_t * diff --git a/Source/o_hash.m b/Source/o_hash.m index fc8c0a944..b4f267494 100644 --- a/Source/o_hash.m +++ b/Source/o_hash.m @@ -743,7 +743,7 @@ o_hash_alloc_with_zone(NSZone * zone) o_hash_t * o_hash_alloc(void) { - return o_hash_alloc_with_zone(0); + return o_hash_alloc_with_zone(NSDefaultMallocZone()); } o_hash_t * @@ -994,7 +994,7 @@ o_hash_copy_with_zone(o_hash_t *old_hash, NSZone * zone) o_hash_t * o_hash_copy(o_hash_t *old_hash) { - return o_hash_copy_with_zone(old_hash, 0); + return o_hash_copy_with_zone(old_hash, NSDefaultMallocZone()); } /** Mapping... **/ diff --git a/Source/o_list.m b/Source/o_list.m index b5d34e3aa..2b4f42c23 100644 --- a/Source/o_list.m +++ b/Source/o_list.m @@ -608,7 +608,7 @@ o_list_alloc_with_zone (NSZone *zone) o_list_t * o_list_alloc (void) { - return o_list_alloc_with_zone (0); + return o_list_alloc_with_zone (NSDefaultMallocZone()); } o_list_t * @@ -782,7 +782,7 @@ o_list_at_index_insert_list(o_list_t *base_list, o_list_t * o_list_copy (o_list_t *old_list) { - return o_list_copy_with_zone (old_list, 0); + return o_list_copy_with_zone (old_list, NSDefaultMallocZone()); } o_list_t * diff --git a/Source/o_x_bas.m.in b/Source/o_x_bas.m.in index 489126ee2..8d0cb614f 100644 --- a/Source/o_x_bas.m.in +++ b/Source/o_x_bas.m.in @@ -50,7 +50,7 @@ o_@XX@_magic_number(o_@XX@_t *xx) inline NSZone * o_@XX@_zone(o_@XX@_t *xx) { - return xx->zone; + return NSZoneFromPointer(xx); } /** Names... **/ @@ -191,7 +191,6 @@ _o_@XX@_alloc_with_zone(NSZone *zone) _o_@XX@_set_serial_number(xx); xx->magic_number = _OBJECTS_MAGIC_@XX@; xx->name = 0; - xx->zone = zone; xx->extra_callbacks = o_callbacks_for_non_owned_void_p; xx->extra = 0;