Changeset 3353 for trunk/src


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.

Location:
trunk/src/kmk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kmk/main.c

    r3297 r3353  
    416416
    417417int make_expensive_statistics = 0;
     418#endif
     419
     420#if defined (WINDOWS32) && defined (CONFIG_NEW_WIN_CHILDREN)
     421/* --job-object[=mode]. */
     422char *win_job_object_mode = NULL;
     423
     424/* --job-object-name=name */
     425char *win_job_object_name = NULL;
    418426#endif
    419427
     
    552560  --statistics                Gather extra statistics for $(make-stats ).\n"),
    553561#endif
     562#if defined (WINDOWS32) && defined (CONFIG_NEW_WIN_CHILDREN)
     563    N_("\
     564  --job-object=mode           Windows job object mode:\n\
     565                                root-kill   = Root make instance only, kill all\n\
     566                                              process when root exits. (def)\n\
     567                                root-nokill = Root make instance only.\n\
     568                                              No killing.\n\
     569                                each-kill   = Each make instances, kill all\n\
     570                                              children when an instance exits.\n\
     571                                each-nokill = Each make instances, no killing.\n\
     572                                none        = No job objects.\n"),
     573    N_("\
     574  --job-object-name=name      Name of windows job object to open or create.\n\
     575                              The default name is 'kmk-job-obj-<date>Z<pid>'.\n"),
     576#endif
    554577    NULL
    555578  };
     
    577600    { 'p', flag, &print_data_base_flag, 1, 1, 0, 0, 0, "print-data-base" },
    578601#ifdef CONFIG_PRETTY_COMMAND_PRINTING
    579     { CHAR_MAX+10, flag, (char *) &pretty_command_printing, 1, 1, 1, 0, 0,
     602    { CHAR_MAX+50, flag, (char *) &pretty_command_printing, 1, 1, 1, 0, 0,
    580603       "pretty-command-printing" },
    581604#endif
    582605#ifdef CONFIG_WITH_PRINT_STATS_SWITCH
    583     { CHAR_MAX+11, flag, (char *) &print_stats_flag, 1, 1, 1, 0, 0,
     606    { CHAR_MAX+51, flag, (char *) &print_stats_flag, 1, 1, 1, 0, 0,
    584607       "print-stats" },
    585608#endif
    586609#ifdef CONFIG_WITH_PRINT_TIME_SWITCH
    587     { CHAR_MAX+12, positive_int, (char *) &print_time_min, 1, 1, 0,
     610    { CHAR_MAX+52, positive_int, (char *) &print_time_min, 1, 1, 0,
    588611      (char *) &no_val_print_time_min, (char *) &default_print_time_min,
    589612      "print-time" },
    590613#endif
    591614#ifdef KMK
    592     { CHAR_MAX+14, positive_int, (char *) &process_priority, 1, 1, 0,
     615    { CHAR_MAX+54, positive_int, (char *) &process_priority, 1, 1, 0,
    593616      (char *) &process_priority, (char *) &process_priority, "priority" },
    594     { CHAR_MAX+15, positive_int, (char *) &process_affinity, 1, 1, 0,
     617    { CHAR_MAX+55, positive_int, (char *) &process_affinity, 1, 1, 0,
    595618      (char *) &process_affinity, (char *) &process_affinity, "affinity" },
    596     { CHAR_MAX+17, flag, (char *) &process_priority, 1, 1, 0, 0, 0, "nice" },
     619    { CHAR_MAX+56, flag, (char *) &process_priority, 1, 1, 0, 0, 0, "nice" },
    597620#endif
    598621    { 'q', flag, &question_flag, 1, 1, 1, 0, 0, "question" },
     
    604627      "no-keep-going" },
    605628#if defined (CONFIG_WITH_MAKE_STATS) || defined (CONFIG_WITH_MINIMAL_STATS)
    606     { CHAR_MAX+16, flag, (char *) &make_expensive_statistics, 1, 1, 1, 0, 0,
     629    { CHAR_MAX+57, flag, (char *) &make_expensive_statistics, 1, 1, 1, 0, 0,
    607630       "statistics" },
    608631#endif
     
    639662    { CHAR_MAX+6, strlist, &eval_strings, 1, 0, 0, 0, 0, "eval" },
    640663    { CHAR_MAX+7, string, &sync_mutex, 1, 1, 0, 0, 0, "sync-mutex" },
     664#if defined (WINDOWS32) && defined (CONFIG_NEW_WIN_CHILDREN)
     665    { CHAR_MAX+58, string, &win_job_object_mode, 1, 1, 1, 0, 0, "job-object" },
     666    { CHAR_MAX+59, string, &win_job_object_name,  1, 1, 1, 0, 0,
     667      "job-object-name" },
     668#endif
    641669    { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
    642670  };
     
    37283756  decode_debug_flags ();
    37293757  decode_output_sync_flags ();
     3758
     3759#if defined (WINDOWS32) && defined (CONFIG_NEW_WIN_CHILDREN)
     3760  /* validate the job object mode value . */
     3761  if (win_job_object_mode == NULL)
     3762    win_job_object_mode = xstrdup("root-kill");
     3763  else if (   strcmp (win_job_object_mode, "none") != 0
     3764           && strcmp (win_job_object_mode, "root-kill") != 0
     3765           && strcmp (win_job_object_mode, "root-nokill") != 0
     3766           && strcmp (win_job_object_mode, "each-kill") != 0
     3767           && strcmp (win_job_object_mode, "each-nokill") != 0)
     3768    OS (fatal, NILF, _("unknown job object mode '%s'"), win_job_object_mode);
     3769#endif
    37303770}
    37313771
  • 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.