Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new library class #116

Merged
merged 23 commits into from
Mar 5, 2018
Merged

Conversation

acki-m
Copy link
Contributor

@acki-m acki-m commented Feb 13, 2018

This adds the functionality to load libraries in a simple cross platform way.

The native OS calls are wrapped behind a class interface.

Highlights:

  • handling of file suffixes (.dll or .so)
  • automatic unload of library on program exit
  • retrieving of loaded types in library
  • avoid of unnecessary loading calls, when same plugin is loaded multiple times

additional:

  • added unit tests
  • added example of loading a plugin

…form way.

The native OS calls are wrapped behind a class interface.

Highlights:

- handling of file suffixes (.dll or .so)
- automatic unload of library on program exit
- etrieving of loaded types in library
- avoid of unncessary loading calls, when same plugin is loaded multiple times

additional:

- added unit tests
- added example of loading a plugin
@acki-m acki-m requested a review from gabyx February 13, 2018 09:45
calling "dlclose()" does not unload the library immediately.
We have to define a "constructor" and "destructor" function,
in order to initialize and deinitialize our plugings.
Additionally, for gcc based compilers we have to use a compiler flag: "-fno-gnu-unique"
@coveralls
Copy link

coveralls commented Feb 13, 2018

Coverage Status

Coverage increased (+1.9%) to 92.355% when pulling b18896c on acki-m:feature-loading-of-libraries into 79b619c on rttrorg:master.

auto var = t.create();
auto value = t.invoke("some_method", var, {});
std::cout << value.to_string() << std::endl;
} // all data has to be remove before unload the library, otherwise we get UB
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this generally like this, or is this a UB due to rttr?

Copy link
Contributor Author

@acki-m acki-m Feb 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is general, when you unload the library, how can you access the data anymore (e.g. the dtor to remove the data from the variant?

However, you are safe to ignore the explicit unload() call, it will be called automatically on application exit. But when you unload the library explicitly, you have to make sure every data is removed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah makes sense :-)

@acki-m
Copy link
Contributor Author

acki-m commented Feb 13, 2018

@gabyx
I saw warnings here:
https://travis-ci.org/rttrorg/rttr/jobs/341122012
How can we remove them?

}
m_state_saver.save_state_begin();

