[FFmpeg-devel] [PATCH] w32pthreads: add pthread_once emulation

Hendrik Leppkes h.leppkes at gmail.com
Wed Oct 7 19:15:01 CEST 2015


On Wed, Oct 7, 2015 at 7:06 PM, Matt Oliver <protogonoi at gmail.com> wrote:
> On 8 October 2015 at 03:29, Hendrik Leppkes <h.leppkes at gmail.com> wrote:
>
>> On Wed, Oct 7, 2015 at 6:23 PM, Matt Oliver <protogonoi at gmail.com> wrote:
>> > On 6 October 2015 at 21:36, Hendrik Leppkes <h.leppkes at gmail.com> wrote:
>> >
>> >> The emulation uses native InitOnce* APIs on Windows Vista+, and a
>> >> lock-free/allocation-free approach using atomics and spinning for
>> Windows
>> >> XP.
>> >> ---
>> >>
>> >> This is in preparation to use pthread_once for global static init
>> >> functions,
>> >> and eventually removing the global lock in avcodec_open2
>> >>
>> >> compat/w32pthreads.h | 68
>> >> ++++++++++++++++++++++++++++++++++++++++++++++++++++
>> >>  1 file changed, 68 insertions(+)
>> >>
>> >> diff --git a/compat/w32pthreads.h b/compat/w32pthreads.h
>> >> index deb1c53..8523976 100644
>> >> --- a/compat/w32pthreads.h
>> >> +++ b/compat/w32pthreads.h
>> >> @@ -154,6 +154,19 @@ static inline int
>> pthread_cond_signal(pthread_cond_t
>> >> *cond)
>> >>      return 0;
>> >>  }
>> >>
>> >> +typedef INIT_ONCE pthread_once_t;
>> >> +#define PTHREAD_ONCE_INIT INIT_ONCE_STATIC_INIT
>> >> +
>> >> +static av_unused int pthread_once(pthread_once_t *once_control, void
>> >> (*init_routine)(void))
>> >> +{
>> >> +    BOOL pending = FALSE;
>> >> +    InitOnceBeginInitialize(once_control, 0, &pending, NULL);
>> >> +    if (pending)
>> >> +        init_routine();
>> >> +    InitOnceComplete(once_control, 0, NULL);
>> >> +    return 0;
>> >> +}
>> >> +
>> >>  #else // _WIN32_WINNT < 0x0600
>> >>  /* for pre-Windows 6.0 platforms we need to define and use our own
>> >> condition
>> >>   * variable and api */
>> >> @@ -304,6 +317,57 @@ static av_unused int
>> >> pthread_cond_signal(pthread_cond_t *cond)
>> >>      pthread_mutex_unlock(&win32_cond->mtx_broadcast);
>> >>      return 0;
>> >>  }
>> >> +
>> >> +/* for pre-Windows 6.0 platforms, define INIT_ONCE struct,
>> >> + * compatible to the one used in the native API */
>> >> +
>> >> +typedef union pthread_once_t  {
>> >> +    void * Ptr;    ///< For the Windows 6.0+ native functions
>> >> +    LONG state;    ///< For the pre-Windows 6.0 compat code
>> >> +} pthread_once_t;
>> >> +
>> >> +#define PTHREAD_ONCE_INIT {0}
>> >> +
>> >> +/* function pointers to init once API on windows 6.0+ kernels */
>> >> +static BOOL (WINAPI *initonce_begin)(pthread_once_t *lpInitOnce, DWORD
>> >> dwFlags, BOOL *fPending, void **lpContext);
>> >> +static BOOL (WINAPI *initonce_complete)(pthread_once_t *lpInitOnce,
>> DWORD
>> >> dwFlags, void *lpContext);
>> >> +
>> >> +static av_unused int pthread_once(pthread_once_t *once_control, void
>> >> (*init_routine)(void))
>> >> +{
>> >> +    /* Use native functions on Windows 6.0+ */
>> >> +    if (initonce_begin && initonce_complete)
>> >> +    {
>> >> +        BOOL pending = FALSE;
>> >> +        initonce_begin(once_control, 0, &pending, NULL);
>> >> +        if (pending)
>> >> +            init_routine();
>> >> +        initonce_complete(once_control, 0, NULL);
>> >> +        return 0;
>> >> +    }
>> >> +
>> >> +    /* pre-Windows 6.0 compat using a spin-lock */
>> >> +    switch (InterlockedCompareExchange(&once_control->state, 1, 0))
>> >> +    {
>> >> +        /* Initial run */
>> >> +        case 0:
>> >> +            init_routine();
>> >> +            InterlockedExchange(&once_control->state, 2);
>> >> +            break;
>> >> +        /* Another thread is running init */
>> >> +        case 1:
>> >> +            while (1) {
>> >> +                MemoryBarrier();
>> >> +                if (once_control->state == 2)
>> >> +                    break;
>> >> +                Sleep(0);
>> >> +            }
>> >> +            break;
>> >> +        /* Initialization complete */
>> >> +        case 2:
>> >> +            break;
>> >> +    }
>> >> +    return 0;
>> >> +}
>> >>  #endif
>> >>
>> >>  static av_unused void w32thread_init(void)
>> >> @@ -319,6 +383,10 @@ static av_unused void w32thread_init(void)
>> >>          (void*)GetProcAddress(kernel_dll, "WakeConditionVariable");
>> >>      cond_wait      =
>> >>          (void*)GetProcAddress(kernel_dll, "SleepConditionVariableCS");
>> >> +    initonce_begin =
>> >> +        (void*)GetProcAddress(kernel_dll, "InitOnceBeginInitialize");
>> >> +    initonce_complete =
>> >> +        (void*)GetProcAddress(kernel_dll, "InitOnceComplete");
>> >>  #endif
>> >>
>> >>  }
>> >> --
>> >> 2.5.3.windows.1
>> >
>> >
>> > LGTM
>>
>> There is a new version of the patch which also solves the
>> w32thread_init mess, so this one isn't the final version anymore.
>> Current working version is here:
>> https://github.com/Nevcairiel/FFmpeg/commits/pthread_once
>>
>>
> This looks better.
> So by win32thread_init mess im assuming your referring to a need to have
> called it before hand. Given that youve added it to the only 2 functions
> that need it then doesnt that mean that win32thread_init be completely
> removed from all other code and only used locally in w32pthread.h and only
> when WIN32_WINNT<0x600.

Yes, it can be removed in a follow up commit.


More information about the ffmpeg-devel mailing list