diff mbox series

[FFmpeg-devel,v2,01/14] vvcdec: add thread executor

Message ID TYSPR06MB643369589AD8F23FD601530CAA2DA@TYSPR06MB6433.apcprd06.prod.outlook.com
State Superseded
Headers show
Series add vvc decoder | expand

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Nuo Mi July 7, 2023, 2:05 p.m. UTC
The executor design pattern was inroduced by java
<https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/Executor.html>
it also adapted by python
<https://docs.python.org/3/library/concurrent.futures.html>
Compared to handcrafted thread pool management, it greatly simplifies the thread code.
---
 libavcodec/Makefile     |   1 +
 libavcodec/executor.c   | 182 ++++++++++++++++++++++++++++++++++++++++
 libavcodec/executor.h   |  67 +++++++++++++++
 libavcodec/vvc/Makefile |   4 +
 4 files changed, 254 insertions(+)
 create mode 100644 libavcodec/executor.c
 create mode 100644 libavcodec/executor.h
 create mode 100644 libavcodec/vvc/Makefile

Comments

Michael Niedermayer July 8, 2023, 9:51 p.m. UTC | #1
On Fri, Jul 07, 2023 at 10:05:27PM +0800, Nuo Mi wrote:
> The executor design pattern was inroduced by java
> <https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/Executor.html>
> it also adapted by python
> <https://docs.python.org/3/library/concurrent.futures.html>
> Compared to handcrafted thread pool management, it greatly simplifies the thread code.
> ---
>  libavcodec/Makefile     |   1 +
>  libavcodec/executor.c   | 182 ++++++++++++++++++++++++++++++++++++++++
>  libavcodec/executor.h   |  67 +++++++++++++++
>  libavcodec/vvc/Makefile |   4 +
>  4 files changed, 254 insertions(+)
>  create mode 100644 libavcodec/executor.c
>  create mode 100644 libavcodec/executor.h
>  create mode 100644 libavcodec/vvc/Makefile

This seems to need some fallback if pthreads are unavailable

src/libavcodec/executor.c: In function ‘executor_worker_task’:
src/libavcodec/executor.c:64:5: error: implicit declaration of function ‘pthread_mutex_lock’; did you mean ‘ff_mutex_lock’? [-Werror=implicit-function-declaration]
     pthread_mutex_lock(&e->lock);
     ^~~~~~~~~~~~~~~~~~
     ff_mutex_lock
src/libavcodec/executor.c:78:13: error: implicit declaration of function ‘pthread_mutex_unlock’; did you mean ‘ff_mutex_unlock’? [-Werror=implicit-function-declaration]
             pthread_mutex_unlock(&e->lock);
             ^~~~~~~~~~~~~~~~~~~~
             ff_mutex_unlock
src/libavcodec/executor.c:83:13: error: implicit declaration of function ‘pthread_cond_wait’; did you mean ‘__fread_chk_warn’? [-Werror=implicit-function-declaration]
             pthread_cond_wait(&e->cond, &e->lock);
             ^~~~~~~~~~~~~~~~~
             __fread_chk_warn
src/libavcodec/executor.c: In function ‘ff_executor_alloc’:
src/libavcodec/executor.c:108:11: error: implicit declaration of function ‘pthread_mutex_init’; did you mean ‘ff_mutex_init’? [-Werror=implicit-function-declaration]
     ret = pthread_mutex_init(&e->lock, NULL);
           ^~~~~~~~~~~~~~~~~~
           ff_mutex_init
src/libavcodec/executor.c:112:11: error: implicit declaration of function ‘pthread_cond_init’ [-Werror=implicit-function-declaration]
     ret = pthread_cond_init(&e->cond, NULL);
           ^~~~~~~~~~~~~~~~~
src/libavcodec/executor.c:119:15: error: implicit declaration of function ‘pthread_create’; did you mean ‘ff_thread_setname’? [-Werror=implicit-function-declaration]
         ret = pthread_create(&ti->thread, NULL, executor_worker_task, ti);
               ^~~~~~~~~~~~~~
               ff_thread_setname
src/libavcodec/executor.c:129:5: error: implicit declaration of function ‘pthread_cond_broadcast’ [-Werror=implicit-function-declaration]
     pthread_cond_broadcast(&e->cond);
     ^~~~~~~~~~~~~~~~~~~~~~
src/libavcodec/executor.c:132:9: error: implicit declaration of function ‘pthread_join’; did you mean ‘ff_thread_once’? [-Werror=implicit-function-declaration]
         pthread_join(e->threads[j].thread, NULL);
         ^~~~~~~~~~~~
         ff_thread_once
src/libavcodec/executor.c:133:5: error: implicit declaration of function ‘pthread_cond_destroy’ [-Werror=implicit-function-declaration]
     pthread_cond_destroy(&e->cond);
     ^~~~~~~~~~~~~~~~~~~~
src/libavcodec/executor.c:135:5: error: implicit declaration of function ‘pthread_mutex_destroy’; did you mean ‘ff_mutex_destroy’? [-Werror=implicit-function-declaration]
     pthread_mutex_destroy(&e->lock);
     ^~~~~~~~~~~~~~~~~~~~~
     ff_mutex_destroy
src/libavcodec/executor.c: In function ‘ff_executor_execute’:
src/libavcodec/executor.c:180:5: error: implicit declaration of function ‘pthread_cond_signal’ [-Werror=implicit-function-declaration]
     pthread_cond_signal(&e->cond);
     ^~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
ffmpeg/ffbuild/common.mak:81: recipe for target 'libavcodec/executor.o' failed
make: *** [libavcodec/executor.o] Error 1
make: *** Waiting for unfinished jobs....

thx

[...]
Nuo Mi July 10, 2023, 7:41 a.m. UTC | #2
On Sun, Jul 9, 2023 at 5:52 AM Michael Niedermayer <michael@niedermayer.cc>
wrote:

