рекурсивный mutex
От: DENIVA Россия http://www.uml3.ru
Дата: 02.11.06 11:42
Оценка:
Пример безусловно надуман, но проблему обрисовывает.
Весь код в какой-то функции

HANDLE hMutex;
hMutex = CreateMutex(NULL, FALSE, NULL);
...
DWORD dwRes;
dwRes = WaitForSingleObject(hMutex, INFINITE); // 1
...
dwRes = WaitForSingleObject(hMutex, INFINITE); // 2
...

if (FALSE == ReleaseMutex(hMutex)) // 2
    return API_NOT_OK;
...
if (FALSE == ReleaseMutex(hMutex)) // 1
    return API_NOT_OK;
...


пусть существует только один поток в котором выполняется эта функция.
Все работает прекрасно, т.е. mutex в рамках одного потока позволяет такую вот "рекурсию".

Можно ли что-то сделать, чтобы второй вызов
dwRes = WaitForSingleObject(hMutex, INFINITE); // 2

ожидал пока mutex станет свободным
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: рекурсивный mutex
От: Draqon  
Дата: 02.11.06 14:26
Оценка:
Здравствуйте, DENIVA, Вы писали:
DEN>Можно ли что-то сделать, чтобы второй вызов
DEN>
DEN>dwRes = WaitForSingleObject(hMutex, INFINITE); // 2
DEN>

DEN>ожидал пока mutex станет свободным

...И кто же его освободит, интересно, если тред один, и мьютекс занят им же?
Re: рекурсивный mutex
От: Кодт Россия  
Дата: 02.11.06 14:30
Оценка:
Здравствуйте, DENIVA, Вы писали:

В данном примере можно пользоваться легковесным рекурсивным мутексом, известным как "критическая секция".
А вообще, стоит задуматься — нужна ли рекурсия.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re: рекурсивный mutex
От: rm822 Россия  
Дата: 02.11.06 15:36
Оценка:
Здравствуйте, DENIVA, Вы писали:
DEN>Можно ли что-то сделать, чтобы второй вызов
DEN>
DEN>dwRes = WaitForSingleObject(hMutex, INFINITE); // 2
DEN>

DEN>ожидал пока mutex станет свободным

Это будет семафор
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: рекурсивный mutex
От: Tonal- Россия www.promsoft.ru
Дата: 02.11.06 15:51
Оценка:
Из MSDN:

After a thread obtains ownership of a mutex, it can specify the same mutex in repeated calls to the wait-functions without blocking its execution. This prevents a thread from deadlocking itself while waiting for a mutex that it already owns. To release its ownership under such circumstances, the thread must call ReleaseMutex once for each time that the mutex satisfied the conditions of a wait function.

Re[2]: рекурсивный mutex
От: Кодт Россия  
Дата: 02.11.06 17:01
Оценка:
Здравствуйте, rm822, Вы писали:

R>Это будет семафор


Нет, у семафора иная логика.
Во-первых, его нельзя неограниченно рекурсивно захватывать одним потоком: как только счётчик дойдёт до нуля, поток повиснет в ожидании.
Во-вторых, если исходное значение счётчика равно 2, то оба потока смогут по одному разу его схватить, а затем повиснуть во взаимном ожидании.
То есть, ко всему прочему, это ещё и возможность для дедлоков.

Я бы сперва определился, какую роль играет этот мутекс — обслуживает критическую секцию, барьер, условную переменную...
И если это не критическая секция, то крепко задумался бы о пользе мониторов и условных переменных.
К сожалению, в WinAPI этих инструментов как примитивов нет; но бог милостив, ещё Дейкстра доказал, что любой механизм синхронизации может быть выражен в базисе семафоров.
Поэтому берём ACE, boost/thread, обероно-образную библиотеку (эх, сейчас ссылку не могу найти)...
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[3]: рекурсивный mutex
От: rm822 Россия  
Дата: 02.11.06 17:09
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, rm822, Вы писали:


R>>Это будет семафор


К>Нет, у семафора иная логика.

К>Во-первых, его нельзя неограниченно рекурсивно захватывать одним потоком: как только счётчик дойдёт до нуля, поток повиснет в ожидании.
К>Во-вторых, если исходное значение счётчика равно 2, то оба потока смогут по одному разу его схватить, а затем повиснуть во взаимном ожидании.
К>То есть, ко всему прочему, это ещё и возможность для дедлоков.

К>Я бы сперва определился, какую роль играет этот мутекс — обслуживает критическую секцию, барьер, условную переменную...

