2

私の質問の起源は、ユーザー提供のスタックをサポートする Windows で pthreads の実装を提供したいという事実から生じています。具体的には、pthread_attr_setstack意味のあることを行う必要があります。私の実際の要件はこれよりも少し複雑ですが、投稿の目的にはこれで十分です。

ファイバーまたはスレッド API でスタックを提供するためのパブリック Win API はありません。卑劣なバックドア、回避策、ハックを探し回ったが、何も進んでいない. 実際、winpthread のソースがインスピレーションを得ており、pthread_attr_setstack.

代わりに、次の「解決策」を試して、うまくいくかどうかを確認しました。と の通常の組み合わせを使用してファイバーを作成ConvertThreadToFiberCreateFiberExますSwitchToFiberCreateFiberEx最小スタック サイズを指定します。ファイバーのエントリ ポイントで、スタックにメモリを割り当て、TIB フィールドの「スタック ベース」と「スタック リミット」を適切に変更します (こちらを参照してください: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block ) 。 ESP をスタックの上位アドレスに設定します。

(実際には、スタックをこれよりも適切にセットアップし、EIP も変更して、このステップが posix funciton のように動作するようにしswapcontextますが、アイデアは得られます)。

この別のスタックでOS呼び出しを行うと、かなり失敗します(printfたとえば、死ぬ)。しかし、これは私にとって問題ではありません。カスタム スタック上では確実に呼び出しを行わないようにすることができます (そのため、実際の要件はもう少し複雑であると言ったのです)。例外...私は働くために例外が必要です。そして、彼らはしません!具体的には、変更されたスタックで例外をスローしてキャッチしようとすると、アサートが発生します

0xXXXXXXXX で未処理の例外が発生しました ....

だから私の(漠然とした)質問は、例外とカスタムスタックがどのようにうまく連携しないかについて、誰か洞察を持っている人はいますか? これは完全にサポートされておらず、nil 応答または「立ち去る」以外は問題ありません。実際、私は別の解決策が必要であるとほぼ判断しており、これには妥協が伴いますが、別の解決策を使用する可能性があります。しかし、好奇心に負けてしまうので、なぜうまくいかないのか知りたいです。

これに関連して、Cygwin が ucontext でこれをどのように処理したのか疑問に思いました。ここのソースhttp://szupervigyor.ddsi.hu/source/in/openjdk-6-6b18-1.8.13/cacao-0.99.4/src/vm/jit/i386/cygwin/ucontext.cGetThreadContext/SetThreadContextを使用して実装します無文脈。ただし、実験から、新しいコンテキスト内から例外がスローされた場合にも失敗することがわかりました。実際、SetThreadContext呼び出しは TIB ブロックを更新しません!

編集(@avakarからの回答に基づく)

次のコードは、あなたのものと非常によく似ていますが、同じ失敗を示しています。違いは、中断された 2 番目のスレッドを開始するのではなく、中断してからコンテキストを変更しようとすることです。このコードは、try-catch ブロックが でヒットしたときに説明したエラーを示していますfoo。おそらく、これは単に合法的ではありません。注目すべきことの 1 つは、この状況でExceptionListは、TIB のメンバーが呼び出されたときに有効なポインターmodifyThreadContextであるのに対し、あなたの例では -1 であるということです。これを手動で編集しても役に立ちません。

あなたの答えに対する私のコメントで述べたように。これはまさに私が必要としているものではありません。現在のスレッドからコンテキストを切り替えたいと思います。SetThreadContextただし、アクティブなスレッドでこれを呼び出さないように警告するためのドキュメント。したがって、以下のコードが機能しない場合、単一のスレッドで機能させる可能性はないと思います。

