Отслеживание повторного входа потока в функцию

Тема в разделе "WASM.WIN32", создана пользователем Nouzui, 28 фев 2009.

  1. Nouzui

    Nouzui New Member

    Публикаций:
    0
    Регистрация:
    17 ноя 2006
    Сообщения:
    856
    Глупый вопрос возник, больше, правда, теоретический - как отследить повторный вход какого-либо потока в многопоточную функцию? Смысл такой: имеется функция, рассчитанная на параллельное выполнение несколькими потоками. Функция, в свою очередь, вызывает другие функции, и может быть вызвана из них рекурсивно, причем эта ситуация не прогнозируется. Так вот, в функции необходимо каким-то образом отследить, что данный поток входит в нее повторно, и отдельно обработать такой случай. Блокировки на всю функцию целиком не допускаются, другие потоки не должны на ней задерживаться. А теперь в чем проблема:
    1. хочется использовать только апи ntdll
    2. не хочется лезть напрямую в специфичные структуры, в частности PEB, TEB, fs:[0x2C] и прочее..

    ну и что тут можно придумать?
     
  2. 2FED

    2FED New Member

    Публикаций:
    0
    Регистрация:
    20 фев 2008
    Сообщения:
    1.002
    хранить флаг о первом заходе в стеке потока.
     
  3. Nouzui

    Nouzui New Member

    Публикаций:
    0
    Регистрация:
    17 ноя 2006
    Сообщения:
    856
    2FED
    неизвестно, как и через сколько других процедур пройдет поток, прежде чем вернуться в текущую функцию, поэтому непонятно, где потом искать этот флаг

    +еще такой изврат - где-то по пути поток может завершиться, а в функцию потом зайдет новый со случайно совпавшим таким же TID. Поэтому отслеживать по ид потока тоже не канает..
     
  4. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    На его вершине, по адресу [TEB.Tib.StackBase] - 4.
     
  5. Partner

    Partner Павел

    Публикаций:
    0
    Регистрация:
    28 фев 2008
    Сообщения:
    917
    Адрес:
    Los Angeles
    А чем мьютекс не правится?
     
  6. MSoft

    MSoft New Member

    Публикаций:
    0
    Регистрация:
    16 дек 2006
    Сообщения:
    2.854
    критические секции не подойдут?
     
  7. asd

    asd New Member

    Публикаций:
    0
    Регистрация:
    12 мар 2005
    Сообщения:
    952
    Адрес:
    Russia
    Partner MSoft Вы читали первое сообщение?
    Nouzui может глобальная структура в которой сохраняется информация о пидах/времени создания/ещё чего-нибудь потоков, и по этим данным уже идентифицировать поток
     
  8. Partner

    Partner Павел

    Публикаций:
    0
    Регистрация:
    28 фев 2008
    Сообщения:
    917
    Адрес:
    Los Angeles
    asd
    Читали, но не совсем внимательно.:)
    По теме.
    Создать семафор с нулевым начальным значением.
    При входе в функцию вызывать ReleaseSemaphore.
    При этом мы увеличим счетчик семафора, а также получим предыдущее значение этого счетчика.
    Если оно равно 0, значит мы первый раз зашли в фунцию, если 1 - то второй и т.д.
    При выходе из функции вызвать WaitForSingleObject с нулевым тайм-аутом чтобы декрементировать счетчик семафора.
     
  9. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    Partner
     
  10. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Не понятно из какого потока отслеживать, из текущего, который выполняет код функции или из другого, ждущего вход какоголибо потока в эту функцию. Если первый вариант, то необходимо знать как выполнен перехват функции. Например если новый хэндлер перехвата записан в память с атрибутом RW, то можно использовать переменную прямо в коде обработчика.
     
  11. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Флаг можно хранить в TLS: выделяем общий индекс TlsAlloc и затем для каждого потока по этому индексу будет собственный флаг TlsSet\GetValue
     
  12. Nouzui

    Nouzui New Member

    Публикаций:
    0
    Регистрация:
    17 ноя 2006
    Сообщения:
    856
    Clerk
    да, извнияюсь - из того, что повторно входит
    число потоков, разумеется, заранее неизвестно

    leo
    ради спортивного интереса - 1. только используя native-функции. 2. не шарясь напрямую в TEB, fs:[0x2C] и прочем.. и, как опция 3. без ассемблера и машинного кода

    да, еще вопрос - в ntdll предусмотренны аналоги Interlocked-функций?
     
  13. Freeman

    Freeman New Member

    Публикаций:
    0
    Регистрация:
    10 фев 2005
    Сообщения:
    1.385
    Адрес:
    Ukraine
    2,3 - глупые ограничения
     
  14. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Nouzui
    С учётом поставленных требований задача качественно не решается.
    Так нужно помечать каждый поток, поэтому необходимо связать счётчик с ним, а это стек и TEB. Остальные все структуры адресуются через TEB, если в него не лезть, то адреса неоткуда взять, я для подобных целей использовал свободное место в конце страницы с TEB. Если это не подходит, то остаётся стек, хотя не совсем надёжно.
    Лучшим вариантом будет создание таблицы потоков и при входе потока в функцию сканить её.
    Базовые атомарные операции нтдлл не предоставляет вроде, толшько для работы со списками(Slist..)
     
  15. Sol_Ksacap

    Sol_Ksacap Миша

    Публикаций:
    0
    Регистрация:
    6 мар 2008
    Сообщения:
    623
    А всё же, почему бы не залезть в TEB? По-моему, это проще всего. Можно даже без ассемблера и машинного кода (:)
    Это если в MS VS, конечно.

    Код (Text):
    1. // const ulong ArbitraryPointerOffset = offsetof(_TEB, NtTib) + offsetof(_NT_TIB, ArbitraryUserPointer);
    2. long  used = __readfsdword(0x14 /*ArbitraryPointerOffset*/);    // TEB.NtTib.ArbitraryUserPointer (x86)
    3. int64 used = __readgsqword(0x28 /*ArbitraryPointerOffset*/);    // TEB.NtTib.ArbitraryUserPointer (x64)
    4.  
    5. __writefsdword(0x14 /*ArbitraryPointerOffset*/, 1);
    6. __writegsqword(0x28 /*ArbitraryPointerOffset*/, 1);
    Насчёт базовых атомарных операций - компилятор из студии поддерживает и такие intrinsic'и.
     
  16. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Sol_Ksacap
    В случае наличия отладчика вы обламаетесь с данной переменной, следует использовать сободное пространство в конце страницы.
     
  17. Partner

    Partner Павел

    Публикаций:
    0
    Регистрация:
    28 фев 2008
    Сообщения:
    917
    Адрес:
    Los Angeles
    А почему все-таки семафор не подходит - готовый глобальный счетчик.
     
  18. Sol_Ksacap

    Sol_Ksacap Миша

    Публикаций:
    0
    Регистрация:
    6 мар 2008
    Сообщения:
    623
    Clerk
    Хммм? Почему так? Единственное, что мы обнаружили через гугл — How loaded module names are communicated to debugger — т.е. ntdll!LdrpMapViewOfDllSection заменяет эту переменную перед вызовом ZwMapViewOfSection на указатель на имя длл (независимо от наличия дебаггера, кстати), но сразу после вызова возвращает всё на место, ага-ага.
     
  19. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Sol_Ksacap
    Недавно обсуждалось: http://wasm.ru/forum/viewtopic.php?id=31233&p=1
    Эту переменная загружается при создании потока из PspUserThreadStartup:
    Код (Text):
    1. // If the create worked then notify the debugger.
    2.     //
    3.     if ((Thread->CrossThreadFlags&
    4.          (PS_CROSS_THREAD_FLAGS_DEADTHREAD|PS_CROSS_THREAD_FLAGS_HIDEFROMDBG)) == 0) {
    5.         DbgkCreateThread (Thread, StartContext);
    Её загружает код:
    Код (Text):
    1.     Teb->NtTib.ArbitraryUserPointer = Teb->StaticUnicodeBuffer;
    Впрочем не важно, в любом случае это поле используется системой и не следует его юзать для хранения своей переменной. В конце страницы куча места, следует его использовать.
     
  20. Sol_Ksacap

    Sol_Ksacap Миша

    Публикаций:
    0
    Регистрация:
    6 мар 2008
    Сообщения:
    623
    Нет. Судя по wrk ArbitraryUserPointer изменяется в nt!DbgkCreateThread при создании процесса (с той же целью, что и внутри ntdll!LdrpMapViewOfDllSection — чтобы как-то передать дебаггеру имя загружаемого модуля (в данном случае это hard-coded строка "ntdll.dll")). Причём через несколько строчек кода переменной возвращается её старое значение — ноль.
    Т.е. как бы система не изменяла значение ArbitraryUserPointer, оно всегда возвращается обратно до передачи управления пользовательскому коду — всё в порядке с миром.