Привет всем, такая пробема. Первый поток захватывает критическую секцию, второй поток входит в ней и начинает ждать освобождение её первым потоком на эвенте в NtWaitForSingleObject с запрещённой обработкой апк(нормальная RtlEnterCriticalSection()). Критическая секция - RtlpCalloutEntryLock, тоесть исключения нельзя вызывать. Как первому потоку определить вход второго потока в критическую секцию, либо к чему и сводится ожидание - начало ожидания на эвенте. Эвент сигнализировать нельзя, так тредов может быть многа. По таймеру или просто в цикле крутится не подходит(как в спинлоке). Никакой код менять нельзя и хватать тоже. Возможно такое реализовать ?
пс. http://www.rsdn.ru/article/baseserv/critsec.xml - тут классно описано что к чему. псс. прошу прощения за очередной самопиар, но http://code.google.com/p/critsect/source/browse/trunk/critsect.cpp
Great Можно смысл кратко, чтото я не пойму принцип. Как ждать вход потоков в критическую секцию, тоесть нужно чтоб ожидающий тред ожидал на какомто обьекте, ждал доставку апк и тп., но не забирал процессорное время выполняя проверку какойто переменной в цикле.
У тебя два потока ожидающие, который ты имеешь в виду? А лучше приведи пример и обзови каждый как-нибудь, там поток А и потом Б. Опиши что делает поток А, что делает поток Б. Я запутался пока читал твой пост) И с первым так же ))
Great Если захватить потоком критическую секцию, который будет мониторить последующие входы в эту критическую секцию, то последующие вызовы приведут к залочиванию всех тредов, они будут ждать сигнализацию эвента в этой критической секции, далее так как вход синхронно выполняется можно уже нормально обработать, например вручную обьект сигнализировать для того, чтобы тред продолжил исполнение. Короче говоря такая ситуация: - Тред A. Захватывает критическую секцию(изначально свободна). Этот поток должен ждать вход остальных потоков в эту критическую секцию. После входа в неё он продолжает исполняться. Что далее делать не понятно. - Тред B. Пытается захватить критическую секцию, что приводит к ожиданию им освобождения её тредом A на эвенте. Ожидание с запретом на обработку апк, что всё и портит. Именно в этот момент тред A должен выполнить какойто код. - Тред C. Ведёт себя аналогично треду B. Остальные также. Тоесть тред A мониторит входы потоков в критическую секцию. Один тред изначально захвативший критическую секцию должен ожидать вход в эту критическую секцию остальных потоков, при входе они не продалжают исполняться а по прежнему находятся в ожидании. Насколько знаю никакой обьект не позволяет такое выполнить.
Если количество тредов известно, то может семафор поможет. Создаем семафор со счетчиком по количеству тредов. Каждый тред перед попыткой захвата КС декрементирует семафор. Захвативший КС тред проверяет семафор, и если он равен нулю, значит все треды висят в ожидании. P.S. Не уверен, что я правильно понял что хочет автор.
Partner Не совсем то, собственная реализация не нужна, необходимо сделать для функции RtlEnterCriticalSection(), например: Код (Text): ; Поток который отслеживает вход других потоков в критическую секцию. invoke RtlInitializeCriticalSection, addr CriticalSection invoke RtlEnterCriticalSection, addr CriticalSection ; Крутится в цикле пока другой тред не войдёт в критическую секцию. @@: cmp CriticalSection.LockCount,0 ; RTL_CRITICAL_SECTION.LockCount je @b [...] Когда какойто поток попытается захватить кс тот поток выйдет из цикла. Аналогично необходимо выполнить, Но без циклов, исполнить какието манипуляции чтобы ожидающий тред ждал как положено на обьекте.
Ждать можно на ядерном объекте. Критическая секция таковым не является, поэтому на ней ждать не получится. Значит по любому нужно использовать какие-то другие объекты. Я предложил семафор.
Partner Разумеется критическая секция это не обьект, это механизм в котором выполняется ожидание на эвенте. Использование семафора ничего не даст, если подразумевается замена хэндла. Код нельзя изменять функции, но можно изменять в пределах разумного любые структуры используемые в ней.
Clerk Насколько мы поняли поток А может ждать на event'е. В этом случае можно попробовать модифицировать поля критической секции RtlCriticalSectionLock. Модифицировать таким образом, чтобы потоки могли входить в эту секцию как обычно, но при выходе всегда бы поднималось её событие. А в потоке А ждать на этом событии. Это мы к тому, что при попытке входа в крысекцию бывает нужно модифицировать глобальный список дебажной информации крысекций – RtlCriticalSectionList. А этот список защищён крысекцией RtlCriticalSectionLock. Таким образом, если ждать на RtlCriticalSectionLock.LockSemaphore, то, теоретически, можно отловить момент входа в некоторые критические секции. Всё ^это^ только самые предварительные рассуждения, нуждающиеся в проверке.
RtlCriticalSectionLock используется при инициализации кс, при входе в RtlpWaitForCriticalSection() не используется, собственно тут и ожидание. Хотел таймаут задать, но тоже не то, будет эксцепшин сгенерирован, после чего деадлок/переполнение стека следящего треда.
Partner Ага, именно. При входе в крысекцию в некоторых случаях (ниже) происходит захват RtlCriticalSectionLock, затем выполнение нескольких инструкций (возможна вставка дебажной инфы крысекции в список RtlCriticalSectionList) и сразу же освобождение RtlCriticalSectionLock, соответсвенно с возможным поднятием события. Именно это мы, лайк, пытались сказать. Clerk Смотрели ntdll под вистой. Код здорово отличается, видимо. RtlpWaitForCriticalSection() – пустая заглушка, а RtlCriticalSectionLock может быть захвачена и внутри RtlEnterCriticalSection() при выполнении всех следующих условий: Код (Text): cs захвачена другим тредом cs.SpinCount & 0x04000000 cs.DebugInfo != -1 cs.DebugInfo->ProcessLocksList == 0
Clerk Так не бывает, это деадлок. Такой объект синхронизации называется семафор. Делаешь его счетчик равным числу потоков, и в каждом из них декрементируешь. А в первом - ждешь на семафоре.
Great Никакой не дедлок, тред вошёл в кс, только RecursionCount инкрементирует, в #8 пример. Наверно никак, еслибы не эта критическая секция, то можно было эксцепшин сгенерировать, но тут нельзя, видимо единственный вариант по таймеру/задержку сделать опрашивать, тогда быстродействие очень низким будет.
Partner Не подходит тут семафор. Еслибы симафор сигнализировался при нулевом значении счётчика захватов, то он бы подошёл. В этом случае мониторящий тред ждал бы сигнализацию семафора, другой тред захватил его и ждал на нём, при этом мониторящий тред вышел из состояния ожидания. Но семафор работает иначе, он сигнализирован пока число захватов не станет равным нулю, тоесть второй тред не может ждать на нём первый.