Предлагаю читателям этого блога ответить на довольно простой вопрос:
зачем в
требованиях Asio к функтору-обработчику существует asio_handler_invoke?
Ответы/догадки оформлять в виде комментариев. Желательно написать что-то свое, а не просто привести ссылку на документацию Asio - в ней нет краткого обобщенного ответа.
Updated:
Я неправильно выразил вопрос. Кхм... скажем по-проще: как Вы думаете, зачем автор Asio усложнил дизайн, введя asio_handler_invoke (будем называть это
invocation strategy)? Что потребовало от него именно такого похода?
Вопрос отчасти связан с тем, что многие считают Asio слишком сложной библиотекой. Этот вопрос направлен на то, чтобы объяснить, что в Asio нет ничего "случайного". Все имеет свою вескую причину.
Жаль, что на BoostCon 2011 Chris не сделал доклада с заголовком, похожим на заголовок данного сообщения.
Updated:
Когда я только начинал использовать Boost.Asio, наличие
invocation strategy меня несколько смутило: что именно в моем MyHandler::operator() относится к
invocation strategy?
И вот, когда я снова перечитал документацию Boost.Asio и нашел пункт X, я понял:
- какая часть MyHandler::operator() относилась к invocation strategy;
- для чего вообще было введено понятие invocation strategy.
Позже я понял, что
invocation strategy лучше вынести из MyHandler::operator(), т.е. оставить ее только в соотв. asio_handler_invoke. В противном случае надо быть готовым к рекурсивному использованию
invocation strategy. Например, вместо boost::mutex использовать boost::recursive_mutex (см. MA_BOOST_ASIO_HEAVY_STRAND_WRAPPED_HANDLER в asio-samples).
Так вот: ответ (и сопутствующее обсуждение) на вопрос, заданный мной в данном сообщении, дополнительно должен помочь разработчикам, использующим Boost.Asio, понять - какую часть MyHandler::operator() следует выносить в asio_handler_invoke и как выносить данный код - оставляя его же в MyHandler::operator() или нет?
Updated:
Как показало время, этот вопрос оказался либо неинтересным, либо слишком сложным.
Ну что ж, вот правильный ответ:
Strands: Use Threads Without Explicit Locking.
Читаем: "if a completion handler goes through a strand, then all intermediate handlers should also go through the same strand. This is needed to ensure thread safe access for any objects that are shared between the caller and the composed operation (in the case of async_read() it's the socket, which the caller can close() to cancel the operation). This is done by having hook functions for all intermediate handlers which forward the calls to the customisable hook associated with the final handler".
Объясняю проще: единственная причина введения asio_handler_invoke - это правильная работа composed operations в свете синхронизации доступа к IoObject. Только такой hook и мог помочь в данном случае. Все остальные места в нем особо не нуждались, так как они так или иначе всегда заканчиваются вызовом Handler::operator() без необходимости повторного доступа к IoObject (могу ошибаться насчет поддержки SSL - еще не разбирался с ней).