2016-05-03 17:15:17 +00:00
//: Reclaiming memory when it's no longer used.
: ( scenario new_reclaim )
def main [
2018-06-24 16:16:17 +00:00
10 : & : num < - new number : type
20 : num < - deaddress 10 : & : num
abandon 10 : & : num
30 : & : num < - new number : type # must be same size as abandoned memory to reuse
40 : num < - deaddress 30 : & : num
50 : bool < - equal 20 : num , 40 : num
2016-05-03 17:15:17 +00:00
]
# both allocations should have returned the same address
2018-06-24 16:16:17 +00:00
+ mem : storing 1 in location 50
2016-05-03 17:15:17 +00:00
//: When abandoning addresses we'll save them to a 'free list', segregated by size.
2018-06-24 17:23:27 +00:00
//: Before, suppose variable V contains address A which points to payload P:
//: location V contains an alloc-id N
//: location V+1 contains A
//: location A contains alloc-id N
//: location A+1 onwards contains P
//: Additionally, suppose the head of the free list is initially F.
//: After abandoning:
//: location V contains invalid alloc-id -1
//: location V+1 contains 0
//: location A contains invalid alloc-id N
//: location A+1 contains the previous head of free-list F
2016-05-03 17:15:17 +00:00
: ( before " End routine Fields " )
map < int , int > free_list ;
2018-01-03 08:31:10 +00:00
: ( before " End Primitive Recipe Declarations " )
ABANDON ,
: ( before " End Primitive Recipe Numbers " )
put ( Recipe_ordinal , " abandon " , ABANDON ) ;
: ( before " End Primitive Recipe Checks " )
case ABANDON : {
if ( ! inst . products . empty ( ) ) {
raise < < maybe ( get ( Recipe , r ) . name ) < < " 'abandon' shouldn't write to any products in ' " < < to_original_string ( inst ) < < " ' \n " < < end ( ) ;
break ;
}
for ( int i = 0 ; i < SIZE ( inst . ingredients ) ; + + i ) {
if ( ! is_mu_address ( inst . ingredients . at ( i ) ) )
raise < < maybe ( get ( Recipe , r ) . name ) < < " ingredients of 'abandon' should be addresses, but ingredient " < < i < < " is ' " < < to_string ( inst . ingredients . at ( i ) ) < < ' \n ' < < end ( ) ;
break ;
2016-05-18 01:32:41 +00:00
}
2018-01-03 08:31:10 +00:00
break ;
}
: ( before " End Primitive Recipe Implementations " )
case ABANDON : {
for ( int i = 0 ; i < SIZE ( current_instruction ( ) . ingredients ) ; + + i ) {
reagent /*copy*/ ingredient = current_instruction ( ) . ingredients . at ( i ) ;
canonize ( ingredient ) ;
2018-06-24 16:16:17 +00:00
abandon ( get_or_insert ( Memory , ingredient . value + /*skip alloc id*/ 1 ) , payload_size ( ingredient ) ) ;
2018-06-24 17:23:27 +00:00
//? cerr << "clear after abandon: " << ingredient.value << '\n';
put ( Memory , /*alloc id*/ ingredient . value , /*invalid*/ - 1 ) ;
put ( Memory , /*address*/ ingredient . value + 1 , 0 ) ;
2016-05-18 01:32:41 +00:00
}
2018-01-03 08:31:10 +00:00
break ;
}
: ( code )
void abandon ( int address , int payload_size ) {
2018-06-24 17:23:27 +00:00
put ( Memory , address , /*invalid alloc-id*/ - 1 ) ;
//? cerr << "abandon: " << address << '\n';
// clear rest of payload
for ( int curr = address + 1 ; curr < address + payload_size ; + + curr )
2016-05-03 17:15:17 +00:00
put ( Memory , curr , 0 ) ;
// append existing free list to address
2018-06-16 05:16:09 +00:00
trace ( " abandon " ) < < " saving " < < address < < " in free-list of size " < < payload_size < < end ( ) ;
2018-06-24 17:23:27 +00:00
put ( Memory , address + /*skip invalid alloc-id*/ 1 , get_or_insert ( Current_routine - > free_list , payload_size ) ) ;
2016-05-18 01:32:41 +00:00
put ( Current_routine - > free_list , payload_size , address ) ;
2016-05-03 17:15:17 +00:00
}
2018-05-13 06:08:39 +00:00
int payload_size ( reagent /*copy*/ x ) {
x . properties . push_back ( pair < string , string_tree * > ( " lookup " , NULL ) ) ;
lookup_memory_core ( x , /*check_for_null*/ false ) ;
2018-06-24 16:16:17 +00:00
return size_of ( x ) + /*alloc id*/ 1 ;
2018-05-13 06:08:39 +00:00
}
2016-06-29 02:33:43 +00:00
: ( after " Allocate Special-cases " )
2016-05-03 17:15:17 +00:00
if ( get_or_insert ( Current_routine - > free_list , size ) ) {
2017-11-03 08:50:46 +00:00
trace ( " abandon " ) < < " picking up space from free-list of size " < < size < < end ( ) ;
2016-05-03 17:15:17 +00:00
int result = get_or_insert ( Current_routine - > free_list , size ) ;
2017-11-03 08:50:46 +00:00
trace ( " mem " ) < < " new alloc from free list: " < < result < < end ( ) ;
2018-06-24 17:23:27 +00:00
put ( Current_routine - > free_list , size , get_or_insert ( Memory , result + /*skip alloc id*/ 1 ) ) ;
// clear 'deleted' tag
2016-06-29 02:33:43 +00:00
put ( Memory , result , 0 ) ;
2018-06-24 17:23:27 +00:00
// clear next pointer
put ( Memory , result + /*skip alloc id*/ 1 , 0 ) ;
2016-10-20 05:10:35 +00:00
for ( int curr = result ; curr < result + size ; + + curr ) {
2016-05-03 17:15:17 +00:00
if ( get_or_insert ( Memory , curr ) ! = 0 ) {
raise < < maybe ( current_recipe_name ( ) ) < < " memory in free list was not zeroed out: " < < curr < < ' / ' < < result < < " ; somebody wrote to us after free!!! \n " < < end ( ) ;
break ; // always fatal
}
}
2016-06-29 02:33:43 +00:00
return result ;
2016-05-03 17:15:17 +00:00
}
: ( scenario new_differing_size_no_reclaim )
def main [
2018-06-18 02:53:52 +00:00
1 : & : num < - new number : type
2 : num < - deaddress 1 : & : num
abandon 1 : & : num
3 : & : @ : num < - new number : type , 2 # different size
4 : num < - deaddress 3 : & : @ : num
2018-06-16 05:16:09 +00:00
5 : bool < - equal 2 : num , 4 : num
2016-05-03 17:15:17 +00:00
]
# no reuse
2018-06-16 05:16:09 +00:00
+ mem : storing 0 in location 5
2016-05-03 17:15:17 +00:00
: ( scenario new_reclaim_array )
def main [
2018-06-24 16:16:17 +00:00
10 : & : @ : num < - new number : type , 2
20 : num < - deaddress 10 : & : @ : num
abandon 10 : & : @ : num
30 : & : @ : num < - new number : type , 2 # same size
40 : num < - deaddress 30 : & : @ : num
50 : bool < - equal 20 : num , 40 : num
2016-05-03 17:15:17 +00:00
]
2016-05-18 00:26:55 +00:00
# both calls to new returned identical addresses
2018-06-24 16:16:17 +00:00
+ mem : storing 1 in location 50
2018-06-24 17:23:27 +00:00
: ( scenario lookup_of_abandoned_address_raises_error )
% Hide_errors = true ;
def main [
1 : & : num < - new num : type
3 : & : num < - copy 1 : & : num
abandon 1 : & : num
5 : num / raw < - copy * 3 : & : num
]
+ error : main : address is already abandoned in ' 5 : num / raw < - copy * 3 : & : num '