From e99337222d1074cf26a91a99316007383a6c1d94 Mon Sep 17 00:00:00 2001 From: Romain Beauxis Date: Tue, 19 Nov 2024 13:27:01 -0600 Subject: [PATCH] Better error handling. --- src/core/clock.ml | 69 +++++++++++++++----------------------- src/core/outputs/output.ml | 2 +- src/core/source.ml | 9 ++--- src/libs/playlist.liq | 23 ++----------- 4 files changed, 36 insertions(+), 67 deletions(-) diff --git a/src/core/clock.ml b/src/core/clock.ml index b1da4168a5..7089e9de5b 100644 --- a/src/core/clock.ml +++ b/src/core/clock.ml @@ -45,26 +45,6 @@ let log = Log.make ["clock"] let conf_clock = Dtools.Conf.void ~p:(Configure.conf#plug "clock") "Clock settings" -(** If true, a clock keeps running when an output fails. Other outputs may - * still be useful. But there may also be some useless inputs left. - * If no active output remains, the clock will exit without triggering - * shutdown. We may need some device to allow this (but active and passive - * clocks will have to be treated separately). *) -let allow_streaming_errors = - Dtools.Conf.bool - ~p:(conf_clock#plug "allow_streaming_errors") - ~d:false "Handling of streaming errors" - ~comments: - [ - "Control the behaviour of clocks when an error occurs during streaming."; - "This has no effect on errors occurring during source initializations."; - "By default, any error will cause liquidsoap to shutdown. If errors"; - "are allowed, faulty sources are simply removed and clocks keep \ - running."; - "Allowing errors can result in complex surprising situations;"; - "use at your own risk!"; - ] - let conf_log_delay = Dtools.Conf.float ~p:(conf_clock#plug "log_delay") @@ -380,6 +360,20 @@ let started c = | `Stopping _ | `Started _ -> true | `Stopped _ -> false +let wrap_errors clock fn s = + try fn s + with exn when exn <> Has_stopped -> + let bt = Printexc.get_raw_backtrace () in + Printf.printf "Error: %s\n%s\n%!" (Printexc.to_string exn) + (Printexc.raw_backtrace_to_string bt); + log#severe "Source %s failed while streaming: %s!\n%s" s#id + (Printexc.to_string exn) + (Printexc.raw_backtrace_to_string bt); + _detach clock s; + if Queue.length clock.on_error > 0 then + Queue.iter clock.on_error (fun fn -> fn exn bt) + else Printexc.raise_with_backtrace exn bt + let rec active_params c = match Atomic.get (Unifier.deref c).state with | `Stopping s | `Started s -> s @@ -387,13 +381,14 @@ let rec active_params c = | _ -> raise Invalid_state and _activate_pending_sources ~clock x = - Queue.flush_iter clock.pending_activations (fun s -> - check_stopped (); - s#wake_up; - match s#source_type with - | `Active _ -> WeakQueue.push x.active_sources s - | `Output _ -> Queue.push x.outputs s - | `Passive -> WeakQueue.push x.passive_sources s) + Queue.flush_iter clock.pending_activations + (wrap_errors clock (fun s -> + check_stopped (); + s#wake_up; + match s#source_type with + | `Active _ -> WeakQueue.push x.active_sources s + | `Output _ -> Queue.push x.outputs s + | `Passive -> WeakQueue.push x.passive_sources s)) and _tick ~clock x = _activate_pending_sources ~clock x; @@ -402,21 +397,11 @@ and _tick ~clock x = in let sources = _animated_sources x in List.iter - (fun s -> - check_stopped (); - try - match s#source_type with - | `Output s | `Active s -> s#output - | _ -> assert false - with exn when exn <> Has_stopped -> - let bt = Printexc.get_raw_backtrace () in - if Queue.is_empty clock.on_error then ( - log#severe "Source %s failed while streaming: %s!\n%s" s#id - (Printexc.to_string exn) - (Printexc.raw_backtrace_to_string bt); - if not allow_streaming_errors#get then Tutils.shutdown 1 - else _detach clock s) - else Queue.iter clock.on_error (fun fn -> fn exn bt)) + (wrap_errors clock (fun s -> + check_stopped (); + match s#source_type with + | `Output s | `Active s -> s#output + | _ -> assert false)) sources; Queue.flush_iter x.on_tick (fun fn -> check_stopped (); diff --git a/src/core/outputs/output.ml b/src/core/outputs/output.ml index fdd892d212..a2622a88c1 100644 --- a/src/core/outputs/output.ml +++ b/src/core/outputs/output.ml @@ -182,7 +182,7 @@ class virtual output ~output_kind ?clock ?(name = "") ~infallible if not self#fallible then ( self#log#critical "Infallible source produced a partial frame!"; assert false); - self#log#important "Source ended (no more tracks) stopping output..."; + self#log#info "Source ended (no more tracks) stopping output..."; self#transition_to `Idle); if skip then ( diff --git a/src/core/source.ml b/src/core/source.ml index d3bd3103e0..8fe69c738d 100644 --- a/src/core/source.ml +++ b/src/core/source.ml @@ -242,11 +242,12 @@ class virtual operator ?(stack = []) ?clock ?(name = "src") sources = List.iter (fun fn -> fn ()) on_wake_up with exn -> Atomic.set is_up `Error; - let bt = Printexc.get_backtrace () in - Utils.log_exception ~log ~bt - (Printf.sprintf "Error when starting source %s: %s!" self#id + let bt = Printexc.get_raw_backtrace () in + Utils.log_exception ~log + ~bt:(Printexc.raw_backtrace_to_string bt) + (Printf.sprintf "Error while starting source %s: %s!" self#id (Printexc.to_string exn)); - Tutils.shutdown 1) + Printexc.raise_with_backtrace exn bt) val mutable on_sleep = [] method on_sleep fn = on_sleep <- fn :: on_sleep diff --git a/src/libs/playlist.liq b/src/libs/playlist.liq index 1885731c72..89251c94c9 100644 --- a/src/libs/playlist.liq +++ b/src/libs/playlist.liq @@ -100,17 +100,8 @@ end # @category File # @param ~mime_type Default MIME type for the playlist. `null` means automatic detection. # @param ~timeout Timeout for resolving the playlist -# @param ~cue_in_metadata Metadata for cue in points. Disabled if `null`. -# @param ~cue_out_metadata Metadata for cue out points. Disabled if `null`. # @param uri Path to the playlist -def playlist.files( - ~id=null(), - ~mime_type=null(), - ~timeout=null(), - ~cue_in_metadata=null("liq_cue_in"), - ~cue_out_metadata=null("liq_cue_out"), - uri -) = +def playlist.files(~id=null(), ~mime_type=null(), ~timeout=null(), uri) = id = id ?? playlist.id(default="playlist.files", uri) if @@ -124,10 +115,7 @@ def playlist.files( files = list.filter(fun (f) -> not (file.is_directory(f)), files) files else - pl = - request.create( - cue_in_metadata=cue_in_metadata, cue_out_metadata=cue_out_metadata, uri - ) + pl = request.create(resolve_metadata=false, uri) result = if request.resolve(timeout=timeout, pl) @@ -533,12 +521,7 @@ def replaces playlist( files = try playlist.files( - id=id, - mime_type=mime_type, - timeout=timeout, - cue_in_metadata=cue_in_metadata, - cue_out_metadata=cue_out_metadata, - playlist_uri + id=id, mime_type=mime_type, timeout=timeout, playlist_uri ) catch err do log.info(