> On Fri, Jul 07, 2023 at 10:05:27PM +0800, Nuo Mi wrote:
> > The executor design pattern was inroduced by java
> > <
> https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/Executor.html
> >
> > it also adapted by python
> > <https://docs.python.org/3/library/concurrent.futures.html>
> > Compared to handcrafted thread pool management, it greatly simplifies
> the thread code.
> > ---
> >  libavcodec/Makefile     |   1 +
> >  libavcodec/executor.c   | 182 ++++++++++++++++++++++++++++++++++++++++
> >  libavcodec/executor.h   |  67 +++++++++++++++
> >  libavcodec/vvc/Makefile |   4 +
> >  4 files changed, 254 insertions(+)
> >  create mode 100644 libavcodec/executor.c
> >  create mode 100644 libavcodec/executor.h
> >  create mode 100644 libavcodec/vvc/Makefile
>
> This seems to need some fallback if pthreads are unavailable
>
> src/libavcodec/executor.c: In function ‘executor_worker_task’:
> src/libavcodec/executor.c:64:5: error: implicit declaration of function
> ‘pthread_mutex_lock’; did you mean ‘ff_mutex_lock’?
> [-Werror=implicit-function-declaration]
>      pthread_mutex_lock(&e->lock);
>      ^~~~~~~~~~~~~~~~~~
>      ff_mutex_lock
> src/libavcodec/executor.c:78:13: error: implicit declaration of function
> ‘pthread_mutex_unlock’; did you mean ‘ff_mutex_unlock’?
> [-Werror=implicit-function-declaration]
>              pthread_mutex_unlock(&e->lock);
>              ^~~~~~~~~~~~~~~~~~~~
>              ff_mutex_unlock
> src/libavcodec/executor.c:83:13: error: implicit declaration of function
> ‘pthread_cond_wait’; did you mean ‘__fread_chk_warn’?
> [-Werror=implicit-function-declaration]
>              pthread_cond_wait(&e->cond, &e->lock);
>              ^~~~~~~~~~~~~~~~~
>              __fread_chk_warn
> src/libavcodec/executor.c: In function ‘ff_executor_alloc’:
> src/libavcodec/executor.c:108:11: error: implicit declaration of function
> ‘pthread_mutex_init’; did you mean ‘ff_mutex_init’?
> [-Werror=implicit-function-declaration]
>      ret = pthread_mutex_init(&e->lock, NULL);
>            ^~~~~~~~~~~~~~~~~~
>            ff_mutex_init
> src/libavcodec/executor.c:112:11: error: implicit declaration of function
> ‘pthread_cond_init’ [-Werror=implicit-function-declaration]
>      ret = pthread_cond_init(&e->cond, NULL);
>            ^~~~~~~~~~~~~~~~~
> src/libavcodec/executor.c:119:15: error: implicit declaration of function
> ‘pthread_create’; did you mean ‘ff_thread_setname’?
> [-Werror=implicit-function-declaration]
>          ret = pthread_create(&ti->thread, NULL, executor_worker_task, ti);
>                ^~~~~~~~~~~~~~
>                ff_thread_setname
> src/libavcodec/executor.c:129:5: error: implicit declaration of function
> ‘pthread_cond_broadcast’ [-Werror=implicit-function-declaration]
>      pthread_cond_broadcast(&e->cond);
>      ^~~~~~~~~~~~~~~~~~~~~~
> src/libavcodec/executor.c:132:9: error: implicit declaration of function
> ‘pthread_join’; did you mean ‘ff_thread_once’?
> [-Werror=implicit-function-declaration]
>          pthread_join(e->threads[j].thread, NULL);
>          ^~~~~~~~~~~~
>          ff_thread_once
> src/libavcodec/executor.c:133:5: error: implicit declaration of function
> ‘pthread_cond_destroy’ [-Werror=implicit-function-declaration]
>      pthread_cond_destroy(&e->cond);
>      ^~~~~~~~~~~~~~~~~~~~
> src/libavcodec/executor.c:135:5: error: implicit declaration of function
> ‘pthread_mutex_destroy’; did you mean ‘ff_mutex_destroy’?
> [-Werror=implicit-function-declaration]
>      pthread_mutex_destroy(&e->lock);
>      ^~~~~~~~~~~~~~~~~~~~~
>      ff_mutex_destroy
> src/libavcodec/executor.c: In function ‘ff_executor_execute’:
> src/libavcodec/executor.c:180:5: error: implicit declaration of function
> ‘pthread_cond_signal’ [-Werror=implicit-function-declaration]
>      pthread_cond_signal(&e->cond);
>      ^~~~~~~~~~~~~~~~~~~
> cc1: some warnings being treated as errors
> ffmpeg/ffbuild/common.mak:81: recipe for target 'libavcodec/executor.o'
> failed
> make: *** [libavcodec/executor.o] Error 1
> make: *** Waiting for unfinished jobs....
>
> thx
>
> [...]
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> Why not whip the teacher when the pupil misbehaves? -- Diogenes of Sinope
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
Hi Michael,
Thank you for the information. I will fixed it
Michael Niedermayer July 14, 2023, 10:38 p.m. UTC | #3
On Fri, Jul 07, 2023 at 10:05:27PM +0800, Nuo Mi wrote:
> The executor design pattern was inroduced by java
> <https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/Executor.html>
> it also adapted by python
> <https://docs.python.org/3/library/concurrent.futures.html>
> Compared to handcrafted thread pool management, it greatly simplifies the thread code.
> ---
>  libavcodec/Makefile     |   1 +
>  libavcodec/executor.c   | 182 ++++++++++++++++++++++++++++++++++++++++
>  libavcodec/executor.h   |  67 +++++++++++++++
>  libavcodec/vvc/Makefile |   4 +
>  4 files changed, 254 insertions(+)
>  create mode 100644 libavcodec/executor.c
>  create mode 100644 libavcodec/executor.h
>  create mode 100644 libavcodec/vvc/Makefile

[...]

