From f2777b497892d2e731f385533ebb1d77edbc34ff Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Fri, 30 Oct 2026 09:18:42 +0100 Subject: [PATCH] audio stuff --- dlna-player.rkt | 50 +++++++++++++++++++++++++---- scribblings/racket-audio-dlna.scrbl | 4 ++- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/dlna-player.rkt b/dlna-player.rkt index a8b04c2..b480f60 100644 --- a/dlna-player.rkt +++ b/dlna-player.rkt @@ -406,12 +406,14 @@ #t))))) (define (poll-player player) - (let loop () - (when (dlna-player-running? player) - (sleep (dlna-player-poll-seconds player)) - (when (dlna-player-running? player) - (refresh-player! player) - (loop))))) + (with-handlers ([exn:break? void]) + (parameterize-break #t + (let loop () + (when (dlna-player-running? player) + (sleep (dlna-player-poll-seconds player)) + (when (dlna-player-running? player) + (refresh-player! player) + (loop))))))) (define (renderer-local-address renderer) (let ([socket (udp-open-socket)]) @@ -882,7 +884,9 @@ (set-dlna-player-running?! player #f) (let ([poll-thread (dlna-player-poll-thread player)]) (when poll-thread - (kill-thread poll-thread) + ;; A break unwinds call-with-semaphore; kill-thread leaves its lock held. + (break-thread poll-thread) + (thread-wait poll-thread) (set-dlna-player-poll-thread! player #f))) (with-handlers ([exn:fail? (lambda (_) (void))]) (call-with-semaphore @@ -898,3 +902,35 @@ (when (dlna-player-owns-server? player) (media-file-server-stop! (dlna-player-server player)))) (void)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Tests for module dlna-player.rkt +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(module+ test + (require rackunit) + + (test-case "closing during a renderer poll releases the transport lock" + (let* ((lock (make-semaphore 1)) + (ready (make-semaphore)) + (custodian (make-custodian)) + (poller + (parameterize ((current-custodian custodian)) + (thread + (λ () + (with-handlers ((exn:break? void)) + (call-with-semaphore + lock + (λ () (semaphore-post ready) (sync never-evt)))))))) + (player + (make-dlna-player-state #f #f #f "" 1 5 lock (make-semaphore 1) + #f #f #f 0 0 (make-hash) 0 #t poller))) + (dynamic-wind + void + (λ () + (semaphore-wait ready) + (let ((closer (parameterize ((current-custodian custodian)) + (thread (λ () (dlna-player-close! player)))))) + (check-not-false (sync/timeout 2 closer)) + (check-true (semaphore-try-wait? lock)))) + (λ () (custodian-shutdown-all custodian)))))) diff --git a/scribblings/racket-audio-dlna.scrbl b/scribblings/racket-audio-dlna.scrbl index 9856e49..988d49f 100644 --- a/scribblings/racket-audio-dlna.scrbl +++ b/scribblings/racket-audio-dlna.scrbl @@ -204,7 +204,9 @@ Changing the file therefore invalidates its cached metadata. @defproc[(dlna-player-close! [player dlna-player?]) void?]{ Stops playback, removes current and next publications, and terminates the -polling thread. A media server created by the player is also stopped. A shared +polling thread. Cancellation unwinds the poller's synchronization locks before +waiting for the thread to exit, so closing during a blocked renderer request +cannot leave the transport lock permanently occupied. A media server created by the player is also stopped. A shared server supplied to @racket[make-dlna-player] remains running. Repeated calls have no effect. }