root/trunk/orca/gc.c

Revision 154, 20.2 kB (checked in by krobillard, 2 years ago)

Orca - Replaced atom variables with fixed atom defines.

Line 
1/*============================================================================
2    ORCA Interpreter
3    Copyright (C) 2005-2006  Karl Robillard
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18===========================================================================*/
19
20
21#include "os.h"
22#include "ovalue.h"
23#include "internal.h"
24#include "orca_atoms.h"
25
26
27#ifdef DEBUG
28//#define GC_REPORT   1
29//#define GC_VERBOSE  1
30#endif
31
32//#define GC_TIME     1
33#ifdef GC_TIME
34#include <time.h>
35#endif
36
37
38/**
39  Excludes all current data from all future garbage collection.
40*/
41void orLockGarbageCollector()
42{
43    orEnv->freeBlocks.sweepStart  = orEnv->blocks.used;
44    orEnv->freeStrings.sweepStart = orEnv->strings.used;
45    orEnv->sweepStartAtom         = orEnv->atoms.used;
46}
47
48
49static OArray*   _abuf;
50static OArrayGC* _agc;
51
52static void _freeArrayX( OArray* arr )
53{
54    if( arr->buf )
55    {
56        memFree( arr->buf );
57        arr->buf  = 0;
58    }
59
60    // Flag as unused.
61    arr->avail = -1;
62
63    // Link to free list.
64    arr->used  = _agc->freeList;
65    _agc->freeList = arr - _abuf;
66
67    _agc->count += 1;
68}
69
70
71/**
72  Do not call orFreeBlock() from inside garbage collector.
73*/
74void orFreeBlock( OIndex blkN )
75{
76    _agc  = &orEnv->freeBlocks;
77    _abuf = (OArray*) orEnv->blocks.buf;
78    _freeArrayX( _abuf + blkN );
79}
80
81
82#define FREE_ARRAY(it) \
83    tmp = it; \
84    if( tmp->avail > -1 ) \
85        _freeArrayX( tmp );
86
87static void orSweepArray( OArray* mark, OArrayGC* af, OArray* arr )
88{
89    int start = af->sweepStart;
90    OArray* tmp;
91    OArray* ait = ((OArray*) arr->buf) + start;
92    uint8_t* it  = mark->byteArray;
93    uint8_t* end = it + mark->used;
94    int count = arr->used - start;
95
96
97    _abuf = (OArray*) arr->buf;
98    _agc  = af;
99
100    // Mark padding bits at end as used.
101    if( count & 7 )
102        end[-1] |= 0xff << (count & 7);
103
104
105    // Check mark bitset for unused elements.
106    while( it != end )
107    {
108        if( *it != 0xff )
109        {
110            int mask = *it;
111
112#if 0
113            dprint( "orSweepArray %p start: %d elem: %d 0x%02x\n",
114                    arr->buf, start, ait - ((OArray*) arr->buf), mask );
115#endif
116
117            if( (mask & 0x0f) != 0x0f )
118            {
119                if( (mask & 0x01) == 0 ) { FREE_ARRAY( ait ) }
120                if( (mask & 0x02) == 0 ) { FREE_ARRAY( ait + 1 ) }
121                if( (mask & 0x04) == 0 ) { FREE_ARRAY( ait + 2 ) }
122                if( (mask & 0x08) == 0 ) { FREE_ARRAY( ait + 3 ) }
123            }
124
125            if( (mask & 0xf0) != 0xf0 )
126            {
127                if( (mask & 0x10) == 0 ) { FREE_ARRAY( ait + 4 ) }
128                if( (mask & 0x20) == 0 ) { FREE_ARRAY( ait + 5 ) }
129                if( (mask & 0x40) == 0 ) { FREE_ARRAY( ait + 6 ) }
130                if( (mask & 0x80) == 0 ) { FREE_ARRAY( ait + 7 ) }
131            }
132        }
133        ++it;
134        ait += 8;
135    }
136
137
138#if 0
139    // Move the top of the array down.
140    ait = ((OArray*) arr->buf) + arr->used - 1;
141    while( arr->used && (ait->avail == -1) )
142    {
143        --ait;
144        --arr->used;
145        *_freeCount -= 1;
146    }
147#endif
148}
149
150
151
152static OBinary bsBlk;
153static OBinary bsStr;
154static OBinary blkChecked;
155
156#ifdef OR_CONFIG_NUMBER_ARRAYS
157static OBinary bsDecArr;
158static OBinary bsIntArr;
159#endif
160
161
162static void checkBlock( OBlock* blk )
163{
164    int idx;
165    OValue* it  = blk->values;
166    OValue* end = it + blk->used;
167
168#ifdef GC_VERBOSE
169    printf( "GC checkBlock %ld\n", orBlockN(blk) );
170#endif
171
172    if( ! it )
173        return;
174
175    while( it != end )
176    {
177        switch( it->type )
178        {
179            case OT_WORD:
180            case OT_SETWORD:
181            //case OT_LITWORD:      // Only atom is used in lit-words?
182                idx = it->word.wordBlk - orEnv->freeBlocks.sweepStart;
183                if( idx >= 0 )
184                    orSetBit( bsBlk.byteArray, idx );
185
186                idx = it->word.context - orEnv->freeBlocks.sweepStart;
187                if( idx >= 0 )
188                    orSetBit( bsBlk.byteArray, idx );
189                break;
190
191            case OT_STRING:
192            case OT_FILE:
193            case OT_ISSUE:
194            case OT_TAG:
195            case OT_BINARY:
196            case OT_BITSET:
197            case OT_MATRIX:
198                idx = it->index - orEnv->freeStrings.sweepStart;
199                if( idx >= 0 )
200                    orSetBit( bsStr.byteArray, idx );
201                break;
202
203#ifdef OR_CONFIG_NUMBER_ARRAYS
204            case OT_DEC_ARRAY:
205                idx = it->index - orEnv->freeDecArr.sweepStart;
206                if( idx >= 0 )
207                    orSetBit( bsDecArr.byteArray, idx );
208                break;
209
210            case OT_INT_ARRAY:
211                idx = it->index - orEnv->freeIntArr.sweepStart;
212                if( idx >= 0 )
213                    orSetBit( bsIntArr.byteArray, idx );
214                break;
215#endif
216
217            case OT_ERROR:
218                if( orErrorType(it) != OR_ERROR_THROW )
219                {
220                    idx = it->error.msg - orEnv->freeStrings.sweepStart;
221                    if( idx >= 0 )
222                        orSetBit( bsStr.byteArray, idx );
223                }
224
225                idx = it->error.block - orEnv->freeBlocks.sweepStart;
226                if( idx >= 0 )
227                    orSetBit( bsBlk.byteArray, idx );
228                break;
229
230            case OT_OBJECT:
231            case OT_PORT:
232                idx = it->ctx.wblkN - orEnv->freeBlocks.sweepStart;
233                if( idx >= 0 )
234                {
235                    // Word block only contains words so block is marked
236                    // as checked.
237                    int ai   = idx >> 3;
238                    int bitm = 1 << (idx & 7);
239                    bsBlk.byteArray[ ai ] |= bitm;
240                    blkChecked.byteArray[ ai ] |= bitm;
241                }
242
243                idx = it->ctx.vblkN - orEnv->freeBlocks.sweepStart;
244                if( idx >= 0 )
245                    orSetBit( bsBlk.byteArray, idx );
246                break;
247
248            case OT_FUNCTION:
249                idx = it->func.specBlk - orEnv->freeBlocks.sweepStart;
250                if( idx >= 0 )
251                    orSetBit( bsBlk.byteArray, idx );
252
253                idx = it->func.bodyBlk - orEnv->freeBlocks.sweepStart;
254                if( idx >= 0 )
255                    orSetBit( bsBlk.byteArray, idx );
256
257                idx = it->func.context - orEnv->freeBlocks.sweepStart;
258                if( idx >= 0 )
259                {
260                    orSetBit( bsBlk.byteArray, idx );
261                }
262                else if( it->func.context )
263                {
264                    // Must check contents of argument block even if below
265                    // sweepStart.
266                    // During recursion this will waste some time
267                    // rechecking stack values.
268                    checkBlock( orBLOCKS + it->func.context );
269                }
270                break;
271
272            case OT_LIST:
273            case OT_BLOCK:
274            case OT_PAREN:
275                idx = it->index - orEnv->freeBlocks.sweepStart;
276                if( idx >= 0 )
277                {
278                    int ai   = idx >> 3;
279                    int bitm = 1 << (idx & 7);
280                    bsBlk.byteArray[ ai ] |= bitm;
281                    if( ! (blkChecked.byteArray[ ai ] & bitm) )
282                    {
283                        blkChecked.byteArray[ ai ] |= bitm;
284                        checkBlock( orBLOCKS + it->index );
285                    }
286                }
287                break;
288
289            case OT_PATH:
290            case OT_SETPATH:
291            case OT_LITPATH:
292                idx = it->index - orEnv->freeBlocks.sweepStart;
293                if( idx >= 0 )
294                    orSetBit( bsBlk.byteArray, idx );
295                break;
296        }
297        ++it;
298    }
299}
300
301
302static void checkHolds( OHold* it, OHold* end )
303{
304    int idx;
305    while( it != end )
306    {
307        switch( it->dataType )
308        {
309            case OT_BLOCK:
310            case OT_PAREN:
311            case OT_PATH:
312            case OT_SETPATH:
313            case OT_LIST:
314                idx = it->which - orEnv->freeBlocks.sweepStart;
315                if( idx >= 0 )
316                    orSetBit( bsBlk.byteArray, idx );
317                break;
318
319            case OT_STRING:
320            case OT_FILE:
321            case OT_ISSUE:
322            case OT_TAG:
323            case OT_BINARY:
324            case OT_BITSET:
325            case OT_MATRIX:
326                idx = it->which - orEnv->freeStrings.sweepStart;
327                if( idx >= 0 )
328                    orSetBit( bsStr.byteArray, idx );
329                break;
330
331#ifdef OR_CONFIG_NUMBER_ARRAYS
332            case OT_DEC_ARRAY:
333                idx = it->which - orEnv->freeDecArr.sweepStart;
334                if( idx >= 0 )
335                    orSetBit( bsDecArr.byteArray, idx );
336                break;
337
338            case OT_INT_ARRAY:
339                idx = it->which - orEnv->freeIntArr.sweepStart;
340                if( idx >= 0 )
341                    orSetBit( bsIntArr.byteArray, idx );
342                break;
343#endif
344
345            case OT_UNSET:
346                break;
347
348            case OT_OBJECT:
349            case OT_PORT:
350            default:
351                assert( 0 && "Reference type not handled" );
352                break;
353        }
354        ++it;
355    }
356}
357
358
359/*
360   Check selected parts of system object.
361   This will be needed as long as orLockGarbageCollector() is used.
362*/
363static void checkSystem()
364{
365    OValue* val;
366
367    val = orLookupPath( OT_WORD, OR_ATOM_SYSTEM,
368                        OT_WORD, OR_ATOM_SCRIPT,
369                        OR_LPATH_END );
370    if( val && orIs(val, OT_OBJECT ) )
371    {
372        checkBlock( orBlockPtr( val->ctx.vblkN ) );
373    }
374}
375
376
377/*
378   Mark values in OC_SETPATH CallRecords as used.
379*/
380static void checkCallStack()
381{
382    CallRecord* start;
383    CallRecord* it;
384    OValue* val;
385    OArray* call;
386    int idx;
387
388    call = &orEnv->callStack;
389    if( call->used > 2 )
390    {
391        start = (CallRecord*) call->buf;
392        it = start + (call->used - 1);
393
394        while( it != start )
395        {
396            if( it->opcode == OC_SETPATH )
397            {
398                it -= 2;
399                val = (OValue*) it;
400                switch( val->type )
401                {
402                    case OT_LIST:
403                    case OT_BLOCK:
404                    case OT_PAREN:
405                    case OT_PATH:
406                    case OT_SETPATH:
407                    case OT_LITPATH:
408                        idx = val->index - orEnv->freeBlocks.sweepStart;
409                        if( idx >= 0 )
410                            orSetBit( bsBlk.byteArray, idx );
411                        break;
412
413                    case OT_STRING:
414                    case OT_FILE:
415                    case OT_ISSUE:
416                    case OT_TAG:
417                    case OT_BINARY:
418                    case OT_BITSET:
419                    case OT_MATRIX:
420                        idx = val->index - orEnv->freeStrings.sweepStart;
421                        if( idx >= 0 )
422                            orSetBit( bsStr.byteArray, idx );
423                        break;
424
425#ifdef OR_CONFIG_NUMBER_ARRAYS
426                    case OT_DEC_ARRAY:
427                        idx = val->index - orEnv->freeDecArr.sweepStart;
428                        if( idx >= 0 )
429                            orSetBit( bsDecArr.byteArray, idx );
430                        break;
431
432                    case OT_INT_ARRAY:
433                        idx = val->index - orEnv->freeIntArr.sweepStart;
434                        if( idx >= 0 )
435                            orSetBit( bsIntArr.byteArray, idx );
436                        break;
437#endif
438                    case OT_WORD:
439                        idx = val->word.wordBlk - orEnv->freeBlocks.sweepStart;
440                        if( idx >= 0 )
441                            orSetBit( bsBlk.byteArray, idx );
442
443                        idx = val->word.context - orEnv->freeBlocks.sweepStart;
444                        if( idx >= 0 )
445                            orSetBit( bsBlk.byteArray, idx );
446                        break;
447
448                    default:
449                        assert( 0 && "set-path type not series! or word!" );
450                        break;
451                }
452            }
453            --it;
454        }
455    }
456}
457
458
459#ifdef DEBUG
460void orShowGC()
461{
462    dprint( "  Array    Buf       Max Used  Free\n" );
463    dprint( "  ---------------------------------\n" );
464    dprint( "  Blocks   %p   %4d   %4d\n", orEnv->blocks.buf,
465                                           orEnv->blocks.used,
466                                           orEnv->freeBlocks.count );
467    dprint( "  Strings  %p   %4d   %4d\n", orEnv->strings.buf,
468                                           orEnv->strings.used,
469                                           orEnv->freeStrings.count );
470}
471#endif
472
473
474/**
475  Perform garbage collection.
476
477  A tracing mark-sweep collector.
478  Starts with global context & data stack and only traces used values.
479*/
480void orCollectGarbage()
481{
482    int byteSize;
483    uint8_t* markset;
484    uint8_t* doneset;
485    int i;
486    int bitm;
487    int ai;
488    OBlock* blk;
489    int blkCount;
490
491#ifdef GC_TIME
492    clock_t t1, t1e;
493#endif
494
495#define GC_BITSET(bin,arr,offset) \
496    byteSize = arr.used - offset; \
497    byteSize = (byteSize + 7) / 8; \
498    orArrayInit(&bin, sizeof(uint8_t), byteSize); \
499    bin.used = byteSize; \
500    memSet(bin.buf, 0, bin.avail)
501
502
503#ifdef GC_REPORT
504    int pass = 0;
505    dprint( "\norCollectGarbage() Report:\n\n" );
506    orShowGC();
507#endif
508
509#ifdef GC_TIME
510    t1 = clock();
511#endif
512
513    GC_BITSET( bsBlk, orEnv->blocks,     orEnv->freeBlocks.sweepStart );
514    GC_BITSET( bsStr, orEnv->strings,    orEnv->freeStrings.sweepStart );
515
516    GC_BITSET( blkChecked, orEnv->blocks,   orEnv->freeBlocks.sweepStart );
517
518#ifdef OR_CONFIG_NUMBER_ARRAYS
519    GC_BITSET( bsDecArr, orEnv->decArr,     orEnv->freeDecArr.sweepStart );
520    GC_BITSET( bsIntArr, orEnv->intArr,     orEnv->freeIntArr.sweepStart );
521#endif
522
523    // Mark all complex values used.
524    // Start by checking the data stack, global context and any blocks
525    // currently being referenced.
526
527    // Make sure there is no unhandled error.
528    assert( orEnv->error == 0 );
529
530
531    // Enable this if orLockGarbageCollector() is removed.
532    //orSetBit( bsStr.byteArray, BIN_ATOM_NAMES );
533
534    blk = (OBlock*) orEnv->blocks.buf;
535
536    checkBlock( &orEnv->dataStack );
537    checkBlock( blk + GLOBAL_CTXN );
538
539    checkSystem();
540    checkCallStack();
541
542    checkHolds( orEnv->quickHolds,
543                orEnv->quickHolds + orEnv->quickHoldsUsed );
544
545    if( orEnv->holds.used )
546    {
547        OHold* hold;
548        hold = (OHold*) orEnv->holds.buf;
549        checkHolds( hold, hold + orEnv->holds.used );
550    }
551
552
553    // Blocks and objects may refer to other blocks and objects so we must
554    // check until no unchecked items are found.
555
556    do
557    {
558        blkCount = 0;
559
560        // Mark complex values used in blocks.
561        {
562        OBlock* bit;
563        OBlock* bend;
564
565        i = 0;
566        markset = bsBlk.byteArray;
567        doneset = blkChecked.byteArray;
568
569        bit  = blk + orEnv->freeBlocks.sweepStart;
570        bend = blk + orEnv->blocks.used;
571
572        while( bit != bend )
573        {
574            ai   = i >> 3;
575            bitm = 1 << (i & 7);
576            if( markset[ ai ] & bitm )              // orBitIsSet(markset, i)
577            {
578                if( ! (doneset[ ai ] & bitm) )      // orBitIsSet(doneset, i)
579                {
580                    doneset[ ai ] |= bitm;          // orSetBit(doneset, i)
581                    ++blkCount;
582                    checkBlock( bit );
583                }
584            }
585            ++bit;
586            ++i;
587        }
588        }
589
590#ifdef GC_REPORT
591        dprint( "GC blocks checked: %d\n", blkCount );
592        ++pass;
593#endif
594    }
595    while( blkCount );
596
597
598    orSweepArray( &bsBlk, &orEnv->freeBlocks,  &orEnv->blocks );
599    orSweepArray( &bsStr, &orEnv->freeStrings, &orEnv->strings );
600
601#ifdef OR_CONFIG_NUMBER_ARRAYS
602    orSweepArray( &bsDecArr, &orEnv->freeDecArr, &orEnv->decArr );
603    orSweepArray( &bsIntArr, &orEnv->freeIntArr, &orEnv->intArr );
604
605    orArrayFree( &bsDecArr );
606    orArrayFree( &bsIntArr );
607#endif
608
609    orArrayFree( &blkChecked );
610
611    orArrayFree( &bsBlk );
612    orArrayFree( &bsStr );
613
614#ifdef GC_TIME
615    t1e = clock() - t1;
616    printf( "gc seconds: %g\n", ((double) t1e) / CLOCKS_PER_SEC );
617#endif
618
619#ifdef GC_REPORT
620    dprint( "\n  %d passes\n", pass );
621    orShowGC();
622#endif
623}
624
625
626/*--------------------------------------------------------------------------*/
627
628
629/**
630  Creates a hold on a complex value so that it will not be garbage collected.
631  Returns a handle to the hold.
632*/
633OIndex orHold( int type, OIndex which )
634{
635    OIndex index;
636    OHold* hold;
637    OArray* arr = &orEnv->holds;
638
639    if( orEnv->freeHolds.count )
640    {
641        OArrayGC* gc = &orEnv->freeHolds;
642
643        index = gc->freeList;
644        hold = ((OHold*) arr->buf) + index;
645
646        assert( gc->freeList > -1 );
647        assert( hold->dataType == OT_UNSET );
648
649        gc->freeList = hold->which;
650        gc->count--;
651    }
652    else
653    {
654        index = arr->used;
655        OA_EXPAND1( OHold, arr, hold );
656    }
657
658    //hold->refCount = 1;        // refCount is currently unused.
659    hold->dataType = type;
660    hold->which    = which;
661
662    return index;
663}
664
665
666OIndex orHoldIndex( OIndex holdIndex )
667{
668    OHold* hold = ((OHold*) orEnv->holds.buf) + holdIndex;
669    return hold->which;
670}
671
672
673/**
674  Releases a hold created with orHold().
675*/
676void orRelease( OIndex holdIndex )
677{
678    OHold* hold;
679    OArrayGC* gc;
680
681    hold = ((OHold*) orEnv->holds.buf) + holdIndex;
682
683    // Flag as unused.
684    hold->dataType = OT_UNSET;
685
686    // Link to free list.
687    gc = &orEnv->freeHolds;
688    hold->which = gc->freeList;
689    gc->freeList = holdIndex;
690
691    gc->count++;
692}
693
694
695/*--------------------------------------------------------------------------*/
696
697
698#ifdef TRACK_MALLOC
699typedef struct OListNode OListNode;
700
701struct OListNode
702{
703    OListNode*  prev;
704    OListNode*  next;
705};
706
707typedef struct
708{
709    OListNode   head;
710    OListNode   tail;
711}
712OList;
713
714static void _listAppend( OListNode* node, OListNode* after )
715{
716    node->prev = after;
717    node->next = after->next;
718    after->next->prev = node;
719    after->next = node;
720}
721
722static void _listRemove( OListNode* node )
723{
724    node->prev->next = node->next;
725    node->next->prev = node->prev;
726    node->prev = node->next = 0;
727}
728
729
730typedef struct
731{
732    OListNode link;
733    void* ptr;
734    size_t size;
735}
736MallocRecord;
737
738
739static OList _list = { {0,&_list.tail}, {&_list.head,0} };
740
741
742void* memAlloc( size_t size )
743{
744    MallocRecord* rec;
745    void* ptr = malloc( size );
746
747    if( ptr )
748    {
749        // Add ptr to list.
750
751        rec = (MallocRecord*) malloc( sizeof(MallocRecord) );
752        assert( rec );
753        rec->ptr  = ptr;
754        rec->size = size;
755        _listAppend( &rec->link, &_list.head );
756    }
757
758    return ptr;
759}
760
761
762void memFree( void* ptr )
763{
764    OListNode* it;
765    MallocRecord* rec;
766
767    // Remove ptr from list.
768
769    rec = 0;
770    it = _list.head.next;
771    while( it->next )
772    {
773        if( ((MallocRecord*) it)->ptr == ptr )
774        {
775            rec = (MallocRecord*) it;
776            break;
777        }
778        it = it->next;
779    }
780
781    if( rec )
782    {
783        free( rec->ptr );
784        _listRemove( &rec->link );
785        free( rec );
786    }
787    else
788    {
789        cprint( "memFree - %p was not allocted with memAlloc!\n", ptr );
790    }
791}
792
793
794void memReport( int verbose )
795{
796    size_t total;
797    int count;
798    OListNode* it;
799    MallocRecord* rec;
800
801    cprint( "memReport:\n" );
802
803    count = total = 0;
804    it = _list.head.next;
805    while( it->next )
806    {
807        rec = (MallocRecord*) it;
808
809        total += rec->size;
810        ++count;
811
812        if( verbose )
813            cprint( "  %d bytes at %p\n", rec->size, rec->ptr );
814
815        it = it->next;
816    }
817
818    cprint( "  %d allocations\n  %d bytes\n", count, total );
819}
820
821
822OR_NATIVE_PUB( orMemoryNative )
823{
824    (void) a1;
825    memReport( 0 );
826}
827#endif
828
829
830/*EOF*/
Note: See TracBrowser for help on using the browser.