Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2017.07.16;
Скачать: [xml.tar.bz2];

Вниз

Отловить завершение чужих потоков   Найти похожие ветки 

 
Crysis ©   (2016-05-17 16:43) [0]

Вопрос знатокам WinAPI (и желательно POSIX)

В произвольное время разные потоки дёргают мою функцию. Причём потоки могут быть созданы в отдельном Dll кем угодно.
В момент вызова функции я могу получить CurretThreadId.
Необходимо, зная эти ThreadId, навесить на потоки какой-нибудь калбек, который будет вызываться после завершения потока. С эксепшном или в штатном режиме.
Каким образом?

Опережая вопрос "зачем тебе это".
Есть несколько мультипоточных высоконагруженных проектов. Причём бывает потоки создаются в сторонней Dll.
Было решено поменять принципы обработки данных, задействуя threadvar и кеширование данных/буферов.
Но беда в том, что threadvar-области не финализируются при завершении потока.
Тот же TParallel постоянно создаёт и удаляет потоки, а у нас течёт память, причём серьёзно.


 
Pavia ©   (2016-05-17 19:18) [1]

Вешаешь Хук на EndThread


 
Crysis ©   (2016-05-17 20:21) [2]

Как?
Сработает ли это на потоки, которые созданы вне Delphi?


 
DayGaykin ©   (2016-05-17 21:43) [3]

Создаешь свой поток-следилку и там делаешь:
OpenThread+WaitForSingleObject.

Чтобы следить сразу за несколькими потоками: WaitForMultipleObjects и алгоритм посложнее. Тут можно добавить еще Event, чтобы можно было прерывать WaitForMultipleObjects и обновлять список слежения.

Чтобы следить за более чем MAXIMUM_WAIT_OBJECTS потоками, необходимо создать несколько потоков-следилок.


 
Crysis ©   (2016-05-17 22:39) [4]

> DayGaykin ©   (17.05.16 21:43) [3]

Такой подход наверное тоже некорректен.
Калбек по идее должен вызываться в завершающемся потоке. Потому что нужен доступ к TLS.
В предложенном тобой варианте - я его не увижу.


 
NoUser ©   (2016-05-18 00:03) [5]

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


 
Crysis ©   (2016-05-18 00:06) [6]

> NoUser ©   (18.05.16 00:03) [5]

Но у меня же не Dll
Точнее не обязательно Dll


 
NoUser ©   (2016-05-18 00:19) [7]

PS.
> Было решено поменять принципы обработки

А если у потока после работы с функцией еще много планов на дальнейшую жизнь, то "ваша" память будет ему в подарок?


 
NoUser ©   (2016-05-18 00:23) [8]

> Но у меня же не Dll
ну, добавьте ещё и dll


 
DayGaykin ©   (2016-05-18 00:27) [9]


> Crysis ©   (17.05.16 22:39) [4]
> > DayGaykin ©   (17.05.16 21:43) [3]
>
> Такой подход наверное тоже некорректен.
> Калбек по идее должен вызываться в завершающемся потоке.
>  Потому что нужен доступ к TLS.
> В предложенном тобой варианте - я его не увижу.

Тогда нормально никак.

Можно, конечно извратиться так:
Перехватываешь createthread своего процесса. Вместо точки входа указываешь свою функцию, которая: навесит SEH фрейм и перейдет по исходной точке входа. А дальше должно быть понятно, если знать что такое SEH фрейм.


 
Crysis ©   (2016-05-18 00:28) [10]

> А если у потока после работы с функцией еще много планов
> на дальнейшую жизнь, то "ваша" память будет ему в подарок?


В том то и дело
"В подарок" только на время работы. А по завершению потока - память надо грамотно забрать.


 
Crysis ©   (2016-05-18 00:30) [11]

> Перехватываешь createthread своего процесса. Вместо точки
> входа указываешь свою функцию, которая: навесит SEH фрейм
> и перейдет по исходной точке входа. А дальше должно быть
> понятно, если знать что такое SEH фрейм.


Тогда уж не CreateThread, а ExitThread
Но я боюсь, Касперский заблокирует наш софт у клиентов. Или иные антивирусы будут ругаться.


 
DayGaykin ©   (2016-05-18 00:32) [12]


> Crysis ©   (18.05.16 00:30) [11]

В ExitThread может и не настать.


> Но я боюсь, Касперский заблокирует наш софт у клиентов.
> Или иные антивирусы будут ругаться.

В своем процессе ты можешь делать что хочешь.


 
Rouse_ ©   (2016-05-18 12:00) [13]

Тогда лучше ZwCreateThread перехватить и там свою потоковую процедуру с SEH.


 
Leonid Troyanovsky ©   (2016-05-18 13:27) [14]


> Crysis ©   (17.05.16 16:43)

>  Причём бывает потоки создаются в сторонней Dll.Было решено
> поменять принципы обработки данных, задействуя threadvar
> и кеширование данных/буферов.Но беда в том, что threadvar-
> области не финализируются при завершении потока.

threadvar - здесь костыль.