К>И если это не критическая секция, то крепко задумался бы о пользе мониторов и условных переменных.
К>К сожалению, в WinAPI этих инструментов как примитивов нет; но бог милостив, ещё Дейкстра доказал, что любой механизм синхронизации может быть выражен в базисе семафоров.
К>Поэтому берём ACE, boost/thread, обероно-образную библиотеку (эх, сейчас ссылку не могу найти)...

Собственно он и хотел чтобы второй Wait заблокировался,
DEN>Можно ли что-то сделать, чтобы второй вызов
DEN>
DEN>dwRes = WaitForSingleObject(hMutex, INFINITE); // 2
DEN>

DEN>ожидал пока mutex станет свободным

что тебе не нравиться?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: рекурсивный mutex
От: Кодт Россия  
Дата: 02.11.06 18:01
Оценка:
К>Поэтому берём ACE, boost/thread, обероно-образную библиотеку (эх, сейчас ссылку не могу найти)...

А, вот, нашёл.
http://qnxclub.net/modules.php?name=Content&amp;pa=showpage&amp;pid=5
http://www.rsdn.ru/Forum/?mid=1815179
Автор: Сергей Губанов
Дата: 31.03.06
— обсуждение
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[2]: рекурсивный mutex
От: DENIVA Россия http://www.uml3.ru
Дата: 03.11.06 06:31
Оценка:
Здравствуйте, Tonal-, Вы писали:

T>Из MSDN:

T>

T>After a thread obtains ownership of a mutex, it can specify the same mutex in repeated calls to the wait-functions without blocking its execution. This prevents a thread from deadlocking itself while waiting for a mutex that it already owns. To release its ownership under such circumstances, the thread must call ReleaseMutex once for each time that the mutex satisfied the conditions of a wait function.


Это я знаю и пример привел как раз для этого правила. Вопрос то звучит так — можно ли избавиться от такой рекурсии? На Linux при создании mutex можно это сделать с помощью специальных флагов, а вот на WIN?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: рекурсивный mutex
От: DENIVA Россия http://www.uml3.ru
Дата: 03.11.06 06:31
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, DENIVA, Вы писали:


К>В данном примере можно пользоваться легковесным рекурсивным мутексом, известным как "критическая секция".

К>А вообще, стоит задуматься — нужна ли рекурсия.

Думать нечего. Это требование спецификации
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: рекурсивный mutex
От: Tonal- Россия www.promsoft.ru
Дата: 03.11.06 07:02
Оценка:
Здравствуйте, DENIVA, Вы писали:

DEN>Это я знаю и пример привел как раз для этого правила. Вопрос то звучит так — можно ли избавиться от такой рекурсии?

Можно. Нужно только немного изменить твой код:
HANDLE hMutex;
hMutex = CreateMutex(NULL, FALSE, NULL);
...
DWORD dwRes;
dwRes = WaitForSingleObject(hMutex, INFINITE); // 1
...
if (FALSE == ReleaseMutex(hMutex)) // 1
    return API_NOT_OK;
...
dwRes = WaitForSingleObject(hMutex, INFINITE); // 2
...

if (FALSE == ReleaseMutex(hMutex)) // 2
    return API_NOT_OK;



В приведённом тобой коде содержится ошибка, или не хватает информации о том, чего ты хочешь добиться.
Ведь если бы mutex не был рекурсивный, то он бы повис в ожидании освобождения на втором вызове WaitForSingleObject, а так как освободить его должен этот же поток, то вот он dedlock!
Опиши задачу более подробно — тогда будет проще тебе помочь.
Re[4]: рекурсивный mutex
От: DENIVA Россия http://www.uml3.ru
Дата: 03.11.06 07:22
Оценка:
Здравствуйте, Tonal-, Вы писали:

T>Здравствуйте, DENIVA, Вы писали:


DEN>>Это я знаю и пример привел как раз для этого правила. Вопрос то звучит так — можно ли избавиться от такой рекурсии?

T>Можно. Нужно только немного изменить твой код:
T>
T>HANDLE hMutex;
T>hMutex = CreateMutex(NULL, FALSE, NULL);
T>...
T>DWORD dwRes;
T>dwRes = WaitForSingleObject(hMutex, INFINITE); // 1
T>...
T>if (FALSE == ReleaseMutex(hMutex)) // 1
T>    return API_NOT_OK;
T>...
T>dwRes = WaitForSingleObject(hMutex, INFINITE); // 2
T>...

T>if (FALSE == ReleaseMutex(hMutex)) // 2
T>    return API_NOT_OK;
T>

T>

T>В приведённом тобой коде содержится ошибка, или не хватает информации о том, чего ты хочешь добиться.

