View Issue Details

IDProjectCategoryView StatusLast Update
0002241Far ManagerPlugins APIpublic2013-04-28 13:41
ReporterNightRoman Assigned To 
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionno change required 
Product Version3.0 
Summary0002241: ACTL_GETWINDOWINFO зависает при вызове из DialogAutoCompletion
DescriptionИмеется макрос для модуля PowerShellFar плагина FarNet:

Macro {
area="Dialog DialogAutoCompletion"; description="Far hangs in DialogAutoCompletion"; key="CtrlShiftF9"; action=function()
Plugin.Call("10435532-9BB3-487B-A045-B0E6ECAAB6BC", ":>:Show-FarMessage $Far.Line.Text")
end;
}

Его задача - получить и показать содержимое текущего поля ввода диалога. Работает нормально, если область Dialog, и висит, если область DialogAutoCompletion.

Что делается в последнем случае. Открываю диалог Apply Command (CtrlG). Набираю что-нибудь, чтобы появилось меню авто дополнения, вызываю макрос. Что делается в FarNet - он пытается получить самиый верхний диалог, чтобы запросить текущее поле ввода и его текст. Для этого получается число окон - оно равно 3 (панели, диалог Apply Command, меню авто дополнения). Начиная с позиции 2 перебираем окна, пока тип не WTYPE_DIALOG. Для окна с индексом 2 ACTL_GETWINDOWINFO отрабатывает, видим, что это не диалог, идем дальше. На окне с индексом 1 ACTL_GETWINDOWINFO зависает.

Если делать это не на тестовом плагине, а на FarNet 5.0.31 и PowerShellFar 5.0.34, как описано, то нужны именно эти указанные версии или более поздние.

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

Far 3.0.3061.
TagsNo tags attached.
Build0

Activities

NightRoman

2013-01-03 12:05

reporter   bugnote:0009911

Есть чуть проще макрос, если именно этим путем смотреть проблему (одно двоеточие в команде убрано):

Macro {
 area="Dialog DialogAutoCompletion"; description="Far hangs in DialogAutoCompletion"; key="CtrlShiftF9"; action=function()
 Plugin.Call("10435532-9BB3-487B-A045-B0E6ECAAB6BC", ">:Show-FarMessage $Far.Line.Text")
 end;
 }

Первый макрос работает с использованием ACTL_SYNCHRO, второй без. Висят оба в описанном сценарии.

lort

2013-01-04 14:22

developer   bugnote:0009915

Здесь проблема в том, что ACTL_GETWINDOWINFO не из основного потока вызывается, в результате получаем зависание при вызове EnterCriticalSection, для критической секции, в которую уже в основном потоке вошли.

NightRoman

2013-01-04 14:31

reporter   bugnote:0009916

В обоих случаях? Второй-то сценарий (макрос) вроде без ACTL_SYNCHRO работает. И тоже висит.

А что делать с этой проблемой? Как-то лечить/обходить в плагине или как?

NightRoman

2013-01-04 14:33

reporter   bugnote:0009917

Или тут потоки с ACTL_SYNCHRO и не связаны?

lort

2013-01-04 14:54

developer   bugnote:0009918

Да, в обоих случаях вызов не из основного потока идет, если вызывать из основного все должно быть нормально

NightRoman

2013-01-04 15:15

reporter   bugnote:0009919

Может, туплю, но так и не понял, кто "виноват"? И почему нельзя вызывать из неосновного? В PowerShell все скрипты по сути из неосновного вызываются, так устроено ядро PowerShell, и это не поменять. И все работает хорошо, ну кроме этого случая. Кроме того, я упомянул, что не висит, если вызывать в области Dialog, и висит, если вызывать в DialogAutoCompletion.

NightRoman

2013-01-04 15:24

reporter   bugnote:0009920

