Карусель потоков

Тема в разделе "WASM.BEGINNERS", создана пользователем HoShiMin, 29 сен 2017.

  1. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.486
    Адрес:
    Россия, Нижний Новгород
    Стало интересно, можно ли переключить два потока между собой, чтобы каждый продолжил выполняться с точки остановки другого. Сказано - сделано.

    Код (C):
    1.  
    2. VOID SwitchThreads(ULONG Thread1Id, ULONG Thread2Id) {
    3.     HANDLE hThread1 = OpenThread(THREAD_ALL_ACCESS, FALSE, Thread1Id);
    4.     HANDLE hThread2 = OpenThread(THREAD_ALL_ACCESS, FALSE, Thread2Id);
    5.  
    6.     CONTEXT ctxThread1 = { 0 }, ctxThread2 = { 0 };
    7.     ctxThread1.ContextFlags = CONTEXT_ALL;
    8.     ctxThread2.ContextFlags = CONTEXT_ALL;
    9.  
    10.     GetThreadContext(hThread1, &ctxThread1);
    11.     GetThreadContext(hThread2, &ctxThread2);
    12.  
    13.     SetThreadContext(hThread1, &ctxThread2);
    14.     SetThreadContext(hThread2, &ctxThread1);
    15.  
    16.     ResumeThread(hThread1);
    17.     ResumeThread(hThread2);
    18.  
    19.     CloseHandle(hThread1);
    20.     CloseHandle(hThread2);
    21. }
    22.  
    23.  
    24.  
    25. VOID WINAPI Thread1() {
    26.     while (true) {
    27.         EnterCriticalSection(&CriticalSection);
    28.         printf("[ID: %i] Thread1\r\n", GetCurrentThreadId());
    29.         LeaveCriticalSection(&CriticalSection);
    30.         Sleep(1000);
    31.     }
    32. }
    33.  
    34.  
    35. VOID WINAPI Thread2() {
    36.     while (true) {
    37.         EnterCriticalSection(&CriticalSection);
    38.         printf("[ID: %i] Thread2\r\n", GetCurrentThreadId());
    39.         LeaveCriticalSection(&CriticalSection);
    40.         Sleep(1000);
    41.     }
    42. }
    43.  
    Но вот проблема: после разморозки первый поток работает исправно (переключился и работает, сообщения выводит, нет проблем), но стоит разморозить второй поток - тот сразу же падает на printf'e в недрах CRT со Stack Overflow в процедуре __chkstk.

    Понимаю, сама постановка задачи - "клиент хочет странного", но всё же, есть ли возможность корректно переключить потоки между собой с сохранением работоспособности?
     
    Последнее редактирование: 29 сен 2017
  2. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.788
    HoShiMin,

    chkstk функция опасная, она обращается ниже стека в младших версиях, потом это пофиксили.
    Стек расширяется согласно параметрам в TEB. Если он выходит за описанные там пределы, то поток наверно закрэшит.
    При таком переключении" потоки должны быть остановлены, иначе будет загружен не текущий контекст, так как поток выполняется после запроса контекста.
    Синхронизации используют TEB/TID и стек, если так переключить поток, то дальнейшее поведение потока не предсказуемо.
    Системная OP-защита проверяет пределы стека и прибьёт поток например в загрузчике(PsValidateUserStack()).
     
  3. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.486
    Адрес:
    Россия, Нижний Новгород
    Но переключая fs'ы, мы переключаем и TEB'ы - по крайней мере, в юзермоде. Разве потоки не должны полностью поменяться телами вместе с окружением, стеком? Потоки на момент снятия контекста остановлены (SuspendThread, только сейчас заметил, что заморозку в пост не вставил - она там есть на оба потока сразу после OpenProcess).
     
  4. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.568
    Адрес:
    Russia
    Хм. Примитивы синхронизации... Не , не слышали)
     
  5. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.486
    Адрес:
    Россия, Нижний Новгород
    Но на момент переключения оба потока стоят, а когда размораживаем, в функции критическая секция
     
  6. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.568
    Адрес:
    Russia
    Потому что , если вам нужна "карусель", вам надо построить что-то в виде кольцевого буфера эвентов. Каждый эвент включается по кольцу.
    Ну это один из вариантов решения.
     
  7. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.486
    Адрес:
    Россия, Нижний Новгород
    Что именно нужно синхронизировать эвентами? Идея в том, чтобы крутить потоки, независимо от кода, который они исполняют. Если в потоках крутить while (true), переключаются нормально, но стоит залезть в CRT второму потоку - краш. Хотя не уверен, что в принципе возможно безопасно переключать потоки. Но открытый вопрос, почему первый-то поток нормально переключается и спокойно юзает CRT, если второй сразу же крашится?

    В идеале диспетчер вообще не знает, какой код исполняют потоки. Пусть это будет отдельная дллка, которая перебирает все потоки процесса и рандомно переставляет их между собой. Соответственно, в сам код некие переключатели на эвентах встроить не можем. И в Waitable-состояния потоки не переходят.
     
    Последнее редактирование: 29 сен 2017
  8. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.788
    HoShiMin,

    > Но переключая fs'ы, мы переключаем и TEB'ы - по крайней мере, в юзермоде.

    А какие fs вы переключаете, селектор TEB это константа и одинакова для всех потоков. Шедулер загружает дескриптор для этого сегмента на основе TID(потока). Поэтому не смотря на общий для них селектор, сегмент разный для каждого треда.

    Вы не видите весь масштаб задачи, она не разрешима.

    На счёт stkchk вот вам разобранная ранее суть, хоть и из немного иной задачи.
     

    Вложения:

    • STK.pdf
      Размер файла:
      95,7 КБ
      Просмотров:
      341
  9. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.568
    Адрес:
    Russia
    Зато он есть. Чувствуете разницу.
    У вас же вообще нет "диспетчера".
    Вы говорите про независимость кода - и при этом все равно делаете зависимость его от критической секции . Где логика?

    И для вашей идеи больше подойдут Fiber (волокна) . ИМХО
     
  10. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.788
    TermoSINteZ,

    Критические секции в данном случае не применимы, так как используют TID, который изменить нельзя.
     
  11. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.568
    Адрес:
    Russia
    Indy_, да я надеялся он сам додумается.
     
  12. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.486
    Адрес:
    Россия, Нижний Новгород
    В этом и была идея - сделать переключалку, не делая предположений о том, какой код выполняют потоки (особенно если использовать переключалку на сторонних (уже скомпилированных) проектах, например, для затруднения отладки).
    Что интересно, после переключения в 32х-битной программе (Wow64) CRT и критические секции работают нормально, никто не крашится, айдишники поменялись местами:
    29-09-2017 23-38-16.png