T>Ведь если бы mutex не был рекурсивный, то он бы повис в ожидании освобождения на втором вызове WaitForSingleObject, а так как освободить его должен этот же поток, то вот он dedlock!
T>Опиши задачу более подробно — тогда будет проще тебе помочь.

Задачи как какой нет. Пример кода — это пример из реально работающих приложений и никто его переделывать не будет. Вместо вызовов CreateMutex, WaitForSingleObject, ReleaseMutex вызываются некие функции, которые предоставляют соответствующую функциональность (может быть реализованную и не через CreateMutex, etc). Эти функции и можно менять. По умолчанию они просто соответствуют WIN32 API функциям и предоставляют рекурсивный mutex, но пользователь хочет, чтобы не меняя свой код, а просто вызывая эти функции (может с дополнительным параметром — RECURSIVE или NON_RECURSIVE) он получал разную функциональность.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: рекурсивный mutex
От: Draqon  
Дата: 03.11.06 07:27
Оценка:
Здравствуйте, DENIVA, Вы писали:

DEN>Задачи как какой нет. Пример кода — это пример из реально работающих приложений и никто его переделывать не будет. Вместо вызовов CreateMutex, WaitForSingleObject, ReleaseMutex вызываются некие функции, которые предоставляют соответствующую функциональность (может быть реализованную и не через CreateMutex, etc). Эти функции и можно менять. По умолчанию они просто соответствуют WIN32 API функциям и предоставляют рекурсивный mutex, но пользователь хочет, чтобы не меняя свой код, а просто вызывая эти функции (может с дополнительным параметром — RECURSIVE или NON_RECURSIVE) он получал разную функциональность.


Ты можешь объяснить внятно, кто будет освобождать мьютекс, занятый первым вызовом, если тред, владеющий им, висит в ожидании на втором вызове?!
Re[6]: рекурсивный mutex
От: DENIVA Россия http://www.uml3.ru
Дата: 03.11.06 07:42
Оценка:
Здравствуйте, Draqon, Вы писали:

D>Здравствуйте, DENIVA, Вы писали:


DEN>>Задачи как какой нет. Пример кода — это пример из реально работающих приложений и никто его переделывать не будет. Вместо вызовов CreateMutex, WaitForSingleObject, ReleaseMutex вызываются некие функции, которые предоставляют соответствующую функциональность (может быть реализованную и не через CreateMutex, etc). Эти функции и можно менять. По умолчанию они просто соответствуют WIN32 API функциям и предоставляют рекурсивный mutex, но пользователь хочет, чтобы не меняя свой код, а просто вызывая эти функции (может с дополнительным параметром — RECURSIVE или NON_RECURSIVE) он получал разную функциональность.


D>Ты можешь объяснить внятно, кто будет освобождать мьютекс, занятый первым вызовом, если тред, владеющий им, висит в ожидании на втором вызове?!


код которы я прислал в самом первом письме работает отлично. никаких deadlock там нет
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[6]: рекурсивный mutex
От: DENIVA Россия http://www.uml3.ru
Дата: 03.11.06 07:44
Оценка:
Здравствуйте, Draqon, Вы писали:

D>Здравствуйте, DENIVA, Вы писали:


DEN>>Задачи как какой нет. Пример кода — это пример из реально работающих приложений и никто его переделывать не будет. Вместо вызовов CreateMutex, WaitForSingleObject, ReleaseMutex вызываются некие функции, которые предоставляют соответствующую функциональность (может быть реализованную и не через CreateMutex, etc). Эти функции и можно менять. По умолчанию они просто соответствуют WIN32 API функциям и предоставляют рекурсивный mutex, но пользователь хочет, чтобы не меняя свой код, а просто вызывая эти функции (может с дополнительным параметром — RECURSIVE или NON_RECURSIVE) он получал разную функциональность.


D>Ты можешь объяснить внятно, кто будет освобождать мьютекс, занятый первым вызовом, если тред, владеющий им, висит в ожидании на втором вызове?!


Это проблема приложений, а не библиотеки. Главное, чтобы у пользователя было две возможности, а как он будет это реализовывать — его дело
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: рекурсивный mutex
От: AgentRX Россия  
Дата: 03.11.06 07:47
Оценка:
Здравствуйте, DENIVA, Вы писали:

DEN>Это я знаю и пример привел как раз для этого правила. Вопрос то звучит так — можно ли избавиться от такой рекурсии? На Linux при создании mutex можно это сделать с помощью специальных флагов, а вот на WIN?

Я так понял, что ты хочешь, чтобы другой поток вместо текущего освобождал мьютекс, а твой ждал?
Чем тебя тогда события с автосбросом не устраивают? Только не надо говорить "ничего менять не будем. надо с помощью мьютекса"
Уважайте собеседника, даже если не согласны с его мнением
Re[4]: рекурсивный mutex
От: DENIVA Россия http://www.uml3.ru
Дата: 03.11.06 08:18
Оценка:
Здравствуйте, AgentRX, Вы писали:

ARX>Здравствуйте, DENIVA, Вы писали:


DEN>>Это я знаю и пример привел как раз для этого правила. Вопрос то звучит так — можно ли избавиться от такой рекурсии? На Linux при создании mutex можно это сделать с помощью специальных флагов, а вот на WIN?

ARX>Я так понял, что ты хочешь, чтобы другой поток вместо текущего освобождал мьютекс, а твой ждал?
Правильно понял
ARX>Чем тебя тогда события с автосбросом не устраивают? Только не надо говорить "ничего менять не будем. надо с помощью мьютекса"
Можно и без Mutex Спасибо за совет. Пойду почитаю про "события с автосбросом"
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: рекурсивный mutex
От: Кодт Россия  
Дата: 03.11.06 08:53
Оценка:
Здравствуйте, DENIVA, Вы писали:

К>>А вообще, стоит задуматься — нужна ли рекурсия.

DEN>Думать нечего. Это требование спецификации

Нечасто встречаются такие спецификации, в которых оговорено: "здесь использовать рекурсивный мутекс".

По моему опыту, рекурсивная блокировка возможна только в очень простых случаях, когда взаимодействие потоков очевидно, и можно доказать, что дедлоков нет.
Цель рекурсивной блокировки — ослабить предусловия (касающиеся статуса мутекса) у функций, вызываемых в рамках потока
void threadfunc() // предусловие: уровень блокировки (нашим потоком) = 0
{
    .....
    subroutine1(); // уровень = 0
    .....
    subroutine2(); // уровень = 0
    .....
}
void subroutine1() // предусловия нет
{
    .....
    lock(); ..... unlock();
    .....
    lock();
        .....
        subroutine2(); // уровень = 1
        .....
    unlock();
    .....
}
void subroutine2() // предусловия нет
{
    .....
    lock(); ..... unlock(); // поэтому мы подстраховываемся
    .....
}

То есть, вместо того, чтобы разделить весь код на "небезопасные" и "безопасные" зоны, мы вставляем блокировки ровно там, где они нам потребовались.

Вот пример, когда из-за этого возникает дедлок:
HEVENT shutdown;
HMUTEX guard;
int data;

// поток 1
void writer()
{
    while(WaitForSingleObject(shutdown,0)!=WAIT_OBJECT_0)
    {
        WaitForSingleObject(guard,INFINITE);
            data = rand();
        ReleaseMutex(guard);
    }
}

// поток 2
void reader()
{
    while(WaitForSingleObject(shutdown,0)!=WAIT_OBJECT_0)
    {
        WaitForSingleObject(guard,INFINITE);
            printf("%d ... ", data);
            subroutine();
            printf("%d\n", data);
        ReleaseMutex(guard);
    }
}
void subroutine() // предусловие: мутекс не заблокирован (но мы упустили это из виду)
{
    int olddata;
    WaitForSingleObject(guard,INFINITE); olddata = data; ReleaseMutex(guard);
    // здесь мы наивно предположили, что блокировка снята

    int count = 0;
    int newdata = olddata;
    while(newdata == olddata)
    {
        Sleep(1000);
        ++count;
        // и поэтому надеемся, что рано или поздно writer() запишет туда что-нибудь новое
        WaitForSingleObject(guard,INFINITE); newdata = data; ReleaseMutex(guard);
    }
    printf("we have waited %d seconds for new data.", count);
}

Естественно, в реальной жизни примеры не столь дистиллированные. Но именно поэтому требование рекурсивной блокировки — это ЗВОНОЧЕК.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[4]: рекурсивный mutex
От: Кодт Россия  
Дата: 03.11.06 08:53
Оценка:
Здравствуйте, AgentRX, Вы писали:

DEN>>Это я знаю и пример привел как раз для этого правила. Вопрос то звучит так — можно ли избавиться от такой рекурсии? На Linux при создании mutex можно это сделать с помощью специальных флагов, а вот на WIN?


ARX>Я так понял, что ты хочешь, чтобы другой поток вместо текущего освобождал мьютекс, а твой ждал?

ARX>Чем тебя тогда события с автосбросом не устраивают? Только не надо говорить "ничего менять не будем. надо с помощью мьютекса"

События с автосбросом... или же семафоры.

А всё-таки, я бы попробовал не маяться с жонглированием низкоуровневыми средствами синхронизации, а выразить взаимодействие через высокий уровень. Ну хотя бы через условные переменные.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.