rttrorg / rttr

C++ Reflection Library
https://www.rttr.org
MIT License
3.06k stars 427 forks source link

rttr::type::get_by_name() is not thread safe #246

Open mrduda opened 5 years ago

mrduda commented 5 years ago

Consider an example when a thread asks for a type using templated rttr::type::get call which can auto-register a type, at a same time when another thread uses rttr::type::get_by_name call without any mutex lock in the implementation which leads to the corrupt memory access.

mrduda commented 5 years ago

Since I already have a working fix for this in my own repository, I will paste it there if it can help.

Add this to public section of type_register_private class (rttr/src/rttr/detail/type/type_register_p.h): type get_by_custom_name(string_view custom_name);

Implement it in the rttr/src/rttr/detail/type/type_register.cpp:

type type_register_private::get_by_custom_name(string_view custom_name)
{
    std::lock_guard<std::mutex> lock(m_mutex);

    auto ret = m_custom_name_to_id.find(custom_name);
    if (ret != m_custom_name_to_id.end())
    {
        return (*ret);
    }

    return get_invalid_type();
}

Modify the type::get_by_name in rttr/src/rttr/type.cpp to be like this:

type type::get_by_name(string_view name) RTTR_NOEXCEPT
{
    return detail::type_register_private::get_instance().get_by_custom_name(name);
}

That's it.

acki-m commented 4 years ago

How are you using the registration process? Are you using it with a plugin system? Because ideally every type should be registered when you enter the main() function

mrduda commented 4 years ago

I do register all object types I work with in static initializer code. However, there are types like built-ins, containers and pointers which I don’t register manually. Such types can be requested (and auto-registered if not registered manually before) by name at any time, from any thread. For example, when deserialization of object from a binary stream is performed in background thread during load of the game scene. There is no guarantee that every single possible type is registered at the moment of type request. To achieve that, you have to write a plenty of manual registration code in main(), including types of containers, built-in types, const and non-const and so on.

P.S. yet another place where mutex lock is required is register_name_if_neccessary() function