Понятно, что нельзя Фар АПИ вызывать просто так из *параллельного фонового* потока. Но этого и не делается в моем случае. Когда вызов идет из неосновного потока, то основной остановлен, эта ситуация "thread safe", проблем быть не должно. На форуме уже обсуждали, что ничего нет плохого в неосновных потоках, надо лишь, чтобы основной был либо остановлен, либо из него не должны идти вызовы АПИ. (DrKnS вводил проверкку поток и возврат ошибки, это убрали, потому что неправильное ограничение). Иначе говоря, задача плагина обеспечить, что только один поток работал с АПИ. А основной или нет - неважно.

NightRoman

2013-01-04 15:53

reporter   bugnote:0009921

И еще примечательно, что зависает всегда при втором вызове ACTL_GETWINDOWINFO. Не может же так быть в нормальной ситуации, что один вызов можно делать, а два нет.

Если это особая ситуация с DialogAutoCompletion - другое дело. Но это хотелось бы выяснить и задокументировать. А если баг, то, пофиксить, конечно.

Кроме того, я попробовал в ShellAutoCompletion тот же макрос и 2 вызова ACTL_GETWINDOWINFO (2 раза $Far.Line.Text):
Macro {
area="ShellAutoCompletion Dialog DialogAutoCompletion"; description="Sync Far hangs in DialogAutoCompletion"; key="CtrlShiftF9"; action=function()
Plugin.Call("10435532-9BB3-487B-A045-B0E6ECAAB6BC", ">:Show-FarMessage ($Far.Line.Text + $Far.Line.Text)")
end;
}

И все работает. Нет, это что-то особенное для DialogAutoCompletion.

lort

2013-01-04 18:56

developer   bugnote:0009922

DialogAutoCompletion тут ничем принципиально от обычного диалога не отличается, такой же эффект можно получить, например, создав диалог, и, в его обработчике сообщений, создав второй диалог. Теперь, если из другого потока вызвать ACTL_GETWINDOWINFO для первого диалога, получим зависание.
Тут дело в том, что в фаре все действия над диалогами синхронизируются с помощью критических секций, в критическую секцию мы входим перед вызовом обработчика сообщений первого диалога, и выйдем из неё теперь только после закрытия второго диалога. Сделано так, как я понимаю, для диалога поиска, он в отдельном потоке создается.

В общем, на мой взгляд, проблема тут в плагине, никто ведь не обещал, что вызов из других потоков будет работать, даже если основной в это время ничего не делает.
Хотя, может быть, и не стоит обработчики сообщений из критических секций вызывать.

NightRoman

2013-01-04 19:27

reporter   bugnote:0009923

Можно считать это проблемой плагина. Но АПИ-то и нужен, чтобы решать задачи плагинов без проблем плагинов.

В общем, диалог из диалога - особая ситуация, тут все не так хорошо, как хотелось бы, так? Ладно, придется избегать тогда. Но поиграюсь еще, у меня есть ведь случаи диалога из диалога, и там все работает, я почти уверен. Мне все равно кажется, что это специфично для авто-дополнения. Иначе я бы давно проблему заметил. А тут решил попробовать работать, не закрывая авто-дополнение, и сразу встал на граблю.

NightRoman

2013-01-04 20:15

reporter   bugnote:0009925

Назапускал несколько тестовых диалогов друг из друга и там в зоне Dialog вызвал макрос. Все работает. Так что это неверно, что диалог из диалога - проблема. Пока проблема наблюдается только при отрытом авто-дополнении. А потому выглядит либо как баг, либо недоработка. Просто никто ранее такой "продвинутый" сценарий еще не пробовал (либо пробовал, но поленился написать).

lort

2013-01-04 20:42

developer  

tst.cpp (2,194 bytes)   
#include "plugin.hpp"


PluginStartupInfo Info;
FarStandardFunctions FSF;


// {BDA97483-6268-4CC7-993D-042067AE639F}
static const GUID PluginGuid = { 0xbda97483, 0x6268, 0x4cc7, { 0x99, 0x3d, 0x4, 0x20, 0x67, 0xae, 0x63, 0x9f } };


// {2A8D376D-5ABE-4AF1-A9F5-66053F8C9F7F}
static const GUID DialogGuid = { 0x2a8d376d, 0x5abe, 0x4af1, { 0xa9, 0xf5, 0x66, 0x5, 0x3f, 0x8c, 0x9f, 0x7f } };


