Стало интересно, можно ли переключить два потока между собой, чтобы каждый продолжил выполняться с точки остановки другого. Сказано - сделано. Код (C): VOID SwitchThreads(ULONG Thread1Id, ULONG Thread2Id) { HANDLE hThread1 = OpenThread(THREAD_ALL_ACCESS, FALSE, Thread1Id); HANDLE hThread2 = OpenThread(THREAD_ALL_ACCESS, FALSE, Thread2Id); CONTEXT ctxThread1 = { 0 }, ctxThread2 = { 0 }; ctxThread1.ContextFlags = CONTEXT_ALL; ctxThread2.ContextFlags = CONTEXT_ALL; GetThreadContext(hThread1, &ctxThread1); GetThreadContext(hThread2, &ctxThread2); SetThreadContext(hThread1, &ctxThread2); SetThreadContext(hThread2, &ctxThread1); ResumeThread(hThread1); ResumeThread(hThread2); CloseHandle(hThread1); CloseHandle(hThread2); } VOID WINAPI Thread1() { while (true) { EnterCriticalSection(&CriticalSection); printf("[ID: %i] Thread1\r\n", GetCurrentThreadId()); LeaveCriticalSection(&CriticalSection); Sleep(1000); } } VOID WINAPI Thread2() { while (true) { EnterCriticalSection(&CriticalSection); printf("[ID: %i] Thread2\r\n", GetCurrentThreadId()); LeaveCriticalSection(&CriticalSection); Sleep(1000); } } Но вот проблема: после разморозки первый поток работает исправно (переключился и работает, сообщения выводит, нет проблем), но стоит разморозить второй поток - тот сразу же падает на printf'e в недрах CRT со Stack Overflow в процедуре __chkstk. Понимаю, сама постановка задачи - "клиент хочет странного", но всё же, есть ли возможность корректно переключить потоки между собой с сохранением работоспособности?
HoShiMin, chkstk функция опасная, она обращается ниже стека в младших версиях, потом это пофиксили. Стек расширяется согласно параметрам в TEB. Если он выходит за описанные там пределы, то поток наверно закрэшит. При таком переключении" потоки должны быть остановлены, иначе будет загружен не текущий контекст, так как поток выполняется после запроса контекста. Синхронизации используют TEB/TID и стек, если так переключить поток, то дальнейшее поведение потока не предсказуемо. Системная OP-защита проверяет пределы стека и прибьёт поток например в загрузчике(PsValidateUserStack()).
Но переключая fs'ы, мы переключаем и TEB'ы - по крайней мере, в юзермоде. Разве потоки не должны полностью поменяться телами вместе с окружением, стеком? Потоки на момент снятия контекста остановлены (SuspendThread, только сейчас заметил, что заморозку в пост не вставил - она там есть на оба потока сразу после OpenProcess).
Потому что , если вам нужна "карусель", вам надо построить что-то в виде кольцевого буфера эвентов. Каждый эвент включается по кольцу. Ну это один из вариантов решения.
Что именно нужно синхронизировать эвентами? Идея в том, чтобы крутить потоки, независимо от кода, который они исполняют. Если в потоках крутить while (true), переключаются нормально, но стоит залезть в CRT второму потоку - краш. Хотя не уверен, что в принципе возможно безопасно переключать потоки. Но открытый вопрос, почему первый-то поток нормально переключается и спокойно юзает CRT, если второй сразу же крашится? В идеале диспетчер вообще не знает, какой код исполняют потоки. Пусть это будет отдельная дллка, которая перебирает все потоки процесса и рандомно переставляет их между собой. Соответственно, в сам код некие переключатели на эвентах встроить не можем. И в Waitable-состояния потоки не переходят.
HoShiMin, > Но переключая fs'ы, мы переключаем и TEB'ы - по крайней мере, в юзермоде. А какие fs вы переключаете, селектор TEB это константа и одинакова для всех потоков. Шедулер загружает дескриптор для этого сегмента на основе TID(потока). Поэтому не смотря на общий для них селектор, сегмент разный для каждого треда. Вы не видите весь масштаб задачи, она не разрешима. На счёт stkchk вот вам разобранная ранее суть, хоть и из немного иной задачи.
Зато он есть. Чувствуете разницу. У вас же вообще нет "диспетчера". Вы говорите про независимость кода - и при этом все равно делаете зависимость его от критической секции . Где логика? И для вашей идеи больше подойдут Fiber (волокна) . ИМХО
TermoSINteZ, Критические секции в данном случае не применимы, так как используют TID, который изменить нельзя.
В этом и была идея - сделать переключалку, не делая предположений о том, какой код выполняют потоки (особенно если использовать переключалку на сторонних (уже скомпилированных) проектах, например, для затруднения отладки). Что интересно, после переключения в 32х-битной программе (Wow64) CRT и критические секции работают нормально, никто не крашится, айдишники поменялись местами: