#376 std::mutex leaks Windows semaphore handle when destroyed

v1.0 (example)
open
nobody
5
2014-01-29
2014-01-13
Andrey H.
No

The C++11 code:

for(;;) {
std::mutex op_mutex;
op_mutex.lock();
op_mutex.unlock();
}

leaks one Windows semaphore handle on each loop iteration. Tested on latest 4.8.2 32bit build, threads posix.

Critical for programs, which create/destroy mutexes in runtime, because handles leak is continiously growing.

Discussion

  • Andrey H.
    Andrey H.
    2014-01-13

    I suspect a problem in mutex.c which implements pthread mutex. When C++11 std::mutex is created, native_handle is initialized by GTHREAD_MUTEX_INIT value. Windows semaphore object is created later by first mutex.lock() invocation, but it is never closed by CloseHandle. std::mutex destructor also does not call __gthread_mutex_destroy, because C++ library assumes that if mutex is intiaized with a value, then destroy method is not needed and destructor is disabled.

    Looks like a design bug of mutex.c. I think that GTHREAD_MUTEX_INIT_FUNCTION should be used instead of GTHREAD_MUTEX_INIT. It will enable propper std::mutex destructor:

    ~__mutex_base() noexcept { __gthread_mutex_destroy(&_M_mutex); }
    

    However, this will always create a Windows semaphore object when std::mutex is created.

     
  • Kai Tietz
    Kai Tietz
    2014-01-14

    Yeah, this seems to me like a c++ bug11 of implementation using pthread.
    Could you please report this issue on gcc's BZ?
    I don't see how we could solve this in context of winpthread.

     
  • Andrey H.
    Andrey H.
    2014-01-14

    Kai, I would propose a fix in gcc c++11 implementation to make the mutex_base destructor permanently enabled, disregard GTHREAD_MUTEX_INIT / GTHREAD_MUTEX_INIT_FUNCTION defines. I have already tested this fix in my project.

    From your side, can you please confirm, that calling gthread_mutex_destroy(&_M_mutex) is safe for a mutex which has not been created, i.e. lock was never called and mutex was just statically initialized with GTHREAD_MUTEX_INIT value. For me it looks safe, though.

     
  • Kai Tietz
    Kai Tietz
    2014-01-14

    Yes, by standard result of calling pthread_mutex_destroy on an none-initialized mutex should result either EINVAL (if mutex is NULL), and zero if mutex has still value of static initializer.

    So yes, I would assume it is fine.

     
  • Andrey H.
    Andrey H.
    2014-01-15

    GCC guru is basically saying, that gth-default.h (which is Mingw-w64 owned file, right? At least, it is not present in libgcc sources) should undefine GTHREAD_MUTEX_INIT, so GTHREAD_MUTEX_INIT_FUNCTION definition remains and enables std::mutex destructor. Kai, what do you think about this way?

     
  • Andrey H.
    Andrey H.
    2014-01-29

    I did some extensive testing and a proposed solution is just to add

    define _GTHREAD_USE_MUTEX_INIT_FUNC

    into the libstdc++-v3/config/os/mingw32-w64/os_defines.h which is indeed part of GCC itself. It will enable the usage of pthread_mutex_init/destory methods in __mutex_base c-tor and d-tor respectively.

    During testing I also discovered a strange behaviour, namely if running the code:

    for(;;) {
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_destroy(&mutex);
    }

    in two parallel threads, then semaphore handles are leaking like crazy. But I'm not sure, if pthread_mutex_destroy method is allowed to be called for statically initialized mutexes or not.