void Dialog();

int cnt=0;

DWORD WINAPI ThreadProc(void*)
{
  WindowInfo wi={sizeof(WindowInfo)};
  wi.Pos=1;
  Info.AdvControl(&PluginGuid, ACTL_GETWINDOWINFO, 0, &wi);
  return 0;
}

intptr_t WINAPI DlgProc(HANDLE hDlg, intptr_t Msg, intptr_t Param1, void  *Param2)
{
	if(Msg==DN_CONTROLINPUT)
	{
	  const INPUT_RECORD* record=(const INPUT_RECORD *)Param2;
	  if(record->EventType==KEY_EVENT && record->Event.KeyEvent.bKeyDown)
	  {
	    WORD Key=record->Event.KeyEvent.wVirtualKeyCode;
      if (Key==VK_F1)
      {
	      if(!cnt)
	      {
      	  ++cnt;
      	  Dialog();
	      }
	      else
	      {
//ThreadProc(NULL);

	        DWORD tid;
          HANDLE thr=CreateThread(NULL, 0, ThreadProc, NULL, 0, &tid);
	        WaitForSingleObject(thr, INFINITE);
	        CloseHandle(thr);
	      }
      }
	  }
	}

  return Info.DefDlgProc(hDlg, Msg, Param1, Param2);
}

void WINAPI GetGlobalInfoW(GlobalInfo *Info)
{
  Info->StructSize=sizeof(*Info);
  Info->MinFarVersion=FARMANAGERVERSION;

	VersionInfo ver={0, 1, 0, 1, VS_ALPHA};
  Info->Version=ver;
	Info->Guid=PluginGuid;
  Info->Title=L"tst";
	Info->Description=L"tst";
	Info->Author=L"tst";
}

void WINAPI SetStartupInfoW(PluginStartupInfo *Info)
{
  ::Info=*Info;
  FSF=*Info->FSF;
}

void WINAPI GetPluginInfoW(PluginInfo *Info)
{
  Info->StructSize=sizeof(*Info);
  Info->CommandPrefix=L"tst";
  Info->Flags=PF_DISABLEPANELS;
}


void Dialog()
{
  FarDialogItem di[]={{DI_DOUBLEBOX, 1, 1, 9, 9}};

  HANDLE hdlg=Info.DialogInit(&PluginGuid, &DialogGuid, -1, -1, 10, 10, NULL, di, 1, 0, FDLG_NONE, DlgProc, NULL);
  Info.DialogRun(hdlg);
  Info.DialogFree(hdlg);
}


HANDLE WINAPI OpenW(OpenInfo *OInfo)
{
  Dialog();
  return NULL;
}
tst.cpp (2,194 bytes)   

lort

2013-01-04 20:44

developer   bugnote:0009926

Прикрепил тестовый плагин, на котором я проверял, запускается через префикс tst: потом, после первого F1 создается второй диалог, после второго - висим

NightRoman

2013-01-04 21:06

reporter   bugnote:0009927

Но у меня нет пока проблем при диалоге из диалога. Твой случай может другую "проблему" показывать. В общем, есть над чем подумать тут, кажется.

NightRoman

2013-01-05 08:33

reporter   bugnote:0009929

Last edited: 2013-01-05 08:38

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

В общем, я у себя это дело отключаю через убирание автодополнения в макросе эскейпом. Остальное у меня работает. Так жить можно вполне. Можно считать фичей, только надо это документировать, чтобы писатели плагинов или макросов не совались в темные места напрасно.

DrKnS

2013-01-05 09:52

administrator   bugnote:0009930

Следует понимать, что "Когда вызов идет из неосновного потока, то основной остановлен" - это ни разу не "thread safe" по вполне понятным причинам.

Far API не спроектировано для вызова из дополнительных потоков, кроме случаев, оговоренных отдельно.
Хотя программного запрета и нет, но корректная работа не гарантируется, всё на свой страх и риск.
Поэтому пока что, увы, могу только порекомендовать "а вы так не делайте".

