Why is ThreadSanitizer (TSAN) reporting data race on Glib::signal_idle().connect_once

674 views Asked by At

Contents of hello.cpp

#include <gtkmm.h>

void RunInMain()
{
    printf("RunInMain\n");
}

void ThreadFunc()
{
    printf("ThreadFunc\n");
    Glib::signal_idle().connect_once(std::bind(&RunInMain));
}

int main()
{
    Gtk::Main kit(0, NULL);

    Gtk::Window window;
    window.set_title("hello world");
    Glib::Thread* pThread = Glib::Thread::create(&ThreadFunc);
    kit.run(window);
    pThread->join();
    return(0);
}

Compile with:

g++ `pkg-config gtkmm-2.4 --cflags --libs` hello.cpp  -Wno-deprecated-declarations -fsanitize=thread

This is the error from TSAN when executing the resulting a.out file:

WARNING: ThreadSanitizer: data race (pid=153699)
  Write of size 8 at 0x7b5000006f90 by thread T1:
    #0 memset <null> (libtsan.so.0+0x37abf)
    #1 g_slice_alloc0 <null> (libglib-2.0.so.0+0x71412)
    #2 sigc::pointer_functor0<void>::operator()() const <null> (a.out+0x402835)
    #3 sigc::adaptor_functor<sigc::pointer_functor0<void> >::operator()() const <null> (a.out+0x402606)
    #4 sigc::internal::slot_call0<void (*)(), void>::call_it(sigc::internal::slot_rep*) <null> (a.out+0x4021d0)
    #5 call_thread_entry_slot /usr/include/sigc++-2.0/sigc++/functors/slot.h:535 (libglibmm-2.4.so.1+0x5d889)

  Previous write of size 8 at 0x7b5000006f90 by main thread:
    #0 posix_memalign <null> (libtsan.so.0+0x3061d)
    #1 allocator_memalign ../glib/gslice.c:1411 (libglib-2.0.so.0+0x706b8)
    #2 allocator_add_slab ../glib/gslice.c:1283 (libglib-2.0.so.0+0x706b8)
    #3 slab_allocator_alloc_chunk ../glib/gslice.c:1329 (libglib-2.0.so.0+0x706b8)
    #4 __libc_start_main ../csu/libc-start.c:308 (libc.so.6+0x27041)

  Location is heap block of size 496 at 0x7b5000006e00 allocated by main thread:
    #0 posix_memalign <null> (libtsan.so.0+0x3061d)
    #1 allocator_memalign ../glib/gslice.c:1411 (libglib-2.0.so.0+0x706b8)
    #2 allocator_add_slab ../glib/gslice.c:1283 (libglib-2.0.so.0+0x706b8)
    #3 slab_allocator_alloc_chunk ../glib/gslice.c:1329 (libglib-2.0.so.0+0x706b8)
    #4 __libc_start_main ../csu/libc-start.c:308 (libc.so.6+0x27041)

  Thread T1 (tid=153701, running) created by main thread at:
    #0 pthread_create <null> (libtsan.so.0+0x5ec29)
    #1 g_system_thread_new ../glib/gthread-posix.c:1308 (libglib-2.0.so.0+0xa0ea0)
    #2 __libc_start_main ../csu/libc-start.c:308 (libc.so.6+0x27041)

SUMMARY: ThreadSanitizer: data race (/lib64/libtsan.so.0+0x37abf) in memset

The code runs as expected (I get all of the prints) but I don't understand why I'm getting the TSAN data race warning. If I comment out the Glib::signal_idle().connect_once line, there is no TSAN error. From what I've read, that function is supposed to be safe to call from any thread. Is TSAN reporting a false positive here or is there a real data race?

Fedora 31 linux
g++ 10.0.1
glibmm24-2.64.2-1
gtkmm24-2.24.5-9
libtsan-10.2.1-9

1

There are 1 answers

0
arkq On

From TSAN wiki:

TSAN generally requires all code to be compiled with -fsanitize=thread. If some code (e.g. dynamic libraries) is not compiled with the flag, it can lead to false positive race reports, false negative race reports and/or missed stack frames in reports depending on the nature of non-instrumented code.

If you are using glib from distribution repository (e.g.: sudo apt get install libglib2.0-dev), the number of false positive reports will depend on how the library was built - number of warnings will vary from distro to distro. In order to get proper TSAN report, one should compile all used shared libraries by hand with -fsanitize=thread. In particular glib should be compiled by hand, because it contains various thread-related APIs.

Compile glib with TSAN (for Debian 11.5 "bullseye"):

# clone TAG 2.66.8 (TAG should match glib version on the host)
git clone --depth=1 --branch=2.66.8 https://github.com/GNOME/glib.git
cd glib
CFLAGS="-O2 -g -fsanitize=thread" meson build
ninja -C build

# add TSAN-enabled glib libraries to lib search path
export LD_LIBRARY_PATH=$PWD/build/gio:$PWD/build/glib:$PWD/build/gmodule:$PWD/build/gobject:$PWD/build/gthread

Before running your project, make sure that it links with freshly compiled glib libraries (all glib libraries if used, i.e.: libglib, libgio, libgmodule, libgobject, libgthread) with ldd a.out.