LINUX.ORG.RU

Можно ли дергать crow api из других (собственных) потоков?

 


1

1

Привет, заюзал crow для http сервера. Вопрос, который я не понял - а можно ли дергать crow апи из других (своих) потоков? Ведь он работает на базе asio, а там свой event_loop, thread_pool и всё такое, и надо помещать задание в очередь asio, которое будет выполнено его потоками. Сомнения были сразу, но после того как увидел Crow::tick(), которое даёт возможность помещать функцию в очередь на выполнение, задумался конкретно. Пример набросал, отдельный мой поток играет в пинг-понг с websocket коннектами и закрывает мертвые. Так можно?

std::mutex g_ws_mtx;
std::unordered_map<crow::websocket::connection*, steady_clock_t::time_point>
   g_ws_connections;


void ws_ping_thread_fn()
{
   while (true) {
      std::this_thread::sleep_for(...);
      if (true) {
         std::lock_guard lck(g_ws_mtx);
         for (auto i : g_ws_connections) {
            ...
            if (last time > threshold)
               // close connection
            else
               i.first->send_ping(...);
            ...
         }
      }
   }
}

int main() {
   crow::SimpleApp app;
   ...
   CROW_WEBSOCKET_ROUTE(app, "/ws")
      .onopen([&](crow::websocket::connection &conn) {
            std::lock_guard lck(g_ws_mtx);
         auto res = g_ws_connections.insert({&conn, steady_clock_t::now()});
      })
      .onclose([&](crow::websocket::connection &conn,
            const std::string& reason, uint16_t) {
         std::lock_guard lck(g_ws_mtx);
         auto res = g_ws_connections.erase(&conn);
      })
      .onmessage([&](crow::websocket::connection &conn,
            const std::string& data,
            bool is_binary) {
         std::lock_guard lck(g_ws_mtx);
         auto e = g_ws_connections.find(&conn);
         e->second = steady_clock_t::now();
      });

   std::thread ws_ping_thread(ws_ping_thread_fn);
   app.port(g_server_port).bindaddr("127.0.0.1").multithreaded().run();
   ws_ping_thread.join();
}

Может здесь есть люди знающие данный framework. Смотреть реализацию больно, там всё виртуальное, надо расчехлять отладчик и прочее. В доках тоже голяк



Последнее исправление: kvpfs_2 (всего исправлений: 1)

Скорее всего можно. Под капотом send_ping вызывает asio::post.

Submits a completion token or function object for execution.

От функции с таким описанием ожидаешь thread safety, потому что явно один тред постит таски, а другой тред их исполняет (и, возможно, даже не один).

Но я бы на твоём месте создал Issue в репе проекта с просьбой указать в документации thread safety статус, потому что для человека со стороны это не очевидно и от явного указания (хотя бы в списке фич «All methods are thread safe» или «All methods except initialization are thread safe» или как там оно) мир станет лучше.

https://github.com/ipkn/crow/issues/244

Вон тут пишут, что каждый сокет привязан к своему треду по round robin модели и все callback для одного подключения будут вызваны на одном и том же треде. То есть теоретически авторы могут писать код методов класса подключения не потокобезопасно.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

каждый сокет привязан к своему треду

обычно в asio асинхронные операции на сокете синхронизируются через свой объект синхронизации это strand (он гарантирует порядок операций). Для каждого сокета свой отдельный strand, и проблем нет.

конечно все зависит от реализации в crow…

сокет привязан к своему треду по round robin модели и все callback для одного подключения будут вызваны на одном и том же треде

asio это больше это про асинхронность с очередью задач, к какой нитке что привязано лучше не думать, там вообще можно запустить контекст с одним concurrency_hint.

anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 2)
Ответ на: комментарий от KivApple

Скорее всего можно. Под капотом send_ping вызывает asio::post.

Пришёл к тому же заключению. Походил под отладчиком, все send_* методы websocket’ов передают лишь колбэк в io_context с payload и указателем на себя, запись в сокет идет лишь в контексте io_context потока. Ну а добавлять хендлеры в asio::io_context вроде как thread_safe. Плюс также норм дергать websocket::connection::close() (если таймаут > порога), та же история, но вызывает asio::dispatch().

Ещё эти нейросетки), я с ней пол дня спорил, она мне пол дня доказывала, что так нельзя, в итоге расчехлил отладчик.

kvpfs_2
() автор топика
Последнее исправление: kvpfs_2 (всего исправлений: 1)
Ответ на: комментарий от kvpfs_2

Испроьзуй платные версии и не дипсик/гигачат. Они очень сильно отличаются по уму и бесплатные/российские/китайские больше мешают чем помогают. Они на пару лет отстают.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от anonymous2

Я думаю, что в crow хотели дать гарантии, что callback не будут вызваны конкурентно на одном и том же сокете, чтобы state machine обработки индивидуального сокета могла работать без дополнительных синхронизаций, только при доступе к общим данным.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

ты чтото путаешь… вот - asio::asinc_write(socket, asio::buffer, callback)
как это не будет на одном и том же сокете если он там есть?! asio не гарантирует что гонки данных не будет, будет обязательно. они сделали свой объект для синхронизации asio::strand который нужно добавлять.

anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 4)