> +++ b/libavcodec/executor.h
> @@ -0,0 +1,67 @@
> +/*
> + * Copyright (C) 2022 Nuo Mi
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#ifndef AVCODEC_EXECUTOR_H
> +#define AVCODEC_EXECUTOR_H
> +
> +typedef struct Executor Executor;
> +typedef struct Tasklet Tasklet;
> +
> +struct Tasklet {
> +    Tasklet *next;
> +};
> +
> +typedef struct TaskletCallbacks {
> +    void *user_data;
> +
> +    int local_context_size;
> +
> +    // return 1 if a's priority > b's priority
> +    int (*priority_higher)(const Tasklet *a, const Tasklet *b);
> +
> +    // task is ready for run
> +    int (*ready)(const Tasklet *t, void *user_data);
> +
> +    // run the task
> +    int (*run)(Tasklet *t, void *local_context, void *user_data);
> +} TaskletCallbacks;
> +
> +/**
> + * Alloc executor
> + * @param callbacks callback strucutre for executor
> + * @param thread_count worker thread number
> + * @return return the executor
> + */
> +Executor* ff_executor_alloc(const TaskletCallbacks *callbacks, int thread_count);
> +
> +/**
> + * Free executor
> + * @param e  pointer to executor
> + */
> +void ff_executor_free(Executor **e);
> +
> +/**
> + * Add task to executor
> + * @param e pointer to executor
> + * @param t pointer to task. If NULL, it will wakeup one work thread
> + */
> +void ff_executor_execute(Executor *e, Tasklet *t);
> +
> +#endif //AVCODEC_EXECUTOR_H

This would be quite useful outside libavcodec
In fact id like to use it in libavradio to do the first stage of FFTs (which
are the overwhelming bulk of computations ATM) and maybe eventually later
stages too

Maybe this could be made available to the outside of libavcodec
as avpriv_ or av_ ?
maybe from libavutil ?

thx

[...]
Nuo Mi July 16, 2023, 6:28 a.m. UTC | #4
On Mon, Jul 10, 2023 at 3:41 PM Nuo Mi <nuomi2021@gmail.com> wrote:

>
>
> On Sun, Jul 9, 2023 at 5:52 AM Michael Niedermayer <michael@niedermayer.cc>
> wrote:
>
>> On Fri, Jul 07, 2023 at 10:05:27PM +0800, Nuo Mi wrote:
>> > The executor design pattern was inroduced by java
>> > <
>> https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/Executor.html
>> >
>> > it also adapted by python
>> > <https://docs.python.org/3/library/concurrent.futures.html>
>> > Compared to handcrafted thread pool management, it greatly simplifies
>> the thread code.
>> > ---
>> >  libavcodec/Makefile     |   1 +
>> >  libavcodec/executor.c   | 182 ++++++++++++++++++++++++++++++++++++++++
>> >  libavcodec/executor.h   |  67 +++++++++++++++
>> >  libavcodec/vvc/Makefile |   4 +
>> >  4 files changed, 254 insertions(+)
>> >  create mode 100644 libavcodec/executor.c
>> >  create mode 100644 libavcodec/executor.h
>> >  create mode 100644 libavcodec/vvc/Makefile
>>
>> This seems to need some fallback if pthreads are unavailable
>>
>> src/libavcodec/executor.c: In function ‘executor_worker_task’:
>> src/libavcodec/executor.c:64:5: error: implicit declaration of function
>> ‘pthread_mutex_lock’; did you mean ‘ff_mutex_lock’?
>> [-Werror=implicit-function-declaration]
>>      pthread_mutex_lock(&e->lock);
>>      ^~~~~~~~~~~~~~~~~~
>>      ff_mutex_lock
>> ...
>> cc1: some warnings being treated as errors
>> ffmpeg/ffbuild/common.mak:81: recipe for target 'libavcodec/executor.o'
>> failed
>> make: *** [libavcodec/executor.o] Error 1
>> make: *** Waiting for unfinished jobs....
>>
>> thx
>>
>> [...]
>> --
>> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>>
>> Why not whip the teacher when the pupil misbehaves? -- Diogenes of Sinope
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>>
> Hi Michael,
> Thank you for the information. I will fixed it
>

Hi Michael,
FFmpeg application has a dependency on threads
Once I --disable-pthreads, I can't build ffmpeg for the decoder md5 test.
Could you share with me how to test the decoder when we disabled threads?
Thank you
Nuo Mi July 16, 2023, 7:07 a.m. UTC | #5
On Sat, Jul 15, 2023 at 6:38 AM Michael Niedermayer <michael@niedermayer.cc>
wrote:

> On Fri, Jul 07, 2023 at 10:05:27PM +0800, Nuo Mi wrote:
> > The executor design pattern was inroduced by java
> > <
> https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/Executor.html
> >
> > it also adapted by python
> > <https://docs.python.org/3/library/concurrent.futures.html>
> > Compared to handcrafted thread pool management, it greatly simplifies
> the thread code.
> > ---
> >  libavcodec/Makefile     |   1 +
> >  libavcodec/executor.c   | 182 ++++++++++++++++++++++++++++++++++++++++
> >  libavcodec/executor.h   |  67 +++++++++++++++
> >  libavcodec/vvc/Makefile |   4 +
> >  4 files changed, 254 insertions(+)
> >  create mode 100644 libavcodec/executor.c
> >  create mode 100644 libavcodec/executor.h
> >  create mode 100644 libavcodec/vvc/Makefile
>
> [...]
>
> > +++ b/libavcodec/executor.h
> > @@ -0,0 +1,67 @@
> > +/*
> > + * Copyright (C) 2022 Nuo Mi
> > + *
> > + * This file is part of FFmpeg.
> > + *
> > + * FFmpeg is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
> > + *
> > + * FFmpeg is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with FFmpeg; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> > + */
> > +
> > +#ifndef AVCODEC_EXECUTOR_H
> > +#define AVCODEC_EXECUTOR_H
> > +
> > +typedef struct Executor Executor;
> > +typedef struct Tasklet Tasklet;
> > +
> > +struct Tasklet {
> > +    Tasklet *next;
> > +};
> > +
> > +typedef struct TaskletCallbacks {
> > +    void *user_data;
> > +
> > +    int local_context_size;
> > +
> > +    // return 1 if a's priority > b's priority
> > +    int (*priority_higher)(const Tasklet *a, const Tasklet *b);
> > +
> > +    // task is ready for run
> > +    int (*ready)(const Tasklet *t, void *user_data);
> > +
> > +    // run the task
> > +    int (*run)(Tasklet *t, void *local_context, void *user_data);
> > +} TaskletCallbacks;
> > +
> > +/**
> > + * Alloc executor
> > + * @param callbacks callback strucutre for executor
> > + * @param thread_count worker thread number
> > + * @return return the executor
> > + */
> > +Executor* ff_executor_alloc(const TaskletCallbacks *callbacks, int
> thread_count);
> > +
> > +/**
> > + * Free executor
> > + * @param e  pointer to executor
> > + */
> > +void ff_executor_free(Executor **e);
> > +
> > +/**
> > + * Add task to executor
> > + * @param e pointer to executor
> > + * @param t pointer to task. If NULL, it will wakeup one work thread
> > + */
> > +void ff_executor_execute(Executor *e, Tasklet *t);
> > +
> > +#endif //AVCODEC_EXECUTOR_H
>
> This would be quite useful outside libavcodec
> In fact id like to use it in libavradio to do the first stage of FFTs
> (which
> are the overwhelming bulk of computations ATM) and maybe eventually later
> stages too
>
Thank you. Glad to hear it.

