View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0002511 | Far Manager | Plugins API | public | 2013-10-12 08:17 | 2016-09-29 17:08 |
Reporter | NightRoman | Assigned To | vskirdin | ||
Priority | normal | Severity | block | Reproducibility | always |
Status | closed | Resolution | fixed | ||
Product Version | 3.0 | ||||
Fixed in Version | 3.0 | ||||
Summary | 0002511: ACTL_GETWINDOWINFO зависает при вызове для диалога при недавнем наличии другого диалога | ||||
Description | Проблема проявляется в модуле PowerShellFar плагина FarNet. В панелях: - Открыть диалог Apply command (CtrlG) - F11 \ PowerShellFar \ Invoke input code... - В диалоге ввести код $Far.Dialog, нажать Enter ==> После закрытия второго диалога обрабатывается код получения $Far.Dialog. Там есть вызов ACTL_GETWINDOWINFO, который зависает. Связано или нет, был тикет http://bugs.farmanager.com/view.php?id=2241 , вдруг связано. Там так и не понятно осталось, кто виноват. Мне думается, что Фар виноват в дедлоке, а не плагин, что вызывает Фар из другого потока. Плагин делает это корректно, так как основной поток в это время заморожен и конфликтов быть не должно. Там я выкрутился, просто отключив часть функциональности для диалога авто-дополнения, не так жалко. В этом же случае так поступать не хотелось бы. | ||||
Tags | No tags attached. | ||||
Build | 3839 | ||||
|
Есть надежда, что на это будет обращено внимание? Что-то очень неправильно стало работать. Именно стало, когда-то это работало. Таким способом из PowerShell исследовались данные диалогов Фара (Id и так далее). Теперь этот пласт функциональности отвалился. Когда произошло, не знаю. Используется это редко (но метко). Как мне поступить? Если проблема остается, то надо отрубить в FarNet и PowerShell значительный пласт возможностей. Нельзя же чтоб пользователь был вынужден прибивать Фар с остальной незаконченной работой. Не хотелось бы отрубать, оно работало и было полезно. Проявляется на многих диалогах, не только на CtrlG. Попробовал на атрибутах файлов, тоже зависает. |
|
А нельзя ли тест-кейс на Lua соорудить? Мне, например, по описанию вообще не понятно, в чём дело. Думаю, что шансы на исправление (если баг действительно имеет место) повысились бы. |
|
А лучше на C. С тщательным применением бритвы Оккама. Сейчас в дебаггере видно только то, что один clr-код ждёт другой clr-код. С такими входными данными надежды нет. |
|
А Lua в основном потоке выполняет скрипт? В том, в каком Фар зовет плагин? Дело (думается) вот в чем (но детали могут и другими оказаться, не пинайте). PowerShell свои скрипты выполняет не в основном потоке, а в новом. Основной при этом ничего не делает, ждет и может, например, снять тот поток при желании (клавиша пользователя и т.п.). С точки зрения thread safe тут все в порядке, хотя есть несколько потоков, работает только один, пусть и не основной. Никаких конфликтов при вызовах Фара нет, вызовы могут быть только из одного потока. Хоть и не обязательно из того, что вызвал плагин. Но Фар иногда может ошибочно предполагать, что обратные вызовы обязаны делаться из главного потока. Что может получиться, глупый пример, но не удивлюсь, если так и происходит - Фар входит в ошибочно добавленную в код критическую секцию и оттуда зовет плагин. Плагин зовет Фар, который зовет код, который тоже входит в эту секцию. Если этот вызов в том же потоке, то плагин работает (может, Lua, тогда в лоб не воспроизвести). Если этот вызов из другого потока (PSF), то вызов будет ждать освобождения этой секции бесконечно, дед лок. Сумбурно немного или понятно примерно? Думается мне, что где-то в Фаре критическая секция либо не по делу, либо не аккуратно добавлена. В PSF огромное количество всего работает, *всегда* из неосновного потока. Это на практике подтверждает, что вызов из основного потока не должен быть обязательством для плагина. Но и Фар не должен полагать, что вызовы будут из того же потока. |
|
На написание примера на С у меня может уйти неограниченное количество времени, ибо на нем я мало пишу, а потоки не писал никогда. И при этом не факт, что удастся воспроизвести, по многим причинам может оказаться. Но есть уже готовый пример проблемы, вот в тикете. При этом плагин ничего неправильного не делает. А проблема проявляется. Наверное, непросто ее разрешить, я понимаю это. Потоки - дел хитрое. Но написать что-то еще с этой проблемой - это не совсем способ ее разрешить. |
|
> Но есть уже готовый пример проблемы, вот в тикете. Вот я взял "готовый пример" и запустил: > ntdll.dll!NtWaitForMultipleObjects() Unknown KernelBase.dll!WaitForMultipleObjectsEx() Unknown kernel32.dll!WaitForMultipleObjectsExImplementation() Unknown user32.dll!RealMsgWaitForMultipleObjectsEx() Unknown user32.dll!MsgWaitForMultipleObjectsEx() Unknown ole32.dll!CCliModalLoop::BlockFn(void * * ahEvent=0x00000000001983d4, unsigned long cEvents=2147549461, unsigned long * lpdwSignaled=0x000007feebea56d2) Line 1222 C++ ole32.dll!CoWaitForMultipleHandles(unsigned long dwFlags=1, unsigned long dwTimeout=57605840, unsigned long cHandles=4294967294, void * * pHandles=0x00000000001983e0, unsigned long * lpdwindex=0x00000000001983d4) Line 109 C++ clr.dll!MsgWaitHelper(int,void * *,int,unsigned long,int) Unknown clr.dll!Thread::DoAppropriateAptStateWait(int,void * *,int,unsigned long,enum WaitMode) Unknown clr.dll!Thread::DoAppropriateWaitWorker(int,void * *,int,unsigned long,enum WaitMode) Unknown clr.dll!Thread::DoAppropriateWait(int,void * *,int,unsigned long,enum WaitMode,struct PendingSync *) Unknown clr.dll!WaitHandleNative::CorWaitOneNative(class SafeHandle *,int,bool,bool) Unknown mscorlib.ni.dll!000007fee756cc34() Unknown Это Main Thread. Кода из far.exe тут нет вообще, поэтому вполне возможно предположить, что это плагин сам что-то ошибочно напредполагал. > С точки зрения thread safe тут все в порядке, хотя есть несколько потоков, работает только один, пусть и не основной. С точки зрения thread safe это полный пэ, т. к. любой такой вызов может изменить глобальное состояние, делая всё дальнейшее поведение программы неопределенным. |
|
> С точки зрения thread safe это полный пэ, т. к. любой такой вызов может изменить глобальное состояние, делая всё дальнейшее поведение программы неопределенным. Я не понимаю эту фразу. Если к Фару в данный момент времени из плагина обращается только один поток, то это thread safe доступ к Фару, никаких конфликтов с данными глобальными быть не должно. При этом в PSF основной поток вообще ничего с Фаром не делает, основной поток уходит в ядро PowerShell и он там просто ждет другой запущенный. Запущенный может делать с Фаром, что хочет, PowerShell до этого дела нет. А в дебаггере входы в критические секции в Фаре кода есть, на которых висит? |
|
> Я не понимаю эту фразу. Ок, псевдокод: main thread: foreach(i, GetCurrentWindows()) { i.show(); } your thread: Mainthread.Suspend(); if (DoSomeJob()) Message("Done"); Mainthread.Resume(); Технические детали: - Message - это экземпляр window; - все создаваемые экземпляр window помещаются в коллекцию; - GetCurrentWindows() возвращает ссылку на эту коллекцию; - изменение коллекции инвалидирует итераторы; Внимание, вопрос: что произойдёт после Mainthread.Resume()? P.S. Я не говорю, что такой же или похожий код обязательно есть в far.exe и твоём плагине. Но он может там быть или может появиться в любой момент. |
|
Висит вот тут C:\-\GIT\FarManager3\unicode_far\dialog.cpp(4431): SCOPED_ACTION(CriticalSectionLock)(CS); Попробую понять, почему. |
|
Тупо закомментил все эти секции. Данный пример заработал. Я не говорю, что это решение проблемы. |
|
Вообще, сам факт, что эти секции введены в код, говорит о том, что он даже задуман для работы из разных потоков. Иначе зачем эти секции. Но вот что-то идет не так, как задумано. Хорошо бы разобраться. Пытаюсь, но я в коде Фара очень слаб. В своем тоже ищу подвох. Но не находится, потому и пришел сюда. |
|
> Иначе зачем эти секции. Это следствие того, что поиск файлов (Alt+F7) выполняется в отдельном потоке и обновляет диалог. Их расставили когда-то давно в объеме, достаточном для того, чтобы хотя бы поиск не падал, и на этом остановились. А дедлок, очевидно, происходит потому, что эту же секцию уже захватил один из твоих дополнительных потоков. |
|
> А дедлок, очевидно, происходит потому, что эту же секцию уже захватил один из твоих дополнительных потоков. А как это возможно? Работает с Фаром только один вроде у меня, где скрипт выполняется. Это предположение, что плагин захватил. А может не он? Может не он. Как проверить? Бряки на всех секциях (попробовал) - не вариант, диалоговая процедура то и дело работает. Темный лес.. |
|
И не понятно, как прагин может "захватить" секцию. Что такого можно вызвать у Фара из плагина в данной ситуации, чтобы управление к плагину не вернулось и из-за этого дед лок случился? Мне все же пока кажется, что это Фар химичит с секциями сам. Но проявляется это, вероятно, в такой ситуации, когда обратный вызов из плагина идет из другого потока. Примерно как описано в предположении в начале обсуждения. У меня пока кончились идеи. Надо код Фара знать лучше, чем я, чтобы понять, что происходит. Но думаю об этом тоже. |
|
Не знаю, полезно ли, но провел эксперимент и отпишусь. Добавил макрос и вызвал его из того же диалога CtrlG: Macro { area="Dialog"; description="$Far.Dialog in Dialog"; key="CtrlShiftF9"; action=function() Plugin.Call("10435532-9BB3-487B-A045-B0E6ECAAB6BC", "ps:Show-FarMessage ($Far.Dialog | Out-String) -LeftAligned") end; } Работает. $Far.Dialog получен и все его данные показаны. Повершелл скрипт, как всегда, и тут работает из другого потока. Именно сценарий с F11 и дополнительным диалогом, похоже, оказывается критическим. |
|
И еще эксперимент. Он подчеркивает важность F11 в приведенном сценарии. Макрос: Macro { area="Dialog"; description="$psf.InvokeInputCode() in Dialog"; key="CtrlShiftF9"; action=function() Plugin.Call("10435532-9BB3-487B-A045-B0E6ECAAB6BC", "ps:$psf.InvokeInputCode()") end; } Он запускает тот же диалог ввода кода из того же диалога CtrlG, только не через F11, а из макроса. Код $Far.Dialog набранный там успешно отрабатывает, данные выводятся во вьюер. Итак, именно такой сценарий, как описано вначале, приводит к зависанию, а не вариации. Может, это знание пригодится. |
|
А нет ли в Фаре возможности залогировать входы и выходы в эти секции с выводом стека в лог? Тогда по анализу лога можно было бы понять, кто именно захватил секцию. |
|
Вроде разобрался, знание про F11 помогло-таки. Дело именно в F11 из диалога ("CtrlG" или другого). При обработке клавиши попадаем сюда и входим в критическую секцию: unicode_far\dialog.cpp(2390): int Dialog::ProcessKey(int Key) Выхода из этой критической секции не происходит, так как при обработке F11 вызывается этот код в этой функции: unicode_far\dialog.cpp(2809): return Global->FrameManager->ProcessKey(Key); В этом вызове F11 обрабатывается Фаром, он вызывает FarNet\PowerShellFar, PowerShellFar запускает скрипт (новый поток), замораживает текущий поток, новый поток работает и зовет Фар для получения данных диалога. В этом диалоге критическая секция уже залочена входом в Dialog::ProcessKey, поэтому новый поток при входе в эту критическую секцию ждет бесконечно, дедлок. Проверка. Достаточно закомментировать одну первую строку SCOPED_ACTION(CriticalSectionLock)(CS); в Dialog::ProcessKey, чтобы проблемный пример заработал без зависания. Если мои наблюдения верны, и это действительно причина зависания, то возможно ли будет ее устранить? |
|
Думается, что это в принципе, может, и нормально, что критические секции там почти в каждом методе. Подвох в том, что из них нельзя звать плагин, так как плагин может перевызвать из другого потока. Причем, даже не из "параллельного", а единственного работающего в данное время, как в случае с PowerShell. Словом, надо бы покинуть секцию, и только потом звать плагин (return Global->FrameManager->ProcessKey(Key)). |
|
Коллеги, кто-нибудь работает над исправлением этого зависания? Не к тому спрашиваю, чтобы поворчать. Если некому заняться, я предложу простой патч, который устранит зависание. Красивый рефакторинг можно будет и потом, если захочется. Но будет ли принят патч? Или с ним тикет также затихнет, вот в чем вопрос. |
|
dialog.cpp.diff (1,187 bytes)
diff --git a/unicode_far/dialog.cpp b/unicode_far/dialog.cpp index 6a97c5d..0e5ba6a 100644 --- a/unicode_far/dialog.cpp +++ b/unicode_far/dialog.cpp @@ -2413,6 +2413,11 @@ __int64 Dialog::VMProcess(int OpCode,void *vParam,__int64 iParam) */ int Dialog::ProcessKey(int Key) { + // flag to call global ProcessKey out of critical section, Mantis#2511 + bool doGlobalProcessKey = false; + + // enter critical section + { SCOPED_ACTION(CriticalSectionLock)(CS); _DIALOG(CleverSysLog CL(L"Dialog::ProcessKey")); _DIALOG(SysLog(L"Param: Key=%s",_FARKEY_ToName(Key))); @@ -2829,9 +2834,7 @@ int Dialog::ProcessKey(int Key) if (!Global->IsProcessAssignMacroKey) { if(!CheckDialogMode(DMODE_NOPLUGINS)) - { - return Global->FrameManager->ProcessKey(Key); - } + doGlobalProcessKey = true; } break; @@ -3068,6 +3071,12 @@ int Dialog::ProcessKey(int Key) return ProcessHighlighting(Key,FocusPos,TRUE); } } + } // exit from critical section + + // call global ProcessKey out of critical section to avoid dead lock + if (doGlobalProcessKey) + return Global->FrameManager->ProcessKey(Key); + return FALSE; } |
|
Прикрепил патч, который устраняет описанное зависание Фара - dialog.cpp.diff |
|
build 3839 |
|
Спасибо. Можно закрывать. |
|
Дело сделано. Закройте, пожалуйста, для порядка. |
Date Modified | Username | Field | Change |
---|---|---|---|
2013-10-12 08:17 | NightRoman | New Issue | |
2014-03-15 14:10 | NightRoman | Note Added: 0011937 | |
2014-03-15 15:09 | shmuel | Note Added: 0011938 | |
2014-03-15 15:22 | DrKnS | Note Added: 0011939 | |
2014-03-15 15:28 | NightRoman | Note Added: 0011940 | |
2014-03-15 15:35 | NightRoman | Note Added: 0011941 | |
2014-03-15 15:54 | DrKnS | Note Added: 0011942 | |
2014-03-15 16:01 | NightRoman | Note Added: 0011943 | |
2014-03-15 16:13 | DrKnS | Note Added: 0011944 | |
2014-03-15 16:17 | NightRoman | Note Added: 0011945 | |
2014-03-15 16:23 | NightRoman | Note Added: 0011946 | |
2014-03-15 16:30 | NightRoman | Note Added: 0011947 | |
2014-03-15 16:36 | DrKnS | Note Added: 0011948 | |
2014-03-15 16:44 | NightRoman | Note Added: 0011949 | |
2014-03-15 17:10 | NightRoman | Note Added: 0011950 | |
2014-03-15 17:58 | NightRoman | Note Added: 0011951 | |
2014-03-15 18:10 | NightRoman | Note Added: 0011952 | |
2014-03-17 08:07 | NightRoman | Note Added: 0011957 | |
2014-03-18 10:20 | NightRoman | Note Added: 0011958 | |
2014-03-18 10:48 | NightRoman | Note Added: 0011962 | |
2014-03-20 10:51 | NightRoman | Note Added: 0011989 | |
2014-03-21 11:08 | NightRoman | File Added: dialog.cpp.diff | |
2014-03-21 11:09 | NightRoman | Note Added: 0012015 | |
2014-03-21 12:35 | vskirdin | Note Added: 0012017 | |
2014-03-21 12:35 | vskirdin | Assigned To | => vskirdin |
2014-03-21 12:35 | vskirdin | Status | new => feedback |
2014-03-21 13:09 | NightRoman | Note Added: 0012018 | |
2014-03-21 13:09 | NightRoman | Status | feedback => assigned |
2016-09-28 20:05 | NightRoman | Note Added: 0014824 | |
2016-09-29 17:08 | DrKnS | Build | => 3839 |
2016-09-29 17:08 | DrKnS | Status | assigned => closed |
2016-09-29 17:08 | DrKnS | Resolution | open => fixed |
2016-09-29 17:08 | DrKnS | Fixed in Version | => 3.0 |