У меня есть очереди без блокировки, написанные на C в виде связанного списка, который содержит запросы из нескольких потоков, отправленных и обработанных в одном потоке. После нескольких часов стресса я получаю следующий указатель последнего запроса, указывающий на себя, что создает бесконечный цикл и блокирует поток обработки.
Приложение работает (и не работает) как в Linux, так и в Windows. Я выполняю отладку в Windows, где мой COMPARE_EXCHANGE_PTR
сопоставляется с InterlockedCompareExchangePointer а>.
Это код, который помещает запрос в начало списка и вызывается из нескольких потоков:
void push_request(struct request * volatile * root, struct request * request)
{
assert(request);
do {
request->next = *root;
} while(COMPARE_EXCHANGE_PTR(root, request, request->next) != request->next);
}
Это код, который получает запрос из конца списка и вызывается только одним потоком, который их обрабатывает:
struct request * pop_request(struct request * volatile * root)
{
struct request * volatile * p;
struct request * request;
do {
p = root;
while(*p && (*p)->next) p = &(*p)->next; // <- loops here
request = *p;
} while(COMPARE_EXCHANGE_PTR(p, NULL, request) != request);
assert(request->next == NULL);
return request;
}
Обратите внимание, что я не использую хвостовой указатель, потому что хотел избежать осложнений, связанных с необходимостью иметь дело с хвостовым указателем в push_request
. Однако я подозреваю, что проблема может заключаться в том, как я нахожу конец списка.
Есть несколько мест, которые помещают запрос в очередь, но все они выглядят примерно так:
// device->requests is defined as struct request * volatile requests;
struct request * request = malloc(sizeof(struct request));
if(request) {
// fill out request fields
push_request(&device->requests, request);
sem_post(device->request_sem);
}
Код, обрабатывающий запрос, делает больше, но по сути делает это в цикле:
if(sem_wait_timeout(device->request_sem, timeout) == sem_success) {
struct request * request = pop_request(&device->requests);
// handle request
free(request);
}
Я также только что добавил функцию, которая проверяет список на наличие дубликатов до и после каждой операции, но я боюсь, что эта проверка изменит время, так что я никогда не столкнусь с моментом, когда она выйдет из строя. (Я жду, когда он сломается, пока пишу это.)
Когда я прерываю зависшую программу, поток обработчика зацикливается на pop_request
в отмеченной позиции. У меня есть действительный список из одного или нескольких запросов, и последний следующий указатель указывает на себя. Очереди запросов обычно короткие, я никогда не видел больше 10, и только 1 и 3 два раза я мог посмотреть на этот сбой в отладчике.
Я обдумал это как можно больше и пришел к выводу, что у меня никогда не получится зациклиться в моем списке, если я не отправлю один и тот же запрос дважды. Я совершенно уверен, что этого никогда не произойдет. Я также почти уверен (хотя и не полностью), что это не проблема ABA.
Я знаю, что могу отправить более одного запроса одновременно, но я считаю, что это не имеет значения здесь, и я никогда не видел, чтобы это происходило. (это тоже исправлю)
Я долго и упорно думал о том, как я могу сломать свою функцию, но я не вижу способа закончить цикл.
Итак, вопрос в следующем: может ли кто-нибудь увидеть, как это можно сломать? Может ли кто-нибудь доказать, что это невозможно?
В конце концов я решу это (возможно, с помощью хвостового указателя или другого решения - блокировка будет проблемой, потому что потоки, которые публикуются, не должны быть заблокированы, хотя у меня есть блокировка RW), но я хотел бы убедиться, что изменение списка фактически решает мою проблему (а не делает ее менее вероятной из-за разного времени).
NULL
, но не очевидно, что вы начинаете с пустого списка, установленного вNULL
. Кроме того, если вы считаете, что отправляете повторяющиеся запросы, вы должны утверждать, чтоrequest->next==NULL
в началеpush_request()
. - person MSN   schedule 26.04.2010device->requests
, содержащего NULL.request->next
перезаписывается в первой строке цикла do-while. Под дублирующим запросом я подразумеваюpush_request(request); push_request(request);
. В этом случае я бы получил петлю. - person Fozi   schedule 26.04.2010