Here is my new version of seesaw:
(define (seesaw thread-1 thread-2)
(seesaw-1 thread-1 thread-2 'doing 'doing #f #f))
(define (seesaw-1 thread-1 thread-2 thread-1-status thread-2-status val-1 val-2)
(cond ((and (eqv? thread-1-status 'done) (eqv? thread-2-status 'done))
(list val-1 val-2))
((and (eqv? thread-1-status 'done) (eqv? thread-2-status 'doing))
(let* ((next-thread (call (tag-value thread-2)))
(new-val-2 (if (eqv? (tag-of next-thread) 'done) (tag-value next-thread) val-2)))
(seesaw-1 thread-1 next-thread 'done (tag-of next-thread) val-1 new-val-2)))
((and (eqv? thread-1-status 'doing) (eqv? thread-2-status 'done))
(let* ((next-thread (call (tag-value thread-1)))
(new-val-1 (if (eqv? (tag-of next-thread) 'done) (tag-value next-thread) val-1)))
(seesaw-1 next-thread thread-2 (tag-of next-thread) 'done new-val-1 val-2)))
(else ; both threads have doing stats - advance both of them
(let ((next-thread-1 (call (tag-value thread-1)))
(next-thread-2 (call (tag-value thread-2))))
(seesaw-1 next-thread-1 next-thread-2
(tag-of next-thread-1) (tag-of next-thread-2)
(tag-value next-thread-1) (tag-value next-thread-2) )))))
As an invariant, thread-1-status is the actual status of thread-1, and val-1 is the actual value produced by thread-1 (when ay). val-1 is only defined in the case where thread-status-1 is done.
The same holds for thread-2.
There are four cases - all possible combinations of done/doing for thread-1 and thread-2. In the case where both thread-1 and thread-2 are doing,
I execute a step in both of them.
You may not like this, as this is different from the original version of seesaw.