Library initialization — pthread_once in Win32 imp

2020-07-18 03:54发布

Hello. I am trying to make a fully thread-safe initialization function for my library and I couldn't easily find an alternative to pthread_once, which should solve the problem very easily. I've come to this code:


void libInit (void)
{
#ifdef WIN32
    static volatile int initialized = 0;
    static HANDLE mtx;

    if (!initialized)
    {
        if (!mtx)
        {
            HANDLE mymtx;
            mymtx = CreateMutex(NULL, 0, NULL);
            if (InterlockedCompareExchangePointer(&mtx, mymtx, NULL) != NULL)
                CloseHandle(mymtx);
        }

        WaitForSingleObject(mtx);
        if (!initialized)
        {
            libInitInternal();
            initialized = 1;
        }
        ReleaseMutex(mtx);
    }
#else
    static pthread_once_t initialized = PTHREAD_ONCE_INIT;

    pthread_once(&initialized, libInitInternal);
#endif
}

The libInitInternal() call leads to a thread-unsafe function, that initializes the library.

I would like to hear any suggestions on what I could be doing wrong or whether you know about a better solution.

5条回答
做个烂人
2楼-- · 2020-07-18 04:22

When using GCC or clang, you can use constructor and destructor attributes. These work for both shared and static libraries, and execute code before and after main is run, respectively. Additionally, you can specify multiple constructor and destructor functions. Much cleaner than the singleton approach, and doesn't require you to remember to call libInit() from your main().

static void __attribute__((constructor))
your_lib_init(void)
{
    fprintf(stderr, "library init\n");
}

static void __attribute__((destructor))
vensim_ctx_destroy(void)
{
    fprintf(stderr, "library destroy\n");
}
查看更多
老娘就宠你
3楼-- · 2020-07-18 04:23

I would check out this article. It is a solution for C++ singletons, but I believe you can use the solution for your code as well: http://www.ddj.com/cpp/199203083?pgno=1

Sadly the listing for the QLock itself is missing, it looks as if they are trying to sell the CD, but there appears to be enough description of it to write one yourself.

查看更多
We Are One
4楼-- · 2020-07-18 04:32

I think you want to use the One-Time Initialization functionality. In synchronous mode, all threads block until the first thread to call it completes. Seems analogous to pthread_once().

There is sample code here.

So in your case, you would say:

BOOL CALLBACK CallLibInitInternal(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex) {
    libInitInternal();
    return TRUE;
}

void libInit() {
#ifdef WIN32
    static INIT_ONCE s_init_once;
    InitOnceExecuteOnce(&s_init_once, CallLibInitInternal, NULL, NULL);
#else
...
#endif
}
查看更多
啃猪蹄的小仙女
5楼-- · 2020-07-18 04:36

You might want to check what pthreads-win32 does in its pthread_once() implementaion. or just use that, if that proves to be easier.

查看更多
Root(大扎)
6楼-- · 2020-07-18 04:48

After looking at the following source code for pthread_once() (from here), It looks like you're on the right track.

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
{
    /* Check first for speed */
    if (once_control->state == PTHREAD_NEEDS_INIT) {
        pthread_mutex_lock(&(once_control->mutex));
        if (once_control->state == PTHREAD_NEEDS_INIT) {
            init_routine();
            once_control->state = PTHREAD_DONE_INIT;
        }
        pthread_mutex_unlock(&(once_control->mutex));
    }
    return(OK);
}

btw, I'll be using pthread_once() to replace some rather convoluted functions in my code.

查看更多
登录 后发表回答