>
> Maybe this could be made available to the outside of libavcodec
> as avpriv_ or av_ ?
> maybe from libavutil ?
>
avpriv_  is better since it is not a public API for ffmpeg's users(yet)
I will move it to libavutil and send separate patches for the executor
only.

>
> thx
>
> [...]
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> Take away the freedom of one citizen and you will be jailed, take away
> the freedom of all citizens and you will be congratulated by your peers
> in Parliament.
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
Michael Niedermayer July 17, 2023, 10:13 p.m. UTC | #6
On Sun, Jul 16, 2023 at 02:28:53PM +0800, Nuo Mi wrote:
> On Mon, Jul 10, 2023 at 3:41 PM Nuo Mi <nuomi2021@gmail.com> wrote:
> 
> >
> >
> > On Sun, Jul 9, 2023 at 5:52 AM Michael Niedermayer <michael@niedermayer.cc>
> > wrote:
> >
> >> On Fri, Jul 07, 2023 at 10:05:27PM +0800, Nuo Mi wrote:
> >> > The executor design pattern was inroduced by java
> >> > <
> >> https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/Executor.html
> >> >
> >> > it also adapted by python
> >> > <https://docs.python.org/3/library/concurrent.futures.html>
> >> > Compared to handcrafted thread pool management, it greatly simplifies
> >> the thread code.
> >> > ---
> >> >  libavcodec/Makefile     |   1 +
> >> >  libavcodec/executor.c   | 182 ++++++++++++++++++++++++++++++++++++++++
> >> >  libavcodec/executor.h   |  67 +++++++++++++++
> >> >  libavcodec/vvc/Makefile |   4 +
> >> >  4 files changed, 254 insertions(+)
> >> >  create mode 100644 libavcodec/executor.c
> >> >  create mode 100644 libavcodec/executor.h
> >> >  create mode 100644 libavcodec/vvc/Makefile
> >>
> >> This seems to need some fallback if pthreads are unavailable
> >>
> >> src/libavcodec/executor.c: In function ‘executor_worker_task’:
> >> src/libavcodec/executor.c:64:5: error: implicit declaration of function
> >> ‘pthread_mutex_lock’; did you mean ‘ff_mutex_lock’?
> >> [-Werror=implicit-function-declaration]
> >>      pthread_mutex_lock(&e->lock);
> >>      ^~~~~~~~~~~~~~~~~~
> >>      ff_mutex_lock
> >> ...
> >> cc1: some warnings being treated as errors
> >> ffmpeg/ffbuild/common.mak:81: recipe for target 'libavcodec/executor.o'
> >> failed
> >> make: *** [libavcodec/executor.o] Error 1
> >> make: *** Waiting for unfinished jobs....
> >>
> >> thx
> >>
> >> [...]
> >> --
> >> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
> >>
> >> Why not whip the teacher when the pupil misbehaves? -- Diogenes of Sinope
> >> _______________________________________________
> >> ffmpeg-devel mailing list
> >> ffmpeg-devel@ffmpeg.org
> >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >>
> >> To unsubscribe, visit link above, or email
> >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> >>
> > Hi Michael,
> > Thank you for the information. I will fixed it
> >
> 
> Hi Michael,
> FFmpeg application has a dependency on threads
> Once I --disable-pthreads, I can't build ffmpeg for the decoder md5 test.
> Could you share with me how to test the decoder when we disabled threads?

it seems this failure occurred on a ppc cross compile
../configure --target-os=linux --enable-cross-compile --disable-iconv --disable-pthreads && make -j32

but its reproduceable on ubuntu x86-64 with a simple
 make distclean ; ./configure   --disable-pthreads && make -j32


[...]
Nuo Mi July 18, 2023, 11:53 a.m. UTC | #7
On Tue, Jul 18, 2023 at 6:14 AM Michael Niedermayer <michael@niedermayer.cc>
wrote:

