EECS 321 Programming Languages: Homework 7

Due: Friday, March 1st, 2013, noon

Two Space Collection

Implement a two space copying collector, as described in lecture 12. Use four state variables in your collector: the allocation pointer, the active semi-space, and two pointers that you use only during collection, one pointing to the left and one to the right side of the queue for copying the data. (It is possible to do this with only three state variables, and it might be possible with only two, but I recommend four.)

You may assume that your heap size is a multiple of 2 and contains at least 12 cells.

Quality

You collector must be able to run these three programs without running out of space:

#lang plai/gc2/mutator
(allocator-setup "gc.rkt" 400)
(define (count-down n)
  (cond
    [(zero? n) (count-down 20)]
    [else (count-down (- n 1))]))
(count-down 0)

#lang plai/gc2/mutator
(allocator-setup "gc.rkt" 400)
(define (mk-list n)
  (cond
    [(zero? n) '()]
    [else (cons n (mk-list (- n 1)))]))
(define (forever)
  (mk-list 10)
  (forever))
(forever)

#lang plai/gc2/mutator
(allocator-setup "gc.rkt" 400)
(define (proc-lst n)
  (cond
    [(zero? n) (lambda () 0)]
    [else (let ([n1 (proc-lst (- n 1))])
            (lambda () (+ (n1) n)))]))
(define (forever)
  ((proc-lst 10))
  (forever))
(forever)

PLAI Allocation: a No No

Your garbage collector may not do any PLAI-level allocation (except stack space). This means that the functions cons, vector, map, and list are off limits. Similarly, the constructors generated by define-type are off limits (e.g., lam, id, num, add, and sub from earlier assignments). Closures with non-empty environments also require allocation, so you can only define functions at the top-level of the file (no lambda expressions in nested scopes).

The only exceptions: you may call get-root-set and vector->roots, both of which allocate at the PLAI level.

You may, of course, use functions that allocate in your tests to build up example heaps.

If you are unsure what primitives allocate memory, ask.

plai/gc2 vs plai

You must use #lang plai/gc2/collector and #lang plai/gc2/mutator. These are the same as the versions without the gc2 in the middle of their names, with the following exceptions:

Those exceptions aside, the documentation on plai/mutator and plai/collector is accurate. You should read it.

Hint

If you insert calls to read in the middle of your collector, DrRacket will pause until you type something and hit return in the interactions window (but will still update the heap GUI). This allows you to see what is happening at various stages in the process of collection (or allocation) and thus can help you understand what is going on.

Even Better Hint

Only use the previous hint to help you formulate test cases. The edit/debug/fix cycle leads to much pain that the edit/debug/write-test-cases/fix cycle avoids. (And, of course, only enter into this cycle with some baseline set of test cases that you've written up front.)

Template

Below is a list of the functions required to implement a garbage collector, along with their contracts and bogus implementations.

#lang plai/gc2/collector

;; init-allocator : -> void
(define (init-allocator) (error 'this-collector-always-fails))

;; gc:deref : loc -> heap-value
;; must signal an error if fl-loc doesn't point to a flat value
(define (gc:deref fl-loc) (error 'gc:deref))

;; gc:alloc-flat : heap-value -> loc
(define (gc:alloc-flat fv) (error 'gc:alloc-flat))

;; gc:cons : loc loc -> loc
;; hd and tl are guaranteed to have been earlier
;; results from either gc:alloc-flat or gc:cons
(define (gc:cons hd tl) (error 'gc:cons))

;; gc:first : loc -> loc
;; must signal an error of pr-loc does not point to a pair
(define (gc:first pr-loc) (error 'gc:first))

;; gc:rest : loc -> loc
;; must signal an error of pr-loc does not point to a pair
(define (gc:rest pr-loc) (error 'gc:rest))

;; gc:flat? : loc -> boolean
;; loc is guaranteed to have been an earlier
;; result from either gc:alloc-flat or gc:cons
(define (gc:flat? loc) (error 'gc:flat?))

;; gc:cons? : loc -> boolean
;; loc is guaranteed to have been an earlier
;; result from either gc:alloc-flat or gc:cons
(define (gc:cons? loc) (error 'gc:cons?))

;; gc:set-first! : loc loc -> void
;; must signal an error of pr-loc does not point to a pair
(define (gc:set-first! pr-loc new) (error 'gc:set-first!))

;; gc:set-rest! : loc loc -> void
;; must signal an error of pr-loc does not point to a pair
(define (gc:set-rest! pr-ptr new) (error 'gc:set-rest!))

;; gc:closure : heap-value (vectorof loc) -> loc
;; allocates a closure with 'code-ptr' and the free variables
;; in the vector 'free-vars'.
(define (gc:closure code-ptr free-vars) (error 'gc:closure))

;; gc:closure-code-ptr : loc -> heap-value
;; given a location returned from an earlier allocation
;; check to see if it is a closure; if not signal an
;; error. if so, return the code-ptr
(define (gc:closure-code-ptr loc) (error 'gc:closure-code-ptr))

;; gc:closure-env-ref : loc number -> loc
;; given a location returned from an earlier allocation, check
;; to see if it is a closure; if not signal an error. if so,
;; return the 'i'th variable in the closure (counting from 0)
(define (gc:closure-env-ref loc i) (error 'gc:closure-env-ref))

;; gc:closure? : loc -> boolean
;; determine if a previously allocated location
;; holds a closure
(define (gc:closure? loc) (error 'gc:closure?))

Handin

Handin your garbage collector. It should begin with the line

#lang plai/gc2/collector

Last update: Sunday, February 24th, 2013
robby@eecs.northwestern.edu