auto result = load_native();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if I understand correctly and as far as I see so far: the load method can be called from multiple threads?
if so, isnt there some troubles with thread-safety, maybe I am missing something: A thread A can potentially be in load_native() which is just about to set the m_handle and another thread B can be at line 73 which then loads the library again, right? (thats not what you want right?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are absolutely right. This is missing. There were some compilers which didn't support threading at all, I think it was minGW 4.9.1.
Mhm, I guess I have to lift the requirements for this. Or at a fallback with using boost.
https://github.com/meganz/mingw-std-threads
When you know something, please tell me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm.. ok, I am suspecting that the whole function should be guarded (isnt it?) (not sure about the m_state_saver.) So to be absolutely sure that no other thread is currently trying to load the library.
(I have never loaded a library with dlopen so far, what I understand is that if one thread loads it, the other thread can use it too because its in shared memory, because of your singelton implementation).

I think the only thing we need here is some mutex or thread guard for the library_manager
such to prevent race condition of threads loading / unloading the library at the same time (isnt it?). std::mutex?


/*!
* A simple manager class, to hold all created library_private pointer objects. A private singleton.
* After application exit he will unload all libs without any reference.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exit, it will

* A simple manager class, to hold all created library_private pointer objects. A private singleton.
* After application exit he will unload all libs without any reference.
*/
class library_manager
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this library manager thread safe?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i mean the remove_item and create_or_find_library...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not thread safe atm.


CHECK(does_plugin_type_exist() == true);

CHECK(lib1.unload() == false);
Copy link
Contributor

@gabyx gabyx Feb 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after lib1.unload(), lib1 is not more valid (right?)
but there is lib2 which is still there (the same actually)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The library class has no valid or invalid state.
However, the same library is still loaded by another instance (ref count increased)
That's why the unload fails.

CHECK(does_plugin_type_exist() == true);

CHECK(lib1.unload() == false);
CHECK(lib1.unload() == false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another unload -> false because there is still lib2 there

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second unload does nothing, it was still unloaded.
And yes lib2 is still loaded, so no unload at all possible.


CHECK(does_plugin_type_exist() == true);

CHECK(lib2.unload() == true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok now all load counters decreased to zero -> library unloaded

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct.

Copy link
Contributor

@gabyx gabyx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as my competence allows: there are some comments which are probably non-sense, but it would be helpful anyway to get your answer. a part from that: impecable style!
I was mainly distracted from the atomic load counter, which seems to be there only for multiple threads?, if so I am not sure about thread safetyness.

Also, this plugin functionality is probably extremely useful. Easy way of loading the library, and getting the types and then construct them

@acki-m
Copy link
Contributor Author

acki-m commented Feb 14, 2018

@gabyx
When you have time, can you look at this warning on MacOS?

@gabyx
Copy link
Contributor

gabyx commented Feb 14, 2018

@acki-m: I have only xtools 8.2 on my mac at home (10 years old), the warnings come from 9.2, I will see if I can find the issue...

and also fixed not loading of plugins, because of wrong implementation errors
in "std::codecvt_utf8_utf16<wchar_t>" for mingw (fixed in >7.0.0)
and increased compiler for mingw test on appveyor
- made it possible to bin raw arrays as property
- added another cpp file for the test plugin, in order to test if these types will be also loaded
@acki-m
Copy link
Contributor Author

acki-m commented Feb 16, 2018

@gabyx
Can you also check, why the unit test fails for XCode 83 x86?
https://travis-ci.org/rttrorg/rttr/jobs/342289234

For all other system it looks good

@gabyx
Copy link
Contributor

gabyx commented Feb 16, 2018

I experience something fishy: Sometimes the make -j3 run_tests passes and sometimes not?

gabrielnuetzi @ ~/Desktop/rttr/build  (feature-loading-of-libraries)
 [38] → make -j4 run_tests
[ 32%] Built target rttr_core
[ 35%] Built target unit_test_plugin
[ 98%] Built target unit_tests
[100%] Running unit_tests
===============================================================================
All tests passed (5115 assertions in 538 test cases)

[100%] Built target run_tests
(python3.6Env)
gabrielnuetzi @ ~/Desktop/rttr/build  (feature-loading-of-libraries)
 [39] → make -j4 run_tests
[ 32%] Built target rttr_core
[ 35%] Built target unit_test_plugin
[ 98%] Built target unit_tests
[100%] Running unit_tests

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
unit_tests is a Catch v1.8.2 host application.
Run with -? for options

-------------------------------------------------------------------------------
library - using types
  sequential view
-------------------------------------------------------------------------------
/Users/gabrielnuetzi/Desktop/rttr/src/unit_tests/misc/library_test.cpp:217
...............................................................................

/Users/gabrielnuetzi/Desktop/rttr/src/unit_tests/misc/library_test.cpp:238: FAILED:
  CHECK( view.is_valid() == true )
with expansion:
  false == true

===============================================================================
test cases:  538 |  537 passed | 1 failed
assertions: 5115 | 5114 passed | 1 failed

make[3]: *** [src/unit_tests/CMakeFiles/run_tests] Error 1
make[2]: *** [src/unit_tests/CMakeFiles/run_tests.dir/all] Error 2
make[1]: *** [src/unit_tests/CMakeFiles/run_tests.dir/rule] Error 2
make: *** [run_tests] Error 2

Smells like an uninitialized variable?

@@ -29,6 +29,9 @@
#include "rttr/detail/library/library_p.h"

#include <map>
#include <mutex>

static std::mutex g_library_mutex;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't it be better to put this variable in the library_manager since it is already a singleton and physically belongs to it (why a global?, ok it is a static meaning only in this translation unit when I understand correct) and thread-safe initialized because of the magic statics static library_manager obj;

@gabyx
Copy link
Contributor

gabyx commented Feb 16, 2018

I see the following:

auto view = var_point_list.create_sequential_view();

var_point_list has been initialized with the variant_data_policy_small meaning the m_data member contains everything (the my_array[100]).
the subsequent call to

 template<typename T, typename RawType = raw_type_t<T>, typename ConstType = remove_pointer_t<T>>
        variant_sequential_view_private(const T& container) RTTR_NOEXCEPT
            :   m_type(type::get<RawType>()),

initialized here the m_type with false.
Why I dont know...

I initialized the array with {1,2,3...,100} to better see it in the debugger (debugging with VS Code, not so handy but it works somehow...)

@acki-m
Copy link
Contributor Author

acki-m commented Feb 17, 2018

Thanks you Gabriel for looking into it. Why are you using VS code? Is the native XCode debugger that bad? When you have a little bit more time, can you check something for me?
I made a picture to show you:

  1. Check, when he wants to retrieve the raw_type in the variant_sequential_view, is the template type really the array?
  2. Check the returning type itself, does the shows correct data?
    image

@gabyx
Copy link
Contributor

gabyx commented Feb 23, 2018

Ahh wrong: Its a random bug, need to catch it when its false...

@acki-m
Copy link
Contributor Author

acki-m commented Feb 23, 2018

the data is completely invalid. So it seems to be used after it was freed. How can this be...

@gabyx
Copy link
Contributor

gabyx commented Feb 23, 2018

nono, dont trust the lldb, its visualizer are garbage...

@gabyx
Copy link
Contributor

gabyx commented Feb 23, 2018

or do you have another indication some integrals or so which are invalid?

@gabyx
Copy link
Contributor

gabyx commented Feb 23, 2018

are address sanitizers enabled for clang?... will see

@gabyx
Copy link
Contributor

gabyx commented Feb 24, 2018

compiled with clang 7.0 and address and leak sanitzer enabled. -> all tests pass multiple times -> no errors... I have no clue. The problem somehow is there with clang 6.0, I will see what the problem is tomorrow or on Sunday...

@acki-m
Copy link
Contributor Author

acki-m commented Feb 24, 2018

@gabyx
I have it...finally, I found the source of the problem.

static auto& instance = get_invalid_type_data_impl();

I create in every module a static invalid type_data object.

There from the address will be stored for invalid types (default ctor of ``type` class):

: m_type_data(&detail::get_invalid_type_data())

In one of the last PR; I adjusted the behaviour how types are compared, instead of using a incremental index, I just use directly the pointer address:

return (m_type_data == other.m_type_data);

That must have resulted to some problem in the code where types were compared. Don't know yet where. The memory order must be different on apple clang.
So I can make the invalid type data object unique or I have to switch back to using the index (which will increase type_data size)

A very tricky bug. Damn it. Thanks for looking into it. I hope I can merge it this WE.

@gabyx
Copy link
Contributor

gabyx commented Feb 24, 2018

So when you compare types: you compare pointers, so does this explain why I see sometimes valid=true or false (why?)
and is that correct, you register the types in a singelton get_registration_manager and a rttr::type only stores a raw pointer to the registered type during runtime?

The problem was that the invalid type_data object was created inside the library module
statically. Because we switched to using the pointer compare of types, instead of using
and separate ID, this lead to problems when comparing two invalid types.
They were the same.

Which specific code which lead to the problem is unclear. However, this behaviour was wrong
and is now fixed.

Additional: no clang memory or address sanitizer could find the source.
The problem was only poping up in MacOSX with apple clang.
So we use "MultiByteToWideChar" and "WideCharToMultiByte" like recommend from MSVC.
@acki-m
Copy link
Contributor Author

acki-m commented Feb 24, 2018

Yes, the type class only holds a pointer to the underlying type_data.
When you retrieve a type, you should always get the same underlying type_data, so comparing the types should work. Also across DLL boundaries.
However, the type_data for the invalid type object, was not always the same, it was a static object created in every module and this was the problem. My newest commit will fix this.

@acki-m
Copy link
Contributor Author

acki-m commented Feb 24, 2018

The error is still present.
Here is the output of address sanitizer:

[100%] Running unit_tests
=================================================================
==3801==ERROR: AddressSanitizer: heap-use-after-free on address 0x60d00000b450 at pc 0x00010d817efa bp 0x7fff547a72a0 sp 0x7fff547a7298
READ of size 1 at 0x60d00000b450 thread T0
    #0 0x10d817ef9 in rttr::variant_sequential_view::is_valid() const (librttr_core.0.9.6.dylib:x86_64+0x8cef9)
    #1 0x10b584798 in ____C_A_T_C_H____T_E_S_T____10() (unit_tests:x86_64+0x100130798)
    #2 0x10b48eb62 in Catch::FreeFunctionTestCase::invoke() const (unit_tests:x86_64+0x10003ab62)
    #3 0x10b4b4f9a in Catch::RunContext::invokeActiveTestCase() (unit_tests:x86_64+0x100060f9a)
    #4 0x10b4b38d3 in Catch::RunContext::runCurrentTest(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) (unit_tests:x86_64+0x10005f8d3)
    #5 0x10b45c4c9 in Catch::RunContext::runTest(Catch::TestCase const&) (unit_tests:x86_64+0x1000084c9)
    #6 0x10b45a4e9 in Catch::runTests(Catch::Ptr<Catch::Config> const&) (unit_tests:x86_64+0x1000064e9)
    #7 0x10b4df3a0 in Catch::Session::run() (unit_tests:x86_64+0x10008b3a0)
    #8 0x10b48b21c in main (unit_tests:x86_64+0x10003721c)
    #9 0x7fff94f21254 in start (libdyld.dylib:x86_64+0x5254)

0x60d00000b450 is located 128 bytes inside of 144-byte region [0x60d00000b3d0,0x60d00000b460)
freed by thread T0 here:
    #0 0x10da640ab in wrap__ZdlPv (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x610ab)
    #1 0x11136c1b3 in rttr::detail::registration_manager::unregister() (libunit_test_plugin.dylib:x86_64+0x31b3)
    #2 0x11136bd0d in rttr_auto_unregister_reflection_function() (libunit_test_plugin.dylib:x86_64+0x2d0d)
    #3 0x11956da07  (<unknown module>)
    #4 0x11955a2fa  (<unknown module>)
    #5 0x11955d577  (<unknown module>)
    #6 0x1195653b6  (<unknown module>)
    #7 0x7fff94f1e9ee in dlclose (libdyld.dylib:x86_64+0x29ee)
    #8 0x10da4e665 in wrap_dlclose (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x4b665)
    #9 0x10d8bce20 in rttr::detail::library_private::unload_native() (librttr_core.0.9.6.dylib:x86_64+0x131e20)
    #10 0x10d7914ed in rttr::library::unload() (librttr_core.0.9.6.dylib:x86_64+0x64ed)
    #11 0x10b571c93 in ____C_A_T_C_H____T_E_S_T____0() (unit_tests:x86_64+0x10011dc93)
    #12 0x10b48eb62 in Catch::FreeFunctionTestCase::invoke() const (unit_tests:x86_64+0x10003ab62)
    #13 0x10b4b4f9a in Catch::RunContext::invokeActiveTestCase() (unit_tests:x86_64+0x100060f9a)
    #14 0x10b4b38d3 in Catch::RunContext::runCurrentTest(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) (unit_tests:x86_64+0x10005f8d3)
    #15 0x10b45c4c9 in Catch::RunContext::runTest(Catch::TestCase const&) (unit_tests:x86_64+0x1000084c9)
    #16 0x10b45a4e9 in Catch::runTests(Catch::Ptr<Catch::Config> const&) (unit_tests:x86_64+0x1000064e9)
    #17 0x10b4df3a0 in Catch::Session::run() (unit_tests:x86_64+0x10008b3a0)
    #18 0x10b48b21c in main (unit_tests:x86_64+0x10003721c)
    #19 0x7fff94f21254 in start (libdyld.dylib:x86_64+0x5254)

previously allocated by thread T0 here:
    #0 0x10da63aab in wrap__Znwm (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x60aab)
    #1 0x111382ab8 in std::__1::unique_ptr<rttr::detail::type_data, std::__1::default_delete<rttr::detail::type_data> > rttr::detail::make_type_data<int [100]>() (libunit_test_plugin.dylib:x86_64+0x19ab8)
    #2 0x1113826da in rttr::detail::type_getter<int [100], void>::get_type() (libunit_test_plugin.dylib:x86_64+0x196da)
    #3 0x11138753f in std::__1::unique_ptr<rttr::detail::type_data, std::__1::default_delete<rttr::detail::type_data> > rttr::detail::make_type_data<int (*) [100]>() (libunit_test_plugin.dylib:x86_64+0x1e53f)
    #4 0x11138714a in rttr::detail::type_getter<int (*) [100], void>::get_type() (libunit_test_plugin.dylib:x86_64+0x1e14a)
    #5 0x1113b0ead in rttr::detail::property_wrapper<rttr::detail::member_object_ptr, int test_container_plugin::* [100], void, (rttr::access_levels)0, rttr::detail::return_as_ptr, rttr::detail::set_as_ptr, 0ul>::get_type() const (libunit_test_plugin.dylib:x86_64+0x47ead)
    #6 0x10d8c3947 in rttr::detail::property_wrapper_base::init() (librttr_core.0.9.6.dylib:x86_64+0x138947)
    #7 0x1113b0d38 in rttr::detail::property_wrapper<rttr::detail::member_object_ptr, int test_container_plugin::* [100], void, (rttr::access_levels)0, rttr::detail::return_as_ptr, rttr::detail::set_as_ptr, 0ul>::property_wrapper(rttr::basic_string_view<char, std::__1::char_traits<char> >, rttr::type, int test_container_plugin::* [100], std::__1::array<rttr::detail::metadata, 0ul>) (libunit_test_plugin.dylib:x86_64+0x47d38)
    #8 0x1113b08de in rttr::detail::_Unique_if<rttr::detail::property_wrapper<rttr::detail::member_object_ptr, int test_container_plugin::* [100], void, (rttr::access_levels)0, rttr::detail::return_as_ptr, rttr::detail::set_as_ptr, 0ul> >::_Single_object rttr::detail::make_unique<rttr::detail::property_wrapper<rttr::detail::member_object_ptr, int test_container_plugin::* [100], void, (rttr::access_levels)0, rttr::detail::return_as_ptr, rttr::detail::set_as_ptr, 0ul>, rttr::basic_string_view<char, std::__1::char_traits<char> >&, rttr::type, int test_container_plugin::* (&) [100], std::__1::array<rttr::detail::metadata, 0ul> >(rttr::basic_string_view<char, std::__1::char_traits<char> >&&&, rttr::type&&, int test_container_plugin::* (&&&) [100], std::__1::array<rttr::detail::metadata, 0ul>&&) (libunit_test_plugin.dylib:x86_64+0x478de)
    #9 0x11136e0a9 in rttr::registration::class_<test_container_plugin> rttr::registration::bind<rttr::detail::prop, test_container_plugin, int test_container_plugin::* [100], rttr::detail::public_access>::operator()<rttr::detail::bind_as_ptr const&>(rttr::detail::bind_as_ptr const&&&) (libunit_test_plugin.dylib:x86_64+0x50a9)
    #10 0x11136d5e7 in rttr_auto_register_reflection_function_() (libunit_test_plugin.dylib:x86_64+0x45e7)
    #11 0x11956d6ae  (<unknown module>)
    #12 0x11956d8b1  (<unknown module>)
    #13 0x1195691ed  (<unknown module>)
    #14 0x119568267  (<unknown module>)
    #15 0x1195682fc  (<unknown module>)
    #16 0x11955d70c  (<unknown module>)
    #17 0x11956514f  (<unknown module>)
    #18 0x7fff94f1ea3d in dlopen (libdyld.dylib:x86_64+0x2a3d)
    #19 0x10da4e61d in wrap_dlopen (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x4b61d)
    #20 0x10d8ba350 in rttr::detail::library_private::load_native() (librttr_core.0.9.6.dylib:x86_64+0x12f350)
    #21 0x10d791384 in rttr::library::load() (librttr_core.0.9.6.dylib:x86_64+0x6384)
    #22 0x10b56fc9c in ____C_A_T_C_H____T_E_S_T____0() (unit_tests:x86_64+0x10011bc9c)
    #23 0x10b48eb62 in Catch::FreeFunctionTestCase::invoke() const (unit_tests:x86_64+0x10003ab62)
    #24 0x10b4b4f9a in Catch::RunContext::invokeActiveTestCase() (unit_tests:x86_64+0x100060f9a)
    #25 0x10b4b38d3 in Catch::RunContext::runCurrentTest(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) (unit_tests:x86_64+0x10005f8d3)
    #26 0x10b45c4c9 in Catch::RunContext::runTest(Catch::TestCase const&) (unit_tests:x86_64+0x1000084c9)
    #27 0x10b45a4e9 in Catch::runTests(Catch::Ptr<Catch::Config> const&) (unit_tests:x86_64+0x1000064e9)
    #28 0x10b4df3a0 in Catch::Session::run() (unit_tests:x86_64+0x10008b3a0)
    #29 0x10b48b21c in main (unit_tests:x86_64+0x10003721c)

SUMMARY: AddressSanitizer: heap-use-after-free (librttr_core.0.9.6.dylib:x86_64+0x8cef9) in rttr::variant_sequential_view::is_valid() const
Shadow bytes around the buggy address:
  0x1c1a00001630: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa fa
  0x1c1a00001640: fa fa fa fa fa fa fd fd fd fd fd fd fd fd fd fd
  0x1c1a00001650: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
  0x1c1a00001660: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c1a00001670: fd fd fa fa fa fa fa fa fa fa fd fd fd fd fd fd
=>0x1c1a00001680: fd fd fd fd fd fd fd fd fd fd[fd]fd fa fa fa fa
  0x1c1a00001690: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c1a000016a0: fd fd fd fd fd fd fa fa fa fa fa fa fa fa fd fd
  0x1c1a000016b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c1a000016c0: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x1c1a000016d0: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==3801==ABORTING

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
unit_tests is a Catch v1.8.2 host application.
Run with -? for options

-------------------------------------------------------------------------------
library - using types
  sequential view
-------------------------------------------------------------------------------
/Users/acki/project/rttr_library_feature_trunk/src/unit_tests/misc/library_test.cpp:218
...............................................................................

/Users/acki/project/rttr_library_feature_trunk/src/unit_tests/misc/library_test.cpp:218: FAILED:
  {Unknown expression after the reported line}
with expansion:

due to a fatal error condition:
  SIGABRT - Abort (abnormal termination) signal

===============================================================================
test cases:  41 |  40 passed | 1 failed
assertions: 248 | 247 passed | 1 failed

/bin/sh: line 1:  3801 Abort trap: 6           ../../bin/unit_tests
make[2]: *** [src/unit_tests/CMakeFiles/run_tests] Error 134
make[1]: *** [src/unit_tests/CMakeFiles/run_tests.dir/all] Error 2
make: *** [all] Error 2

@gabyx
Copy link
Contributor

gabyx commented Feb 24, 2018

so the variant_sequential_view has been deleted before (or is it some submember?)

@gabyx
Copy link
Contributor

gabyx commented Feb 24, 2018

so the type_data is somehow gone...?

@gabyx
Copy link
Contributor

gabyx commented Feb 24, 2018

What I see is, the data accessed is already freed in test ____C_A_T_C_H____T_E_S_T____0()
and accessed in test 10:
____C_A_T_C_H____T_E_S_T____10

What do you mean with module?

@acki-m
Copy link
Contributor Author

acki-m commented Mar 2, 2018

@gabyx
I have finally found the source of the issue. The local static member variable when caching the type information was not local to the loaded module, it was unique to the whole process. Which is in my case, when you unload the library and all type information should be gone too, not good.
I will hopefully make a commit this weekend. Then the 0.9.6 release is near...

PS: I could write a whole blog post about this bug...took me sooo long

@dand-oss
Copy link
Contributor

dand-oss commented Mar 3, 2018

This is great - a very common use case!

I'd love an example of using this for a plug-in, as described in the FAQ

you can work with types, where you don't even have the headers available. Just think about plugins, where it is not necessary anymore to derive from some common base class and implement standard interfaces.

I am puzzling though tutorial, docs, and code
aiming to replace our "scripting interface" which uses the "common base class" plugin approach
with RTTR
over the WE

Actually the problem was also present under linux, (with gcc or clang).
This situation was following:
1. A plugin was loaded with type "int[100]" and registered
2. The plugin was unloaded => "int[100]" was removed from the type system and deleted
3. The type[100] was used in the unit_test later and was accessing the memory
of the already deleted type_data

The reason for this behavioue lies in the usage of local static member variables
(which is used to cache the retrieving and registering of type_data information).
This variable was process wide unique, this lead to problems when unloading a plugin.
The static was no reinitialized when unloading the plugin.
So the variable was pointing to type_data information which was alreadyl deleted.

Now we are using RTTR_LOCAL to hide the symbols for exporting. This fixes the issues.
@acki-m
Copy link
Contributor Author

acki-m commented Mar 3, 2018

@dand-oss
This PR, contains an example, how to use RTTR with plugins. A short tutorial will follow.

@acki-m
Copy link
Contributor Author

acki-m commented Mar 5, 2018

Fixes: #94, #114, #115 #116

@acki-m acki-m merged commit 7232d52 into rttrorg:master Mar 5, 2018
@acki-m acki-m deleted the feature-loading-of-libraries branch March 5, 2018 19:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants