[vlc-devel] [RFC 1/2] executor: introduce new executor API

Romain Vimont rom1v at videolabs.io
Wed Aug 26 22:11:08 CEST 2020


On Wed, Aug 26, 2020 at 10:01:19PM +0300, Rémi Denis-Courmont wrote:
> Le keskiviikkona 26. elokuuta 2020, 20.19.56 EEST Romain Vimont a écrit :
> > +/**
> > + * Create a new executor.
> > + *
> > + * \param parent      a VLC object
> > + * \param max_threads the maximum number of threads used to execute
> > runnables + * \return a pointer to a new executor, or NULL if an error
> > occurred + */
> > +VLC_API vlc_executor_t *
> > +vlc_executor_New(vlc_object_t *parent, unsigned max_threads);
> 
> Don't really get why a VLC object is needed here. This can be limiting for 
> what's supposed to be a generic helper.

True. Will remove.

> > + * \code{c}
> > + *  static void Run(void *userdata)
> > + *  {
> > + *      char *str = userdata;
> > + *      printf("start of %s\n", str);
> > + *      sleep(3);
> 
> ONCE AGAIN, sleep is not portably thread-safe. This is not a good example.

Because it may be implemented using alarm?

I could replace by vlc_tick_sleep().

> 
> > + *      printf("end of %s\n", str);
> > + *  }
> 
> > + *  void foo(vlc_executor_t *executor, const char *name)
> > + *  {
> > + *      // error handling replaced by assertions for brevity
> > + *      char *str = strdup(name);
> > + *      assert(str);
> 
> Example code should not assume !defined(NDEBUG) either.

That's just an example to illustrate, I could even remove the assert()
and ignore error handling.

> 
> > + *      int ret = vlc_executor_Submit(executor, &runnable, str, NULL, 0);
> > + *      assert(ret == VLC_SUCCESS);
> > + *  }
> > + * \endcode
> 
> > +VLC_API int
> > +vlc_executor_Submit(vlc_executor_t *executor,
> > +                    const struct vlc_runnable *runnable, void *userdata,
> > +                    void *id, vlc_tick_t timeout);
> 
> Type name for works and workers really ought to be in the same semantic field 
> IMO.

(the naming was inpired from ExecutorService in Java, but Executor is
quite "standard" in other languages too.)
> 
> It is unlikely that callers will (or even should have to) handle errors well 
> at this point.

The error handling is simple here:
 - VLC_SUCCESS: the runnable is submitted
 - other: there was an error, the runnable will not be executed

> 
> > +/**
> > + * Cancel all submitted tasks having the specified id.
> > + *
> > + * If id is NULL, then cancel all tasks.
> > + *
> > + * If may_interrupt_if_running is true, then the current running tasks may
> > be + * interrupted using the interrupt() callback.
> > + *
> > + * \param executor the executor
> > + * \param id       the id of the tasks to cancel (NULL for all tasks)
> > + * \param may_interrupt_if_running indicate if the tasks currently running
> > may + *                                 be interrupted
> > + */
> > +VLC_API void
> > +vlc_executor_Cancel(vlc_executor_t *executor, void *id,
> > +                    bool may_interrupt_if_running);
> 
> Two separate external functions may be easier to read than a bool.
> 
> > diff --git a/src/misc/executor.c b/src/misc/executor.c
> > new file mode 100644
> > index 0000000000..88c7064b72
> 
> > +/**
> > + * Make sure that an interrupt thread is running and will consider the
> > + * interruption for a new task just added.
> > + */
> > +static int
> > +RequireInterruptThread(vlc_executor_t *executor)
> > +{
> > +    vlc_mutex_assert(&executor->lock);
> > +
> > +    if (executor->interrupt_thread_running)
> > +        vlc_cond_signal(&executor->interrupt_wait);
> > +    else
> > +    {
> > +        if (vlc_clone_detach(NULL, InterruptThreadRun, executor,
> > +                             VLC_THREAD_PRIORITY_LOW))
> 
> Detached threads cannot be safe in dynamically linked LibVLC. There's a risk 
> that LibVLC will be unloaded before the thread exits, crashing the process.

In vlc_executor_Delete(), the threads are awaited via a condition
variable (nothreads_wait). But yes that could be replaced by a join().
> 
> > +        {
> > +            vlc_mutex_unlock(&executor->lock);
> > +            return VLC_EGENERIC;
> 
> You probably can't do that since you're not handling errors at the call site.

Hmmm, true. I will probably create the interrupt thread from
vlc_executor_Submit() if necessary (so that I can report the error).

> > +        }
> > +
> > +        executor->interrupt_thread_running = true;
> > +    }
> > +
> > +    return VLC_SUCCESS;
> > +}


More information about the vlc-devel mailing list