#lang scheme/base
(require "util.scm")
(provide make-threaded-task-queue
sleep-task-thread-for-at-least
task-inspector-lock
task-inspector-num-tasks-thunk)
(define (make-threaded-task-queue #:return-inspector (return-inspector #f))
(let ((queue (make-task-queue))
(just-created #t))
(letrec ((thread-thunk (lambda ()
(when just-created
(set! just-created #f)
(thread-suspend the-thread))
(let lp ()
(let ((msg (thread-try-receive)))
(if msg
(let ((wait-until (task-queue-msg-wait-until msg))
(now (current-seconds)))
(if (> wait-until now)
(begin (sleep (- wait-until now))
(thread-thunk))
(lp)))
(let ((more? (task-pop-and-handle! queue)))
(if more?
(thread-thunk)
(begin (thread-suspend the-thread)
(thread-thunk)))))))))
(the-thread (thread thread-thunk)))
(let ((add-task-fn (lambda (task-thunk #:add-to-front (add-to-front #f))
(task-push! queue task-thunk #:add-to-front add-to-front)
(thread-resume the-thread))))
(if return-inspector
(values the-thread add-task-fn (make-a-task-inspector queue))
(values the-thread add-task-fn))))))
(define-struct task-inspector (lock num-tasks-thunk))
(define (make-a-task-inspector queue)
(make-task-inspector (task-queue-dat-lock queue)
(lambda () (length (task-queue-dat-lst queue)))))
(define-struct task-queue-msg (wait-until))
(define (sleep-task-thread-for-at-least a-thread secs-to-wait)
(thread-send a-thread (make-task-queue-msg (+ (current-seconds) secs-to-wait))))
(define-struct task-queue-dat (lock lst last-task-ptr) #:mutable)
(define (make-task-queue)
(make-task-queue-dat (make-lock) '() #f))
(define (task-pop-and-handle! queue)
(sync-on-lock
(task-queue-dat-lock queue)
(let ((lst (task-queue-dat-lst queue)))
(when (tasks-todo? queue)
(let ((next-task (mcar lst)))
(set-task-queue-dat-lst! queue (mcdr lst))
(next-task)))
(tasks-todo? queue))))
(define (task-push! queue task-thunk #:add-to-front (add-to-front #f))
(let ((cell-for-end (mcons task-thunk '())))
(sync-on-lock
(task-queue-dat-lock queue)
(if (not (tasks-todo? queue))
(set-task-queue-dat-lst! queue cell-for-end)
(if add-to-front
(set-task-queue-dat-lst! queue (mcons task-thunk (task-queue-dat-lst queue)))
(set-mcdr! (task-queue-dat-last-task-ptr queue) cell-for-end)))
(unless add-to-front
(set-task-queue-dat-last-task-ptr! queue cell-for-end)))))
(define (tasks-todo? queue)
(not (null? (task-queue-dat-lst queue))))