> On Sun, Jul 16, 2023 at 02:28:53PM +0800, Nuo Mi wrote:
> > On Mon, Jul 10, 2023 at 3:41 PM Nuo Mi <nuomi2021@gmail.com> wrote:
> >
> > >
> > >
> > > On Sun, Jul 9, 2023 at 5:52 AM Michael Niedermayer <
> michael@niedermayer.cc>
> > > wrote:
> > >
> > >> On Fri, Jul 07, 2023 at 10:05:27PM +0800, Nuo Mi wrote:
> > >> > The executor design pattern was inroduced by java
> > >> > <
> > >>
> https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/Executor.html
> > >> >
> > >> > it also adapted by python
> > >> > <https://docs.python.org/3/library/concurrent.futures.html>
> > >> > Compared to handcrafted thread pool management, it greatly
> simplifies
> > >> the thread code.
> > >> > ---
> > >> >  libavcodec/Makefile     |   1 +
> > >> >  libavcodec/executor.c   | 182
> ++++++++++++++++++++++++++++++++++++++++
> > >> >  libavcodec/executor.h   |  67 +++++++++++++++
> > >> >  libavcodec/vvc/Makefile |   4 +
> > >> >  4 files changed, 254 insertions(+)
> > >> >  create mode 100644 libavcodec/executor.c
> > >> >  create mode 100644 libavcodec/executor.h
> > >> >  create mode 100644 libavcodec/vvc/Makefile
> > >>
> > >> This seems to need some fallback if pthreads are unavailable
> > >>
> > >> src/libavcodec/executor.c: In function ‘executor_worker_task’:
> > >> src/libavcodec/executor.c:64:5: error: implicit declaration of
> function
> > >> ‘pthread_mutex_lock’; did you mean ‘ff_mutex_lock’?
> > >> [-Werror=implicit-function-declaration]
> > >>      pthread_mutex_lock(&e->lock);
> > >>      ^~~~~~~~~~~~~~~~~~
> > >>      ff_mutex_lock
> > >> ...
> > >> cc1: some warnings being treated as errors
> > >> ffmpeg/ffbuild/common.mak:81: recipe for target
> 'libavcodec/executor.o'
> > >> failed
> > >> make: *** [libavcodec/executor.o] Error 1
> > >> make: *** Waiting for unfinished jobs....
> > >>
> > >> thx
> > >>
> > >> [...]
> > >> --
> > >> Michael     GnuPG fingerprint:
> 9FF2128B147EF6730BADF133611EC787040B0FAB
> > >>
> > >> Why not whip the teacher when the pupil misbehaves? -- Diogenes of
> Sinope
> > >> _______________________________________________
> > >> ffmpeg-devel mailing list
> > >> ffmpeg-devel@ffmpeg.org
> > >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > >>
> > >> To unsubscribe, visit link above, or email
> > >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> > >>
> > > Hi Michael,
> > > Thank you for the information. I will fixed it
> > >
> >
> > Hi Michael,
> > FFmpeg application has a dependency on threads
> > Once I --disable-pthreads, I can't build ffmpeg for the decoder md5 test.
> > Could you share with me how to test the decoder when we disabled threads?
>
> it seems this failure occurred on a ppc cross compile
> ../configure --target-os=linux --enable-cross-compile --disable-iconv
> --disable-pthreads && make -j32




> but its reproduceable on ubuntu x86-64 with a simple
>  make distclean ; ./configure   --disable-pthreads && make -j32
>
I used a similar configuration, but both my configuration and the one you
provided do not generate the ffmpeg executable. We only get the ffprobe.
However, I need the ffmpeg executable to obtain the MD5 for the conformance
test.



>
> [...]
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> Those who are too smart to engage in politics are punished by being
> governed by those who are dumber. -- Plato
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
Michael Niedermayer July 18, 2023, 2:55 p.m. UTC | #8
On Tue, Jul 18, 2023 at 07:53:09PM +0800, Nuo Mi wrote:
> On Tue, Jul 18, 2023 at 6:14 AM Michael Niedermayer <michael@niedermayer.cc>
> wrote:
> 
> > On Sun, Jul 16, 2023 at 02:28:53PM +0800, Nuo Mi wrote:
> > > On Mon, Jul 10, 2023 at 3:41 PM Nuo Mi <nuomi2021@gmail.com> wrote:
> > >
> > > >
> > > >
> > > > On Sun, Jul 9, 2023 at 5:52 AM Michael Niedermayer <
> > michael@niedermayer.cc>
> > > > wrote:
> > > >
> > > >> On Fri, Jul 07, 2023 at 10:05:27PM +0800, Nuo Mi wrote:
> > > >> > The executor design pattern was inroduced by java
> > > >> > <
> > > >>
> > https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/Executor.html
> > > >> >
> > > >> > it also adapted by python
> > > >> > <https://docs.python.org/3/library/concurrent.futures.html>
> > > >> > Compared to handcrafted thread pool management, it greatly
> > simplifies
> > > >> the thread code.
> > > >> > ---
> > > >> >  libavcodec/Makefile     |   1 +
> > > >> >  libavcodec/executor.c   | 182
> > ++++++++++++++++++++++++++++++++++++++++
> > > >> >  libavcodec/executor.h   |  67 +++++++++++++++
> > > >> >  libavcodec/vvc/Makefile |   4 +
> > > >> >  4 files changed, 254 insertions(+)
> > > >> >  create mode 100644 libavcodec/executor.c
> > > >> >  create mode 100644 libavcodec/executor.h
> > > >> >  create mode 100644 libavcodec/vvc/Makefile
> > > >>
> > > >> This seems to need some fallback if pthreads are unavailable
> > > >>
> > > >> src/libavcodec/executor.c: In function ‘executor_worker_task’:
> > > >> src/libavcodec/executor.c:64:5: error: implicit declaration of
> > function
> > > >> ‘pthread_mutex_lock’; did you mean ‘ff_mutex_lock’?
> > > >> [-Werror=implicit-function-declaration]
> > > >>      pthread_mutex_lock(&e->lock);
> > > >>      ^~~~~~~~~~~~~~~~~~
> > > >>      ff_mutex_lock
> > > >> ...
> > > >> cc1: some warnings being treated as errors
> > > >> ffmpeg/ffbuild/common.mak:81: recipe for target
> > 'libavcodec/executor.o'
> > > >> failed
> > > >> make: *** [libavcodec/executor.o] Error 1
> > > >> make: *** Waiting for unfinished jobs....
> > > >>
> > > >> thx
> > > >>
> > > >> [...]
> > > >> --
> > > >> Michael     GnuPG fingerprint:
> > 9FF2128B147EF6730BADF133611EC787040B0FAB
> > > >>
> > > >> Why not whip the teacher when the pupil misbehaves? -- Diogenes of
> > Sinope
> > > >> _______________________________________________
> > > >> ffmpeg-devel mailing list
> > > >> ffmpeg-devel@ffmpeg.org
> > > >> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> > > >>
> > > >> To unsubscribe, visit link above, or email
> > > >> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> > > >>
> > > > Hi Michael,
> > > > Thank you for the information. I will fixed it
> > > >
> > >
> > > Hi Michael,
> > > FFmpeg application has a dependency on threads
> > > Once I --disable-pthreads, I can't build ffmpeg for the decoder md5 test.
> > > Could you share with me how to test the decoder when we disabled threads?
> >
> > it seems this failure occurred on a ppc cross compile
> > ../configure --target-os=linux --enable-cross-compile --disable-iconv
> > --disable-pthreads && make -j32
> 
> 
> 
> 
> > but its reproduceable on ubuntu x86-64 with a simple
> >  make distclean ; ./configure   --disable-pthreads && make -j32
> >
> I used a similar configuration, but both my configuration and the one you
> provided do not generate the ffmpeg executable. We only get the ffprobe.
> However, I need the ffmpeg executable to obtain the MD5 for the conformance
> test.