Если какому-либо потоку нужны приватные данные, то он может
распределить память и передать твоей функции указатель на нее.
Перед завершением работы он должен ее освободить.

В случае если код потоков менять нельзя, то память для
потоков может выделять твоя функция, при условии, что
один (или более поток) приложения будет следить путем
WaitForMultipleObjects за рабочими и по их завершении
освобождать связанную с ними память.

Про синхронизацию доступа к общим ресурсам промолчу,
бо, с этим, наверное, все в порядке.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2016-05-18 14:02) [15]


> Leonid Troyanovsky ©   (18.05.16 13:27) [14]

> будет следить путем WaitForMultipleObjects за рабочими и
> по их завершении освобождать связанную с ними память.

Схему можно упростить до следующей:

Функция ведет список (ThreadList) троек: ThreadId,
OTHandle(хендл OpenThread),
Data(указ. на распред. память).

Когда список (или память) переполнится, проходим по списку,
проверяем состояние OTHandle (f.e., GetThreadExitCode),
утилизируем память, CloseHandle, удаляем лишнее.

--
Regards, LVT.


 
Crysis ©   (2016-05-18 14:56) [16]

> Rouse_ ©   (18.05.16 12:00) [13]
> Тогда лучше ZwCreateThread перехватить и там свою потоковую
> процедуру с SEH.


А можно поподробнее?
Подойдёт ли этот способ для потоков, созданных вне Delphi?


 
NoUser ©   (2016-05-18 17:14) [17]

> А можно поподробнее?
Лучше расскажи/покажи как это "созданные кем угодно в отдельном Dll" потоки дёргают функцию в твоём exe ?


 
Crysis ©   (2016-05-18 17:33) [18]

> NoUser ©   (18.05.16 17:14) [17]

А что удивительного? Передаётся калбек, он вызывает в отдельных потоках.


 
NoUser ©   (2016-05-18 17:50) [19]

> Передаётся калбек
И что, внутри калбека каждому "новенькому" потоку даруется безвозмездно кусок памяти? - хорошенький "принцип обработки данных" !


 
Crysis ©   (2016-05-18 18:42) [20]

> NoUser ©   (18.05.16 17:50) [19]

Ну какое значение имеет, даём мы кому-то кусок памяти или нет...
Есть особенность использования. Если завершения всех потоков не получится отследить - я сделаю через SystemThreadEndProc для своих потоков и более сложный и медленный вариант для чужих. Но хочется универсально.


 
DayGaykin ©   (2016-05-18 19:59) [21]

Facepalm


 
NoUser ©   (2016-05-18 20:05) [22]

> Но хочется универсально.
Сделай второй калбек (для зачистки), сделай свою длл-ку в которую "передай" его и там вызывай в момент DLL_THREAD_DETACH.


 
Rouse_ ©   (2016-05-18 22:54) [23]


> Crysis ©   (18.05.16 14:56) [16]
> А можно поподробнее?
> Подойдёт ли этот способ для потоков, созданных вне Delphi?

Что значит "вне Delphi"?
Так ты перехватишь создание нити в твоем приложении (за исключением случая CreateRemoteThread из другого процесса)


 
Crysis ©   (2016-05-19 12:01) [24]

> Rouse_ ©   (18.05.16 22:54) [23]

Допустим я использую сторонний движок, будь то звуковая библиотека, физика, базы данных. Они написаны на другом языке программирования, исходников нет; зато известно, что они вызывают CreateThread и внутри потока дёргают мой калбек. "Перехват ZwCreateThread" поможет в данной ситуации или нет? Можно поподробнее, как перехватить?


 
Rouse_ ©   (2016-05-19 16:04) [25]

Поможет если будешь делать перехват сплайсингом делать.
http://alexander-bagel.blogspot.ru/2013/01/intercept.html
http://alexander-bagel.blogspot.ru/2013/05/intercept2.html


 
Crysis ©   (2016-05-19 17:41) [26]

Спасибо, буду пробовать!


 
han_malign ©   (2016-05-19 17:48) [27]


> решено поменять принципы обработки данных, задействуя threadvar
> и кеширование данных/буферов

- кеширование внешнего контекста в реентерабельной функции - нонсенс...
А промежутоные буфера надо выделять на стеке(в пределах отведенных(по умолчанию) ~ 1Мб естественно)...


 
DayGaykin ©   (2016-05-19 20:32) [28]


> Rouse_ ©   (19.05.16 16:04) [25]

А не проще перехватить импорт DLL?


 
Rouse_ ©   (2016-05-19 20:37) [29]


> DayGaykin ©   (19.05.16 20:32) [28]
> А не проще перехватить импорт DLL?

Он только для статической линковки, используется.


 
DayGaykin ©   (2016-05-19 20:39) [30]


> Rouse_ ©   (19.05.16 20:37) [29]

Со сплайсингом в многопоточности точно не без проблем обойдется, да еще если на 64 битах запускать.


 
Rouse_ ©   (2016-05-19 21:10) [31]

