#47: Tick Thread and better scheduling
-------------------------+--------------------------------------------------
Reporter: toots | Owner: toots
Type: Feature | Status: assigned
Priority: 8 | Milestone:
Component: Liquidsoap | Version: 0.3.4+svn
Resolution: | Keywords:
-------------------------+--------------------------------------------------
Old description:
> Liquidsoap consumes too much CPU on scheduling when empty or broken
> playlist is given.
>
> More generaly, we should have a scheduler that is capable of:
> * Scheduling new tasks immediatly
> * Go to sleep for some time if no task are to be executed for now
>
> This conditions are difficult to reach since the delay call cannot be
> canceled, so you can go to sleep but not wake up.
>
> My proposition is to add another "tick" thread that will perform regular
> actions based on a minimal delay.
> Then, using a global_sleep lock (as it is in place actually) in the
> scheduler, we'll be able to either wake it up every [delay] sec, or as
> soon as a task is scheduled.
>
> This tick system will also be very usefull for lastfm sumbission, later,
> and I suspect it may be of a great help for synchronisation
> debugging/managing..
>
> A proposed implementation is attached as patch and pasted here:
>
> {{{
> Index: tools/tutils.ml
> ===================================================================
> --- tools/tutils.ml (révision 4720)
> +++ tools/tutils.ml (copie de travail)
> @@ -97,6 +97,56 @@
> id
> ) ()
>
> +module Tick =
> +struct
> +
> + let conf_tick =
> + Conf.float ~p:(Configure.conf#plug "tick_delay") ~d:2.
> + "Set the maximun delay between two ticks, in seconds."
> +
> + type action = Simple of (unit -> unit) | Recursive of (unit ->
> (float*action))
> +
> + let alist = ref []
> + let alist_lock = Mutex.create ()
> + let delay = ref conf_tick#get
> +
> + let add a =
> + Mutex.lock alist_lock ;
> + let time = Unix.time () in
> + alist := (time,a) :: !alist ;
> + Mutex.unlock alist_lock
> +
> + let rec tick_task () =
> + Mutex.lock alist_lock ;
> + let newlist = ref [] in
> + delay := conf_tick#get ;
> + let time = Unix.time () in
> + let apply (t,a) =
> + if time >= t then
> + match a with
> + | Simple f -> f ()
> + | Recursive f ->
> + let (d,a') = f () in
> + newlist := (time +. d, a') :: !newlist ;
> + delay := min !delay d
> + else
> + delay := min !delay (t -. time) ;
> + newlist := (t,a) :: !newlist
> + in
> + List.iter apply !alist ;
> + alist := !newlist ;
> + Mutex.unlock alist_lock ;
> + Thread.delay !delay ;
> + tick_task ()
> +
> + let tick () =
> + ignore (create tick_task () "Tick thread")
> +
> + let () =
> + ignore (Dtools.Init.at_start tick)
> +
> +end
> +
> module Task =
> struct
>
> Index: tools/tutils.mli
> ===================================================================
> --- tools/tutils.mli (révision 4720)
> +++ tools/tutils.mli (copie de travail)
> @@ -41,6 +41,30 @@
> (** Check if a thread is running. *)
> val running : string -> Thread.t -> bool
>
> +module Tick :
> +sig
> +
> + (** Regular tick thread for liquidsoap
> + * You can add actions to be executed regulary
> + * Simple actions are executed when the thread
> + * wakes up.
> + *
> + * Recursive actions are executed, and then
> + * rescheduled after the ammount of time they
> + * ask for. Resursive actions return (delay,new_action)
> + *
> + * When this thread is asleep, nothing can wake it up
> + * Its maximal delay between two wake ups is set
> + * using "tick_delay" configuration key, and is 2. by default
> + * Because of this, you shouldn't use it for an immediate
> + * task *)
> +
> + type action = Simple of (unit -> unit) | Recursive of (unit ->
> (float*action))
> +
> + val add : action -> unit
> +
> +end
> +
> module Task :
> sig
> type task = unit -> return_t
> Index: Makefile
> ===================================================================
> --- Makefile (révision 4720)
> +++ Makefile (copie de travail)
> @@ -81,9 +81,9 @@
> $(if $(W_PORTAUDIO),io/portaudio_io.ml) \
> $(if $(W_JACK),io/jack_io.ml)
>
> -tools = tools/doc.ml tools/plug.ml tools/utils.ml \
> +tools = tools/doc.ml tools/plug.ml tools/utils.ml configure.ml \
> tools/tutils.ml tools/rqueue.ml tools/ringbuffer.ml \
> - tools/wav.ml $(if $(BYTE),tools/dynliq.ml) configure.ml \
> + tools/wav.ml $(if $(BYTE),tools/dynliq.ml) \
> tools/sutils.ml tools/http.ml \
> $(if $(W_XML),tools/xmliq.ml tools/lastfm.ml)
>
> }}}
New description:
Liquidsoap consumes too much CPU on scheduling when empty or broken
playlist is given.
More generaly, we should have a scheduler that is capable of:
* Scheduling new tasks immediatly
* Go to sleep for some time if no task are to be executed for now
This conditions are difficult to reach since the delay call cannot be
canceled, so you can go to sleep but not wake up.
My proposition is to add another "tick" thread that will perform regular
actions based on a minimal delay.
Then, using a global_sleep lock (as it is in place actually) in the
scheduler, we'll be able to either wake it up every [delay] sec, or as
soon as a task is scheduled.
This tick system will also be very usefull for lastfm sumbission, later,
and I suspect it may be of a great help for synchronisation
debugging/managing..
A proposed implementation is attached.
--
Ticket URL: <http://savonet.rastageeks.org/ticket/47#comment:2>
Savonet <http://savonet.rastageeks.org/>
Let's program our stream !