I dont understand fully

the command results in a build failure of libavcodec, theres no need to build
the ffmpeg excutable, it should fail before

Its true that once the build failure is corrected that testing this without pthreads
would be impossible with tools depending on pthreads and build in the same pass.
I dont think testing beyond build is critical here.
But you can test it likely either with older ffmpeg or with a bit of "duct tape"
building with shared libs building ffmpeg with threads and linking to libs build
without. Again, iam not asking fr that to be done, iam happy if the build failure is
fixed and its believed to work.

thx

[...]
Nuo Mi July 19, 2023, 1:19 p.m. UTC | #9
On Wed, Jul 19, 2023 at 9:13 PM Nuo Mi <nuomi2021@gmail.com> wrote:

> The executor design pattern was inroduced by java
> <
> https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/concurrent/Executor.html
> >
> it also adapted by python
> <https://docs.python.org/3/library/concurrent.futures.html>
> Compared to handcrafted thread pool management, it greatly simplifies the
> thread code.
> ---
>  libavutil/Makefile   |   2 +
>  libavutil/executor.c | 201 +++++++++++++++++++++++++++++++++++++++++++
>  libavutil/executor.h |  67 +++++++++++++++
>  3 files changed, 270 insertions(+)
>  create mode 100644 libavutil/executor.c
>  create mode 100644 libavutil/executor.h
>
> diff --git a/libavutil/Makefile b/libavutil/Makefile
> index bd9c6f9e32..b40aacdd06 100644
> --- a/libavutil/Makefile
> +++ b/libavutil/Makefile
> @@ -31,6 +31,7 @@ HEADERS = adler32.h
>                \
>            encryption_info.h                                             \
>            error.h                                                       \
>            eval.h                                                        \
> +          executor.h                                                    \
>            fifo.h                                                        \
>            file.h                                                        \
>            frame.h                                                       \
> @@ -126,6 +127,7 @@ OBJS = adler32.o
>                   \
>         encryption_info.o                                                \
>         error.o                                                          \
>         eval.o                                                           \
> +       executor.o                                                       \
>         fifo.o                                                           \
>         file.o                                                           \
>         file_open.o                                                      \
> diff --git a/libavutil/executor.c b/libavutil/executor.c
> new file mode 100644
> index 0000000000..9823fc3d5d
> --- /dev/null
> +++ b/libavutil/executor.c
> @@ -0,0 +1,201 @@
> +/*
> + * Copyright (C) 2023 Nuo Mi
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +#include "internal.h"
> +#include "mem.h"
> +#include "thread.h"
> +
> +#include "executor.h"
> +
> +#if !HAVE_THREADS
> +#define pthread_create(t, a, s, ar)     0
> +#define pthread_join(t, r)              do {} while(0)
> +
> +#define pthread_cond_init(c, a)         0
> +#define pthread_cond_broadcast(c)       do {} while(0)
> +#define pthread_cond_signal(c)          do {} while(0)
> +#define pthread_cond_wait(c, m)         do {} while(0)
> +#define pthread_cond_destroy(c)         do {} while(0)
> +
> +#define pthread_mutex_init(m, a)        0
> +#define pthread_mutex_lock(l)           do {} while(0)
> +#define pthread_mutex_unlock(l)         do {} while(0)
> +#define pthread_mutex_destroy(l)        do {} while(0)
> +#endif
> +
> +typedef struct ThreadInfo {
> +    AVExecutor *e;
> +    pthread_t thread;
> +} ThreadInfo;
> +
> +struct AVExecutor {
> +    AVTaskCallbacks cb;
> +    int thread_count;
> +
> +    ThreadInfo *threads;
> +    uint8_t *local_contexts;
> +
> +    pthread_mutex_t lock;
> +    pthread_cond_t cond;
> +    int die;
> +
> +    AVTask *tasks;
> +};
> +
> +static AVTask* remove_task(AVTask **prev, AVTask *t)
> +{
> +    *prev  = t->next;
> +    t->next = NULL;
> +    return t;
> +}
> +
> +static void add_task(AVTask **prev, AVTask *t)
> +{
> +    t->next = *prev;
> +    *prev   = t;
> +}
> +
> +static int run_one_task(AVExecutor *e, void *lc)
> +{
> +    AVTaskCallbacks *cb = &e->cb;
> +    AVTask **prev;
> +
> +    for (prev = &e->tasks; *prev && !cb->ready(*prev, cb->user_data);
> prev = &(*prev)->next)
> +        /* nothing */;
> +    if (*prev) {
> +        AVTask *t = remove_task(prev, *prev);
> +        pthread_mutex_unlock(&e->lock);
> +        cb->run(t, lc, cb->user_data);
> +        pthread_mutex_lock(&e->lock);
> +        return 1;
> +    }
> +    return 0;
> +}
> +
> +#if HAVE_THREADS
> +static void *executor_worker_task(void *data)
> +{
> +    ThreadInfo *ti = (ThreadInfo*)data;
> +    AVExecutor *e  = ti->e;
> +    void *lc       = e->local_contexts + (ti - e->threads) *
> e->cb.local_context_size;
> +
> +    pthread_mutex_lock(&e->lock);
> +    while (1) {
> +        if (e->die) break;
> +
> +        if (!run_one_task(e, lc)) {
> +            //no task in one loop
> +            pthread_cond_wait(&e->cond, &e->lock);
> +        }
> +    }
> +    pthread_mutex_unlock(&e->lock);
> +    return NULL;
> +}
> +#endif
> +
> +static void executor_free(AVExecutor *e, const int has_lock, const int
> has_cond)
> +{
> +    if (e->thread_count) {
> +        //signal die
> +        pthread_mutex_lock(&e->lock);
> +        e->die = 1;
> +        pthread_cond_broadcast(&e->cond);
> +        pthread_mutex_unlock(&e->lock);
> +
> +        for (int i = 0; i < e->thread_count; i++)
> +            pthread_join(e->threads[i].thread, NULL);
> +    }
> +    if (has_cond)
> +        pthread_cond_destroy(&e->cond);
> +    if (has_lock)
> +        pthread_mutex_destroy(&e->lock);
> +
> +    av_free(e->threads);
> +    av_free(e->local_contexts);
> +
> +    av_free(e);
> +}
> +
> +AVExecutor* avpriv_executor_alloc(const AVTaskCallbacks *cb, int
> thread_count)
> +{
> +    AVExecutor *e;
> +    int has_lock = 0, has_cond = 0;
> +    if (!cb || !cb->user_data || !cb->ready || !cb->run ||
> !cb->priority_higher)
> +        return NULL;
> +
> +    e = av_calloc(1, sizeof(*e));
> +    if (!e)
> +        return NULL;
> +    e->cb = *cb;
> +
> +    e->local_contexts = av_calloc(thread_count, e->cb.local_context_size);
> +    if (!e->local_contexts)
> +        goto free_executor;
> +
> +    e->threads = av_calloc(thread_count, sizeof(*e->threads));
> +    if (!e->threads)
> +        goto free_executor;
> +
> +    has_lock = !pthread_mutex_init(&e->lock, NULL);
> +    has_cond = !pthread_cond_init(&e->cond, NULL);
> +
> +    if (!has_lock || !has_cond)
> +        goto free_executor;
> +
> +    for (/* nothing */; e->thread_count < thread_count;
> e->thread_count++) {
> +        ThreadInfo *ti = e->threads + e->thread_count;
> +        ti->e = e;
> +        if (pthread_create(&ti->thread, NULL, executor_worker_task, ti))
> +            goto free_executor;
> +    }
> +    return e;
> +
> +free_executor:
> +    executor_free(e, has_lock, has_cond);
> +    return NULL;
> +}
> +
> +void avpriv_executor_free(AVExecutor **executor)
> +{
> +    if (!executor || !*executor)
> +        return;
> +    executor_free(*executor, 1, 1);
> +    *executor = NULL;
> +}
> +
> +void avpriv_executor_execute(AVExecutor *e, AVTask *t)
> +{
> +    AVTaskCallbacks *cb = &e->cb;
> +    AVTask **prev;
> +
> +    pthread_mutex_lock(&e->lock);
> +    if (t) {
> +        for (prev = &e->tasks; *prev && cb->priority_higher(*prev, t);
> prev = &(*prev)->next)
> +            /* nothing */;
> +        add_task(prev, t);
> +    }
> +    pthread_cond_signal(&e->cond);
> +    pthread_mutex_unlock(&e->lock);
> +
> +#if !HAVE_THREADS
> +    // We are running in a single-threaded environment, so we must handle
> all tasks ourselves
> +    while (run_one_task(e, e->local_contexts))
> +        /* nothing */;
> +#endif
> +}
> diff --git a/libavutil/executor.h b/libavutil/executor.h
> new file mode 100644
> index 0000000000..48c7d47c7d
> --- /dev/null
> +++ b/libavutil/executor.h
> @@ -0,0 +1,67 @@
> +/*
> + * Copyright (C) 2023 Nuo Mi
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +#ifndef AVUTIL_EXECUTOR_H
> +#define AVUTIL_EXECUTOR_H
> +
> +typedef struct AVExecutor AVExecutor;
> +typedef struct AVTask AVTask;
> +
> +struct AVTask {
> +    AVTask *next;
> +};
> +
> +typedef struct AVTaskCallbacks {
> +    void *user_data;
> +
> +    int local_context_size;
> +
> +    // return 1 if a's priority > b's priority
> +    int (*priority_higher)(const AVTask *a, const AVTask *b);
> +
> +    // task is ready for run
> +    int (*ready)(const AVTask *t, void *user_data);
> +
> +    // run the task
> +    int (*run)(AVTask *t, void *local_context, void *user_data);
> +} AVTaskCallbacks;
> +
> +/**
> + * Alloc executor
> + * @param callbacks callback strucutre for executor
> + * @param thread_count worker thread number
> + * @return return the executor
> + */
> +AVExecutor* avpriv_executor_alloc(const AVTaskCallbacks *callbacks, int
> thread_count);
> +
> +/**
> + * Free executor
> + * @param e  pointer to executor
> + */
> +void avpriv_executor_free(AVExecutor **e);
> +
> +/**
> + * Add task to executor
> + * @param e pointer to executor
> + * @param t pointer to task. If NULL, it will wakeup one work thread
> + */
> +void avpriv_executor_execute(AVExecutor *e, AVTask *t);
> +
> +#endif //AVUTIL_EXECUTOR_H
> --
> 2.25.1
>
>
Changes since v2:
* move executor to avutil
* fix for --disable-pthreads
* remove duplicate free code in executor_alloc and executor_free

