0

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

Vern
  • 3
  • 2
  • Are you sure you're calling it correctly? Not an expert on gtk, but according to the [docs](https://developer.gnome.org/glibmm/stable/classGlib_1_1SignalIdle.html) connect_once is not thread-safe. – rustyx Jan 22 '21 at 21:52
  • Below is copied from the doc. Because I'm using std::bind, I took this to mean that it **is** thread safe. `Because sigc::trackable is not thread-safe, if the slot represents a non-static method of a class deriving from sigc::trackable, and the slot is created by sigc::mem_fun(), connect_once() should only be called from the thread where the SignalIdle object's MainContext runs. You can use, say, boost::bind() or, in C++11, std::bind() or a C++11 lambda expression instead of sigc::mem_fun().` – Vern Jan 22 '21 at 21:59
  • Are you running MainContext in your thread or in the main thread? – rustyx Jan 22 '21 at 22:33
  • My first line in main() is **Gtk::Main kit(0, NULL);** and then later within main() I call **kit.run()**. I believe that means that MainContext is running in the main thread. – Vern Jan 22 '21 at 22:41
  • I'm wondering if this particular one could be the "Unsynchronized access to contention counters in slice allocator." listed here [link](https://gitlab.gnome.org/GNOME/glib/-/issues/1672). I say that because g_slice_alloc0 is listed in that first stack trace. I'm new to TSAN and I'm thinking maybe I should just suppress all TSAN errors coming from glib. I'm seeing some other ones from glib that I'm not so sure about either and that link shows they have plenty of TSAN errors to clean up. – Vern Jan 22 '21 at 23:12
  • If there are false positives involving the slice allocator, you can run your program with the environment variable `G_SLICE=always-malloc` when doing the sanitizer checks. – ptomato Jan 23 '21 at 20:01
  • @ptomato thanks for the suggestion. I gave it a try and it ends up producing 7 of data races now which all originate from that connect_once call (If I comment it out they all go away). It's too big to post it all in the comments here. – Vern Jan 25 '21 at 16:15
  • What is odd to me is in the glib documentation they admit sigc::slot is not thread-safe but claim that connect_once is thread-safe even though it takes a sigc::slot object. They say to use std::bind instead but that doesn't make sense to me because isn't the std::bind output just going to get converted to a sigc::slot before the call to connect_once so it still won't be thread-safe? I think that is what these TSAN error are showing. – Vern Jan 25 '21 at 16:15
  • I think I'm going to abandon the connect_once function since I was able to come up with an alternate solution that uses Glib::Dispatcher instead and runs error free with TSAN. – Vern Jan 25 '21 at 16:16

1 Answers1

0

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.

arkq
  • 46
  • 5