|
|
| Line 92: |
Line 92: |
| return r; | | return r; |
| } | | } |
| </syntaxhighlight>
| |
|
| |
| = 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> | | </syntaxhighlight> |
|
| |
|