Changeset 3161 for trunk/src/kmk/w32


Ignore:
Timestamp:
Mar 19, 2018, 11:40:35 PM (7 years ago)
Author:
bird
Message:

kmk/win: More child process work, focusing on making kmk_builtin_redirect not requiring the shared lock.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kmk/w32/winchildren.c

    r3159 r3161  
    7676
    7777#include <Windows.h>
     78#include <Winternl.h>
    7879#include <assert.h>
    7980#include <process.h>
     
    270271/** Kernel32!SetThreadGroupAffinity */
    271272static BOOL (WINAPI        *g_pfnSetThreadGroupAffinity)(HANDLE, CONST GROUP_AFFINITY *, GROUP_AFFINITY *);
     273/** NTDLL!NtQueryInformationProcess */
     274static NTSTATUS (NTAPI     *g_pfnNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
     275/** Set if the windows host is 64-bit. */
     276static BOOL                 g_f64BitHost = (K_ARCH_BITS == 64);
    272277/** Windows version info.
    273278 * @note Putting this before the volatile stuff, hoping to keep it in a
     
    289294static unsigned volatile    g_idxLastChildcareWorker = 0;
    290295
    291 /** Temporary RW lock for serializing kmkbuiltin_redirect and CreateProcess. */
     296/** RW lock for serializing kmkbuiltin_redirect and CreateProcess. */
    292297static SRWLOCK              g_RWLock;
    293298
     
    301306void MkWinChildInit(unsigned int cJobSlots)
    302307{
     308    HMODULE hmod;
     309
    303310    /*
    304311     * Figure out how many childcare workers first.
     
    322329
    323330    /*
     331     * NTDLL imports that we need.
     332     */
     333    hmod = GetModuleHandleA("NTDLL.DLL");
     334    *(FARPROC *)&g_pfnNtQueryInformationProcess = GetProcAddress(hmod, "NtQueryInformationProcess");
     335    if (!g_pfnNtQueryInformationProcess)
     336        fatal(NILF, 0, _("MkWinChildInit: NtQueryInformationProcess not found"));
     337
     338#if K_ARCH_BITS == 32
     339    /*
     340     * Initialize g_f64BitHost.
     341     */
     342    if (!IsWow64Process(GetCurrentProcess(), &g_f64BitHost))
     343        fatal(NILF, INTSTR_LENGTH, _("MkWinChildInit: IsWow64Process failed: %u"), GetLastError());
     344#elif K_ARCH_BITS == 64
     345    assert(g_f64BitHost);
     346#else
     347# error "K_ARCH_BITS is bad/missing"
     348#endif
     349
     350    /*
    324351     * Figure out how many processor groups there are.
    325352     * For that we need to first figure the windows version.
     
    334361    if (g_VersionInfo.dwMajorVersion >= 6)
    335362    {
    336         HMODULE hmod = GetModuleHandleA("KERNEL32.DLL");
     363        hmod = GetModuleHandleA("KERNEL32.DLL");
    337364        *(FARPROC *)&g_pfnGetActiveProcessorGroupCount = GetProcAddress(hmod, "GetActiveProcessorGroupCount");
    338365        *(FARPROC *)&g_pfnGetActiveProcessorCount      = GetProcAddress(hmod, "GetActiveProcessorCount");
     
    365392    }
    366393
    367     /* Temporary: */
     394    /*
     395     * For serializing with standard file handle manipulation (kmkbuiltin_redirect).
     396     */
    368397    InitializeSRWLock(&g_RWLock);
    369398}
     
    465494    }
    466495}
     496
     497
     498/**
     499 * Does the actual process creation given.
     500 *
     501 * @returns 0 if there is anything to wait on, otherwise non-zero windows error.
     502 * @param   pWorker             The childcare worker.
     503 * @param   pChild              The child.
     504 * @param   pwszImageName       The image path.
     505 * @param   pwszCommandLine     The command line.
     506 * @param   pwszzEnvironment    The enviornment block.
     507 */
     508static int mkWinChildcareWorkerCreateProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild, WCHAR const *pwszImageName,
     509                                             WCHAR const *pwszCommandLine, WCHAR const *pwszzEnvironment)
     510{
     511    PROCESS_INFORMATION ProcInfo;
     512    STARTUPINFOW        StartupInfo;
     513    DWORD               fFlags       = CREATE_UNICODE_ENVIRONMENT;
     514    BOOL const          fHaveHandles = pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE
     515                                    || pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE;
     516    BOOL                fRet;
     517    DWORD               dwErr;
     518#ifdef KMK
     519    extern int          process_priority;
     520#endif
     521
     522    /*
     523     * Populate startup info.
     524     *
     525     * Turns out we can get away without passing TRUE for the inherit handles
     526     * parameter to CreateProcess when we're not using STARTF_USESTDHANDLES.
     527     * At least on NT, which is all worth caring about at this point + context IMO.
     528     *
     529     * Not inherting the handles is a good thing because it means we won't
     530     * accidentally end up with a pipe handle or such intended for a different
     531     * child process, potentially causing the EOF/HUP event to be delayed.
     532     *
     533     * Since the present handle inhertiance requirements only involves standard
     534     * output and error, we'll never set the inherit handles flag and instead
     535     * do manual handle duplication and planting.
     536     */
     537    memset(&StartupInfo, 0, sizeof(StartupInfo));
     538    StartupInfo.cb = sizeof(StartupInfo);
     539    GetStartupInfoW(&StartupInfo);
     540    if (!fHaveHandles)
     541        StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
     542    else
     543    {
     544        fFlags |= CREATE_SUSPENDED;
     545        StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
     546
     547        /* Don't pass CRT inheritance info to the child (from our parent actually). */
     548        StartupInfo.cbReserved2 = 0;
     549        StartupInfo.lpReserved2 = 0;
     550    }
     551
     552    /*
     553     * Flags.
     554     */
     555#ifdef KMK
     556    switch (process_priority)
     557    {
     558        case 1: fFlags |= CREATE_SUSPENDED | IDLE_PRIORITY_CLASS; break;
     559        case 2: fFlags |= CREATE_SUSPENDED | BELOW_NORMAL_PRIORITY_CLASS; break;
     560        case 3: fFlags |= CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS; break;
     561        case 4: fFlags |= CREATE_SUSPENDED | HIGH_PRIORITY_CLASS; break;
     562        case 5: fFlags |= CREATE_SUSPENDED | REALTIME_PRIORITY_CLASS; break;
     563    }
     564#endif
     565    if (g_cProcessorGroups > 1)
     566        fFlags |= CREATE_SUSPENDED;
     567
     568    /*
     569     * Try create the process.
     570     */
     571    DB(DB_JOBS, ("CreateProcessW(%ls, %ls,,, TRUE, %#x...)\n", pwszImageName, pwszCommandLine, fFlags));
     572    memset(&ProcInfo, 0, sizeof(ProcInfo));
     573    AcquireSRWLockShared(&g_RWLock);
     574
     575    fRet = CreateProcessW((WCHAR *)pwszImageName, (WCHAR *)pwszCommandLine, NULL /*pProcSecAttr*/, NULL /*pThreadSecAttr*/,
     576                          FALSE /*fInheritHandles*/, fFlags, (WCHAR *)pwszzEnvironment, NULL /*pwsz*/, &StartupInfo, &ProcInfo);
     577    dwErr = GetLastError();
     578
     579    ReleaseSRWLockShared(&g_RWLock);
     580    if (fRet)
     581        pChild->u.Process.hProcess = ProcInfo.hProcess;
     582    else
     583    {
     584        fprintf(stderr, "CreateProcess(%ls) failed: %u\n", pwszImageName, dwErr);
     585        return pChild->iExitCode = (int)dwErr;
     586    }
     587
     588    /*
     589     * If the child is suspended, we've got some adjustment work to be done.
     590     */
     591    dwErr = ERROR_SUCCESS;
     592    if (fFlags & CREATE_SUSPENDED)
     593    {
     594        /*
     595         * First do handle inhertiance as that's the most complicated.
     596         */
     597        if (fHaveHandles)
     598        {
     599            /*
     600             * Get the PEB address and figure out the child process bit count.
     601             */
     602            ULONG                     cbActual1 = 0;
     603            PROCESS_BASIC_INFORMATION BasicInfo = { 0, 0, };
     604            NTSTATUS rcNt = g_pfnNtQueryInformationProcess(ProcInfo.hProcess, ProcessBasicInformation,
     605                                                           &BasicInfo, sizeof(BasicInfo), &cbActual1);
     606            if (NT_SUCCESS(rcNt))
     607            {
     608                /*
     609                 * Read the user process parameter pointer from the PEB.
     610                 *
     611                 * Note! Seems WOW64 processes starts out with a 64-bit PEB and
     612                 *       process parameter block.
     613                 */
     614                BOOL const   f32BitPeb  = !g_f64BitHost;
     615                ULONG  const cbChildPtr = f32BitPeb ? 4 : 8;
     616                PVOID        pvSrcInPeb = (char *)BasicInfo.PebBaseAddress + (f32BitPeb ? 0x10 : 0x20);
     617                char *       pbDst      = 0;
     618                SIZE_T       cbActual2  = 0;
     619                if (ReadProcessMemory(ProcInfo.hProcess, pvSrcInPeb, &pbDst, cbChildPtr, &cbActual2))
     620                {
     621                    /*
     622                     * Duplicate the handles into the child.
     623                     */
     624                    union
     625                    {
     626                        ULONGLONG   au64Bit[2];
     627                        ULONG       au32Bit[2];
     628                    } WriteBuf;
     629                    ULONG  idx = 0;
     630                    HANDLE hChildStdOut = INVALID_HANDLE_VALUE;
     631                    HANDLE hChildStdErr = INVALID_HANDLE_VALUE;
     632
     633                    pbDst += (f32BitPeb ? 0x1c : 0x28);
     634                    if (pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE)
     635                    {
     636                        if (DuplicateHandle(GetCurrentProcess(), pChild->u.Process.hStdOut, ProcInfo.hProcess,
     637                                            &hChildStdOut, 0, TRUE /*fInheritable*/, DUPLICATE_SAME_ACCESS))
     638                        {
     639                            if (f32BitPeb)
     640                                WriteBuf.au32Bit[idx++] = (DWORD)(uintptr_t)hChildStdOut;
     641                            else
     642                                WriteBuf.au64Bit[idx++] = (uintptr_t)hChildStdOut;
     643                        }
     644                        else
     645                        {
     646                            dwErr = GetLastError();
     647                            fprintf(stderr, "Failed to duplicate %p (stdout) into the child: %u\n",
     648                                    pChild->u.Process.hStdOut, dwErr);
     649                        }
     650                    }
     651                    else
     652                        pbDst += cbChildPtr;
     653
     654                    if (pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE)
     655                    {
     656                        if (DuplicateHandle(GetCurrentProcess(), pChild->u.Process.hStdErr, ProcInfo.hProcess,
     657                                            &hChildStdErr, 0, TRUE /*fInheritable*/, DUPLICATE_SAME_ACCESS))
     658                        {
     659                            if (f32BitPeb)
     660                                WriteBuf.au32Bit[idx++] = (DWORD)(uintptr_t)hChildStdOut;
     661                            else
     662                                WriteBuf.au64Bit[idx++] = (uintptr_t)hChildStdOut;
     663                        }
     664                        else
     665                        {
     666                            dwErr = GetLastError();
     667                            fprintf(stderr, "Failed to duplicate %p (stderr) into the child: %u\n",
     668                                    pChild->u.Process.hStdOut, dwErr);
     669                        }
     670                    }
     671
     672                    /*
     673                     * Finally write the handle values into the child.
     674                     */
     675                    if (   idx > 0
     676                        && !WriteProcessMemory(ProcInfo.hProcess, pbDst, &WriteBuf, idx * cbChildPtr, &cbActual2))
     677                    {
     678                        dwErr = GetLastError();
     679                        fprintf(stderr, "Failed to write %p LB %u into child: %u\n", pbDst, idx * cbChildPtr, dwErr);
     680                    }
     681                }
     682                else
     683                {
     684                    dwErr = GetLastError();
     685                    fprintf(stderr, "Failed to read %p LB %u from the child: %u\n", pvSrcInPeb, cbChildPtr, dwErr);
     686                }
     687            }
     688            else
     689            {
     690                fprintf(stderr, "NtQueryInformationProcess failed on child: %#x\n", rcNt);
     691                dwErr = (DWORD)rcNt;
     692            }
     693        }
     694
     695        /*
     696         * Assign processor group (ignore failure).
     697         */
     698        if (g_cProcessorGroups > 1)
     699        {
     700            GROUP_AFFINITY Affinity = { ~(ULONG_PTR)0, pWorker->iProcessorGroup, { 0, 0, 0 } };
     701            fRet = g_pfnSetThreadGroupAffinity(ProcInfo.hThread, &Affinity, NULL);
     702            assert(fRet);
     703        }
     704
     705#ifdef KMK
     706        /*
     707         * Set priority (ignore failure).
     708         */
     709        switch (process_priority)
     710        {
     711            case 1: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_IDLE); break;
     712            case 2: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_BELOW_NORMAL); break;
     713            case 3: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_NORMAL); break;
     714            case 4: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_HIGHEST); break;
     715            case 5: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_TIME_CRITICAL); break;
     716            default: fRet = TRUE;
     717        }
     718        assert(fRet);
     719#endif
     720
     721        /*
     722         * Resume the thread if the adjustments succeeded, otherwise kill it.
     723         */
     724        if (dwErr == ERROR_SUCCESS)
     725        {
     726            fRet = ResumeThread(ProcInfo.hThread);
     727            assert(fRet);
     728            if (!fRet)
     729            {
     730                dwErr = GetLastError();
     731                fprintf(stderr, "ResumeThread failed on child process: %u\n", dwErr);
     732            }
     733        }
     734        if (dwErr != ERROR_SUCCESS)
     735            TerminateProcess(ProcInfo.hProcess, dwErr);
     736    }
     737
     738    /*
     739     * Close unnecessary handles.
     740     */
     741    if (   pChild->u.Process.fCloseStdOut
     742        && pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE)
     743    {
     744        CloseHandle(pChild->u.Process.hStdOut);
     745        pChild->u.Process.hStdOut      = INVALID_HANDLE_VALUE;
     746        pChild->u.Process.fCloseStdOut = FALSE;
     747    }
     748    if (   pChild->u.Process.fCloseStdErr
     749        && pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE)
     750    {
     751        CloseHandle(pChild->u.Process.hStdErr);
     752        pChild->u.Process.hStdErr      = INVALID_HANDLE_VALUE;
     753        pChild->u.Process.fCloseStdErr = FALSE;
     754    }
     755
     756    CloseHandle(ProcInfo.hThread);
     757    return 0;
     758}
     759
    467760
    468761#define MKWCCWCMD_F_CYGWIN_SHELL    1
     
    12151508static void mkWinChildcareWorkerThreadHandleProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
    12161509{
    1217     PROCESS_INFORMATION     ProcInfo;
    1218     STARTUPINFOW            StartupInfo;
    12191510    WCHAR const            *pwszPath         = NULL;
    12201511    WCHAR                  *pwszzEnvironment = NULL;
     
    12221513    WCHAR                  *pwszImageName    = NULL;
    12231514    BOOL                    fNeedShell       = FALSE;
    1224     DWORD                   fFlags = CREATE_UNICODE_ENVIRONMENT;
    1225     BOOL                    fRet;
    12261515    int                     rc;
    1227 #ifdef KMK
    1228     extern int process_priority;
    1229 #endif
    12301516
    12311517    /*
     
    12491535        else
    12501536            rc = mkWinChildcareWorkerConvertCommandlineWithShell(pwszImageName, pChild->u.Process.papszArgs, &pwszCommandLine);
    1251     }
    1252     if (rc == 0)
    1253     {
    1254         AcquireSRWLockShared(&g_RWLock); /* temporary */
    12551537
    12561538        /*
    1257          * Populate startup info.
     1539         * Create the child process.
    12581540         */
    1259         memset(&StartupInfo, 0, sizeof(StartupInfo));
    1260         StartupInfo.cb = sizeof(StartupInfo);
    1261         GetStartupInfoW(&StartupInfo);
    1262         if (   pChild->u.Process.hStdErr == INVALID_HANDLE_VALUE
    1263             && pChild->u.Process.hStdOut == INVALID_HANDLE_VALUE)
    1264             StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
    1265         else
    1266         {
    1267             StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
    1268             StartupInfo.hStdOutput = pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE
    1269                                    ? pChild->u.Process.hStdOut : GetStdHandle(STD_OUTPUT_HANDLE);
    1270             StartupInfo.hStdError  = pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE
    1271                                    ? pChild->u.Process.hStdErr : GetStdHandle(STD_ERROR_HANDLE);
    1272             StartupInfo.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
    1273         }
    1274 
    1275         /*
    1276          * Flags.
    1277          */
    1278 #ifdef KMK
    1279         switch (process_priority)
    1280         {
    1281             case 1: fFlags |= CREATE_SUSPENDED | IDLE_PRIORITY_CLASS; break;
    1282             case 2: fFlags |= CREATE_SUSPENDED | BELOW_NORMAL_PRIORITY_CLASS; break;
    1283             case 3: fFlags |= CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS; break;
    1284             case 4: fFlags |= CREATE_SUSPENDED | HIGH_PRIORITY_CLASS; break;
    1285             case 5: fFlags |= CREATE_SUSPENDED | REALTIME_PRIORITY_CLASS; break;
    1286         }
    1287 #endif
    1288         if (g_cProcessorGroups > 1)
    1289             fFlags |= CREATE_SUSPENDED;
    1290 
    1291         /*
    1292          * Try create the process.
    1293          */
    1294         DB(DB_JOBS, ("CreateProcessW(%ls, %ls,,, TRUE, %#x...)\n", pwszImageName, pwszCommandLine, fFlags));
    1295         memset(&ProcInfo, 0, sizeof(ProcInfo));
    1296         fRet = CreateProcessW(pwszImageName, pwszCommandLine, NULL /*pProcSecAttr*/, NULL /*pThreadSecAttr*/,
    1297                               TRUE /*fInheritHandles*/, fFlags, pwszzEnvironment, NULL /*pwsz*/, &StartupInfo, &ProcInfo);
    1298         rc = GetLastError();
    1299         ReleaseSRWLockShared(&g_RWLock); /* temporary */
    1300         if (fRet)
    1301         {
    1302             /*
    1303              * Make thread priority and affinity changes if necessary.
    1304              */
    1305             if (fFlags & CREATE_SUSPENDED)
    1306             {
    1307                 BOOL fRet;
    1308                 if (g_cProcessorGroups > 1)
    1309                 {
    1310                     GROUP_AFFINITY Affinity = { ~(ULONG_PTR)0, pWorker->iProcessorGroup, { 0, 0, 0 } };
    1311                     fRet = g_pfnSetThreadGroupAffinity(ProcInfo.hThread, &Affinity, NULL);
    1312                     assert(fRet);
    1313                 }
    1314 #ifdef KMK
    1315                 switch (process_priority)
    1316                 {
    1317                     case 1: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_IDLE); break;
    1318                     case 2: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_BELOW_NORMAL); break;
    1319                     case 3: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_NORMAL); break;
    1320                     case 4: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_HIGHEST); break;
    1321                     case 5: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_TIME_CRITICAL); break;
    1322                     default: fRet = TRUE;
    1323                 }
    1324                 assert(fRet);
    1325 #endif
    1326                 fRet = ResumeThread(ProcInfo.hThread);
    1327                 assert(fRet);
    1328                 (void)fRet;
    1329             }
    1330 
    1331             /*
    1332              * Close unncessary handles.
    1333              */
    1334             CloseHandle(ProcInfo.hThread);
    1335             pChild->u.Process.hProcess = ProcInfo.hProcess;
    1336 
    1337             if (   pChild->u.Process.fCloseStdOut
    1338                 && pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE)
    1339             {
    1340                 CloseHandle(pChild->u.Process.hStdOut);
    1341                 pChild->u.Process.hStdOut      = INVALID_HANDLE_VALUE;
    1342                 pChild->u.Process.fCloseStdOut = FALSE;
    1343             }
    1344             if (   pChild->u.Process.fCloseStdErr
    1345                 && pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE)
    1346             {
    1347                 CloseHandle(pChild->u.Process.hStdErr);
    1348                 pChild->u.Process.hStdErr      = INVALID_HANDLE_VALUE;
    1349                 pChild->u.Process.fCloseStdErr = FALSE;
    1350             }
    1351 
    1352             /*
    1353              * Wait for the child to complete.
    1354              */
    1355             mkWinChildcareWorkerWaitForProcess(pWorker, pChild, ProcInfo.hProcess);
     1541        if (rc == 0)
     1542        {
     1543            rc = mkWinChildcareWorkerCreateProcess(pWorker, pChild, pwszImageName, pwszCommandLine, pwszzEnvironment);
     1544            if (rc == 0)
     1545            {
     1546                /*
     1547                 * Wait for the child to complete.
     1548                 */
     1549                mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Process.hProcess);
     1550            }
     1551            else
     1552                pChild->iExitCode = rc;
    13561553        }
    13571554        else
     
    15131710                    pChild->pNext = pTailExpect;
    15141711                    pTailActual = _InterlockedCompareExchangePointer(&g_pTailCompletedChildren, pChild, pTailExpect);
    1515                     if (pTailActual == pTailExpect)
     1712                    if (pTailActual != pTailExpect)
     1713                        pTailExpect = pTailActual;
     1714                    else
    15161715                    {
    15171716                        _InterlockedDecrement(&g_cPendingChildren);
     
    22052404}
    22062405
    2207 /** Temporary serialization with kmkbuiltin_redirect. */
     2406/** Serialization with kmkbuiltin_redirect. */
    22082407void MkWinChildExclusiveAcquire(void)
    22092408{
     
    22112410}
    22122411
    2213 /** Temporary serialization with kmkbuiltin_redirect. */
     2412/** Serialization with kmkbuiltin_redirect. */
    22142413void MkWinChildExclusiveRelease(void)
    22152414{
Note: See TracChangeset for help on using the changeset viewer.