VirtualBox

Changeset 3187 in kBuild


Ignore:
Timestamp:
Mar 24, 2018 3:26:58 AM (7 years ago)
Author:
bird
Message:

kmk/winchildren: Finally squashed the intermixed char-by-char output from MS tools by catching all normal child output and funnel it thru pipes. Except kmk_redirect, that is.

File:
1 edited

Legend:

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

    r3185 r3187  
    4040 *         making the main thread focus on managing child processes.
    4141 *
    42  *      5. Output synchronization using reusable pipes [not yet implemented].
     42 *      5. Output synchronization using reusable pipes.
    4343 *
    4444 *
     
    6262 * on different groups.  This code will try distribute children evenly among the
    6363 * processor groups, using a very simple algorithm (see details in code).
    64  *
    65  *
    66  * @todo Output buffering using pipes, finally making 'link' output readable.
    6764 *
    6865 *
     
    198195            /** Whether to close hStdErr after creating the process.  */
    199196            BOOL            fCloseStdErr;
     197            /** Whether to catch output from the process. */
     198            BOOL            fCatchOutput;
    200199
    201200            /** Child process handle. */
     
    250249#define WINCHILD_MAGIC      0xbabebabeU
    251250
     251/**
     252 * A childcare worker pipe.
     253 */
     254typedef struct WINCCWPIPE
     255{
     256    /** My end of the pipe. */
     257    HANDLE              hPipeMine;
     258    /** The child end of the pipe. */
     259    HANDLE              hPipeChild;
     260    /** The event for asynchronous reading. */
     261    HANDLE              hEvent;
     262    /** Which pipe this is (1 == stdout, 2 == stderr). */
     263    unsigned char       iWhich;
     264    /** Set if we've got a read pending already. */
     265    BOOL                fReadPending;
     266    /** Number of bytes at the start of the buffer that we've already
     267     * written out.  We try write out whole lines. */
     268    DWORD               cbWritten;
     269    /** The buffer offset of the read currently pending. */
     270    DWORD               offPendingRead;
     271    /** Read buffer size. */
     272    DWORD               cbBuffer;
     273    /** The read buffer allocation. */
     274    unsigned char      *pbBuffer;
     275    /** Overlapped I/O structure. */
     276    OVERLAPPED          Overlapped;
     277} WINCCWPIPE;
     278typedef WINCCWPIPE *PWINCCWPIPE;
     279
    252280
    253281/**
     
    272300    /** Magic / eyecatcher (WINCHILDCAREWORKER_MAGIC). */
    273301    ULONG                   uMagic;
     302    /** The worker index. */
     303    unsigned int            idxWorker;
    274304    /** The processor group for this worker. */
    275305    unsigned int            iProcessorGroup;
     
    280310    /** The event the thread is idling on. */
    281311    HANDLE                  hEvtIdle;
     312    /** The pipe catching standard output from a child. */
     313    WINCCWPIPE              StdOut;
     314    /** The pipe catching standard error from a child. */
     315    WINCCWPIPE              StdErr;
     316
    282317    /** Pointer to the current child. */
    283318    PWINCHILD volatile      pCurChild;
     
    592627}
    593628
     629
     630/**
     631 * Used to flush data we're read but not yet written at the termination of a
     632 * process.
     633 *
     634 * @param   pChild          The child.
     635 * @param   pPipe           The pipe.
     636 */
     637static void mkWinChildcareWorkerFlushUnwritten(PWINCHILD pChild, PWINCCWPIPE pPipe)
     638{
     639    /** @todo integrate with output.c   */
     640    DWORD cbUnwritten = pPipe->cbWritten - pPipe->offPendingRead;
     641    if (cbUnwritten)
     642    {
     643        DWORD cbWritten = 0;
     644        if (WriteFile(GetStdHandle(pPipe->iWhich == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE),
     645                      &pPipe->pbBuffer[pPipe->cbWritten], cbUnwritten, &cbWritten, NULL))
     646            pPipe->cbWritten += cbWritten <= cbUnwritten ? cbWritten : cbUnwritten; /* paranoia */
     647    }
     648}
     649
     650/**
     651 * Adds output to the given standard output for the child.
     652 *
     653 * There is no pending read when this function is called, so we're free to
     654 * reshuffle the buffer if desirable.
     655 *
     656 * @param   pChild          The child.
     657 * @param   iWhich          Which standard descriptor number.
     658 * @param   cbNewData       How much more output was caught.
     659 */
     660static void mkWinChildcareWorkerCaughtMoreOutput(PWINCHILD pChild, PWINCCWPIPE pPipe, DWORD cbNewData)
     661{
     662    DWORD offStart = pPipe->cbWritten;
     663    assert(offStart <= pPipe->offPendingRead);
     664    if (cbNewData > 0)
     665    {
     666        DWORD offRest;
     667
     668        /* Move offPendingRead ahead by cbRead. */
     669        pPipe->offPendingRead += cbNewData;
     670        assert(pPipe->offPendingRead < pPipe->cbBuffer);
     671        if (pPipe->offPendingRead > pPipe->cbBuffer)
     672            pPipe->offPendingRead = pPipe->cbBuffer;
     673
     674        /* Locate the last newline in the buffer. */
     675        offRest = pPipe->offPendingRead;
     676        while (offRest > offStart && pPipe->pbBuffer[offRest - 1] != '\n')
     677            offRest--;
     678
     679        /* If none were found and we've less than 16 bytes left in the buffer, try
     680           find a word boundrary to flush on instead. */
     681        if (   offRest > offStart
     682            || pPipe->cbBuffer - pPipe->offPendingRead + offStart > 16)
     683        { /* likely */ }
     684        else
     685        {
     686            offRest = pPipe->offPendingRead;
     687            while (   offRest > offStart
     688                   && isalnum(pPipe->pbBuffer[offRest - 1]))
     689                offRest--;
     690            if (offRest == offStart)
     691                offRest = pPipe->offPendingRead;
     692        }
     693        if (offRest > offStart)
     694        {
     695            /** @todo integrate with output.c   */
     696            /* Write out offStart..offRest. */
     697            DWORD cbToWrite = offRest - offStart;
     698            DWORD cbWritten = 0;
     699            if (WriteFile(GetStdHandle(pPipe->iWhich == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE),
     700                          &pPipe->pbBuffer[offStart], cbToWrite, &cbWritten, NULL))
     701            {
     702                offStart += cbWritten <= cbToWrite ? cbWritten : cbToWrite; /* paranoia */
     703                pPipe->cbWritten = offStart;
     704            }
     705        }
     706    }
     707
     708    /* Shuffle the data to the front of the buffer.  */
     709    if (offStart > 0)
     710    {
     711        DWORD cbUnwritten = pPipe->offPendingRead - offStart;
     712        if (cbUnwritten > 0)
     713            memmove(pPipe->pbBuffer, &pPipe->pbBuffer[offStart], cbUnwritten);
     714        pPipe->offPendingRead -= pPipe->cbWritten;
     715        pPipe->cbWritten       = 0;
     716    }
     717}
     718
     719/**
     720 * Catches output from the given pipe.
     721 *
     722 * @param   pChild          The child.
     723 * @param   pPipe           The pipe.
     724 * @param   fFlushing       Set if we're flushing the pipe after the process
     725 *                          terminated.
     726 */
     727static void mkWinChildcareWorkerCatchOutput(PWINCHILD pChild, PWINCCWPIPE pPipe, BOOL fFlushing)
     728{
     729    /*
     730     * Deal with already pending read.
     731     */
     732    if (pPipe->fReadPending)
     733    {
     734        DWORD cbRead = 0;
     735        if (GetOverlappedResult(pPipe->hPipeMine, &pPipe->Overlapped, &cbRead, !fFlushing))
     736        {
     737            mkWinChildcareWorkerCaughtMoreOutput(pChild, pPipe, cbRead);
     738            pPipe->fReadPending = FALSE;
     739        }
     740        else if (fFlushing && GetLastError() == ERROR_IO_INCOMPLETE)
     741        {
     742            if (pPipe->offPendingRead > pPipe->cbWritten)
     743                mkWinChildcareWorkerFlushUnwritten(pChild, pPipe);
     744            return;
     745        }
     746        else
     747        {
     748            fprintf(stderr, "warning: GetOverlappedResult failed: %u\n", GetLastError());
     749            pPipe->fReadPending = FALSE;
     750            if (fFlushing)
     751                return;
     752        }
     753    }
     754
     755    /*
     756     * Read data till one becomes pending.
     757     */
     758    for (;;)
     759    {
     760        DWORD cbRead;
     761
     762        memset(&pPipe->Overlapped, 0, sizeof(pPipe->Overlapped));
     763        pPipe->Overlapped.hEvent = pPipe->hEvent;
     764        ResetEvent(pPipe->hEvent);
     765
     766        assert(pPipe->offPendingRead < pPipe->cbBuffer);
     767        SetLastError(0);
     768        cbRead = 0;
     769        if (!ReadFile(pPipe->hPipeMine, &pPipe->pbBuffer[pPipe->offPendingRead],
     770                      pPipe->cbBuffer - pPipe->offPendingRead, &cbRead, &pPipe->Overlapped))
     771        {
     772            DWORD dwErr = GetLastError();
     773            if (dwErr == ERROR_IO_PENDING)
     774                pPipe->fReadPending = TRUE;
     775            else
     776                fprintf(stderr, "warning: ReadFile failed on standard %s: %u\n",
     777                        pPipe->iWhich == 1 ? "output" : "error",  GetLastError());
     778            return;
     779        }
     780
     781        mkWinChildcareWorkerCaughtMoreOutput(pChild, pPipe, cbRead);
     782    }
     783}
     784
    594785/**
    595786 * Commmon worker for waiting on a child process and retrieving the exit code.
     
    599790 * @param   hProcess            The process handle.
    600791 * @param   pwszJob             The job name.
     792 * @param   fCatchOutput        Set if we need to work the output pipes
     793 *                              associated with the worker.
    601794 */
    602795static void mkWinChildcareWorkerWaitForProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild, HANDLE hProcess,
    603                                                WCHAR const *pwszJob)
     796                                               WCHAR const *pwszJob, BOOL fCatchOutput)
    604797{
    605798    DWORD const msStart = GetTickCount();
     
    607800    for (;;)
    608801    {
    609         DWORD dwExitCode = -42;
    610         DWORD dwStatus = WaitForSingleObject(hProcess, 15001 /*ms*/);
     802        /*
     803         * Do the waiting and output catching.
     804         */
     805        DWORD dwStatus;
     806        if (!fCatchOutput)
     807            dwStatus = WaitForSingleObject(hProcess, 15001 /*ms*/);
     808        else
     809        {
     810            HANDLE ahHandles[3] = { hProcess, pWorker->StdOut.hEvent, pWorker->StdErr.hEvent };
     811            dwStatus = WaitForMultipleObjects(3, ahHandles, FALSE /*fWaitAll*/, 1000 /*ms*/);
     812            if (dwStatus == WAIT_OBJECT_0 + 1)
     813                mkWinChildcareWorkerCatchOutput(pChild, &pWorker->StdOut, FALSE /*fFlushing*/);
     814            else if (dwStatus == WAIT_OBJECT_0 + 2)
     815                mkWinChildcareWorkerCatchOutput(pChild, &pWorker->StdErr, FALSE /*fFlushing*/);
     816        }
    611817        assert(dwStatus != WAIT_FAILED);
     818
     819        /*
     820         * Get the exit code and return if the process was signalled as done.
     821         */
    612822        if (dwStatus == WAIT_OBJECT_0)
    613823        {
     
    616826            {
    617827                pChild->iExitCode = (int)dwExitCode;
     828                if (!fCatchOutput)
     829                {
     830                    mkWinChildcareWorkerCatchOutput(pChild, &pWorker->StdOut, TRUE /*fFlushing*/);
     831                    mkWinChildcareWorkerCatchOutput(pChild, &pWorker->StdErr, TRUE /*fFlushing*/);
     832                }
    618833                return;
    619834            }
    620835        }
    621         else if (   dwStatus == WAIT_TIMEOUT /* whatever */
     836        /*
     837         * Loop again if just a timeout or pending output?
     838         * Put out a message every 15 or 30 seconds if the job takes a while.
     839         */
     840        else if (   dwStatus == WAIT_TIMEOUT
     841                 || dwStatus == WAIT_OBJECT_0 + 1
     842                 || dwStatus == WAIT_OBJECT_0 + 2
    622843                 || dwStatus == WAIT_IO_COMPLETION)
    623844        {
     
    724945    StartupInfo.lpReserved2 = 0; /* No CRT file handle + descriptor info possible, sorry. */
    725946    StartupInfo.cbReserved2 = 0;
    726     if (!fHaveHandles)
     947    if (   !fHaveHandles
     948        && !pChild->u.Process.fCatchOutput)
    727949        StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
    728950    else
     
    7811003         * First do handle inhertiance as that's the most complicated.
    7821004         */
    783         if (fHaveHandles)
     1005        if (fHaveHandles || pChild->u.Process.fCatchOutput)
    7841006        {
    7851007            char    szErrMsg[128];
    786             BOOL    afReplace[3] =
    787             { FALSE, pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE, pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE };
    788             HANDLE  ahChild[3] =
    789             { NULL,  pChild->u.Process.hStdOut,                         pChild->u.Process.hStdErr  };
     1008            BOOL    afReplace[3];
     1009            HANDLE  ahChild[3];
     1010
     1011            afReplace[0] = FALSE;
     1012            ahChild[0]   = INVALID_HANDLE_VALUE;
     1013            if (fHaveHandles)
     1014            {
     1015                afReplace[1] = pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE;
     1016                ahChild[1]   = pChild->u.Process.hStdOut;
     1017                afReplace[2] = pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE;
     1018                ahChild[2]   = pChild->u.Process.hStdErr;
     1019            }
     1020            else
     1021            {
     1022                afReplace[1] = TRUE;
     1023                ahChild[1]   = pWorker->StdOut.hPipeChild;
     1024                afReplace[2] = TRUE;
     1025                ahChild[2]   = pWorker->StdErr.hPipeChild;
     1026            }
    7901027
    7911028            dwErr = nt_child_inject_standard_handles(ProcInfo.hProcess, afReplace, ahChild, szErrMsg, sizeof(szErrMsg));
     
    16701907                 * Wait for the child to complete.
    16711908                 */
    1672                 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Process.hProcess, pwszImageName);
     1909                mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Process.hProcess, pwszImageName,
     1910                                                   pChild->u.Process.fCatchOutput);
    16731911            }
    16741912            else
     
    17872025static void mkWinChildcareWorkerThreadHandleRedirect(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
    17882026{
    1789     mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Redirect.hProcess, L"kmk_redirect");
     2027    mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Redirect.hProcess, L"kmk_redirect", FALSE /*fCatchOutput*/);
    17902028}
    17912029
     
    18312069
    18322070                _InterlockedIncrement((long *)&g_cIdleChildcareWorkers);
     2071                _InterlockedExchange((long *)&g_idxLastChildcareWorker, pWorker->idxWorker);
    18332072                dwStatus = WaitForSingleObject(pWorker->hEvtIdle, INFINITE);
    18342073                _InterlockedExchange(&pWorker->fIdle, FALSE);
     
    19092148
    19102149/**
     2150 * Creates a pipe for catching child output.
     2151 *
     2152 * This is a custom CreatePipe implementation that allows for overlapped I/O on
     2153 * our end of the pipe.  Silly that they don't offer an API that does this.
     2154 *
     2155 * @returns Success indicator.
     2156 * @param   pPipe               The structure for the pipe.
     2157 * @param   iWhich              Which standard descriptor this is a pipe for.
     2158 * @param   idxWorker           The worker index.
     2159 */
     2160static BOOL mkWinChildcareCreateWorkerPipe(PWINCCWPIPE pPipe, unsigned iWhich, unsigned int idxWorker)
     2161{
     2162    /*
     2163     * We try generate a reasonably unique name from the get go, so this retry
     2164     * loop shouldn't really ever be needed.  But you never know.
     2165     */
     2166    static unsigned s_iSeqNo = 0;
     2167    DWORD const     cMaxInstances = 1;
     2168    DWORD const     cbPipe        = 4096;
     2169    DWORD const     cMsTimeout    = 0;
     2170    unsigned        cTries        = 256;
     2171    while (cTries-- > 0)
     2172    {
     2173        /* Create the pipe (our end). */
     2174        HANDLE hPipeRead;
     2175        DWORD  fOpenMode = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE;
     2176        DWORD  fPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS;
     2177        WCHAR  wszName[MAX_PATH];
     2178        s_iSeqNo++;
     2179        _snwprintf(wszName, MAX_PATH, L"\\\\.\\pipe\\kmk-winchildren-%u-%u-%u-%s-%u-%u",
     2180                   GetCurrentProcessId(), GetCurrentThreadId(), idxWorker, iWhich == 1 ? L"out" : L"err", s_iSeqNo, GetTickCount());
     2181        hPipeRead = CreateNamedPipeW(wszName, fOpenMode, fPipeMode, cMaxInstances, cbPipe, cbPipe, cMsTimeout, NULL /*pSecAttr*/);
     2182        if (hPipeRead == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
     2183        {
     2184            fOpenMode &= ~FILE_FLAG_FIRST_PIPE_INSTANCE;
     2185            fPipeMode &= ~PIPE_REJECT_REMOTE_CLIENTS;
     2186            hPipeRead = CreateNamedPipeW(wszName, fOpenMode, fPipeMode, cMaxInstances, cbPipe, cbPipe, cMsTimeout, NULL /*pSecAttr*/);
     2187        }
     2188        if (hPipeRead != INVALID_HANDLE_VALUE)
     2189        {
     2190            /* Connect the other end. */
     2191            HANDLE hPipeWrite = CreateFileW(wszName, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0 /*fShareMode*/, NULL /*pSecAttr*/,
     2192                                            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
     2193            if (hPipeWrite != INVALID_HANDLE_VALUE)
     2194            {
     2195                /*
     2196                 * Create the event object and we're done.
     2197                 *
     2198                 * It starts in signalled stated so we don't need special code
     2199                 * for handing when we start waiting.
     2200                 */
     2201                HANDLE hEvent = CreateEventW(NULL /*pSecAttr*/, TRUE /*fManualReset*/, TRUE /*fInitialState*/, NULL /*pwszName*/);
     2202                if (hEvent != NULL)
     2203                {
     2204                    pPipe->hPipeMine    = hPipeRead;
     2205                    pPipe->hPipeChild   = hPipeWrite;
     2206                    pPipe->hEvent       = hEvent;
     2207                    pPipe->iWhich       = iWhich;
     2208                    pPipe->fReadPending = FALSE;
     2209                    pPipe->cbBuffer     = cbPipe;
     2210                    pPipe->pbBuffer     = xcalloc(cbPipe);
     2211                    return TRUE;
     2212                }
     2213
     2214                CloseHandle(hPipeWrite);
     2215                CloseHandle(hPipeRead);
     2216                return FALSE;
     2217            }
     2218            CloseHandle(hPipeRead);
     2219        }
     2220    }
     2221    return FALSE;
     2222}
     2223
     2224/**
     2225 * Destroys a childcare worker pipe.
     2226 *
     2227 * @param   pPipe       The pipe.
     2228 */
     2229static void mkWinChildcareDeleteWorkerPipe(PWINCCWPIPE pPipe)
     2230{
     2231    if (pPipe->hPipeChild != NULL)
     2232    {
     2233        CloseHandle(pPipe->hPipeChild);
     2234        pPipe->hPipeChild = NULL;
     2235    }
     2236
     2237    if (pPipe->hPipeMine != NULL)
     2238    {
     2239        if (pPipe->fReadPending)
     2240            if (!CancelIo(pPipe->hPipeMine))
     2241                WaitForSingleObject(pPipe->hEvent, INFINITE);
     2242        CloseHandle(pPipe->hPipeMine);
     2243        pPipe->hPipeMine = NULL;
     2244    }
     2245
     2246    if (pPipe->hEvent != NULL)
     2247    {
     2248        CloseHandle(pPipe->hEvent);
     2249        pPipe->hEvent = NULL;
     2250    }
     2251
     2252    if (pPipe->pbBuffer)
     2253    {
     2254        free(pPipe->pbBuffer);
     2255        pPipe->pbBuffer = NULL;
     2256    }
     2257}
     2258
     2259/**
    19112260 * Creates another childcare worker.
    19122261 *
     
    19162265{
    19172266    PWINCHILDCAREWORKER pWorker = (PWINCHILDCAREWORKER)xcalloc(sizeof(*pWorker));
    1918     pWorker->uMagic = WINCHILDCAREWORKER_MAGIC;
    1919     pWorker->hEvtIdle = CreateEvent(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pszName*/);
     2267    pWorker->uMagic    = WINCHILDCAREWORKER_MAGIC;
     2268    pWorker->idxWorker = g_cChildCareworkers;
     2269    pWorker->hEvtIdle  = CreateEventW(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pwszName*/);
    19202270    if (pWorker->hEvtIdle)
    19212271    {
    1922         /* Before we start the thread, assign it to a processor group. */
    1923         if (g_cProcessorGroups > 1)
    1924         {
    1925             unsigned int cMaxInGroup;
    1926             unsigned int cInGroup;
    1927             unsigned int iGroup = g_idxProcessorGroupAllocator % g_cProcessorGroups;
    1928             pWorker->iProcessorGroup = iGroup;
    1929 
    1930             /* Advance.  We employ a very simple strategy that does 50% in
    1931                each group for each group cycle.  Odd processor counts are
    1932                caught in odd group cycles.  The init function selects the
    1933                starting group based on make nesting level to avoid stressing
    1934                out the first group. */
    1935             cInGroup = ++g_idxProcessorInGroupAllocator;
    1936             cMaxInGroup = g_pacProcessorsInGroup[iGroup];
    1937             if (   !(cMaxInGroup & 1)
    1938                 || !((g_idxProcessorGroupAllocator / g_cProcessorGroups) & 1))
    1939                 cMaxInGroup /= 2;
     2272        if (mkWinChildcareCreateWorkerPipe(&pWorker->StdOut, 1, pWorker->idxWorker))
     2273        {
     2274            if (mkWinChildcareCreateWorkerPipe(&pWorker->StdErr, 2, pWorker->idxWorker))
     2275            {
     2276                /* Before we start the thread, assign it to a processor group. */
     2277                if (g_cProcessorGroups > 1)
     2278                {
     2279                    unsigned int cMaxInGroup;
     2280                    unsigned int cInGroup;
     2281                    unsigned int iGroup = g_idxProcessorGroupAllocator % g_cProcessorGroups;
     2282                    pWorker->iProcessorGroup = iGroup;
     2283
     2284                    /* Advance.  We employ a very simple strategy that does 50% in
     2285                       each group for each group cycle.  Odd processor counts are
     2286                       caught in odd group cycles.  The init function selects the
     2287                       starting group based on make nesting level to avoid stressing
     2288                       out the first group. */
     2289                    cInGroup = ++g_idxProcessorInGroupAllocator;
     2290                    cMaxInGroup = g_pacProcessorsInGroup[iGroup];
     2291                    if (   !(cMaxInGroup & 1)
     2292                        || !((g_idxProcessorGroupAllocator / g_cProcessorGroups) & 1))
     2293                        cMaxInGroup /= 2;
     2294                    else
     2295                        cMaxInGroup = cMaxInGroup / 2 + 1;
     2296                    if (cInGroup >= cMaxInGroup)
     2297                    {
     2298                        g_idxProcessorInGroupAllocator = 0;
     2299                        g_idxProcessorGroupAllocator++;
     2300                    }
     2301                }
     2302
     2303                /* Try start the thread. */
     2304                pWorker->hThread = (HANDLE)_beginthreadex(NULL, 0 /*cbStack*/, mkWinChildcareWorkerThread, pWorker,
     2305                                                          0, &pWorker->tid);
     2306                if (pWorker->hThread != NULL)
     2307                {
     2308                    pWorker->idxWorker = g_cChildCareworkers++; /* paranoia */
     2309                    g_papChildCareworkers[pWorker->idxWorker] = pWorker;
     2310                    return pWorker;
     2311                }
     2312
     2313                /* Bail out! */
     2314                fprintf(stderr, "warning! _beginthreadex failed: %u (%s)\n", errno, strerror(errno));
     2315                mkWinChildcareDeleteWorkerPipe(&pWorker->StdErr);
     2316            }
    19402317            else
    1941                 cMaxInGroup = cMaxInGroup / 2 + 1;
    1942             if (cInGroup >= cMaxInGroup)
    1943             {
    1944                 g_idxProcessorInGroupAllocator = 0;
    1945                 g_idxProcessorGroupAllocator++;
    1946             }
    1947         }
    1948 
    1949         /* Try start the thread. */
    1950         pWorker->hThread = (HANDLE)_beginthreadex(NULL, 0 /*cbStack*/, mkWinChildcareWorkerThread, pWorker,
    1951                                                   0, &pWorker->tid);
    1952         if (pWorker->hThread != NULL)
    1953         {
    1954             g_papChildCareworkers[g_cChildCareworkers++] = pWorker;
    1955             return pWorker;
    1956         }
     2318                fprintf(stderr, "warning! Failed to create stderr pipe: %u\n", GetLastError());
     2319            mkWinChildcareDeleteWorkerPipe(&pWorker->StdOut);
     2320        }
     2321        else
     2322            fprintf(stderr, "warning! Failed to create stdout pipe: %u\n", GetLastError());
    19572323        CloseHandle(pWorker->hEvtIdle);
    19582324    }
     2325    else
     2326        fprintf(stderr, "warning! CreateEvent failed: %u\n", GetLastError());
    19592327    pWorker->uMagic = ~WINCHILDCAREWORKER_MAGIC;
    19602328    free(pWorker);
     
    22542622    pChild->u.Process.hStdErr = INVALID_HANDLE_VALUE;
    22552623
     2624    /* We always catch the output in order to prevent character soups courtesy
     2625       of the microsoft CRT and/or linkers writing character by character to the
     2626       console.  Always try write whole lines, even when --output-sync is none. */
     2627    pChild->u.Process.fCatchOutput = TRUE;
     2628
    22562629    return mkWinChildPushToCareWorker(pChild, pPid);
    22572630}
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette