diff --git a/basis/stack-checker/known-words/known-words.factor b/basis/stack-checker/known-words/known-words.factor index 22ad8d2d72..0ef3976e62 100644 --- a/basis/stack-checker/known-words/known-words.factor +++ b/basis/stack-checker/known-words/known-words.factor @@ -1,4 +1,4 @@ -! Copyright (C) 2004, 2010 Slava Pestov, Daniel Ehrenberg. +! Copyright (C) 2004, 2011 Slava Pestov, Daniel Ehrenberg. ! See http://factorcode.org/license.txt for BSD license. USING: fry accessors alien alien.accessors alien.private arrays byte-arrays classes continuations.private effects generic @@ -10,7 +10,7 @@ quotations.private sbufs sbufs.private sequences sequences.private slots.private strings strings.private system threads.private classes.tuple classes.tuple.private vectors vectors.private words words.private definitions assocs summary -compiler.units system.private combinators +compiler.units system.private combinators tools.memory.private combinators.short-circuit locals locals.backend locals.types combinators.private stack-checker.values generic.single generic.single.private alien.libraries tools.dispatch.private @@ -348,13 +348,13 @@ M: object infer-call* \ call bad-macro-input ; \ callstack-for { c-ptr } { callstack } define-primitive \ callstack make-flushable \ callstack>array { callstack } { array } define-primitive \ callstack>array make-flushable \ check-datastack { array integer integer } { object } define-primitive \ check-datastack make-flushable -\ code-room { } { byte-array } define-primitive \ code-room make-flushable +\ (code-room) { } { byte-array } define-primitive \ (code-room) make-flushable \ compact-gc { } { } define-primitive \ compute-identity-hashcode { object } { } define-primitive \ context-object { fixnum } { object } define-primitive \ context-object make-flushable \ context-object-for { fixnum c-ptr } { object } define-primitive \ context-object-for make-flushable \ current-callback { } { fixnum } define-primitive \ current-callback make-flushable -\ data-room { } { byte-array } define-primitive \ data-room make-flushable +\ (data-room) { } { byte-array } define-primitive \ (data-room) make-flushable \ datastack { } { array } define-primitive \ datastack make-flushable \ datastack-for { c-ptr } { array } define-primitive \ datastack-for make-flushable \ die { } { } define-primitive diff --git a/basis/tools/memory/memory-docs.factor b/basis/tools/memory/memory-docs.factor index b18396538f..61c51dbf92 100644 --- a/basis/tools/memory/memory-docs.factor +++ b/basis/tools/memory/memory-docs.factor @@ -48,3 +48,11 @@ HELP: gc-summary. HELP: gc-events { $var-description "A sequence of " { $link gc-event } " instances, set by " { $link collect-gc-events } ". Can be inspected directly, or with the " { $link gc-events. } ", " { $link gc-stats. } " and " { $link gc-summary. } " words." } ; + +HELP: data-room +{ $values { "data-heap-room" data-heap-room } } +{ $description "Queries the VM for memory usage information." } ; + +HELP: code-room +{ $values { "mark-sweep-sizes" mark-sweep-sizes } } +{ $description "Queries the VM for memory usage information." } ; diff --git a/basis/tools/memory/memory.factor b/basis/tools/memory/memory.factor index 1f1a9876b5..04e8c47d4f 100644 --- a/basis/tools/memory/memory.factor +++ b/basis/tools/memory/memory.factor @@ -1,12 +1,11 @@ -! Copyright (C) 2005, 2010 Slava Pestov. +! Copyright (C) 2005, 2011 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. USING: accessors arrays assocs binary-search classes classes.struct combinators combinators.smart continuations fry generalizations generic grouping io io.styles kernel make math -math.order math.parser math.statistics memory memory.private -layouts namespaces parser prettyprint sequences -sequences.generalizations sorting splitting strings system vm -words hints hashtables ; +math.order math.parser math.statistics memory layouts namespaces +parser prettyprint sequences sequences.generalizations sorting +splitting strings system vm words hints hashtables ; IN: tools.memory +: data-room ( -- data-heap-room ) + (data-room) data-heap-room memory>struct ; + : data-room. ( -- ) "== Data heap ==" print nl - data-room data-heap-room memory>struct { + data-room { [ nursery-room. nl ] [ aging-room. nl ] [ tenured-room. nl ] @@ -286,9 +288,12 @@ INSTANCE: code-blocks immutable-sequence PRIVATE> +: code-room ( -- mark-sweep-sizes ) + (code-room) mark-sweep-sizes memory>struct ; + : code-room. ( -- ) "== Code heap ==" print nl - code-room mark-sweep-sizes memory>struct mark-sweep-table. nl + code-room mark-sweep-table. nl code-blocks code-block-stats code-block-table. ; : room. ( -- ) diff --git a/core/bootstrap/primitives.factor b/core/bootstrap/primitives.factor index 7ce47a0d97..bf2ec4defc 100755 --- a/core/bootstrap/primitives.factor +++ b/core/bootstrap/primitives.factor @@ -103,6 +103,7 @@ call( -- ) "system.private" "threads.private" "tools.dispatch.private" + "tools.memory.private" "tools.profiler.private" "words" "words.private" @@ -515,12 +516,12 @@ tuple { "float>bignum" "math.private" "primitive_float_to_bignum" (( x -- y )) } { "float>fixnum" "math.private" "primitive_float_to_fixnum" (( x -- y )) } { "all-instances" "memory" "primitive_all_instances" (( -- array )) } - { "(code-blocks)" "memory.private" "primitive_code_blocks" (( -- array )) } - { "code-room" "memory" "primitive_code_room" (( -- code-room )) } + { "(code-blocks)" "tools.memory.private" "primitive_code_blocks" (( -- array )) } + { "(code-room)" "tools.memory.private" "primitive_code_room" (( -- code-room )) } { "compact-gc" "memory" "primitive_compact_gc" (( -- )) } - { "data-room" "memory" "primitive_data_room" (( -- data-room )) } - { "disable-gc-events" "memory" "primitive_disable_gc_events" (( -- events )) } - { "enable-gc-events" "memory" "primitive_enable_gc_events" (( -- )) } + { "(data-room)" "tools.memory.private" "primitive_data_room" (( -- data-room )) } + { "disable-gc-events" "tools.memory.private" "primitive_disable_gc_events" (( -- events )) } + { "enable-gc-events" "tools.memory.private" "primitive_enable_gc_events" (( -- )) } { "gc" "memory" "primitive_full_gc" (( -- )) } { "minor-gc" "memory" "primitive_minor_gc" (( -- )) } { "size" "memory" "primitive_size" (( obj -- n )) } diff --git a/core/memory/memory-docs.factor b/core/memory/memory-docs.factor index acf187a33a..29ac20a14c 100644 --- a/core/memory/memory-docs.factor +++ b/core/memory/memory-docs.factor @@ -9,14 +9,6 @@ HELP: instances HELP: gc ( -- ) { $description "Performs a full garbage collection." } ; -HELP: data-room ( -- data-room ) -{ $values { "data-room" data-room } } -{ $description "Queries the VM for memory usage information." } ; - -HELP: code-room ( -- code-room ) -{ $values { "code-room" code-room } } -{ $description "Queries the VM for memory usage information." } ; - HELP: size ( obj -- n ) { $values { "obj" "an object" } { "n" "a size in bytes" } } { $description "Outputs the size of the object in memory, in bytes. Tagged immediate objects such as fixnums and " { $link f } " will yield a size of 0." } ; diff --git a/core/memory/memory-tests.factor b/core/memory/memory-tests.factor index 45e6090e77..74e002faa9 100644 --- a/core/memory/memory-tests.factor +++ b/core/memory/memory-tests.factor @@ -1,34 +1,56 @@ -USING: generic kernel kernel.private math memory prettyprint io -sequences tools.test words namespaces layouts classes -classes.builtin arrays quotations io.launcher system ; +USING: accessors kernel kernel.private math memory prettyprint +io sequences tools.test words namespaces layouts classes +classes.builtin arrays quotations system ; +FROM: tools.memory => data-room code-room ; IN: memory.tests +[ save-image-and-exit ] must-fail + +! Tests for 'instances' +[ [ ] instances ] must-infer +2 [ [ [ 3 throw ] instances ] must-fail ] times + +! Tests for 'become' [ ] [ { } { } become ] unit-test -! LOL -[ ] [ - vm - "-i=" image append - "-generations=2" - "-e=USING: memory io prettyprint system ; input-stream gc . 0 exit" - 4array try-process -] unit-test +! Bug found on Windows build box, having too many words in the +! image breaks 'become' +[ ] [ 100000 [ f ] replicate { } { } become drop ] unit-test -[ [ ] instances ] must-infer - -! Code GC wasn't kicking in when needed +! Bug: code heap collection had to be done when data heap was +! full, not just when code heap was full. If the code heap +! contained dead code blocks referring to large data heap +! objects, those large objects would continue to live on even +! if the code blocks were not reachable, as long as the code +! heap did not fill up. : leak-step ( -- ) 800000 f 1quotation call( -- obj ) drop ; : leak-loop ( -- ) 100 [ leak-step ] times ; [ ] [ leak-loop ] unit-test -TUPLE: testing x y z ; +! Bug: allocation of large objects directly into tenured space +! can proceed past the high water mark. +! +! Suppose the nursery and aging spaces are mostly comprised of +! reachable objects. When doing a full GC, objects from young +! generations ere promoted *before* unreachable objects in +! tenured space are freed by the sweep phase. So if large object +! allocation filled up the heap past the high water mark, this +! promotion might trigger heap growth, even if most of those +! large objects are unreachable. +SYMBOL: foo -[ save-image-and-exit ] must-fail +[ ] [ + gc -! Erg's bug -2 [ [ [ 3 throw ] instances ] must-fail ] times - -! Bug found on Windows build box, having too many words in the image breaks 'become' -[ ] [ 100000 [ f ] replicate { } { } become drop ] unit-test + data-room tenured>> size>> + + 10 [ + 4 [ 120 1024 * f ] replicate foo set-global + 100 [ 256 1024 * f drop ] times + ] times + + data-room tenured>> size>> + assert= +] unit-test diff --git a/extra/bunny/deploy.factor b/extra/bunny/deploy.factor index 1289caadb6..c7604c31bf 100644 --- a/extra/bunny/deploy.factor +++ b/extra/bunny/deploy.factor @@ -2,11 +2,13 @@ USING: tools.deploy.config ; H{ { deploy-name "Bunny" } { deploy-ui? t } + { deploy-help? f } { deploy-c-types? f } + { deploy-console? t } { deploy-unicode? f } { "stop-after-last-window?" t } { deploy-io 3 } - { deploy-reflection 2 } + { deploy-reflection 1 } { deploy-word-props? f } { deploy-math? t } { deploy-threads? t } diff --git a/extra/game/loop/benchmark/benchmark.factor b/extra/game/loop/benchmark/benchmark.factor new file mode 100644 index 0000000000..9e1b3fe915 --- /dev/null +++ b/extra/game/loop/benchmark/benchmark.factor @@ -0,0 +1,37 @@ +! (c)2009 Joe Groff bsd license +USING: accessors alien.c-types classes.struct game.loop +game.loop.private kernel sequences specialized-vectors +tools.time.struct ; +IN: game.loop.benchmark + +STRUCT: game-loop-benchmark + { benchmark-data-pair benchmark-data-pair } + { tick# ulonglong } + { frame# ulonglong } ; + +SPECIALIZED-VECTOR: game-loop-benchmark + +: ( benchmark-data-pair tick frame -- obj ) + \ game-loop-benchmark + swap >>frame# + swap >>tick# + swap >>benchmark-data-pair ; inline + +: ensure-benchmark-data ( loop -- vector ) + dup benchmark-data>> [ + game-loop-benchmark-vector{ } clone + >>benchmark-data + ] unless + benchmark-data>> ; inline + +M: game-loop record-benchmarking ( loop quot: ( loop -- benchmark-data-pair ) -- ) + [ + [ [ call( loop -- ) ] with-benchmarking ] + [ drop tick#>> ] + [ drop frame#>> ] + 2tri + + ] + [ drop ensure-benchmark-data ] + 2bi push ; + diff --git a/extra/game/loop/loop.factor b/extra/game/loop/loop.factor index fa9d17eb0f..164789f78a 100755 --- a/extra/game/loop/loop.factor +++ b/extra/game/loop/loop.factor @@ -1,9 +1,8 @@ ! (c)2009 Joe Groff bsd license USING: accessors timers alien.c-types calendar classes.struct continuations destructors fry kernel math math.order memory -namespaces sequences specialized-vectors system -ui ui.gadgets.worlds vm vocabs.loader arrays -tools.time.struct locals ; +namespaces sequences system ui ui.gadgets.worlds vm +vocabs.loader arrays locals ; IN: game.loop TUPLE: game-loop @@ -17,19 +16,6 @@ TUPLE: game-loop draw-timer benchmark-data ; -STRUCT: game-loop-benchmark - { benchmark-data-pair benchmark-data-pair } - { tick# ulonglong } - { frame# ulonglong } ; - -SPECIALIZED-VECTOR: game-loop-benchmark - -: ( benchmark-data-pair tick frame -- obj ) - \ game-loop-benchmark - swap >>frame# - swap >>tick# - swap >>benchmark-data-pair ; inline - GENERIC: tick* ( delegate -- ) GENERIC: draw* ( tick-slice delegate -- ) @@ -48,26 +34,24 @@ TUPLE: game-loop-error game-loop error ; > ] - [ frame#>> ] - [ benchmark-data>> ] tri push ; - : last-tick-percent-offset ( loop -- float ) [ draw-timer>> iteration-start-nanos>> nano-count swap - ] [ tick-interval-nanos>> ] bi /f 1.0 min ; +GENERIC# record-benchmarking 1 ( loop quot -- ) + +M: object record-benchmarking + call( loop -- ) ; + : redraw ( loop -- ) [ 1 + ] change-frame# [ [ last-tick-percent-offset ] [ draw-delegate>> ] bi - [ draw* ] with-benchmarking - ] keep record-benchmarking ; + draw* + ] record-benchmarking ; : tick ( loop -- ) - [ - [ tick-delegate>> tick* ] with-benchmarking - ] keep record-benchmarking ; + [ tick-delegate>> tick* ] record-benchmarking ; : increment-tick ( loop -- ) [ 1 + ] change-tick# @@ -105,9 +89,7 @@ PRIVATE> [ tick-timer>> ] [ draw-timer>> ] bi [ stop-timer ] bi@ ; : ( tick-interval-nanos tick-delegate draw-delegate -- loop ) - f 0 0 f f - game-loop-benchmark-vector{ } clone - game-loop boa ; + f 0 0 f f f game-loop boa ; : ( tick-interval-nanos delegate -- loop ) dup ; inline @@ -116,3 +98,4 @@ M: game-loop dispose stop-loop ; { "game.loop" "prettyprint" } "game.loop.prettyprint" require-when +{ "game.loop" "tools.memory" } "game.loop.benchmark" require-when diff --git a/extra/readline/ffi/platforms.txt b/extra/readline/ffi/platforms.txt new file mode 100644 index 0000000000..47e0a6946a --- /dev/null +++ b/extra/readline/ffi/platforms.txt @@ -0,0 +1 @@ +unix \ No newline at end of file diff --git a/extra/tools/time/struct/struct.factor b/extra/tools/time/struct/struct.factor index 1f63fc0528..022124cc12 100644 --- a/extra/tools/time/struct/struct.factor +++ b/extra/tools/time/struct/struct.factor @@ -1,7 +1,7 @@ ! Copyright (C) 2010 Doug Coleman. ! See http://factorcode.org/license.txt for BSD license. USING: accessors alien.c-types classes.struct kernel memory -system vm ; +tools.memory system vm ; IN: tools.time.struct STRUCT: benchmark-data diff --git a/vm/aging_collector.cpp b/vm/aging_collector.cpp index c747592f42..4bd1b81ec7 100644 --- a/vm/aging_collector.cpp +++ b/vm/aging_collector.cpp @@ -14,10 +14,8 @@ void factor_vm::collect_aging() /* Promote objects referenced from tenured space to tenured space, copy everything else to the aging semi-space, and reset the nursery pointer. */ { - /* Change the op so that if we fail here, we proceed to a full - tenured collection. We are collecting to tenured space, and - cards were unmarked, so we can't proceed with a to_tenured - collection. */ + /* Change the op so that if we fail here, an assertion will be + raised. */ current_gc->op = collect_to_tenured_op; to_tenured_collector collector(this); diff --git a/vm/compaction.cpp b/vm/compaction.cpp index 343a61b8ba..3d1ed89a47 100644 --- a/vm/compaction.cpp +++ b/vm/compaction.cpp @@ -330,14 +330,22 @@ void factor_vm::collect_compact(bool trace_contexts_p) { collect_mark_impl(trace_contexts_p); collect_compact_impl(trace_contexts_p); + + if(data->high_fragmentation_p()) + { + /* Compaction did not free up enough memory. Grow the heap. */ + set_current_gc_op(collect_growing_heap_op); + collect_growing_heap(0,trace_contexts_p); + } + code->flush_icache(); } -void factor_vm::collect_growing_heap(cell requested_bytes, bool trace_contexts_p) +void factor_vm::collect_growing_heap(cell requested_size, bool trace_contexts_p) { /* Grow the data heap and copy all live objects to the new heap. */ data_heap *old = data; - set_data_heap(data->grow(requested_bytes)); + set_data_heap(data->grow(requested_size)); collect_mark_impl(trace_contexts_p); collect_compact_code_impl(trace_contexts_p); code->flush_icache(); diff --git a/vm/data_heap.cpp b/vm/data_heap.cpp index 3648ba7f48..08f3e929ca 100755 --- a/vm/data_heap.cpp +++ b/vm/data_heap.cpp @@ -100,12 +100,12 @@ void data_heap::reset_generation(tenured_space *gen) bool data_heap::high_fragmentation_p() { - return (tenured->largest_free_block() <= nursery->size + aging->size); + return (tenured->largest_free_block() <= high_water_mark()); } bool data_heap::low_memory_p() { - return (tenured->free_space() <= nursery->size + aging->size); + return (tenured->free_space() <= high_water_mark()); } void data_heap::mark_all_cards() diff --git a/vm/data_heap.hpp b/vm/data_heap.hpp index cef43ef5fe..2be2cab93d 100755 --- a/vm/data_heap.hpp +++ b/vm/data_heap.hpp @@ -32,6 +32,9 @@ struct data_heap { bool high_fragmentation_p(); bool low_memory_p(); void mark_all_cards(); + cell high_water_mark() { + return nursery->size + aging->size; + } }; struct data_heap_room { diff --git a/vm/full_collector.cpp b/vm/full_collector.cpp index 852c058bd2..71f04cf6ce 100644 --- a/vm/full_collector.cpp +++ b/vm/full_collector.cpp @@ -112,11 +112,14 @@ void factor_vm::collect_full(bool trace_contexts_p) if(data->low_memory_p()) { + /* Full GC did not free up enough memory. Grow the heap. */ set_current_gc_op(collect_growing_heap_op); collect_growing_heap(0,trace_contexts_p); } else if(data->high_fragmentation_p()) { + /* Enough free memory, but it is not contiguous. Perform a + compaction. */ set_current_gc_op(collect_compact_op); collect_compact_impl(trace_contexts_p); } diff --git a/vm/gc.cpp b/vm/gc.cpp index 1bb339a70a..b1cdaa52af 100755 --- a/vm/gc.cpp +++ b/vm/gc.cpp @@ -116,19 +116,19 @@ void factor_vm::start_gc_again() switch(current_gc->op) { case collect_nursery_op: + /* Nursery collection can fail if aging does not have enough + free space to fit all live objects from nursery. */ current_gc->op = collect_aging_op; break; case collect_aging_op: + /* Aging collection can fail if the aging semispace cannot fit + all the live objects from the other aging semispace and the + nursery. */ current_gc->op = collect_to_tenured_op; break; - case collect_to_tenured_op: - current_gc->op = collect_full_op; - break; - case collect_full_op: - case collect_compact_op: - current_gc->op = collect_growing_heap_op; - break; default: + /* Nothing else should fail mid-collection due to insufficient + space in the target generation. */ critical_error("Bad GC op",current_gc->op); break; } @@ -143,15 +143,21 @@ void factor_vm::set_current_gc_op(gc_op op) if(gc_events) current_gc->event->op = op; } -void factor_vm::gc(gc_op op, cell requested_bytes, bool trace_contexts_p) +void factor_vm::gc(gc_op op, cell requested_size, bool trace_contexts_p) { assert(!gc_off); assert(!current_gc); + /* Important invariant: tenured space must have enough contiguous free + space to fit the entire contents of the aging space and nursery. This is + because when doing a full collection, objects from younger generations + are promoted before any unreachable tenured objects are freed. */ + assert(!data->high_fragmentation_p()); + current_gc = new gc_state(op,this); - /* Keep trying to GC higher and higher generations until we don't run out - of space */ + /* Keep trying to GC higher and higher generations until we don't run + out of space in the target generation. */ for(;;) { try @@ -164,17 +170,23 @@ void factor_vm::gc(gc_op op, cell requested_bytes, bool trace_contexts_p) collect_nursery(); break; case collect_aging_op: + /* We end up here if the above fails. */ collect_aging(); if(data->high_fragmentation_p()) { + /* Change GC op so that if we fail again, + we crash. */ set_current_gc_op(collect_full_op); collect_full(trace_contexts_p); } break; case collect_to_tenured_op: + /* We end up here if the above fails. */ collect_to_tenured(); if(data->high_fragmentation_p()) { + /* Change GC op so that if we fail again, + we crash. */ set_current_gc_op(collect_full_op); collect_full(trace_contexts_p); } @@ -186,7 +198,7 @@ void factor_vm::gc(gc_op op, cell requested_bytes, bool trace_contexts_p) collect_compact(trace_contexts_p); break; case collect_growing_heap_op: - collect_growing_heap(requested_bytes,trace_contexts_p); + collect_growing_heap(requested_size,trace_contexts_p); break; default: critical_error("Bad GC op",current_gc->op); @@ -197,7 +209,7 @@ void factor_vm::gc(gc_op op, cell requested_bytes, bool trace_contexts_p) } catch(const must_start_gc_again &) { - /* We come back here if a generation is full */ + /* We come back here if the target generation is full. */ start_gc_again(); continue; } @@ -207,6 +219,9 @@ void factor_vm::gc(gc_op op, cell requested_bytes, bool trace_contexts_p) delete current_gc; current_gc = NULL; + + /* Check the invariant again, just in case. */ + assert(!data->high_fragmentation_p()); } /* primitive_minor_gc() is invoked by inline GC checks, and it needs to fill in @@ -283,12 +298,13 @@ void factor_vm::primitive_compact_gc() object *factor_vm::allot_large_object(cell type, cell size) { /* If tenured space does not have enough room, collect and compact */ - if(!data->tenured->can_allot_p(size)) + cell requested_size = size + data->high_water_mark(); + if(!data->tenured->can_allot_p(requested_size)) { primitive_compact_gc(); /* If it still won't fit, grow the heap */ - if(!data->tenured->can_allot_p(size)) + if(!data->tenured->can_allot_p(requested_size)) { gc(collect_growing_heap_op, size, /* requested size */ diff --git a/vm/vm.hpp b/vm/vm.hpp index 9539ba04e1..28e9c8c1aa 100755 --- a/vm/vm.hpp +++ b/vm/vm.hpp @@ -314,8 +314,8 @@ struct factor_vm void collect_compact_impl(bool trace_contexts_p); void collect_compact_code_impl(bool trace_contexts_p); void collect_compact(bool trace_contexts_p); - void collect_growing_heap(cell requested_bytes, bool trace_contexts_p); - void gc(gc_op op, cell requested_bytes, bool trace_contexts_p); + void collect_growing_heap(cell requested_size, bool trace_contexts_p); + void gc(gc_op op, cell requested_size, bool trace_contexts_p); void scrub_context(context *ctx); void scrub_contexts(); void primitive_minor_gc();