C読めないけどprunsrvで登録したサービスが1分で強制的に停止する仕組みを読もうとした

公開日:2018-10-26
最終更新:2018-10-26

タイトルが長い。

何をしたかったのか

prunsrvで登録したサービスの停止時の処理で時間がかかるものがあったので、
timeoutの設定を3分にしていたのにどう頑張っても1分で止まってしまうので調べたかった。

結論

timeoutの設定は、stop時に呼ばれるメソッドに対して停止要求が出てからの時間で、
その後はスレッドの停止を1分間待つ、という仕組みに因るものだった。
実際にやりたかったことは、Java内で作り出した非同期スレッドの停止時の後処理なので別途スレッドからの抜け出しを遅らせる。

読んだソース

サービスが起動されると、serviceMainがイベントを待機する。
停止要求はmainから引数でdocmdStopServiceメソッドを呼ぶと思われる。
docmdStopServiceからはserviceStopが呼ばれるっぽい。
(ここの経路は、おそらくprunsrv.cservice_ctrl_handlerが呼ばれるんだと思うけれど、その呼び出し元は辿れなかった。
service.cの中でリフレクションみたいなことしているんでは、と想定)
その後、JNI経由でstopMethodの定義をしたものが呼ばれる。 JNI経由での呼び出しが終わった跡あとはserviceMainがスレッドの終了まで1分間待機する。

それっぽいところを抽出。

//...
#define ONE_MINUTE    (60 * 1000)
//...
/* Allowed procrun parameters */
static APXCMDLINEOPT _options[] = {
/* 26 */    { L"StopTimeout",       L"Timeout",         L"Stop",        APXCMDOPT_INT | APXCMDOPT_REG, NULL, 0},
};
//...
#define GET_OPT_I(x)  _options[x].dwValue
//...
#define SO_STOPTIMEOUT      GET_OPT_I(26)
//...

static DWORD WINAPI serviceStop(LPVOID lpParameter)
{
    //...
    DWORD  timeout     = SO_STOPTIMEOUT * 1000;
        //...
        if (!apxJavaStart(&gSargs)) {
            apxLogWrite(APXLOG_MARK_ERROR "Failed starting Java");
            rv = 3;
        }
        //...
    if (timeout) {
        FILETIME fts, fte;
        ULARGE_INTEGER s, e;
        DWORD    nms;
        /* Wait to give it a chance to die naturally, then kill it. */
        apxLogWrite(APXLOG_MARK_DEBUG "Waiting for worker to die naturally...");
        GetSystemTimeAsFileTime(&fts);
        rv = apxHandleWait(gWorker, timeout, TRUE);
        GetSystemTimeAsFileTime(&fte);
        s.LowPart  = fts.dwLowDateTime;
        s.HighPart = fts.dwHighDateTime;
        e.LowPart  = fte.dwLowDateTime;
        e.HighPart = fte.dwHighDateTime;
        nms = (DWORD)((e.QuadPart - s.QuadPart) / 10000);
        if (rv == WAIT_OBJECT_0) {
            rv = 0;
            apxLogWrite(APXLOG_MARK_DEBUG "Worker finished gracefully in %d ms.", nms);
        }
        else
            apxLogWrite(APXLOG_MARK_DEBUG "Worker was killed in %d ms.", nms);
    }
    apxLogWrite(APXLOG_MARK_INFO "Service stop thread completed.");
    SetEvent(gShutdownEvent);
    return rv;
}

void WINAPI serviceMain(DWORD argc, LPTSTR *argv)
{
//...
    if (gShutdownEvent) {

        /* Ensure that shutdown thread exits before us */
        apxLogWrite(APXLOG_MARK_DEBUG "Waiting for ShutdownEvent");
        reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, ONE_MINUTE);
        WaitForSingleObject(gShutdownEvent, ONE_MINUTE);
        apxLogWrite(APXLOG_MARK_DEBUG "ShutdownEvent signaled");
        CloseHandle(gShutdownEvent);

        /* This will cause to wait for all threads to exit
         */
        apxLogWrite(APXLOG_MARK_DEBUG "Waiting 1 minute for all threads to exit");
        reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, ONE_MINUTE);
        apxDestroyJvm(ONE_MINUTE);
        }
//...
}
//...
BOOL
apxJavaLoadMainClass(APXHANDLE hJava, LPCSTR szClassName,
                     LPCSTR szMethodName,
                     LPCVOID lpArguments)
{
//...
    jClazz = JNICALL_1(FindClass, JAVA_CLASSSTRING);
//...
}
//...
/* Main Java application worker thread
 * It will launch Java main and wait until
 * it finishes.
 */
static DWORD WINAPI __apxJavaWorkerThread(LPVOID lpParameter)
{
//...
    if (!apxJavaLoadMainClass(pArgs->hJava,
                              pArgs->szClassName,
                              pArgs->szMethodName,
                              pArgs->lpArguments)) {
        WORKER_EXIT(3);
    }
//...
}
//...
BOOL
apxJavaStart(LPAPXJAVA_THREADARGS pArgs)
{
//...

    lpJava->hWorkerThread  = CreateThread(NULL,
                                          lpJava->szStackSize,
                                          __apxJavaWorkerThread,
                                          pArgs, CREATE_SUSPENDED,
                                          &lpJava->iWorkerThread);
//...
}
//...
記事が少しでもいいなと思ったらクラップを送ってみよう!
36
+1
@anizozinaの技術ブログ

よく一緒に読まれている記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる

技術ブログをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

技術ブログを開設する

Qrunchでアウトプットをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

Markdownで書ける

ログ機能でアウトプットを加速

デザインのカスタマイズが可能

技術ブログ開設

ここから先はアカウント(ブログ)開設が必要です

英数字4文字以上
.qrunch.io
英数字6文字以上
ログインする