Здравствуйте.
Пишу прогу на си++. Пытаюсь обработать fastcgi запрос в нескольких потоках, принимая только одним
код примерно следующий:
while(FCGX_Accept_r(&req)==0) {
// кладу в общую очередь объект req
FCGX_Init_req(&req); // точно не помню названия функции
// семафор.post();
}
в потоках делаю следующее:
while(true) {
семафор.wait();
// req = вынимаю из очереди
// обрабатываю, вывожу ответ
FCGX_Finish_r(&req); // объекты req локальные в каждом из потоков
}тестирую совместо с nginx, и поведение неоднозначно, в основном nginx выдает 502, бывает падает с ошибками памяти
пробывал разные варианты, когда потоки "засыпали"
сейчас меня больше интересует, сможет ли меня кто нибудь проконсультировать, потому что не нашел обсуждений fastcgi на си
лучше бы код привел, а не на память как-то так. зае*бись вопросы пошли.напрашиваются 3 ошибки( в порядке вероятности ):
- кладешь в очеред, непроинициализированная FCGX_InitRequest'ом структура копируется, потом ты инициализируешь стековую переменную, а в очереди остается фигня. Тобишь измени порядок вызова FCGX_InitRequest и покладки в очередь.
- фцги у тебя работает через std{in,out,err} - должен в многопоточном приложении по сокетам.
- конкурентный доступ без блокировки к очереди в многопоточном приложении.
учись задавать полные вопросы с описанием.
> - кладешь в очеред, непроинициализированная FCGX_InitRequest'ом структура копируется,
> потом ты инициализируешь стековую переменную, а в очереди остается фигня. Тобишь
> измени порядок вызова FCGX_InitRequest и покладки в очередь.инициализация есть
полный код http://govnokod.com/6123> - фцги у тебя работает через std{in,out,err} - должен в многопоточном приложении
> по сокетам.можно конкретнее?
я пробывал вариант где while(FCGX_Request_r(&req) == 0) у каждого потока свой, тогда все отлично работает, но такой вариант не катит> - конкурентный доступ без блокировки к очереди в многопоточном приложении.
это все есть, в коде только не привел
ну теперь понятней. )у тебя:
while(FCGX_Accept_r(&req) == 0)
{
...
handler_pool_.put_request(req);
...
}А вот код из libfcgi:
int FCGX_Accept_r(FCGX_Request *reqDataPtr)
{
FCGX_Finish_r(reqDataPtr);
...
}где FCGX_Finish_r() все чистит, закрывает и освобождает память. тобишь поток не успевает ответить, а дескрипторы закрываются, память освобождается.
нужно дописать строчку:
while(FCGX_Accept_r(&req) == 0)
{
...
handler_pool_.put_request(req);
FCGX_InitRequest( req, ... ); // < по коду фцги - не занимается освобождением ресурсов, такчто подайдет.
...
}
пс. почему топик в базах данных и sql?
> пс. почему топик в базах данных и sql?кто то перенес, я клал его в веб/CGI
вообщем то виновато что происходит это в следующем порядке:
while(FCGX_Accept_r(&req) == 0)
{
req2 = req1;
FCGX_InitRequest(&req, listen_socket, 0); // переинициализируем чтобы при FCGX_Accept_r не очистились стримы
std::string str = "Content-Type: text/html;\r\n\r\nasads";
FCGX_PutStr(str.c_str(), str.size(), req2.out);
FCGX_Finish_r(&req2);
}
верно.
> верно.ну да, до этого додумался, а в чем дальше трабл непонятно
в FCGX_InitRequest обнуляется память структуры и устанавливаются некоторые значения, но nginx почему то перестает принимает ответ от моей проги - выдает браузеру 502
как при memset'е может затронуться содержимое указателей?
поправка:
while(FCGX_Accept_r(&req) == 0)
{
req2 = req;
Поэксперементировал.Натолкнуло на мысль что разработчики fastcgi пи**расы. Полез в код и понял что разбираюсь в людях:
Accept_r(reqDataPtr) дергает NewReader(reqDataPtr, ...) для создания потока in, NewReader(reqDataPtr, ...) дергает NewStream(reqDataPtr, ...), который содержит вот такие строчки:
--------------
...
FCGX_Stream_Data *data = (FCGX_Stream_Data *)Malloc(sizeof(FCGX_Stream_Data));
data->reqDataPtr = reqDataPtr; // Указатель на себя прикапывают. :)
...
--------------Тобишь паттерт поставщика-потребители в твоей реализации не прокатит. Нельзя реквест трогать, пока не отработает.
Вот такой суперкастыль популярен в мире. )) Хоть сам реализовывай.
удивляюсь, как вам не жалко времени на один из 100500 вопросов, спасибо) сейчас попробую понять что написали
дело 10мин. сейчас на проекте где предстоит реализовывать компонент над фцги. уже проводил исследовательскую работу на подготовительном этапе - смотрел, пробывал, но в глубь не лазял. отвечая на этот вопрос сам опыту набрался, который скоро пригодится, тк тоже бы первым делом писал один поток-поставщик и пул потоков потребителей. и как следствие словил бы ту-же граблю. такчто незачто. )
и все же не догнал, почему это влияет на результат?
там же новые стримы делаются, на стримы req2 никак не должны влиять, не?
строчка: FCGX_Accept_r(&req) - вот этот указатель прикапывается внутрях. даже если мы req скопируем куда-либо и занулим его память, внутрях будет использована эта переменная.
это то понятно, но нам не важна сама переменная, указатели на потоки то скопировали в другую, куда и производится вывод
req2 = req;
FCGX_InitRequest(&req, listen_socket, 0); // почистили req
FCGX_PutStr(str.c_str(), str.size(), req2.out); // выводим в req2, где указатели потоков сохранились и не очищались
в том-то и дело, что переменная важна. они прикапывают указатель именно req. И через нее осуществляется доступ к содержимому реквеста:FCGX_PutStr() содержит: stream->emptyBuffProc(stream, FALSE);
где, stream->emptyBuffProc - для in отока - NULL, для out потока - указатель на функцию EmptyBuffProc(), которая содержит строчки:
----------------------->
*((FCGI_Header *) data->buff)
= MakeHeader(data->type,
data->reqDataPtr->requestId, cLen, eLen - cLen);
----------------------->
if (write_it_all(data->reqDataPtr->ipcFd, (char *)data->buff, stream->wrNext - data->buff) < 0) {
----------------------->тобишь, при записи в out поток используется указатель на структуру запроса для получения идентификатора запроса и дескриптора сокета. Т.к. указатель смотрит на req - в этих местах они для него - 0.
спасибо, добрый чел, теперь дошло)
вставил костыль, где меняю этот указатель на адрес req2 - теперь все пашет
не забудь что там еще err и in потоки есть. а вообще хреново костыль костылем закрывать.
куда теперь деваться, какие еще варианты многопоточности с одним приемником можно тогда придумать?