20,741
edits
m (→Pool storage) |
|||
| Line 3: | Line 3: | ||
= NaRef = | = NaRef = | ||
= Pool storage = | |||
A memory pool is basically a preallocated region of memory | |||
The interesting part is the naGC_get() function which isn't directly mapped to naAlloc() or malloc(), but instead manages the Nasal memory pools. | |||
For each native Nasal data type (scalars, vectors, hashes, funcs etc) , there are separate memory pools in globals->pools[n]. The index (n) is one of T_VEC, T_HASH, T_FUNC etc. | |||
== Nasal memory pools == | |||
The layout of the Nasal memory structure pool can be found in data.h, line 172: https://gitorious.org/fg/simgear/blobs/next/simgear/nasal/data.h#line172 | |||
<syntaxhighlight lang="C"> | |||
struct naPool { | |||
int type; | |||
int elemsz; | |||
struct Block* blocks; | |||
void** free0; // pointer to the alloced buffer | |||
int freesz; // size of the alloced buffer | |||
void** free; // current "free frame" | |||
int nfree; // down-counting index within the free frame | |||
int freetop; // curr. top of the free list | |||
}; | |||
</syntaxhighlight> | |||
For each of the 7 Nasal data types, there is a separate storage pool available (0..6). Each storage pool is addressed by its enum index: https://gitorious.org/fg/simgear/blobs/next/simgear/nasal/data.h#line65 | |||
<syntaxhighlight lang="C"> | |||
enum { T_STR, T_VEC, T_HASH, T_CODE, T_FUNC, T_CCODE, T_GHOST, NUM_NASAL_TYPES }; // V. important that this come last! | |||
</syntaxhighlight> | |||
For example, Globals->pools[T_VEC] refers to the storage pools for vectors: | |||
<syntaxhighlight lang="C"> | |||
Globals->pools[T_VEC]; | |||
</syntaxhighlight> | |||
The 7 storage pools are declared in code.h as part of the "Globals" structure, line 39: https://gitorious.org/fg/simgear/blobs/next/simgear/nasal/code.h#line39 | |||
<syntaxhighlight lang="C"> | |||
struct Globals { | |||
// Garbage collecting allocators: | |||
struct naPool pools[NUM_NASAL_TYPES]; | |||
int allocCount; | |||
// Dead blocks waiting to be freed when it is safe | |||
void** deadBlocks; | |||
int deadsz; | |||
int ndead; | |||
// Threading stuff | |||
int nThreads; | |||
int waitCount; | |||
int needGC; | |||
int bottleneck; | |||
void* sem; | |||
void* lock; | |||
// Constants | |||
naRef meRef; | |||
naRef argRef; | |||
naRef parentsRef; | |||
// A hash of symbol names | |||
naRef symbols; | |||
naRef save; | |||
struct Context* freeContexts; | |||
struct Context* allContexts; | |||
}; | |||
</syntaxhighlight> | |||
Each memory pool must be initialized using naGCInit(): https://gitorious.org/fg/simgear/blobs/next/simgear/nasal/gc.c#line175 | |||
<syntaxhighlight lang="C"> | |||
void naGC_init(struct naPool* p, int type) | |||
{ | |||
p->type = type; | |||
p->elemsz = naTypeSize(type); | |||
p->blocks = 0; | |||
p->free0 = p->free = 0; | |||
p->nfree = p->freesz = p->freetop = 0; | |||
reap(p); | |||
} | |||
</syntaxhighlight> | |||
The size of a memory pool is determined using poolsize(): https://gitorious.org/fg/simgear/blobs/next/simgear/nasal/gc.c#line186 | |||
<syntaxhighlight lang="C"> | |||
static int poolsize(struct naPool* p) | |||
{ | |||
int total = 0; | |||
struct Block* b = p->blocks; | |||
while(b) { total += b->size; b = b->next; } | |||
return total; | |||
} | |||
</syntaxhighlight> | |||
== The pool manager == | |||
All high level type allocators (naNewString, naNewVector, naNewHash etc) make use of the naNew() call introduced earlier. | |||
naNew() doesn't directly allocate new memory from the heap, but instead use the naGC_get() helper which manages pool memory. | |||
The naGC_get() pool "manager" is located in gc.nas line 194: https://gitorious.org/fg/simgear/blobs/next/simgear/nasal/gc.c#line194 | |||
<syntaxhighlight lang="C"> | |||
struct naObj** naGC_get(struct naPool* p, int n, int* nout) | |||
{ | |||
struct naObj** result; | |||
naCheckBottleneck(); | |||
LOCK(); | |||
while(globals->allocCount < 0 || (p->nfree == 0 && p->freetop >= p->freesz)) { | |||
globals->needGC = 1; | |||
bottleneck(); | |||
} | |||
if(p->nfree == 0) | |||
newBlock(p, poolsize(p)/8); | |||
n = p->nfree < n ? p->nfree : n; | |||
*nout = n; | |||
p->nfree -= n; | |||
globals->allocCount -= n; | |||
result = (struct naObj**)(p->free + p->nfree); | |||
UNLOCK(); | |||
return result; | |||
} | |||
</syntaxhighlight> | |||
As can be seen, the garbage collector itself is triggered by setting the global "needGC" flag to 1 in the globals structure and then calling the bottleneck() function: [https://gitorious.org/fg/simgear/blobs/next/simgear/nasal/gc.c#line199]. | |||
In line 209, the memory region from the global pool is cast back to a pointer (struct naObj**): https://gitorious.org/fg/simgear/blobs/next/simgear/nasal/gc.c#line209 | |||
the "nout" paramter is just there to update c->nfree[type] with the amount of free memory. | |||
== Memory blocks == | |||
A memory block is a singly linked list: https://gitorious.org/fg/simgear/blobs/next/simgear/nasal/gc.c#line10 | |||
<syntaxhighlight lang="C"> | |||
struct Block { | |||
int size; | |||
char* block; | |||
struct Block* next; | |||
}; | |||
</syntaxhighlight> | |||
New memory blocks are allocated using newBlock(): https://gitorious.org/fg/simgear/blobs/next/simgear/nasal/gc.c#line150 | |||
<syntaxhighlight lang="C"> | |||
static void newBlock(struct naPool* p, int need) | |||
{ | |||
int i; | |||
struct Block* newb; | |||
if(need < MIN_BLOCK_SIZE) need = MIN_BLOCK_SIZE; | |||
newb = naAlloc(sizeof(struct Block)); | |||
newb->block = naAlloc(need * p->elemsz); | |||
newb->size = need; | |||
newb->next = p->blocks; | |||
p->blocks = newb; | |||
naBZero(newb->block, need * p->elemsz); | |||
if(need > p->freesz - p->freetop) need = p->freesz - p->freetop; | |||
p->nfree = 0; | |||
p->free = p->free0 + p->freetop; | |||
for(i=0; i < need; i++) { | |||
struct naObj* o = (struct naObj*)(newb->block + i*p->elemsz); | |||
o->mark = 0; | |||
p->free[p->nfree++] = o; | |||
} | |||
p->freetop += need; | |||
} | |||
</syntaxhighlight> | |||
All core memory allocation takes place via wrappers in [https://gitorious.org/fg/simgear/blobs/next/simgear/nasal/misc.c#line8 misc.c]: | |||
<syntaxhighlight lang="C"> | |||
void naFree(void* m) { free(m); } | |||
void* naAlloc(int n) { return malloc(n); } | |||
void* naRealloc(void* b, int n) { return realloc(b, n); } | |||
void naBZero(void* m, int n) { memset(m, 0, n); } | |||
</syntaxhighlight> | |||
= Allocating Nasal types = | = Allocating Nasal types = | ||