namespace
{
HANDLE ghSemaphore = 0;

void foo()
{
    try
    {
        throw 6;
    }
    catch(...){}

    ExitThread(0);
}

void modifyThreadContext(HANDLE thread)
{
    typedef NTSTATUS WINAPI NtQueryInformationThread_t(HANDLE ThreadHandle, DWORD ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);

    HMODULE hNtdll = LoadLibraryW(L"ntdll.dll");
    auto NtQueryInformationThread = (NtQueryInformationThread_t *)GetProcAddress(hNtdll, "NtQueryInformationThread");

    DWORD stackSize = 1024 * 1024;
    void * mystack = VirtualAlloc(0, stackSize, MEM_COMMIT, PAGE_READWRITE);

    DWORD threadInfo[7];
    NtQueryInformationThread(thread, 0, threadInfo, sizeof threadInfo, 0);

    NT_TIB * tib = (NT_TIB *)threadInfo[1];

    CONTEXT ctx = {};
    ctx.ContextFlags = CONTEXT_ALL;
    GetThreadContext(thread, &ctx);

    ctx.Esp = (DWORD)mystack + stackSize - ((DWORD)tib->StackBase - ctx.Esp);
    ctx.Eip = (DWORD)&foo;
    tib->StackBase = (PVOID)((DWORD)mystack + stackSize);
    tib->StackLimit = (PVOID)((DWORD)mystack);
    SetThreadContext(thread, &ctx);
}

DWORD CALLBACK threadMain(LPVOID)
{
    ReleaseSemaphore(ghSemaphore, 1, NULL);
    while (1)
        Sleep(10000);
    // Never gets here
    return 1;
}
} // namespace

int main()
{
    ghSemaphore = CreateSemaphore(NULL, 0, 1, NULL);
    HANDLE th = CreateThread(0, 0, threadMain, 0, 0, 0);

    while (WaitForSingleObject(ghSemaphore, INFINITE) != WAIT_OBJECT_0);

    SuspendThread(th);

    modifyThreadContext(th);
    ResumeThread(th);

    while (WaitForSingleObject(th, 10) != WAIT_OBJECT_0);

    return 0;
}
4

1 に答える 1

1

例外と両方ともprintf私にとってはうまくいきますが、なぜそうすべきでないのかわかりません。コードを投稿していただければ、何が起こっているのかを特定することができます。

#include <windows.h>
#include <stdio.h>

DWORD CALLBACK ThreadProc(LPVOID)
{
    try
    {
        throw 1;
    }
    catch (int i)
    {
        printf("%d\n", i);
    }
    return 0;
}

typedef NTSTATUS WINAPI NtQueryInformationThread_t(HANDLE ThreadHandle, DWORD ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);

int main()
{
    HMODULE hNtdll = LoadLibraryW(L"ntdll.dll");
    auto NtQueryInformationThread = (NtQueryInformationThread_t *)GetProcAddress(hNtdll, "NtQueryInformationThread");

    DWORD stackSize = 1024 * 1024;
    void * mystack = VirtualAlloc(0, stackSize, MEM_COMMIT, PAGE_READWRITE);

    DWORD dwThreadId;
    HANDLE hThread = CreateThread(0, 0, &ThreadProc, 0, CREATE_SUSPENDED, &dwThreadId);

    DWORD threadInfo[7];
    NtQueryInformationThread(hThread, 0, threadInfo, sizeof threadInfo, 0);

    NT_TIB * tib = (NT_TIB *)threadInfo[1];

    CONTEXT ctx = {};
    ctx.ContextFlags = CONTEXT_ALL;
    GetThreadContext(hThread, &ctx);

    ctx.Esp = (DWORD)mystack + stackSize - ((DWORD)tib->StackBase - ctx.Esp);
    tib->StackBase = (PVOID)((DWORD)mystack + stackSize);
    tib->StackLimit = (PVOID)((DWORD)mystack);
    SetThreadContext(hThread, &ctx);

    ResumeThread(hThread);
    WaitForSingleObject(hThread, INFINITE);
}
于 2015-06-09T08:00:23.480 に答える