microsoft / cpprestsdk

The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services.
Other
8.01k stars 1.66k forks source link

Stable crash in http_client destructor #540

Open georgebu opened 7 years ago

georgebu commented 7 years ago

code:

class A
{
public:
    ~A() { delete p; }
    http_client* p;
} a;

int main()
{
    a.p = new http_client(U("http://www.bing.com/"));
    a.p->request(web::http::methods::POST);
    return 0;
}

environment:

Apple LLVM version 8.1.0 (clang-802.0.42)
Target: x86_64-apple-darwin16.7.0
Boost: 1.64.0

thread backtrace:

* thread #1: tid = 0x301b6e, 0x00007fff9f13738c libsystem_pthread.dylib`pthread_mutex_lock, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
  * frame #0: 0x00007fff9f13738c libsystem_pthread.dylib`pthread_mutex_lock
    frame #1: 0x000000010b1b5325 test_rest`boost::asio::detail::posix_mutex::lock(this=0x00097fc722c05480) + 21 at posix_mutex.hpp:52
    frame #2: 0x000000010b1b52d3 test_rest`boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex>::scoped_lock(this=0x00007fff54dbb3f0, m=0x00097fc722c05480) + 51 at scoped_lock.hpp:46
    frame #3: 0x000000010b1b4fed test_rest`boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex>::scoped_lock(this=0x00007fff54dbb3f0, m=0x00097fc722c05480) + 29 at scoped_lock.hpp:45
    frame #4: 0x000000010b1cc773 test_rest`unsigned long boost::asio::detail::kqueue_reactor::cancel_timer<boost::asio::time_traits<boost::posix_time::ptime> >(this=0x00097fc722c05450, queue=0x00007fc722c02b78, timer=0x00007fc722c05428, max_cancelled=18446744073709551615) + 51 at kqueue_reactor.hpp:66
    frame #5: 0x000000010b1cc6fa test_rest`boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >::cancel(this=0x00007fc722c02b78, impl=0x00007fc722c05418, ec=0x00007fff54dbb4b0) + 122 at deadline_timer_service.hpp:108
    frame #6: 0x000000010b1dba85 test_rest`boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >::destroy(this=0x00007fc722c02b78, impl=0x00007fc722c05418) + 53 at deadline_timer_service.hpp:94
    frame #7: 0x000000010b1dba1c test_rest`boost::asio::deadline_timer_service<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime> >::destroy(this=0x00007fc722c02b50, impl=0x00007fc722c05418) + 44 at deadline_timer_service.hpp:90
    frame #8: 0x000000010b1db9aa test_rest`boost::asio::basic_io_object<boost::asio::deadline_timer_service<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime> >, false>::~basic_io_object(this=0x00007fc722c05410) + 42 at basic_io_object.hpp:130
    frame #9: 0x000000010b1db975 test_rest`boost::asio::basic_deadline_timer<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime>, boost::asio::deadline_timer_service<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime> > >::~basic_deadline_timer(this=0x00007fc722c05410) + 21 at basic_deadline_timer.hpp:126
    frame #10: 0x000000010b1db935 test_rest`boost::asio::basic_deadline_timer<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime>, boost::asio::deadline_timer_service<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime> > >::~basic_deadline_timer(this=0x00007fc722c05410) + 21 at basic_deadline_timer.hpp:126
    frame #11: 0x000000010b228045 test_rest`web::http::client::details::asio_connection_pool::~asio_connection_pool(this=0x00007fc722c05378) + 37 at http_client_asio.cpp:272
    frame #12: 0x000000010b228015 test_rest`web::http::client::details::asio_connection_pool::~asio_connection_pool(this=0x00007fc722c05378) + 21 at http_client_asio.cpp:272
    frame #13: 0x000000010b227b99 test_rest`std::__1::__shared_ptr_emplace<web::http::client::details::asio_connection_pool, std::__1::allocator<web::http::client::details::asio_connection_pool> >::__on_zero_shared(this=0x00007fc722c05360) + 41 at memory:3850
    frame #14: 0x00007fff9daebdae libc++.1.dylib`std::__1::__shared_weak_count::__release_shared() + 44
    frame #15: 0x000000010ae4b60c test_rest`std::__1::shared_ptr<pplx::details::_Task_completion_event_impl<web::http::http_response> >::~shared_ptr(this=0x00007fc722e005b8) + 44 at memory:4605
    frame #16: 0x000000010ae475e5 test_rest`std::__1::shared_ptr<shield::rest::adapter>::~shared_ptr(this=0x00007fc722e005b8) + 21 at memory:4603
    frame #17: 0x000000010b2240c3 test_rest`web::http::client::details::asio_client::~asio_client(this=0x00007fc722e00240) + 51 at http_client_asio.cpp:361
    frame #18: 0x000000010b1a8b35 test_rest`web::http::client::details::asio_client::~asio_client(this=0x00007fc722e00240) + 21 at http_client_asio.cpp:361
    frame #19: 0x000000010b225219 test_rest`std::__1::__shared_ptr_emplace<web::http::client::details::asio_client, std::__1::allocator<web::http::client::details::asio_client> >::__on_zero_shared(this=0x00007fc722e00220) + 41 at memory:3850
    frame #20: 0x00007fff9daebdae libc++.1.dylib`std::__1::__shared_weak_count::__release_shared() + 44
    frame #21: 0x000000010ae4b60c test_rest`std::__1::shared_ptr<pplx::details::_Task_completion_event_impl<web::http::http_response> >::~shared_ptr(this=0x00007fc722c05548) + 44 at memory:4605
    frame #22: 0x000000010ae475e5 test_rest`std::__1::shared_ptr<shield::rest::adapter>::~shared_ptr(this=0x00007fc722c05548) + 21 at memory:4603
    frame #23: 0x000000010b0aabcc test_rest`web::http::client::http_pipeline::~http_pipeline(this=0x00007fc722c05548) + 60 at http_client.cpp:257
    frame #24: 0x000000010b0aab85 test_rest`web::http::client::http_pipeline::~http_pipeline(this=0x00007fc722c05548) + 21 at http_client.cpp:257
    frame #25: 0x000000010b0aa8f9 test_rest`std::__1::__shared_ptr_emplace<web::http::client::http_pipeline, std::__1::allocator<web::http::client::http_pipeline> >::__on_zero_shared(this=0x00007fc722c05530) + 41 at memory:3850
    frame #26: 0x00007fff9daebdae libc++.1.dylib`std::__1::__shared_weak_count::__release_shared() + 44
    frame #27: 0x000000010ae4b60c test_rest`std::__1::shared_ptr<pplx::details::_Task_completion_event_impl<web::http::http_response> >::~shared_ptr(this=0x00007fc722e00210) + 44 at memory:4605
    frame #28: 0x000000010ae475e5 test_rest`std::__1::shared_ptr<shield::rest::adapter>::~shared_ptr(this=0x00007fc722e00210) + 21 at memory:4603
    frame #29: 0x000000010b099185 test_rest`web::http::client::http_client::~http_client(this=0x00007fc722e00210) + 21 at http_client.cpp:372
    frame #30: 0x000000010b0991a5 test_rest`web::http::client::http_client::~http_client(this=0x00007fc722e00210) + 21 at http_client.cpp:372
    frame #31: 0x000000010aeb18fa test_rest`A::~A(this=0x000000010b2ef1b0) + 42 at test_rest.cpp:9
    frame #32: 0x000000010ae47995 test_rest`A::~A(this=0x000000010b2ef1b0) + 21 at test_rest.cpp:9
    frame #33: 0x00007fff9efb5178 libsystem_c.dylib`__cxa_finalize_ranges + 332
    frame #34: 0x00007fff9efb54b2 libsystem_c.dylib`exit + 55
    frame #35: 0x00007fff9ef2023c libdyld.dylib`start + 8
reneme commented 7 years ago

Seems that the http_client cannot clean up properly if it is instantiated in global scope (and hence destructed after main() finished).

If I run the following, it doesn't crash:

class A
{
public:
    ~A() { delete p; }
    http_client* p;
};

int main()
{
    A a;

    a.p = new http_client(U("https://www.bing.com/"));
    auto task =
        a.p->request(web::http::methods::GET)
        .then([] (pplx::task<http_response> resp) {
            try {
                resp.get();
                std::cout << "worked" << std::endl;
            } catch (...) {
                std::cout << "didn't work" << std::endl;
            }
        });

    task.wait();
    return 0;
}

Also note that you should never leave tasks dangling but always call .get() or .wait() on them.

inhwankim-sm commented 2 years ago

I had the same issue and I agree with your assessment re. global scope.

This seems to be due to global instance destruction order which is undefined across different translation units.

I had http_client as a global in my program, and there is the shared_threadpool which is a local static in cpprestsdk.

When destroying http_client it accesses shared_threadpool which was already destroyed first.

barcharcraz commented 2 years ago

Yeah, this is an initialization order thing. I think more clear documentation is the way to go here.