NightRoman

2013-01-05 15:33

reporter   bugnote:0009931

> "Когда вызов идет из неосновного потока, то основной остановлен" - это ни разу не "thread safe" по вполне понятным причинам.
 
Нет, непонятно. Википедия гласит:

Потоковая безопасность (англ. thread-safety) — это концепция программирования, применимая к многопоточным программам. Код потоково-безопасный, если он функционирует корректно при использовании из нескольких потоков одновременно. В частности, он должен обеспечивать корректный доступ нескольких потоков к разделяемым данным.

Но все равно, как называть. Если будет работать, то у плагинов будет больше возможностей. Если не будет, то не будет :)

DrKnS

2013-01-05 15:55

administrator   bugnote:0009932

> Нет, непонятно.

"- Ну вот представь, живешь ты себе, ходишь в институт, учишься, работаешь, а потом однажды просыпаешься... А ты - кот о_О"


Это к тому, что доступ к разделяемым данным может быть сколь угодно корректным, но если они будут изменены, а уснувший такого не ожидал, то будет ой.

NightRoman

2013-01-05 17:56

reporter   bugnote:0009933

Ладно, бага или не бага - не суть важно. Важно, как плагину получить работу с диалогом, когда открыто автодополнение. Особенно из PowerShellFar скрипта, который из другого потока работает при остановленном основном (ну или цикл там, главное, что он не зовет Фар АПИ).

А также пример lort-а. Там-то висит без всяких потоков вроде (но я не смотрел детально), это ж не PowerShell скрипт.

NightRoman

2013-01-08 03:22

reporter   bugnote:0009943

Подумалось тут. EnterCriticalSection вызывается, и из-за двойного входа случается зависание, дедлок. То есть получается, код все-таки предназначен для вызовов из других потоков (и даже, по идее, фоновых, параллельных, а не просто других). А иначе зачем EnterCriticalSection?

NightRoman

2013-01-15 16:23

reporter   bugnote:0010009

Уточнение. На всякий пожарный.

> Если делать это не на тестовом плагине, а на FarNet 5.0.31 и PowerShellFar 5.0.34, как описано, то нужны именно эти указанные версии или более поздние.

"Более поздние" не годятся, там зависание устранено ничегонеделанием.

Issue History

Date Modified Username Field Change
2013-01-03 11:55 NightRoman New Issue
2013-01-03 12:05 NightRoman Note Added: 0009911
2013-01-04 14:22 lort Note Added: 0009915
2013-01-04 14:31 NightRoman Note Added: 0009916
2013-01-04 14:33 NightRoman Note Added: 0009917
2013-01-04 14:54 lort Note Added: 0009918
2013-01-04 15:15 NightRoman Note Added: 0009919
2013-01-04 15:24 NightRoman Note Added: 0009920
2013-01-04 15:53 NightRoman Note Added: 0009921
2013-01-04 18:56 lort Note Added: 0009922
2013-01-04 19:27 NightRoman Note Added: 0009923
2013-01-04 20:15 NightRoman Note Added: 0009925
2013-01-04 20:42 lort File Added: tst.cpp
2013-01-04 20:44 lort Note Added: 0009926
2013-01-04 21:06 NightRoman Note Added: 0009927
2013-01-05 08:33 NightRoman Note Added: 0009929
2013-01-05 08:36 NightRoman Note Edited: 0009929
2013-01-05 08:38 NightRoman Note Edited: 0009929
2013-01-05 09:52 DrKnS Note Added: 0009930
2013-01-05 15:33 NightRoman Note Added: 0009931
2013-01-05 15:55 DrKnS Note Added: 0009932
2013-01-05 17:56 NightRoman Note Added: 0009933
2013-01-08 03:22 NightRoman Note Added: 0009943
2013-01-15 16:23 NightRoman Note Added: 0010009
2013-04-28 13:41 alexy Build => 0
2013-04-28 13:41 alexy Status new => closed
2013-04-28 13:41 alexy Resolution open => no change required