{"action":"created","issue":{"url":"https://api.github.com/repos/Microsoft/cpprestsdk/issues/111","repository_url":"https://api.github.com/repos/Microsoft/cpprestsdk","labels_url":"https://api.github.com/repos/Microsoft/cpprestsdk/issues/111/labels{/name}","comments_url":"https://api.github.com/repos/Microsoft/cpprestsdk/issues/111/comments","events_url":"https://api.github.com/repos/Microsoft/cpprestsdk/issues/111/events","html_url":"https://github.com/Microsoft/cpprestsdk/issues/111","id":144516846,"number":111,"title":"deadlock in ~basic_file_buffer","user":{"login":"vladimir-kovalev","id":7860588,"avatar_url":"https://avatars.githubusercontent.com/u/7860588?v=3","gravatar_id":"","url":"https://api.github.com/users/vladimir-kovalev","html_url":"https://github.com/vladimir-kovalev","followers_url":"https://api.github.com/users/vladimir-kovalev/followers","following_url":"https://api.github.com/users/vladimir-kovalev/following{/other_user}","gists_url":"https://api.github.com/users/vladimir-kovalev/gists{/gist_id}","starred_url":"https://api.github.com/users/vladimir-kovalev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vladimir-kovalev/subscriptions","organizations_url":"https://api.github.com/users/vladimir-kovalev/orgs","repos_url":"https://api.github.com/users/vladimir-kovalev/repos","events_url":"https://api.github.com/users/vladimir-kovalev/events{/privacy}","received_events_url":"https://api.github.com/users/vladimir-kovalev/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2016-03-30T09:26:31Z","updated_at":"2016-03-30T09:57:41Z","closed_at":null,"body":"Hi,\r\n\r\nEither I do something wrong or there's a deadlock. I've implemented simple REST server using cpprestsdk. Basically it just serves a couple of endpoints and static content. Here's an implementation for static serving:\r\n\r\n```c++\r\nclass StaticServer : public Endpoint {\r\npublic:\r\n\tStaticServer(): Endpoint(web::http::methods::GET, \"/\",\r\n\t\t\tstd::bind(&StaticServer::serve, this, std::placeholders::_1)) {}\r\n\r\nprivate:\r\n\tpplx::task<void> serve(web::http::http_request request) {\r\n\t\tconst std::string file = file_from_request(request.request_uri());\r\n\r\n\t\tif (access(file.c_str(), F_OK) != 0) {\r\n\t\t\treturn request.reply(status_codes::NotFound);\r\n\t\t}\r\n\t\tif (access(file.c_str(), R_OK) != 0) {\r\n\t\t\treturn request.reply(status_codes::Forbidden);\r\n\t\t}\r\n\r\n\t\tweb::http::http_response response(status_codes::OK);\r\n\t\tresponse.headers().set_content_type(file_mime(file));\r\n\t\tresponse.headers().set_cache_control(file_cache(file));\r\n\r\n\t\treturn concurrency::streams::fstream::open_istream(file, std::ios_base::in | std::ios_base::binary)\r\n\t\t.then([=](concurrency::streams::istream stream) mutable {\r\n\t\t\tresponse.set_body(stream);\r\n\t\t\treturn request.reply(response);\r\n\t\t});\r\n\t}\r\n};\r\n```\r\n\r\nAnd here's how listeners are created:\r\n\r\n```c++\r\nvirtual void listen() {\r\n\tfor (auto endpoint: endpoints_) {\r\n\t\tfor (auto method: endpoint->methods()) {\r\n\t\t\tauto listener = createListener(endpoint->route()); // returns std::unique_ptr<http_listener>\r\n\t\t\tlistener->support(method, [=](http_request request) {\r\n\t\t\t\tendpoint->handler()(request).then([=] { // endpoint->handler() returns std::function<...> which handles this endpoint. See StaticServer::serve\r\n\t\t\t\t\tLOG << \"request finished: \" << request.request_uri().to_string() << std::endl;\r\n\t\t\t\t});\r\n\t\t\t});\r\n\t\t\tlistener->open().wait();\r\n\t\t\tlisteners_.push_back(std::move(listener));\r\n\t\t}\r\n\t}\r\n}\r\n```\r\n\r\nI've restricted threadpool used in cpprestsdk from 40 to 4 threads because server is running on embedded device. Main server's thread calls `listen()` and then just sleeps in an infinite loop. When I open the browser and send the request (just `http://localhost:port/`) it works for a couple of times and then locks. Here's the backtrace of one of the threads:\r\n\r\n```\r\n==10080==    at 0x55A2DA0: pthread_cond_wait@@GLIBC_2.3.2 (pthread_cond_wait.S:185)\r\n==10080==    by 0x5EAFCDB: std::condition_variable::wait(std::unique_lock<std::mutex>&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)\r\n==10080==    by 0x432F12: void std::condition_variable::wait<pplx::details::event_impl::wait(unsigned int)::{lambda()#1}>(std::unique_lock<std::mutex>&, pplx::details::event_impl::wait(unsigned int)::{lambda()#1}) (condition_variable:98)\r\n==10080==    by 0x428A1E: pplx::details::event_impl::wait(unsigned int) (pplxlinux.h:117)\r\n==10080==    by 0x428AF2: pplx::details::event_impl::wait() (pplxlinux.h:133)\r\n==10080==    by 0x42A06B: pplx::details::_TaskCollectionImpl::_Wait() (pplx.h:182)\r\n==10080==    by 0x42A051: pplx::details::_TaskCollectionImpl::_RunAndWait() (pplx.h:177)\r\n==10080==    by 0x42AD49: pplx::details::_Task_impl_base::_Wait() (pplxtasks.h:1778)\r\n==10080==    by 0x4347E9: pplx::task<unsigned char>::wait() const (pplxtasks.h:3519)\r\n==10080==    by 0x42C253: pplx::task<void>::wait() const (pplxtasks.h:4428)\r\n==10080==    by 0x46D406: Concurrency::streams::details::basic_file_buffer<unsigned char>::~basic_file_buffer() (filestream.h:111)\r\n==10080==    by 0x46D4AD: Concurrency::streams::details::basic_file_buffer<unsigned char>::~basic_file_buffer() (filestream.h:118)\r\n==10080==    by 0x47A88B: std::_Sp_counted_ptr<Concurrency::streams::details::basic_file_buffer<unsigned char>*, (__gnu_cxx::_Lock_policy)2>::_M_dispose()\r\n==10080==    by 0x4388F5: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:149)\r\n==10080==    by 0x432DFA: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr_base.h:666)\r\n==10080==    by 0x435573: std::__shared_ptr<Concurrency::streams::details::basic_streambuf<unsigned char>, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr_base.h:914)\r\n==10080==    by 0x4355B3: std::shared_ptr<Concurrency::streams::details::basic_streambuf<unsigned char> >::~shared_ptr() (shared_ptr.h:93)\r\n==10080==    by 0x43AEEE: Concurrency::streams::streambuf<unsigned char>::~streambuf()\r\n==10080==    by 0x46BF3F: Concurrency::streams::details::basic_istream_helper<unsigned char>::~basic_istream_helper() (streams.h:59)\r\n==10080==    by 0x4672E5: void __gnu_cxx::new_allocator<Concurrency::streams::details::basic_istream_helper<unsigned char> >::destroy<Concurrency::streams::details::basic_istream_helper<unsigned char> >(Concurrency::streams::details::basic_istream_helper<unsigned char>*) (new_allocator.h:124)\r\n==10080==    by 0x462AC6: std::enable_if<std::__and_<std::allocator_traits<std::allocator<Concurrency::streams::details::basic_istream_helper<unsigned char> > >::__destroy_helper<Concurrency::streams::details::basic_istream_helper<unsigned char> >::type>::value, void>::type std::allocator_traits<std::allocator<Concurrency::streams::details::basic_istream_helper<unsigned char> > >::_S_destroy<Concurrency::streams::details::basic_istream_helper<unsigned char> >(std::allocator<Concurrency::streams::details::basic_istream_helper<unsigned char> >&, Concurrency::streams::details::basic_istream_helper<unsigned char>*) (alloc_traits.h:282)\r\n==10080==    by 0x45F0F4: void std::allocator_traits<std::allocator<Concurrency::streams::details::basic_istream_helper<unsigned char> > >::destroy<Concurrency::streams::details::basic_istream_helper<unsigned char> >(std::allocator<Concurrency::streams::details::basic_istream_helper<unsigned char> >&, Concurrency::streams::details::basic_istream_helper<unsigned char>*) (alloc_traits.h:411)\r\n==10080==    by 0x458EE0: std::_Sp_counted_ptr_inplace<Concurrency::streams::details::basic_istream_helper<unsigned char>, std::allocator<Concurrency::streams::details::basic_istream_helper<unsigned char> >, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:524)\r\n==10080==    by 0x4388F5: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:149)\r\n==10080==    by 0x4F62ADD: web::http::details::_http_response::~_http_response()\r\n==10080==    by 0x4677F4: void __gnu_cxx::new_allocator<web::http::details::_http_response>::destroy<web::http::details::_http_response>(web::http::details::_http_response*) (new_allocator.h:124)\r\n==10080==    by 0x462E1D: std::enable_if<std::__and_<std::allocator_traits<std::allocator<web::http::details::_http_response> >::__destroy_helper<web::http::details::_http_response>::type>::value, void>::type std::allocator_traits<std::allocator<web::http::details::_http_response> >::_S_destroy<web::http::details::_http_response>(std::allocator<web::http::details::_http_response>&, web::http::details::_http_response*) (alloc_traits.h:282)\r\n==10080==    by 0x45F44E: void std::allocator_traits<std::allocator<web::http::details::_http_response> >::destroy<web::http::details::_http_response>(std::allocator<web::http::details::_http_response>&, web::http::details::_http_response*) (alloc_traits.h:411)\r\n==10080==    by 0x45972A: std::_Sp_counted_ptr_inplace<web::http::details::_http_response, std::allocator<web::http::details::_http_response>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:524)\r\n==10080==    by 0x4388F5: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:149)\r\n==10080==    by 0x4F73A44: std::_Sp_counted_ptr_inplace<pplx::details::_Task_completion_event_impl<web::http::http_response>, std::allocator<pplx::details::_Task_completion_event_impl<web::http::http_response> >, (__gnu_cxx::_Lock_policy)2>::_M_dispose() \r\n==10080==    by 0x4388F5: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:149)\r\n==10080==    by 0x4F62C17: web::http::details::_http_request::~_http_request()\r\n==10080==    by 0x4388F5: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:149)\r\n==10080==    by 0x50979E1: web::http::experimental::listener::details::connection::handle_http_line(boost::system::error_code const&)\r\n==10080==    by 0x509A301: boost::asio::detail::read_until_match_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, std::allocator<char>, web::http::experimental::listener::details::crlf_nonascii_searcher_t, web::http::experimental::listener::details::connection::start_request_response()::{lambda(boost::system::error_code const&, unsigned long)#2}>::operator()(boost::system::error_code const&, unsigned long, int)\r\n==10080==    by 0x509BD86: boost::asio::detail::reactive_socket_recv_op<boost::asio::mutable_buffers_1, boost::asio::detail::read_until_match_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, std::allocator<char>, web::http::experimental::listener::details::crlf_nonascii_searcher_t, web::http::experimental::listener::details::connection::start_request_response()::{lambda(boost::system::error_code const&, unsigned long)#2}> >::do_complete(boost::asio::detail::task_io_service*, boost::asio::detail::task_io_service_operation*, boost::system::error_code const&, unsigned long)\r\n==10080==    by 0x4FDC694: boost::asio::detail::epoll_reactor::descriptor_state::do_complete(boost::asio::detail::task_io_service*, boost::asio::detail::task_io_service_operation*, boost::system::error_code const&, unsigned long)\r\n==10080==    by 0x4FDC322: boost::asio::detail::task_io_service::run(boost::system::error_code&)\r\n==10080==    by 0x505BD25: crossplat::threadpool::thread_start(void*)\r\n==10080==    by 0x559D6A9: start_thread (pthread_create.c:333)\r\n==10080==    by 0x649BE9C: clone (clone.S:109)\r\n```\r\n\r\nAll 4 threads in a threadpool are locked in this condition (all have the same backtrace). So am I doing something wrong? All 4 threads locked waiting for a signal. Here's the relevant code (`include/cpprest/filestream.h`):\r\n\r\n```c++\r\n        virtual ~basic_file_buffer()\r\n        {\r\n            if( this->can_read() )\r\n            {\r\n                this->_close_read().wait(); // Line 111: locks here\r\n            }\r\n\r\n            if (this->can_write())\r\n            {\r\n                this->_close_write().wait();\r\n            }\r\n        }\r\n```\r\n\r\nSo, all 4 threads are `wait`ing and no free threads are in threadpool to schedule a task which will fire `set` (this is just my guess). The `~basic_file_buffer` is called because I set `concurrency::streams::istream` as a body for response. So when response is destructed the stream is destructed too causing destruction of the underlying buffer.\r\n\r\nThe server (and so cpprestsdk) is compiled and running on x86_64 linux (ubuntu 15.10). gcc version is 4.9.3. I can reproduce this in 100% of the cases. It serves the files for 2-3 times (sending around 20-30 files each time) and then locks.\r\n\r\nThanks in advance."},"comment":{"url":"https://api.github.com/repos/Microsoft/cpprestsdk/issues/comments/203356057","html_url":"https://github.com/Microsoft/cpprestsdk/issues/111#issuecomment-203356057","issue_url":"https://api.github.com/repos/Microsoft/cpprestsdk/issues/111","id":203356057,"user":{"login":"vladimir-kovalev","id":7860588,"avatar_url":"https://avatars.githubusercontent.com/u/7860588?v=3","gravatar_id":"","url":"https://api.github.com/users/vladimir-kovalev","html_url":"https://github.com/vladimir-kovalev","followers_url":"https://api.github.com/users/vladimir-kovalev/followers","following_url":"https://api.github.com/users/vladimir-kovalev/following{/other_user}","gists_url":"https://api.github.com/users/vladimir-kovalev/gists{/gist_id}","starred_url":"https://api.github.com/users/vladimir-kovalev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vladimir-kovalev/subscriptions","organizations_url":"https://api.github.com/users/vladimir-kovalev/orgs","repos_url":"https://api.github.com/users/vladimir-kovalev/repos","events_url":"https://api.github.com/users/vladimir-kovalev/events{/privacy}","received_events_url":"https://api.github.com/users/vladimir-kovalev/received_events","type":"User","site_admin":false},"created_at":"2016-03-30T09:57:41Z","updated_at":"2016-03-30T09:57:41Z","body":"And basically I think that no code inside the library (cpprestsdk) should implicitly call `wait` or `get` because there may be a situation when all available threads in a threadpool are locked (e.g. waiting) and no free thread exists to send a signal for those are waiting."}}