Skip to content

Commit

Permalink
Better error handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
toots committed Nov 19, 2024
1 parent 738a986 commit e993372
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 67 deletions.
69 changes: 27 additions & 42 deletions src/core/clock.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -380,20 +360,35 @@ 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
| _ when Atomic.get global_stop -> raise Has_stopped
| _ -> 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;
Expand All @@ -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 ();
Expand Down
2 changes: 1 addition & 1 deletion src/core/outputs/output.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
9 changes: 5 additions & 4 deletions src/core/source.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 3 additions & 20 deletions src/libs/playlist.liq
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit e993372

Please sign in to comment.