Thread: [Cppcms-users] async_flush_output and multipart/x_mixed_replace
Brought to you by:
artyom-beilis
From: Tobias R. <rei...@go...> - 2012-02-06 15:55:28
|
Hello, I'm trying to get a multipart response by doing a single request. The motivation behind is a big computation which gives some progress feedback. So far I stumbled over "context::async_flush_output" which (given that name) should flush the responding data. Simulating the computational part by using a timer, I expect to get five distinct responses, each in a second (see following code). Instead, I get the whole response after the five seconds en bloc. Did I miss something? class Application : public cppcms::application { int counter_; booster::aio::deadline_timer timer_; booster::shared_ptr<cppcms::http::context> context_; boost::thread thread_; public: Application(cppcms::service &srv) : cppcms::application(srv) { timer_.set_io_service(service().get_io_service()); dispatcher().assign("^/compute$", &Application::compute, this); } void async_handler(cppcms::http::context::complition_type t) { timer_.expires_from_now(booster::ptime::seconds(1)); timer_.async_wait(boost::bind(&Application::compute_thread, booster::intrusive_ptr<Application>(this))); } void flush_output() { context_->async_flush_output(boost::bind(&Application::async_handler, booster::intrusive_ptr<Application>(this), _1)); } void compute_thread() { std::ostream& o = context_->response().out(); if (counter_++ < 5) { o << "Content-type: text/plain\n\n"; o << "count: " << counter_ << std::endl; o << "--progress-token\n"; // flush_output(); service().post(boost::bind(&Application::flush_output, booster::intrusive_ptr<Application>(this))); return; } o << "Content-type: text/plain\n\n"; o << "Computation finished\n"; o << "--progress-token\n"; context_->async_complete_response(); } void compute() { context_ = release_context(); context_->response().set_content_header("multipart/x-mixed-replace; boundary=progress-token"); context_->response().out() << "--progress-token\n"; counter_ = 0; // compute_thread(); thread_ = boost::thread(boost::bind(&Application::compute_thread, booster::intrusive_ptr<Application>(this))); } }; int main(int argc, char** argv) { try { cppcms::service srv(argc, argv); booster::intrusive_ptr<Application> app = new Application(srv); srv.applications_pool().mount(app, cppcms::mount_point("/counter")); srv.run(); } catch(std::exception const& e) { std::cerr << e.what() << std::endl; } } On the client side I have a simple Javascript: var xmlHttp = new XMLHttpRequest(); xmlHttp.multipart = true; xmlHttp.open('GET', 'counter/compute', true); xmlHttp.onreadystatechange = function (event) { console.log("readystate = " + event.target.readyState + ": " + event.target.responseText + "\n"); } xmlHttp.send(null); Best regards, Tobias |
From: Artyom B. <art...@ya...> - 2012-02-07 11:31:10
|
You are mixing calls from the boost::thread and the mail event loop thread and you should not. Slightly changed example that works (without boost::thread) Read this: http://cppcms.com/wikipp/en/page/thread_safety Also you can't access context_->response().out() from the thread, you can't call async_flush or async_complete_response from different thread. Artyom Beilis ------------- Support CppCMS by donating money: https://sourceforge.net/donate/index.php?group_id=209965 ------------------- #include <cppcms/service.h> #include <cppcms/application.h> #include <cppcms/http_context.h> #include <cppcms/http_response.h> #include <cppcms/applications_pool.h> #include <booster/aio/deadline_timer.h> #include <boost/bind.hpp> class Application : public cppcms::application { int counter_; booster::aio::deadline_timer timer_; booster::shared_ptr<cppcms::http::context> context_; public: Application(cppcms::service &srv) : cppcms::application(srv) { timer_.set_io_service(service().get_io_service()); } void async_handler(cppcms::http::context::completion_type t) { timer_.expires_from_now(booster::ptime::seconds(1)); timer_.async_wait(boost::bind(&Application::compute_thread, booster::intrusive_ptr<Application>(this))); } void flush_output() { context_->async_flush_output(boost::bind(&Application::async_handler, booster::intrusive_ptr<Application>(this), _1)); } void compute_thread() { std::ostream& o = context_->response().out(); if (counter_++ < 5) { o << "Content-type: text/plain\n\n"; o << "count: " << counter_ << std::endl; o << "--progress-token\n"; flush_output(); return; } o << "Content-type: text/plain\n\n"; o << "Computation finished\n"; o << "--progress-token\n"; context_->async_complete_response(); } void main(std::string) { context_ = release_context(); context_->response().set_content_header("multipart/x-mixed-replace; boundary=progress-token"); context_->response().out() << "--progress-token\n"; counter_ = 0; compute_thread(); } }; int main(int argc, char** argv) { try { cppcms::service srv(argc, argv); booster::intrusive_ptr<Application> app = new Application(srv); srv.applications_pool().mount(app); srv.run(); } catch(std::exception const& e) { std::cerr << e.what() << std::endl; } } |
From: Tobias R. <rei...@go...> - 2012-02-07 12:45:54
|
Artyom Beilis <artyomtnk@...> writes: > You are mixing calls from the boost::thread and the mail event loop thread and you should not. > Slightly changed example that works (without boost::thread) Since the context is not thread safe, this makes sense. However, I already tried your version without boost::thread and service::post in the first place. That's why I left the two corresponding lines in my code as comments: [...] //flush_output(); <--- tried this already service().post(boost::bind(&Application::flush_output, ...); [...] //compute_thread(); <--- tried this already thread_ = boost::thread(boost::bind(&Application::compute_thread, ...); [...] Since you say it works this way, there must be something else which is different. Could it have something to do with my web server or my browser? I'm using nginx and Firefox, but tried Chrome as well. I'm not caching anything on purpose. I even tried to send bigger packages each time to prevent some lazy sending mechanisms. > Read this: > > http://cppcms.com/wikipp/en/page/thread_safety "[...] If asynchronous application or any other object that runs in the event wants to execute some long running, blocking or heavy operation, it may do it by submitting an execution handler to the thread pool. [...]" As I understand this, there's the Event Loop for asynchronous operations and there's another Thread Pool for synchronous operations. In my case I have a heavy computation, which I should send via service::post (is that right?) to the Thread Pool. On the other side, I'd like to send back some progress or intermediate computation results, which I can do only by using context::async_flush_output, which is an asynchronous operation and thus part of the Event Loop. So, is it possible at all to call asynchronous operations from synchronous threads? Anyway, thank you very much for your time, Artyom. Best regards, Tobias |