I also disabled the HAVE_THREADS macro in this file only for testing
purposes.
The executor is also functional in single-threaded environments.
diff mbox series

Patch

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 1b0226c089..4a3c7a7a1f 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -62,6 +62,7 @@  OBJS = ac3_parser.o                                                     \
        xiph.o                                                           \
 
 # subsystems
+include $(SRC_PATH)/libavcodec/vvc/Makefile
 OBJS-$(CONFIG_AANDCTTABLES)            += aandcttab.o
 OBJS-$(CONFIG_AC3DSP)                  += ac3dsp.o ac3.o ac3tab.o
 OBJS-$(CONFIG_ADTS_HEADER)             += adts_header.o mpeg4audio_sample_rates.o
diff --git a/libavcodec/executor.c b/libavcodec/executor.c
new file mode 100644
index 0000000000..c5094f608a
--- /dev/null
+++ b/libavcodec/executor.c
@@ -0,0 +1,182 @@ 
+/*
+ * VVC video Decoder
+ *
+ * Copyright (C) 2022 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "libavutil/avutil.h"
+#include "libavutil/thread.h"
+
+#include "executor.h"
+
+typedef struct ThreadInfo {
+    Executor *e;
+    pthread_t thread;
+} ThreadInfo;
+
+struct Executor {
+    TaskletCallbacks cb;
+    ThreadInfo *threads;
+    uint8_t *local_contexts;
+    int thread_count;
+
+    pthread_mutex_t lock;
+    pthread_cond_t cond;
+    int die;
+    Tasklet *tasks;
+};
+
+static void remove_task(Tasklet **prev, Tasklet *t)
+{
+    *prev  = t->next;
+    t->next = NULL;
+}
+
+static void add_task(Tasklet **prev, Tasklet *t)
+{
+    t->next = *prev;
+    *prev   = t;
+}
+
+static void *executor_worker_task(void *data)
+{
+    ThreadInfo *ti = (ThreadInfo*)data;
+    Executor *e = ti->e;
+    void *lc       = e->local_contexts + (ti - e->threads) * e->cb.local_context_size;
+    Tasklet **prev;
+    TaskletCallbacks *cb = &e->cb;
+
+    pthread_mutex_lock(&e->lock);
+    while (1) {
+        Tasklet* t = NULL;
+        if (e->die) break;
+
+        for (prev = &e->tasks; *prev; prev = &(*prev)->next) {
+            if (cb->ready(*prev, cb->user_data)) {
+                t = *prev;
+                break;
+            }
+        }
+        if (t) {
+            //found one task
+            remove_task(prev, t);
+            pthread_mutex_unlock(&e->lock);
+            cb->run(t, lc, cb->user_data);
+            pthread_mutex_lock(&e->lock);
+        } else {
+            //no task in one loop
+            pthread_cond_wait(&e->cond, &e->lock);
+        }
+    }
+    pthread_mutex_unlock(&e->lock);
+    return NULL;
+}
+
+Executor* ff_executor_alloc(const TaskletCallbacks *cb, int thread_count)
+{
+    Executor *e;
+    int i, j, ret;
+    if (!cb || !cb->user_data || !cb->ready || !cb->run || !cb->priority_higher)
+        return NULL;
+    e = av_calloc(1, sizeof(*e));
+    if (!e)
+        return NULL;
+    e->cb = *cb;
+
+    e->local_contexts = av_calloc(thread_count, e->cb.local_context_size);
+    if (!e->local_contexts)
+        goto free_executor;
+
+    e->threads = av_calloc(thread_count, sizeof(*e->threads));
+    if (!e->threads)
+        goto free_contexts;
+    ret = pthread_mutex_init(&e->lock, NULL);
+    if (ret)
+        goto free_threads;
+
+    ret = pthread_cond_init(&e->cond, NULL);
+    if (ret)
+        goto destroy_lock;
+
+    for (i = 0; i < thread_count; i++) {
+        ThreadInfo *ti = e->threads + i;
+        ti->e = e;
+        ret = pthread_create(&ti->thread, NULL, executor_worker_task, ti);
+        if (ret)
+            goto join_threads;
+    }
+    e->thread_count = thread_count;
+    return e;
+
+join_threads:
+    pthread_mutex_lock(&e->lock);
+    e->die = 1;
+    pthread_cond_broadcast(&e->cond);
+    pthread_mutex_unlock(&e->lock);
+    for (j = 0; j < i; j++)
+        pthread_join(e->threads[j].thread, NULL);
+    pthread_cond_destroy(&e->cond);
+destroy_lock:
+    pthread_mutex_destroy(&e->lock);
+free_threads:
+    av_free(e->threads);
+free_contexts:
+    av_free(e->local_contexts);
+free_executor:
+    free(e);
+    return NULL;
+}
+
+void ff_executor_free(Executor **executor)
+{
+    Executor *e;
+    if (!executor || !*executor)
+        return;
+    e = *executor;
+
+    //singal die
+    pthread_mutex_lock(&e->lock);
+    e->die = 1;
+    pthread_cond_broadcast(&e->cond);
+    pthread_mutex_unlock(&e->lock);
+
+    for (int i = 0; i < e->thread_count; i++)
+        pthread_join(e->threads[i].thread, NULL);
+    pthread_cond_destroy(&e->cond);
+    pthread_mutex_destroy(&e->lock);
+
+    av_free(e->threads);
+    av_free(e->local_contexts);
+
+    av_freep(executor);
+}
+
+void ff_executor_execute(Executor *e, Tasklet *t)
+{
+    TaskletCallbacks *cb = &e->cb;
+    Tasklet **prev;
+
+    pthread_mutex_lock(&e->lock);
+    if (t) {
+        for (prev = &e->tasks; *prev && cb->priority_higher(*prev, t); prev = &(*prev)->next)
+            /* nothing */;
+        add_task(prev, t);
+    }
+    pthread_cond_signal(&e->cond);
+    pthread_mutex_unlock(&e->lock);
+}
\ No newline at end of file
diff --git a/libavcodec/executor.h b/libavcodec/executor.h
new file mode 100644
index 0000000000..4b641acce6
--- /dev/null
+++ b/libavcodec/executor.h
@@ -0,0 +1,67 @@ 
+/*
+ * Copyright (C) 2022 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_EXECUTOR_H
+#define AVCODEC_EXECUTOR_H
+
+typedef struct Executor Executor;
+typedef struct Tasklet Tasklet;
+
+struct Tasklet {
+    Tasklet *next;
+};
+
+typedef struct TaskletCallbacks {
+    void *user_data;
+
+    int local_context_size;
+
+    // return 1 if a's priority > b's priority
+    int (*priority_higher)(const Tasklet *a, const Tasklet *b);
+
+    // task is ready for run
+    int (*ready)(const Tasklet *t, void *user_data);
+
+    // run the task
+    int (*run)(Tasklet *t, void *local_context, void *user_data);
+} TaskletCallbacks;
+
+/**
+ * Alloc executor
+ * @param callbacks callback strucutre for executor
+ * @param thread_count worker thread number
+ * @return return the executor
+ */
+Executor* ff_executor_alloc(const TaskletCallbacks *callbacks, int thread_count);
+
+/**
+ * Free executor
+ * @param e  pointer to executor
+ */
+void ff_executor_free(Executor **e);
+
+/**
+ * Add task to executor
+ * @param e pointer to executor
+ * @param t pointer to task. If NULL, it will wakeup one work thread
+ */
+void ff_executor_execute(Executor *e, Tasklet *t);
+
+#endif //AVCODEC_EXECUTOR_H
diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile
new file mode 100644
index 0000000000..16cdd04307
--- /dev/null
+++ b/libavcodec/vvc/Makefile
@@ -0,0 +1,4 @@ 
+clean::
+	$(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%)
+
+OBJS-$(CONFIG_VVC_DECODER)          +=  executor.o