Если правильно делать с использование хотпатча (атомарной замены 2 байт в области нопов  в прологе функции) - никаких проблем не будет. Этож штатный механизм.


 
Rouse_ ©   (2016-05-19 21:17) [32]

В частности в 64 битах делается воот так:

Вот тело ZwCreateThread (адрес точки входа 77AFD890), перед ней идет пятибайтный длинный NOP

77AFD88B: 0F 1F 44 00 00                            nop dword ptr [rax+rax+00h]
77AFD890: B8 DB 3C 03 00                            mov eax, 00033CDBh
77AFD895: 48 63 C0                                  movsxd rax, rax
77AFD898: FF E0                                     jmp rax
77AFD89A: C3                                        ret


первым шагом делаем установку джампа в область длинного нопа,
вторым шагом - атомарно (через LOCK XCHG) джамп на эту область вместо инструкции JMP RAX (для других функций вместо него будет SYSCALL)


 
DayGaykin ©   (2016-05-20 03:33) [33]


> Rouse_ ©   (19.05.16 21:10) [31]
> Если правильно делать с использование хотпатча (атомарной
> замены 2 байт в области нопов  в прологе функции) - никаких
> проблем не будет. Этож штатный механизм.

Если я правильно помню, условия хот-патча не выполняются если программа запущена в 64хбитной ос.


 
Leonid Troyanovsky ©   (2016-05-20 08:40) [34]


> Leonid Troyanovsky ©   (18.05.16 14:02) [15]

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

type
  TMItem = packed record
    ThreadId: Cardinal;
    OTHandle: THandle;
    Data: DWord;
  end;
  PMItem = ^TMItem;

var
 ftl: TThreadList;

function GetDataPtr: PDWord;
var
 i: Longint;
 pmi: PMItem;
 exCode: Cardinal;
begin
 Result := nil;
 with ftl.LockList do
   try
     for i:= Count-1 downto 0 do
       begin
         pmi := PMItem(List[i]);
         if pmi.ThreadId = GetCurrentThreadId then
           begin
             Result := @pmi.Data;
             Continue;
           end;

         GetExitCodeThread(pmi.OTHandle, exCode);
         if ExCode <> STILL_ACTIVE then
           begin
             OutputDebugString(PChar(IntToStr(ExCode)));
             CloseHandle(pmi.OTHandle);
             Dispose(pmi);
             Delete(i);
           end;
       end;

     if Result = nil then
       begin
         New(pmi);
         pmi.ThreadId := GetCurrentThreadId;
         DuplicateHandle( GetCurrentProcess,
                          GetCurrentThread,
                          GetCurrentProcess,
                          @pmi.OTHandle,
                          0,
                          False,
                          DUPLICATE_SAME_ACCESS);
         pmi.Data := 0;
         Add(pmi);
         Result := @pmi.Data;
       end;
   finally
     ftl.UnlockList;
   end;
end;

type
 TMyThread = class(TThread)
   procedure Execute; override;
 end;

procedure TMyThread.Execute;
var
 pData: PDWord;
begin
 pData := GetDataPtr;
 pData^ := Random(10000);
 Sleep (pData^);
 ReturnValue := pData^;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 ftl := TThreadList.Create;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 TMyThread.Create(False).FreeOnTerminate := True;
end;

Смотрим в EventLog.

--
Regards, LVT.


 
Rouse_ ©   (2016-05-20 09:13) [35]


> DayGaykin ©   (20.05.16 03:33) [33]
> Если я правильно помню, условия хот-патча не выполняются
> если программа запущена в 64хбитной ос.

Все выполняется иначе как бы перехваты в моем софте работали?


 
Rouse_ ©   (2016-05-20 09:37) [36]

А, или ты про 32 битный софт в 64 битной ОС? Да, там некоторые функции идут без пролога, но там тоже можно пошаманить


 
DayGaykin ©   (2016-05-20 13:32) [37]


> Rouse_ ©   (20.05.16 09:37) [36]
> А, или ты про 32 битный софт в 64 битной ОС? Да, там некоторые
> функции идут без пролога, но там тоже можно пошаманить

Да-да. Ты мне про это, кстати, и говорил.


 
Cobalt ©   (2016-05-25 11:53) [38]

Почему бы не поменять способ работы на что-то типа
BeginUpdate;
EndUpdate;

?



Страницы: 1 вся ветка

Форум: "Прочее";
Текущий архив: 2017.07.16;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.56 MB
Время: 0.003 c
15-1463907824
Jeer
2016-05-22 12:03
2017.07.16
Питерцы - победители в чм по программированию.


4-1223453904
Denol
2008-10-08 12:18
2017.07.16
Как получить список устройств, подключенных к компьютеру


11-1265717864
tippa
2010-02-09 15:17
2017.07.16
runtime error 216


15-1463911682
MsGuns
2016-05-22 13:08
2017.07.16
VS-2014/MS SQL Server


15-1463758069
Empleado
2016-05-20 18:27
2017.07.16
Получение данных о папке без FindFirst..FindNext





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский