Changeset 3353 for trunk/src/kmk/w32


Ignore:
Timestamp:
Jun 5, 2020, 2:57:13 AM (5 years ago)
Author:
bird
Message:

kmk/winchildren: Use windows job objects to terminate all orphaned children when the topmost kmk process terminates. There are a couple of command line options to disable/tweak this behaviour.

File:
1 edited

Legend:

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

    r3313 r3353  
    107107extern void kmk_cache_exec_image_w(const wchar_t *); /* imagecache.c */
    108108#endif
     109
     110/* Option values from main.c: */
     111extern const char *win_job_object_mode;
     112extern const char *win_job_object_name;
    109113
    110114
     
    481485    InitializeSRWLock(&g_RWLock);
    482486#endif
     487
     488    /*
     489     * Depending on the --job-object=mode value, we typically create a job
     490     * object here if we're the root make instance.  The job object is then
     491     * typically configured to kill all remaining processes when the root make
     492     * terminates, so that there aren't any stuck processes around messing up
     493     * subsequent builds.  This is very handy on build servers, provided of
     494     * course that, there aren't parallel kmk instance that wants to share
     495     * mspdbsrv.exe or something like that.
     496     *
     497     * If we're not in a -kill mode, the job object is pretty pointless for
     498     * manual cleanup as the job object becomes invisible (or something) when
     499     * the last handle to it closes, i.e. hJob below.  On windows 8 and later
     500     * it looks like any orphaned children are immediately assigned to the
     501     * parent job object. Too bad for kmk_kill and such.
     502     *
     503     * win_job_object_mode values: none, root-kill, root-nokill, all-kill, all-nokill
     504     */
     505    if (   strcmp(win_job_object_mode, "none") != 0
     506        && (   makelevel == 0
     507            || strstr(win_job_object_mode, "root") == NULL))
     508    {
     509        HANDLE      hJob = NULL;
     510        BOOL        fCreate = TRUE;
     511        const char *pszJobName = win_job_object_name;
     512        if (pszJobName)
     513        {
     514            /* Try open it first, in case it already exists. If it doesn't we'll try create it. */
     515            fCreate = FALSE;
     516            hJob = OpenJobObjectA(JOB_OBJECT_ASSIGN_PROCESS, FALSE /*bInheritHandle*/, pszJobName);
     517            if (hJob)
     518            {
     519                DWORD dwErr = GetLastError();
     520                if (dwErr == ERROR_PATH_NOT_FOUND || dwErr == ERROR_FILE_NOT_FOUND)
     521                    fCreate = TRUE;
     522                else
     523                    OSN(message, 0, _("OpenJobObjectA(,,%s) failed: %u"), pszJobName, GetLastError());
     524            }
     525        }
     526
     527        if (fCreate)
     528        {
     529            char       szJobName[128];
     530            SYSTEMTIME Now = {0};
     531            GetSystemTime(&Now);
     532            snprintf(szJobName, sizeof(szJobName) / 2, "kmk-job-obj-%04u-%02u-%02uT%02u-%02u-%02uZ%u",
     533                     Now.wYear, Now.wMonth, Now.wDay, Now.wHour, Now.wMinute, Now.wSecond, getpid());
     534            if (!pszJobName)
     535                pszJobName = szJobName;
     536
     537            hJob = CreateJobObjectA(NULL, pszJobName);
     538            if (hJob)
     539            {
     540                /* We need to set the BREAKAWAY_OK flag, as we don't want make CreateProcess fail if
     541                   someone tries to break way.  Also set KILL_ON_JOB_CLOSE unless 'nokill' is given. */
     542                JOBOBJECT_EXTENDED_LIMIT_INFORMATION Info;
     543                DWORD cbActual = 0;
     544                memset(&Info, 0, sizeof(Info));
     545                if (QueryInformationJobObject(hJob, JobObjectExtendedLimitInformation, &Info, sizeof(Info), &cbActual))
     546                {
     547                    Info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK;
     548                    if (strstr(win_job_object_mode, "nokill") == NULL)
     549                        Info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
     550                    else
     551                        Info.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
     552                    if (!SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &Info, sizeof(Info)))
     553                        OSN(message, 0, _("SetInformationJobObject(%s,JobObjectExtendedLimitInformation,{%s},) failed: %u"),
     554                            pszJobName, win_job_object_mode, GetLastError());
     555                }
     556                else
     557                    OSN(message, 0, _("QueryInformationJobObject(%s,JobObjectExtendedLimitInformation,,,) failed: %u"),
     558                        pszJobName, GetLastError());
     559            }
     560            else
     561                OSN(message, 0, _("CreateJobObjectA(NULL,%s) failed: %u"), pszJobName, GetLastError());
     562        }
     563
     564        if (hJob)
     565        {
     566            if (!(AssignProcessToJobObject(hJob, GetCurrentProcess())))
     567                OSN(message, 0, _("AssignProcessToJobObject(%s, me) failed: %u"), pszJobName, GetLastError());
     568        }
     569    }
    483570
    484571    /*
Note: See TracChangeset for help on using the changeset viewer.