I was working on guile-nrepl (asyncronous network repl for guile) and stumbled upon an issue: we can't do [interruptable] eval inside fiber because it blocks the whole thread until evaluation is complete. So we need to do eval in a separate thread and thus we need to interact with this threads from fibers somehow.
I hacked together an implementation of thread-operation and it works great, but because of my lack of knowledge of fibers internals the implementation consumes unreasonable amounts of memory and probably contains some other flaws. Any ideas on a proper implementation of this operation?
(use-modules (fibers))
(use-modules (fibers channels))
(use-modules (fibers operations))
(use-modules (fibers timers))
(use-modules (fibers conditions))
(use-modules (fibers scheduler))
(use-modules (ice-9 threads))
(use-modules (ice-9 match))
(use-modules (ice-9 atomic))
(define (thread-operation th)
"Make an operation that will succeed when the thread is
exited. The operation will succeed with the value returned by thread."
(define (wrap-fn)
(join-thread th))
(define (try-fn) (and (thread-exited? th) values))
(define (block-fn flag sched resume)
(define (commit)
(match (atomic-box-compare-and-swap! flag 'W 'S)
('W (resume values))
('C (commit))
('S #f)))
(when sched
(schedule-task
sched
(lambda ()
(perform-operation (thread-operation th))
(commit)))))
(make-base-operation wrap-fn try-fn block-fn))
(define (async-program)
(let ((th (call-with-new-thread (lambda () (sleep 7) 'thread-value)))
(ch (make-channel))
(cnd (make-condition)))
(spawn-fiber
(lambda ()
(put-message
ch
(perform-operation
(choice-operation
(wrap-operation
(wait-operation cnd)
(lambda ()
(format #t "condition signal recieved\n")
'recieved-cnd-signal))
(wrap-operation
(thread-operation th)
(lambda (. v)
(format #t "thread finished: ~a\n" v)
'finished-long-operation)))))))
(spawn-fiber
(lambda ()
(sleep 5)
(format #t "sending signal\n")
(signal-condition! cnd)))
(get-message ch)))
(format #t "return value: ~a\n"
(run-fibers async-program #:drain? #t))