I can't find any clear description of how memory allocation is supposed to work. I see bits and pieces in pdd09_gc.pod, resources.c, etc., but I still haven't been able to wrap my head around it all.
Here's a hypothesis. Could someone correct me where I'm wrong? There are two kinds of memory: untracked and tracked. Untracked memory is yours to do with as you see fit, and must be explicitly returned to the system. Tracked memory is handled by the garbage collector and never needs to be explicitly freed. Untracked memory Untracked memory should be handled through the routines in memory.h. This provides platform independence. The routines are listed below: void *mem_allocate_aligned(size_t); void *mem_sys_allocate(size_t); void *mem_sys_realloc(void *, size_t); void mem_sys_free(void *); void mem_setup_allocator(struct Parrot_Interp *); All of those are clear except for mem_setup_allocator(), which should only be called by pregnant pigeons with fewer than 7 hit points. Tracked memory Tracked memory is managed through the routines in resources.h: PMC *new_pmc_header(Interp *); void free_pmc(PMC *); STRING *new_string_header(Interp *); Buffer *new_tracked_header(Interp *, size_t size); Buffer *new_buffer_header(Interp *); void free_buffer(Buffer *); void *new_bigint_header(Interp *); void free_bigint(void); void *new_bignum_header(Interp *); void free_bignum(void); void *Parrot_allocate(Interp *, void *, size_t size); void *Parrot_allocate_about(Interp *, void *, size_t size); void *mem_allocate(Interp *, size_t *req_size); void *Parrot_alloc_new_block(Interp *, size_t, UINTVAL); void Parrot_new_pmc_header_arena(Interp *interpreter); void Parrot_do_dod_run(Interp *); void Parrot_go_collect(Interp *); void *Parrot_reallocate(Interp *interpreter, void *from, size_t tosize); void *Parrot_reallocate_about(Interp *interpreter, void *from, size_t tosize); void buffer_lives(Buffer *); o) free_bigint() and free_bignum() are declarations that bigints and bignums are now free; if you call these functions and are caught shoplifting bigints or bignums, you cannot be prosecuted. They obviously don't free any memory because they don't take any arguments. o) mem_allocate() should be called in preference to Parrot_allocate() if your compiler restricts the number of syllables in function names. o) The difference between new_buffer_header() and new_tracked_header() is that new_tracked_header() has longstanding problems with intravenous drug abuse. [From the code, it seems like new_tracked_header() is for allocating *untracked* memory! Or are you expected to set flags and stuff it into something that will track it?] o) buffer_lives() is so named to avoid copyright infringement with the name of a Buffy the Vampire Slayer episode. [Seriously, why is this not static?] The various sorts of tracked memory form a small class hierarchy rooted at Buffer: Buffer STRING Other subclasses of Buffer can be created copying the fields of the Buffer struct into the very start of the subclass's struct. Alternatively, the subclass may just include a Buffer struct as its first field. [Are there any alignment considerations?] Oops. Just read some more code. The last paragraph is an evil deception, because if you make your own subclass, there will be no way to allocate it since new_buffer_header() only allocates enough space for the Buffer itself. There is a separate new_string_header() for allocating STRING structures. Or maybe that's exactly what new_tracked_header() is for? PMCs are not subclasses of Buffer because PMC stands for "Parrot Meaty Chunk" and m-w.com defines buffer as "something that serves as a protective barrier", so clearly a PMC is something that lacked a buffer at some unfortunate moment in its past. [Better hypothesis: PMCs are fixed-size allocations, Buffers are variable-sized?] To allocate a header for your memory, you will call one of: STRING *new_string_header(Interp *); Buffer *new_tracked_header(Interp *, size_t size); Buffer *new_buffer_header(Interp *); This will give a header to hang memory off of. To actually allocate a nonzero amount of memory to play with, use one of: void *Parrot_allocate(Interp *, void *, size_t size); void *Parrot_allocate_about(Interp *, void *, size_t size); The memory is now available under buffer->bufstart. If this call triggers a garbage collection, then your header will not be garbage collected if and only if you have changed the oil on your car at least once every 10,000 miles. Any Buffer or Buffer subclass must immediately have various flags set on it. The available flags are defined in string.h because if they were in resources.h bad people might find them. Currently, they are: BUFFER_private0_FLAG = 1<< 0, BUFFER_private1_FLAG = 1<< 1, BUFFER_private2_FLAG = 1<< 2, BUFFER_private3_FLAG = 1<< 3, BUFFER_private4_FLAG = 1<< 4, BUFFER_private5_FLAG = 1<< 5, BUFFER_private6_FLAG = 1<< 6, BUFFER_private7_FLAG = 1<< 7, BUFFER_immobile_FLAG = 1 << 8, BUFFER_external_FLAG = 1 << 9, BUFFER_sysmem_FLAG = 1 << 10, BUFFER_COW_FLAG = 1 << 11, BUFFER_live_FLAG = 1 << 12, BUFFER_needs_GC_FLAG = 1 << 13, BUFFER_on_free_list_FLAG = 1 << 14, BUFFER_constant_FLAG = 1 << 15 BUFFER_private*_FLAG is for the use of subclasses that might want to use their own flags. The remaining flags are described in pdd09_gc.pod. STRINGs use the same set of flags as Buffers because they *are* Buffers. PMCs also have a set of flags. They are defined in pdd09_gc.pod too. As a simple rule of thumb, if your Buffer's bufstart points to a complex data structure that contains a mixture of PMCs and Buffer pointers and other stuff, then in order to prevent things from getting garbage collected out from under you, you must perform the Blood Dance of the Pale Chicken under full moonlight. If your bufstart points to a simple array of possibly NULL PMC pointers, then it should be sufficient to set the PMC_is_PMC_ptr_FLAG and PMC_is_buffer_ptr_FLAG flags, which is a lot easier than explaining to a postal worker what you need all those chicken heads for. Unfortunately, this hypothesis is still insufficient for figuring out how to allocate memory for my hashtable objects. Is there some way to define my own piece of the "detect PMC pointers inside a Buffer" piece of the dead object detection? I'm not much of a Blood Dancer.