[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Condor-users] Condor and processor affinity



Sorry for the late reply I have been off list for a while.

I suspect the number of people using job objects on windows is relatively low and any that are are sufficiently savvy to spot issues with condor conflicting with theirs.

Having condor default to use job objects on windows to control everything (optionally enforcing the memory and cpu limiting) would make the common code path much simpler (and more robust). You get:
  * Hassle of spotting/counting multiple processes is completely gone
     * Free killing of everything associated with the slot
     * memory/cpu counting for total effort
  * PriorityClass/SchedulingClass handles the renice
  * Memory can be limited rather than killed after exceeding
  * CPU affinity
  * a fork bomb style problem can be prevented

However because some people may want total control over the job objects you will end up needing two code paths, reducing the simplicity considerably (and making testing much harder).

I'd argue that it is worth doing if there are few/no users making use of job objects currently who would not have all their needs served by simply using the integrated condor functionality.

One option is to eliminate the need for two code paths by making the job object path the only supported way to achieve significant control and have the other route rely on simple mechanisms like the per slot user to function. Thus people willing to take control themselves on a per job basis can do as they wish without significantly impacting the code complexity.

It's a balancing act between your users and your developers, from my point of view having it optionally integrated into condor would be excellent since I could let the configuration of slot based cpu affinity and memory be centrally controlled rather than each job having to 'work it out on the fly' as it currently does.

Also it would allow the possibility of things like:

Dynamic controls where by a minimum number of cores are always available but, if the machine as a whole is partly unused those cores can be dynamically assigned to the existing slots then taken away again.

Ganging together multiple slots so that you could take ownership of (say) 2 and get access to all their resources with an eviction of one allowing either a reduction in your resources or an evict from all of them.

In relation to the above using NUMA specific information to group slots/cores intelligently, so ganged jobs would be cleanly migrated onto a shared node where possible (moving the other jobs about as needed to achieve this)

IO related aspects are not currently present but are reserved for future use, this could be a route to IO throttling or disk usage limits (very speculative)

All that said if you have existing users using jobs in alternate ways you may just want to accept the simple use what already exists and work route.

Matt

-----Original Message-----
From: condor-users-bounces@xxxxxxxxxxx [mailto:condor-users-bounces@xxxxxxxxxxx] On Behalf Of Matthew Farrellee
Sent: 13 July 2009 14:48
To: Ben Burnett
Cc: Condor-Users Mail List
Subject: Re: [Condor-users] Condor and processor affinity

(added condor-users back, it had been dropped)

This is all good to know. Maybe Matt Hope has some other thoughts on
managing processes on Windows.

Best,


matt

Ben Burnett wrote:
> Yes, we have considered using Windows "Job" objects, but because, as Matt
> pointed out, a process can only be a member of one Windows Job object.
> Thus, if Condor used Job objects, no Condor jobs could use processes that
> used Windows Job objects as the Condor jobs would fail to execute (being
> unable to add themselves to another Windows Job object).
>
> My understanding is that we have also spoken to MS about this in the past,
> and that while they have agreed that nested Job objects, or processes in
> more than one Job would be cool, that there  has been little call or
> interest in it.  Maybe it is time to try talking to them again, given the
> large rise in multi-core and cpu machines? (Since it seems to be a way to
> manage a group of process on one cpu [or core].)
>
> Assuming child process don't inherit processor affinity, we could easily
> fake the inheritance by receiving callbacks each time a new child process
> is created.  If its parent has its processor affinity set, and we
> recognize it (process family code), then set the child's affinity as well.
>
> Regards,
> -B
>
> On Thu, July 9, 2009 3:10 pm, Matthew Farrellee wrote:
>> Todd, Ben,
>>
>> Have you considered such an interface? Does it have the nesting problem
>> of Job objects within Job objects?
>>
>> Best,
>>
>>
>> matt
>>
>> Matt Hope wrote:
>>> As an aside on windows are you using Job objects to control this or
>>> simply setting the Affinity of the first launched process?
>>>
>>> Using job objects sorts the child processes problem but might not play
>>> nicely with peoples usage of this (a process can only be a member of one
>>> job and cannot stop being a member of a job once attached to it) so it
>>> would be nice to know how this is enforced.
>>>
>>> Here's some .Net code to do this yourself (as well as control your
>>> memory) in case anyone else finds it useful
>>> Translating to raw win32 is pretty trivial
>>>
>>> You could extend this on windows to do many of the complex/error prone
>>> things condor currently has to do by hand such as killing all child
>>> processes within a job, determing cpu time of the job as a whole, the
>>> peak memory usage etc...
>>>
>>> using System;
>>> using System.Collections;
>>> using System.Diagnostics;
>>> using System.Runtime.InteropServices;
>>>
>>> namespace JobObjects
>>> {
>>>   /// <summary>An interface to the Windows Job Objects API.</summary>
>>>   public class Controller
>>>   {
>>>     #region Native 32/64 Bit Switching Flag
>>>     /// <summary>
>>>     /// The structures returned by Windows are different sizes depending
>>> on whether
>>>     /// the operating system is running in 32bit or 64bit mode.
>>>     /// </summary>
>>>     private static readonly bool Is32Bit = (IntPtr.Size == 4);
>>>     #endregion
>>>
>>>     #region JobObjectInfoClass Enumeration
>>>     /// <summary>
>>>     /// Information class for the limits to be set. This parameter can
>>> be one of
>>>     /// the following values.
>>>     /// </summary>
>>>     private enum JobObjectInfoClass
>>>     {
>>>       /// <summary>
>>>       /// The lpJobObjectInfo parameter is a pointer to a
>>>       /// JOBOBJECT_BASIC_ACCOUNTING_INFORMATION structure.
>>>       /// </summary>
>>>       BasicAccountingInformation = 1,
>>>
>>>       /// <summary>
>>>       /// The lpJobObjectInfo parameter is a pointer to a
>>>       /// JOBOBJECT_BASIC_LIMIT_INFORMATION structure.
>>>       /// </summary>
>>>       BasicLimitInformation = 2,
>>>
>>>       /// <summary>
>>>       /// The lpJobObjectInfo parameter is a pointer to a
>>>       /// JOBOBJECT_BASIC_PROCESS_ID_LIST structure.
>>>       /// </summary>
>>>       BasicProcessIdList = 3,
>>>
>>>       /// <summary>
>>>       /// The lpJobObjectInfo parameter is a pointer to a
>>>       /// JOBOBJECT_BASIC_UI_RESTRICTIONS structure.
>>>       /// </summary>
>>>       BasicUIRestrictions = 4,
>>>
>>>       /// <summary>
>>>       /// The lpJobObjectInfo parameter is a pointer to a
>>>       /// JOBOBJECT_SECURITY_LIMIT_INFORMATION structure.
>>>       /// The hJob handle must have the
>>> JOB_OBJECT_SET_SECURITY_ATTRIBUTES
>>>       /// access right associated with it.
>>>       /// </summary>
>>>       SecurityLimitInformation = 5,
>>>
>>>       /// <summary>
>>>       /// The lpJobObjectInfo parameter is a pointer to a
>>>       /// JOBOBJECT_END_OF_JOB_TIME_INFORMATION structure.
>>>       /// </summary>
>>>       EndOfJobTimeInformation = 6,
>>>
>>>       /// <summary>
>>>       /// The lpJobObjectInfo parameter is a pointer to a
>>>       /// JOBOBJECT_ASSOCIATE_COMPLETION_PORT structure.
>>>       /// </summary>
>>>       AssociateCompletionPortInformation = 7,
>>>
>>>       /// <summary>
>>>       /// The lpJobObjectInfo parameter is a pointer to a
>>>       /// JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION structure.
>>>       /// </summary>
>>>       BasicAndIoAccountingInformation = 8,
>>>
>>>       /// <summary>
>>>       /// The lpJobObjectInfo parameter is a pointer to a
>>>       /// JOBOBJECT_EXTENDED_LIMIT_INFORMATION structure.
>>>       /// </summary>
>>>       ExtendedLimitInformation = 9
>>>     }
>>>     #endregion
>>>
>>>     #region LimitFlags Enumeration
>>>     /// <summary>
>>>     /// Limit flags that are in effect. This member is a bit field that
>>> determines
>>>     /// whether other structure members are used. Any combination of the
>>> following
>>>     /// values can be specified.
>>>     /// </summary>
>>>     [Flags]
>>>     private enum LimitFlags
>>>     {
>>>       /// <summary>
>>>       /// Causes all processes associated with the job to use the same
>>> minimum and maximum working set sizes.
>>>       /// </summary>
>>>       LimitWorkingSet = 0x00000001,
>>>
>>>       /// <summary>
>>>       /// Establishes a user-mode execution time limit for each
>>> currently active process
>>>       /// and for all future processes associated with the job.
>>>       /// </summary>
>>>       LimitProcessTime = 0x00000002,
>>>
>>>       /// <summary>
>>>       /// Establishes a user-mode execution time limit for the job. This
>>> flag cannot
>>>       /// be used with JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME.
>>>       /// </summary>
>>>       LimitJobTime = 0x00000004,
>>>
>>>       /// <summary>
>>>       /// Establishes a maximum number of simultaneously active
>>> processes associated
>>>       /// with the job.
>>>       /// </summary>
>>>       LimitActiveProcesses = 0x00000008,
>>>
>>>       /// <summary>
>>>       /// Causes all processes associated with the job to use the same
>>> processor
>>>       /// affinity.
>>>       /// </summary>
>>>       LimitAffinity = 0x00000010,
>>>
>>>       /// <summary>
>>>       /// Causes all processes associated with the job to use the same
>>> priority class.
>>>       /// For more information, see Scheduling Priorities.
>>>       /// </summary>
>>>       LimitPriorityClass = 0x00000020,
>>>
>>>       /// <summary>
>>>       /// Preserves any job time limits you previously set. As long as
>>> this flag is
>>>       /// set, you can establish a per-job time limit once, then alter
>>> other limits
>>>       /// in subsequent calls. This flag cannot be used with
>>> JOB_OBJECT_LIMIT_JOB_TIME.
>>>       /// </summary>
>>>       PreserveJobTime = 0x00000040,
>>>
>>>       /// <summary>
>>>       /// Causes all processes in the job to use the same scheduling
>>> class.
>>>       /// </summary>
>>>       LimitSchedulingClass = 0x00000080,
>>>
>>>       /// <summary>
>>>       /// Causes all processes associated with the job to limit their
>>> committed memory.
>>>       /// When a process attempts to commit memory that would exceed the
>>> per-process
>>>       /// limit, it fails. If the job object is associated with a
>>> completion port, a
>>>       /// JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT message is sent to the
>>> completion port.
>>>       /// This limit requires use of a
>>> JOBOBJECT_EXTENDED_LIMIT_INFORMATION structure.
>>>       /// Its BasicLimitInformation member is a
>>> JOBOBJECT_BASIC_LIMIT_INFORMATION
>>>       /// structure.
>>>       /// </summary>
>>>       LimitProcessMemory = 0x00000100,
>>>
>>>       /// <summary>
>>>       /// Causes all processes associated with the job to limit the
>>> job-wide sum of
>>>       /// their committed memory. When a process attempts to commit
>>> memory that would
>>>       /// exceed the job-wide limit, it fails. If the job object is
>>> associated with a
>>>       /// completion port, a JOB_OBJECT_MSG_JOB_MEMORY_LIMIT message is
>>> sent to the
>>>       /// completion port.  This limit requires use of a
>>>       /// JOBOBJECT_EXTENDED_LIMIT_INFORMATION structure. Its
>>> BasicLimitInformation
>>>       /// member is a JOBOBJECT_BASIC_LIMIT_INFORMATION structure.
>>>       /// </summary>
>>>       LimitJobMemory = 0x00000200,
>>>
>>>       /// <summary>
>>>       /// Forces a call to the SetErrorMode function with the
>>> SEM_NOGPFAULTERRORBOX
>>>       /// flag for each process associated with the job.  If an
>>> exception occurs and
>>>       /// the system calls the UnhandledExceptionFilter function, the
>>> debugger will
>>>       /// be given a chance to act. If there is no debugger, the
>>> functions returns
>>>       /// EXCEPTION_EXECUTE_HANDLER. Normally, this will cause
>>> termination of the
>>>       /// process with the exception code as the exit status.  This
>>> limit requires
>>>       /// use of a JOBOBJECT_EXTENDED_LIMIT_INFORMATION structure. Its
>>>       /// BasicLimitInformation member is a
>>> JOBOBJECT_BASIC_LIMIT_INFORMATION structure.
>>>       /// </summary>
>>>       DieOnUnhandledException = 0x00000400,
>>>
>>>       /// <summary>
>>>       /// If any process associated with the job creates a child process
>>> using the
>>>       /// CREATE_BREAKAWAY_FROM_JOB flag while this limit is in effect,
>>> the child
>>>       /// process is not associated with the job.  This limit requires
>>> use of a
>>>       /// JOBOBJECT_EXTENDED_LIMIT_INFORMATION structure. Its
>>> BasicLimitInformation
>>>       /// member is a JOBOBJECT_BASIC_LIMIT_INFORMATION structure.
>>>       /// </summary>
>>>       LimitBreakawayOk = 0x00000800,
>>>
>>>       /// <summary>
>>>       /// Allows any process associated with the job to create child
>>> processes
>>>       /// that are not associated with the job.  This limit requires use
>>> of a
>>>       /// JOBOBJECT_EXTENDED_LIMIT_INFORMATION structure. Its
>>> BasicLimitInformation
>>>       /// member is a JOBOBJECT_BASIC_LIMIT_INFORMATION structure.
>>>       /// </summary>
>>>       LimitSilentBreakawayOk = 0x00001000,
>>>
>>>       /// <summary>
>>>       /// Causes all processes associated with the job to terminate when
>>> the last
>>>       /// handle to the job is closed.  This limit requires use of a
>>>       /// JOBOBJECT_EXTENDED_LIMIT_INFORMATION structure. Its
>>> BasicLimitInformation
>>>       /// member is a JOBOBJECT_BASIC_LIMIT_INFORMATION structure.
>>>       /// Windows 2000:  This flag is not supported.
>>>       /// </summary>
>>>       LimitKillOnJobClose = 0x00002000
>>>     }
>>>     #endregion
>>>
>>>     #region IoCounters Structures
>>>     /// <summary>
>>>     /// Various counters for different types of IO operations.  Don't
>>> believe
>>>     /// this is currently implemented by Windows.
>>>     /// </summary>
>>>     [StructLayout(LayoutKind.Explicit)]
>>>     private struct IoCounters32
>>>     {
>>>       /// <summary>
>>>       /// The number of read operations.
>>>       /// </summary>
>>>       [FieldOffset(0)]
>>>       public ulong ReadOperationCount;
>>>
>>>       /// <summary>
>>>       /// The number of write operations.
>>>       /// </summary>
>>>       [FieldOffset(8)]
>>>       public ulong WriteOperationCount;
>>>
>>>       /// <summary>
>>>       /// The number of other operations.
>>>       /// </summary>
>>>       [FieldOffset(16)]
>>>       public ulong OtherOperationCount;
>>>
>>>       /// <summary>
>>>       /// The number of read transfers.
>>>       /// </summary>
>>>       [FieldOffset(24)]
>>>       public ulong ReadTransferCount;
>>>
>>>       /// <summary>
>>>       /// The number of write transfers.
>>>       /// </summary>
>>>       [FieldOffset(32)]
>>>       public ulong WriteTransferCount;
>>>
>>>       /// <summary>
>>>       /// The number of other transfers.
>>>       /// </summary>
>>>       [FieldOffset(40)]
>>>       public ulong OtherTransferCount;
>>>     }
>>>
>>>     /// <summary>
>>>     /// Various counters for different types of IO operations.  Don't
>>> believe
>>>     /// this is currently implemented by Windows.
>>>     /// </summary>
>>>     [StructLayout(LayoutKind.Explicit)]
>>>     private struct IoCounters64
>>>     {
>>>       /// <summary>
>>>       /// The number of read operations.
>>>       /// </summary>
>>>       [FieldOffset(0)]
>>>       public ulong ReadOperationCount;
>>>
>>>       /// <summary>
>>>       /// The number of write operations.
>>>       /// </summary>
>>>       [FieldOffset(8)]
>>>       public ulong WriteOperationCount;
>>>
>>>       /// <summary>
>>>       /// The number of other operations.
>>>       /// </summary>
>>>       [FieldOffset(16)]
>>>       public ulong OtherOperationCount;
>>>
>>>       /// <summary>
>>>       /// The number of read transfers.
>>>       /// </summary>
>>>       [FieldOffset(24)]
>>>       public ulong ReadTransferCount;
>>>
>>>       /// <summary>
>>>       /// The number of write transfers.
>>>       /// </summary>
>>>       [FieldOffset(32)]
>>>       public ulong WriteTransferCount;
>>>
>>>       /// <summary>
>>>       /// The number of other transfers.
>>>       /// </summary>
>>>       [FieldOffset(40)]
>>>       public ulong OtherTransferCount;
>>>     }
>>>     #endregion
>>>
>>>     #region BasicLimits Structures
>>>     /// <summary>
>>>     /// The JOBOBJECT_BASIC_LIMIT_INFORMATION structure contains basic
>>> limit
>>>     /// information for a job object.
>>>     /// </summary>
>>>     [StructLayout(LayoutKind.Explicit)]
>>>     private struct BasicLimits32
>>>     {
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_PROCESS_TIME, this
>>> member is
>>>       /// the per-process user-mode execution time limit, in
>>> 100-nanosecond ticks.
>>>       /// Otherwise, this member is ignored.  The system periodically
>>> checks to
>>>       /// determine whether each process associated with the job has
>>> accumulated
>>>       /// more user-mode time than the set limit. If it has, the process
>>> is terminated.
>>>       /// </summary>
>>>       [FieldOffset(0)]
>>>       public long PerProcessUserTimeLimit;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_JOB_TIME, this member
>>> is the
>>>       /// per-job user-mode execution time limit, in 100-nanosecond
>>> ticks. Otherwise,
>>>       /// this member is ignored. The system adds the current time of
>>> the processes
>>>       /// associated with the job to this limit. For example, if you set
>>> this limit
>>>       /// to 1 minute, and the job has a process that has accumulated 5
>>> minutes of
>>>       /// user-mode time, the limit actually enforced is 6 minutes.  The
>>> system
>>>       /// periodically checks to determine whether the sum of the
>>> user-mode execution
>>>       /// time for all processes is greater than this end-of-job limit.
>>> If it is, the
>>>       /// action specified in the EndOfJobTimeAction member of the
>>>       /// JOBOBJECT_END_OF_JOB_TIME_INFORMATION structure is carried
>>> out. By default,
>>>       /// all processes are terminated and the status code is set to
>>>       /// ERROR_NOT_ENOUGH_QUOTA.
>>>       /// </summary>
>>>       [FieldOffset(8)]
>>>       public long PerJobUserTimeLimit;
>>>
>>>       /// <summary>
>>>       /// Limit flags that are in effect. This member is a bit field
>>> that determines
>>>       /// whether other structure members are used. Any combination
>>> LimitFlag values
>>>       /// can be specified.
>>>       /// </summary>
>>>       [FieldOffset(16)]
>>>       public LimitFlags LimitFlags;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_WORKINGSET, this
>>> member is the
>>>       /// minimum working set size for each process associated with the
>>> job. Otherwise,
>>>       /// this member is ignored.  If MaximumWorkingSetSize is nonzero,
>>>       /// MinimumWorkingSetSize cannot be zero.
>>>       /// </summary>
>>>       [FieldOffset(20)]
>>>       public uint MinimumWorkingSetSize;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_WORKINGSET, this
>>> member is the
>>>       /// maximum working set size for each process associated with the
>>> job. Otherwise,
>>>       /// this member is ignored.  If MinimumWorkingSetSize is nonzero,
>>>       /// MaximumWorkingSetSize cannot be zero.
>>>       /// </summary>
>>>       [FieldOffset(24)]
>>>       public uint MaximumWorkingSetSize;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_ACTIVE_PROCESS, this
>>> member is the
>>>       /// active process limit for the job. Otherwise, this member is
>>> ignored.  If you
>>>       /// try to associate a process with a job, and this causes the
>>> active process
>>>       /// count to exceed this limit, the process is terminated and the
>>> association
>>>       /// fails.
>>>       /// </summary>
>>>       [FieldOffset(28)]
>>>       public int ActiveProcessLimit;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_AFFINITY, this member
>>> is the
>>>       /// processor affinity for all processes associated with the job.
>>> Otherwise,
>>>       /// this member is ignored.  The affinity must be a proper subset
>>> of the system
>>>       /// affinity mask obtained by calling the GetProcessAffinityMask
>>> function. The
>>>       /// affinity of each thread is set to this value, but threads are
>>> free to
>>>       /// subsequently set their affinity, as long as it is a subset of
>>> the specified
>>>       /// affinity mask. Processes cannot set their own affinity mask.
>>>       /// </summary>
>>>       [FieldOffset(32)]
>>>       public IntPtr Affinity;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_PRIORITY_CLASS, this
>>> member is the
>>>       /// priority class for all processes associated with the job.
>>> Otherwise, this
>>>       /// member is ignored. Processes and threads cannot modify their
>>> priority class.
>>>       /// The calling process must enable the SE_INC_BASE_PRIORITY_NAME
>>> privilege.
>>>       /// </summary>
>>>       [FieldOffset(36)]
>>>       public int PriorityClass;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_SCHEDULING_CLASS,
>>> this member is
>>>       /// the scheduling class for all processes associated with the
>>> job. Otherwise,
>>>       /// this member is ignored.  The valid values are 0 to 9. Use 0
>>> for the least
>>>       /// favorable scheduling class relative to other threads, and 9
>>> for the most
>>>       /// favorable scheduling class relative to other threads. By
>>> default, this
>>>       /// value is 5. To use a scheduling class greater than 5, the
>>> calling process
>>>       /// must enable the SE_INC_BASE_PRIORITY_NAME privilege.
>>>       /// </summary>
>>>       [FieldOffset(40)]
>>>       public int SchedulingClass;
>>>     }
>>>
>>>     /// <summary>
>>>     /// The JOBOBJECT_BASIC_LIMIT_INFORMATION structure contains basic
>>> limit
>>>     /// information for a job object.
>>>     /// </summary>
>>>     [StructLayout(LayoutKind.Explicit)]
>>>     private struct BasicLimits64
>>>     {
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_PROCESS_TIME, this
>>> member is
>>>       /// the per-process user-mode execution time limit, in
>>> 100-nanosecond ticks.
>>>       /// Otherwise, this member is ignored.  The system periodically
>>> checks to
>>>       /// determine whether each process associated with the job has
>>> accumulated
>>>       /// more user-mode time than the set limit. If it has, the process
>>> is terminated.
>>>       /// </summary>
>>>       [FieldOffset(0)]
>>>       public long PerProcessUserTimeLimit;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_JOB_TIME, this member
>>> is the
>>>       /// per-job user-mode execution time limit, in 100-nanosecond
>>> ticks. Otherwise,
>>>       /// this member is ignored. The system adds the current time of
>>> the processes
>>>       /// associated with the job to this limit. For example, if you set
>>> this limit
>>>       /// to 1 minute, and the job has a process that has accumulated 5
>>> minutes of
>>>       /// user-mode time, the limit actually enforced is 6 minutes.  The
>>> system
>>>       /// periodically checks to determine whether the sum of the
>>> user-mode execution
>>>       /// time for all processes is greater than this end-of-job limit.
>>> If it is, the
>>>       /// action specified in the EndOfJobTimeAction member of the
>>>       /// JOBOBJECT_END_OF_JOB_TIME_INFORMATION structure is carried
>>> out. By default,
>>>       /// all processes are terminated and the status code is set to
>>>       /// ERROR_NOT_ENOUGH_QUOTA.
>>>       /// </summary>
>>>       [FieldOffset(8)]
>>>       public long PerJobUserTimeLimit;
>>>
>>>       /// <summary>
>>>       /// Limit flags that are in effect. This member is a bit field
>>> that determines
>>>       /// whether other structure members are used. Any combination
>>> LimitFlag values
>>>       /// can be specified.
>>>       /// </summary>
>>>       [FieldOffset(16)]
>>>       public LimitFlags LimitFlags;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_WORKINGSET, this
>>> member is the
>>>       /// minimum working set size for each process associated with the
>>> job. Otherwise,
>>>       /// this member is ignored.  If MaximumWorkingSetSize is nonzero,
>>>       /// MinimumWorkingSetSize cannot be zero.
>>>       /// </summary>
>>>       [FieldOffset(24)]
>>>       public ulong MinimumWorkingSetSize;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_WORKINGSET, this
>>> member is the
>>>       /// maximum working set size for each process associated with the
>>> job. Otherwise,
>>>       /// this member is ignored.  If MinimumWorkingSetSize is nonzero,
>>>       /// MaximumWorkingSetSize cannot be zero.
>>>       /// </summary>
>>>       [FieldOffset(32)]
>>>       public ulong MaximumWorkingSetSize;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_ACTIVE_PROCESS, this
>>> member is the
>>>       /// active process limit for the job. Otherwise, this member is
>>> ignored.  If you
>>>       /// try to associate a process with a job, and this causes the
>>> active process
>>>       /// count to exceed this limit, the process is terminated and the
>>> association
>>>       /// fails.
>>>       /// </summary>
>>>       [FieldOffset(40)]
>>>       public int ActiveProcessLimit;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_AFFINITY, this member
>>> is the
>>>       /// processor affinity for all processes associated with the job.
>>> Otherwise,
>>>       /// this member is ignored.  The affinity must be a proper subset
>>> of the system
>>>       /// affinity mask obtained by calling the GetProcessAffinityMask
>>> function. The
>>>       /// affinity of each thread is set to this value, but threads are
>>> free to
>>>       /// subsequently set their affinity, as long as it is a subset of
>>> the specified
>>>       /// affinity mask. Processes cannot set their own affinity mask.
>>>       /// </summary>
>>>       [FieldOffset(48)]
>>>       public IntPtr Affinity;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_PRIORITY_CLASS, this
>>> member is the
>>>       /// priority class for all processes associated with the job.
>>> Otherwise, this
>>>       /// member is ignored. Processes and threads cannot modify their
>>> priority class.
>>>       /// The calling process must enable the SE_INC_BASE_PRIORITY_NAME
>>> privilege.
>>>       /// </summary>
>>>       [FieldOffset(56)]
>>>       public int PriorityClass;
>>>
>>>       /// <summary>
>>>       /// If LimitFlags specifies JOB_OBJECT_LIMIT_SCHEDULING_CLASS,
>>> this member is
>>>       /// the scheduling class for all processes associated with the
>>> job. Otherwise,
>>>       /// this member is ignored.  The valid values are 0 to 9. Use 0
>>> for the least
>>>       /// favorable scheduling class relative to other threads, and 9
>>> for the most
>>>       /// favorable scheduling class relative to other threads. By
>>> default, this
>>>       /// value is 5. To use a scheduling class greater than 5, the
>>> calling process
>>>       /// must enable the SE_INC_BASE_PRIORITY_NAME privilege.
>>>       /// </summary>
>>>       [FieldOffset(60)]
>>>       public int SchedulingClass;
>>>     }
>>>     #endregion
>>>
>>>     #region ExtendedLimits Structures
>>>     /// <summary>
>>>     /// The JOBOBJECT_EXTENDED_LIMIT_INFORMATION structure contains
>>> basic and extended limit
>>>     /// information for a job object.
>>>     /// </summary>
>>>     [StructLayout(LayoutKind.Explicit)]
>>>     private struct ExtendedLimits32
>>>     {
>>>       /// <summary>
>>>       /// A JOBOBJECT_BASIC_LIMIT_INFORMATION structure that contains
>>>       /// basic limit information.
>>>       /// </summary>
>>>       [FieldOffset(0)]
>>>       public BasicLimits32 BasicLimits;
>>>
>>>       /// <summary>
>>>       /// Resereved.
>>>       /// </summary>
>>>       [FieldOffset(48)]
>>>       public IoCounters32 IoInfo;
>>>
>>>       /// <summary>
>>>       /// If the LimitFlags member of the
>>> JOBOBJECT_BASIC_LIMIT_INFORMATION structure
>>>       /// specifies the JOB_OBJECT_LIMIT_PROCESS_MEMORY value, this
>>> member specifies
>>>       /// the limit for the virtual memory that can be committed by a
>>> process.
>>>       /// Otherwise, this member is ignored.
>>>       /// </summary>
>>>       [FieldOffset(96)]
>>>       public uint ProcessMemoryLimit;
>>>
>>>       /// <summary>
>>>       /// If the LimitFlags member of the
>>> JOBOBJECT_BASIC_LIMIT_INFORMATION structure
>>>       /// specifies the JOB_OBJECT_LIMIT_JOB_MEMORY value, this member
>>> specifies the
>>>       /// limit for the virtual memory that can be committed for the
>>> job. Otherwise,
>>>       /// this member is ignored.
>>>       /// </summary>
>>>       [FieldOffset(100)]
>>>       public uint JobMemoryLimit;
>>>
>>>       /// <summary>
>>>       /// Peak memory used by any process ever associated with the job.
>>>       /// </summary>
>>>       [FieldOffset(104)]
>>>       public uint PeakProcessMemoryUsed;
>>>
>>>       /// <summary>
>>>       /// Peak memory usage of all processes currently associated with
>>> the job.
>>>       /// </summary>
>>>       [FieldOffset(108)]
>>>       public uint PeakJobMemoryUsed;
>>>     }
>>>
>>>     /// <summary>
>>>     /// The JOBOBJECT_EXTENDED_LIMIT_INFORMATION structure contains
>>> basic and extended limit
>>>     /// information for a job object.
>>>     /// </summary>
>>>     [StructLayout(LayoutKind.Explicit)]
>>>     private struct ExtendedLimits64
>>>     {
>>>       /// <summary>
>>>       /// A JOBOBJECT_BASIC_LIMIT_INFORMATION structure that contains
>>>       /// basic limit information.
>>>       /// </summary>
>>>       [FieldOffset(0)]
>>>       public BasicLimits64 BasicLimits;
>>>
>>>       /// <summary>
>>>       /// Resereved.
>>>       /// </summary>
>>>       [FieldOffset(64)]
>>>       public IoCounters64 IoInfo;
>>>
>>>       /// <summary>
>>>       /// If the LimitFlags member of the
>>> JOBOBJECT_BASIC_LIMIT_INFORMATION structure
>>>       /// specifies the JOB_OBJECT_LIMIT_PROCESS_MEMORY value, this
>>> member specifies
>>>       /// the limit for the virtual memory that can be committed by a
>>> process.
>>>       /// Otherwise, this member is ignored.
>>>       /// </summary>
>>>       [FieldOffset(112)]
>>>       public ulong ProcessMemoryLimit;
>>>
>>>       /// <summary>
>>>       /// If the LimitFlags member of the
>>> JOBOBJECT_BASIC_LIMIT_INFORMATION structure
>>>       /// specifies the JOB_OBJECT_LIMIT_JOB_MEMORY value, this member
>>> specifies the
>>>       /// limit for the virtual memory that can be committed for the
>>> job. Otherwise,
>>>       /// this member is ignored.
>>>       /// </summary>
>>>       [FieldOffset(120)]
>>>       public ulong JobMemoryLimit;
>>>
>>>       /// <summary>
>>>       /// Peak memory used by any process ever associated with the job.
>>>       /// </summary>
>>>       [FieldOffset(128)]
>>>       public ulong PeakProcessMemoryUsed;
>>>
>>>       /// <summary>
>>>       /// Peak memory usage of all processes currently associated with
>>> the job.
>>>       /// </summary>
>>>       [FieldOffset(136)]
>>>       public ulong PeakJobMemoryUsed;
>>>     }
>>>     #endregion
>>>
>>>     #region JobObjectInfo Union
>>>     /// <summary>
>>>     /// Union of different limit data structures that may be passed
>>>     /// to SetInformationJobObject / from QueryInformationJobObject.
>>>     /// This union also contains separate 32 and 64 bit versions of
>>>     /// each structure.
>>>     /// </summary>
>>>     [StructLayout(LayoutKind.Explicit)]
>>>     private struct JobObjectInfo
>>>     {
>>>       #region 32 bit structures
>>>       /// <summary>
>>>       /// The BasicLimits32 structure contains basic limit information
>>>       /// for a job object on a 32bit platform.
>>>       /// </summary>
>>>       [FieldOffset(0)]
>>>       public BasicLimits32 basicLimits32;
>>>
>>>       /// <summary>
>>>       /// The ExtendedLimits32 structure contains extended limit
>>> information
>>>       /// for a job object on a 32bit platform.
>>>       /// </summary>
>>>       [FieldOffset(0)]
>>>       public ExtendedLimits32 extendedLimits32;
>>>       #endregion
>>>
>>>       #region 64 bit structures
>>>       /// <summary>
>>>       /// The BasicLimits64 structure contains basic limit information
>>>       /// for a job object on a 64bit platform.
>>>       /// </summary>
>>>       [FieldOffset(0)]
>>>       public BasicLimits64 basicLimits64;
>>>
>>>       /// <summary>
>>>       /// The ExtendedLimits64 structure contains extended limit
>>> information
>>>       /// for a job object on a 64bit platform.
>>>       /// </summary>
>>>       [FieldOffset(0)]
>>>       public ExtendedLimits64 extendedLimits64;
>>>       #endregion
>>>     }
>>>     #endregion
>>>
>>>     #region WinAPI Class
>>>     /// <summary>
>>>     /// Private class that holds all the Windows API calls made by this
>>>     /// </summary>
>>>     private class WinAPI
>>>     {
>>>       /// <summary>
>>>       /// This function returns a pseudohandle for the current process.
>>>       /// </summary>
>>>       /// <returns>The return value is a pseudohandle to the current
>>> process.</returns>
>>>       [DllImport("kernel32.dll", SetLastError = true)]
>>>       public static extern IntPtr GetCurrentProcess();
>>>
>>>       /// <summary>
>>>       /// The IsProcessInJob function determines if the process is
>>> running in the specified job.
>>>       /// </summary>
>>>       /// <param name="processHandle">Handle to the process to be
>>> tested.
>>>       /// The handle must have the PROCESS_QUERY_INFORMATION access
>>> right.
>>>       /// For more information, see Process Security and Access
>>> Rights.</param>
>>>       /// <param name="jobHandle">Handle to the job. If this parameter
>>> is NULL,
>>>       /// the function tests if the process is running under any job.
>>> If this
>>>       /// parameter is not NULL, the handle must have the
>>> JOB_OBJECT_QUERY access
>>>       /// right. For more information, see Job Object Security and
>>> Access Rights.</param>
>>>       /// <param name="result">Pointer to a value that receives TRUE if
>>> the process
>>>       /// is running in the job, and FALSE otherwise.</param>
>>>       /// <returns>If the function succeeds, the return value is
>>> nonzero.
>>>       /// If the function fails, the return value is zero. To get
>>> extended error information,
>>>       /// call GetLastError.</returns>
>>>       [DllImport("kernel32.dll", SetLastError = true)]
>>>       public static extern bool IsProcessInJob(
>>>         IntPtr processHandle,
>>>         IntPtr jobHandle,
>>>         [Out] out bool result);
>>>
>>>       /// <summary>
>>>       /// The CreateJobObject function creates or opens a job object.
>>>       /// </summary>
>>>       /// <param name="jobAttributes">Pointer to a SECURITY_ATTRIBUTES
>>> structure that
>>>       /// specifies the security descriptor for the job object and
>>> determines whether
>>>       /// child processes can inherit the returned handle. If
>>> lpJobAttributes is NULL,
>>>       /// the job object gets a default security descriptor and the
>>> handle cannot be inherited.
>>>       /// The ACLs in the default security descriptor for a job object
>>> come from the primary
>>>       /// or impersonation token of the creator.</param>
>>>       /// <param name="name"> Pointer to a null-terminated string
>>> specifying the name of the
>>>       /// job. The name is limited to MAX_PATH characters. Name
>>> comparison is case-sensitive.
>>>       /// If lpName is NULL, the job is created without a name.   If
>>> lpName matches the name
>>>       /// of an existing event, semaphore, mutex, waitable timer, or
>>> file-mapping object, the
>>>       /// function fails and the GetLastError function returns
>>> ERROR_INVALID_HANDLE. This
>>>       /// occurs because these objects share the same name
>>> space.</param>
>>>       /// <returns>If the function succeeds, the return value is a
>>> handle to the job object.
>>>       /// The handle has the JOB_OBJECT_ALL_ACCESS access right. If the
>>> object existed before
>>>       /// the function call, the function returns a handle to the
>>> existing job object and
>>>       /// GetLastError returns ERROR_ALREADY_EXISTS.  If the function
>>> fails, the return value
>>>       /// is NULL. To get extended error information, call
>>> GetLastError.</returns>
>>>       [DllImport("kernel32.dll", SetLastError = true)]
>>>       public static extern IntPtr CreateJobObject(
>>>         IntPtr jobAttributes,
>>>         string name);
>>>
>>>       /// <summary>
>>>       /// The AssignProcessToJobObject function assigns a process to an
>>> existing job object.
>>>       /// </summary>
>>>       /// <param name="jobHandle">Handle to the job object to which the
>>> process will be
>>>       /// associated.  The CreateJobObject or OpenJobObject function
>>> returns this handle.
>>>       /// The handle must have the JOB_OBJECT_ASSIGN_PROCESS access
>>> right. For more
>>>       /// information, see Job Object Security and Access
>>> Rights.</param>
>>>       /// <param name="processHandle">Handle to the process to associate
>>> with the job object.
>>>       /// The process must not already be assigned to a job. The handle
>>> must have the
>>>       /// PROCESS_SET_QUOTA and PROCESS_TERMINATE access rights. For
>>> more information,
>>>       /// see Process Security and Access Rights.</param>
>>>       /// <returns>If the function succeeds, the return value is
>>> nonzero.  If the function
>>>       /// fails, the return value is zero. To get extended error
>>> information, call
>>>       /// GetLastError.</returns>
>>>       [DllImport("kernel32.dll", SetLastError = true)]
>>>       public static extern bool AssignProcessToJobObject(
>>>         IntPtr jobHandle,
>>>         IntPtr processHandle);
>>>
>>>       /// <summary>
>>>       /// The QueryInformationJobObject function retrieves limit and job
>>> state information
>>>       /// from the job object.
>>>       /// </summary>
>>>       /// <param name="jobHandle">Handle to the job whose information is
>>> being queried.
>>>       /// The CreateJobObject or OpenJobObject function returns this
>>> handle. The handle
>>>       /// must have the JOB_OBJECT_QUERY access right. For more
>>> information, see Job
>>>       /// Object Security and Access Rights.  If this value is NULL and
>>> the calling
>>>       /// process is associated with a job, the job associated with the
>>> calling process
>>>       /// is used.</param>
>>>       /// <param name="jobObjectInfoClass">Information class for the
>>> limits to be set. This
>>>       /// parameter can be one of the following values.</param>
>>>       /// <param name="jobObjectInfo">Limit information. The format of
>>> this data
>>>       /// depends on the value of the JobObjectInfoClass
>>> parameter.</param>
>>>       /// <param name="jobObjectInfoLength">Count of the job information
>>> being queried, in
>>>       /// bytes.</param>
>>>       /// <param name="returnLength">Pointer to a variable that receives
>>> the length of data
>>>       /// written to the structure pointed to by the lpJobObjectInfo
>>> parameter. If you
>>>       /// do not want to receive this information, specify NULL.</param>
>>>       /// <returns>If the function succeeds, the return value is
>>> nonzero.  If the
>>>       /// function fails, the return value is zero. To get extended
>>> error information,
>>>       /// call GetLastError.</returns>
>>>       [DllImport("kernel32.dll", SetLastError = true)]
>>>       public static extern bool QueryInformationJobObject(
>>>         [In] IntPtr jobHandle,
>>>         [In] JobObjectInfoClass jobObjectInfoClass,
>>>         [Out] out JobObjectInfo jobObjectInfo,
>>>         [In] int jobObjectInfoLength,
>>>         [Out] out int returnLength);
>>>
>>>       /// <summary>
>>>       /// The SetInformationJobObject function sets limits for a job
>>> object.
>>>       /// </summary>
>>>       /// <param name="jobHandle">Handle to the job whose limits are
>>> being set.
>>>       /// The CreateJobObject or OpenJobObject function returns this
>>> handle. The handle must
>>>       /// have the JOB_OBJECT_SET_ATTRIBUTES access right. For more
>>> information, see Job
>>>       /// Object Security and Access Rights.</param>
>>>       /// <param name="jobObjectInfoClass">Information class for the
>>> limits to be set. This
>>>       /// parameter can be one of the following values.</param>
>>>       /// <param name="jobObjectInfo">Limits to be set for the job. The
>>> format of this data
>>>       /// depends on the value of JobObjectInfoClass.</param>
>>>       /// <param name="jobObjectInfoLength">Size of the job information
>>> being set, in
>>>       /// bytes.</param>
>>>       /// <returns>If the function succeeds, the return value is
>>> nonzero.  If the function
>>>       /// fails, the return value is zero. To get extended error
>>> information,
>>>       /// call GetLastError.</returns>
>>>       [DllImport("kernel32.dll", SetLastError = true)]
>>>       public static extern bool SetInformationJobObject(
>>>         [In] IntPtr jobHandle,
>>>         [In] JobObjectInfoClass jobObjectInfoClass,
>>>         [In] ref JobObjectInfo jobObjectInfo,
>>>         [In] int jobObjectInfoLength);
>>>
>>>       /// <summary>
>>>       /// The CloseHandle function lets us destroy a JobObject handle.
>>>       /// </summary>
>>>       /// <param name="jobHandle">Handle to the job</param>
>>>       /// <returns>If the function succeeds, the return value true.  If
>>> the function
>>>       /// fails, the return value is false. To get extended error
>>> information,
>>>       /// call GetLastError.</returns>
>>>       [DllImport("kernel32.dll", SetLastError=true)]
>>>       public static extern bool CloseHandle(
>>>         [In] IntPtr jobHandle);
>>>     }
>>>     #endregion
>>>
>>>     #region State
>>>     /// <summary>
>>>     /// This is a pseudo handle to the currently running process.
>>>     /// </summary>
>>>     static private readonly IntPtr currentProcessHandle =
>>> WinAPI.GetCurrentProcess();
>>>
>>>     /// <summary>
>>>     /// This is a pseudo handle to the "any" job object handle.
>>>     /// </summary>
>>>     static private readonly IntPtr anyJobHandle = IntPtr.Zero;
>>>
>>>     /// <summary>
>>>     /// The job created for this process.
>>>     /// </summary>
>>>     static private IntPtr currentJobHandle = IntPtr.Zero;
>>>     #endregion
>>>
>>>     #region Controlling Methods
>>>     /// <summary>
>>>     /// Checks to see if the current process is a member of any Windows
>>> job.
>>>     /// </summary>
>>>     /// <returns>True if the current process is a member of a job, false
>>> otherwise</returns>
>>>     static public bool IsCurrentProcessMemberOfAnyJob()
>>>     {
>>>       bool isInJob;
>>>       bool result;
>>>
>>>       result = WinAPI.IsProcessInJob(
>>>         currentProcessHandle,
>>>         anyJobHandle,
>>>         out isInJob);
>>>
>>>       if (!result)
>>>       {
>>>         int error = Marshal.GetLastWin32Error();
>>>         throw new ApplicationException("Failed calling IsProcessInJob,
>>> error = " + error);
>>>       }
>>>
>>>       return isInJob;
>>>     }
>>>
>>>     /// <summary>
>>>     /// Creates a new job object and assigns the current process to that
>>> job.
>>>     /// </summary>
>>>     static public void CreateNewJobForCurrentProcess()
>>>     {
>>>       IntPtr currentProcessHandle = WinAPI.GetCurrentProcess();
>>>       IntPtr newJobHandle = WinAPI.CreateJobObject(
>>>         IntPtr.Zero,
>>>         null);
>>>
>>>       if (newJobHandle == IntPtr.Zero)
>>>       {
>>>         int error = Marshal.GetLastWin32Error();
>>>         throw new ApplicationException("Failed calling CreateJobObject,
>>> error = " + error);
>>>       }
>>>
>>>       bool result = WinAPI.AssignProcessToJobObject(
>>>         newJobHandle,
>>>         currentProcessHandle);
>>>
>>>       if (!result)
>>>       {
>>>         int error = Marshal.GetLastWin32Error();
>>>         throw new ApplicationException("Failed calling
>>> AssignProcessToJobObject, error = " + error);
>>>       }
>>>
>>>       currentJobHandle = newJobHandle;
>>>     }
>>>
>>>         /// <summary>
>>>     /// Gets the current process memory limit settings for the job
>>> object the
>>>     /// current process belongs to.
>>>     /// </summary>
>>>     /// <param name="processMemoryLimit">Out parameter set to the
>>> maximum amount of
>>>     /// commit memory a single individual process in the job may consume
>>> in bytes.</param>
>>>     /// <returns>True if the limit is enforced, false
>>> otherwise.</returns>
>>>     static public bool GetProcessMemoryLimit(out ulong
>>> processMemoryLimit)
>>>     {
>>>       JobObjectInfo jobObjectInfo;
>>>       IntPtr currentJob = IntPtr.Zero;
>>>       bool result;
>>>       int returnLength;
>>>
>>>       if (Is32Bit)
>>>       {
>>>         result = WinAPI.QueryInformationJobObject(
>>>           currentJobHandle,
>>>           JobObjectInfoClass.ExtendedLimitInformation,
>>>           out jobObjectInfo,
>>>           Marshal.SizeOf(typeof(ExtendedLimits32)),
>>>           out returnLength
>>>         );
>>>
>>>         if (!result)
>>>         {
>>>           int error = Marshal.GetLastWin32Error();
>>>           throw new ApplicationException("Failed calling
>>> QueryInformationJobObject, error = " + error);
>>>         }
>>>
>>>         Trace.Assert(
>>>           returnLength == Marshal.SizeOf(typeof(ExtendedLimits32)),
>>>           "QueryInformationJobObject returned " + returnLength + " bytes
>>> instead of expected " +
>>> Marshal.SizeOf(typeof(ExtendedLimits32))
>>>         );
>>>
>>>         processMemoryLimit =
>>> jobObjectInfo.extendedLimits32.ProcessMemoryLimit;
>>>         return (jobObjectInfo.basicLimits32.LimitFlags &
>>> LimitFlags.LimitProcessMemory) != 0;
>>>       }
>>>       else
>>>       {
>>>         result = WinAPI.QueryInformationJobObject(
>>>           currentJobHandle,
>>>           JobObjectInfoClass.BasicLimitInformation,
>>>           out jobObjectInfo,
>>>           Marshal.SizeOf(typeof(BasicLimits64)),
>>>           out returnLength
>>>         );
>>>
>>>         if (!result)
>>>         {
>>>           int error = Marshal.GetLastWin32Error();
>>>           throw new ApplicationException("Failed calling
>>> QueryInformationJobObject, error = " + error);
>>>         }
>>>
>>>         result = WinAPI.QueryInformationJobObject(
>>>           currentJobHandle,
>>>           JobObjectInfoClass.ExtendedLimitInformation,
>>>           out jobObjectInfo,
>>>           Marshal.SizeOf(typeof(ExtendedLimits64)),
>>>           out returnLength
>>>         );
>>>
>>>         if (!result)
>>>         {
>>>           int error = Marshal.GetLastWin32Error();
>>>           throw new ApplicationException("Failed calling
>>> QueryInformationJobObject, error = " + error);
>>>         }
>>>
>>>         Trace.Assert(
>>>           returnLength == Marshal.SizeOf(typeof(ExtendedLimits64)),
>>>           "QueryInformationJobObject returned " + returnLength + " bytes
>>> instead of expected " +
>>> Marshal.SizeOf(typeof(ExtendedLimits64))
>>>         );
>>>
>>>         processMemoryLimit =
>>> jobObjectInfo.extendedLimits64.ProcessMemoryLimit;
>>>         return (jobObjectInfo.basicLimits64.LimitFlags &
>>> LimitFlags.LimitProcessMemory) != 0;
>>>       }
>>>     }
>>>
>>>     /// <summary>
>>>     /// Set the process memory limit in the current job to the given
>>> value.
>>>     /// </summary>
>>>     /// <param name="processMemoryLimit">The maximum amount of commit
>>> memory a
>>>     /// single individual process in the job may consume in
>>> bytes.</param>
>>>     static public void SetProcessMemoryLimit(ulong processMemoryLimit)
>>>     {
>>>       ChangeJob(ji =>
>>>         {
>>>           Trace.Assert(processMemoryLimit <= uint.MaxValue,
>>>             "Trying to set processMemoryLimit too high for a 32 bit
>>> platform");
>>>           ji.basicLimits32.LimitFlags |= LimitFlags.LimitProcessMemory;
>>>           ji.extendedLimits32.ProcessMemoryLimit =
>>> (uint)processMemoryLimit;
>>>           return ji;
>>>         },
>>>         ji =>
>>>           {
>>>             ji.basicLimits64.LimitFlags |=
>>> LimitFlags.LimitProcessMemory;
>>>             ji.extendedLimits64.ProcessMemoryLimit = processMemoryLimit;
>>>             return ji;
>>>           });
>>>     }
>>>
>>>     /// <summary>
>>>     /// change something about the job
>>>     /// </summary>
>>>     private static void ChangeJob(
>>>       Func<JobObjectInfo,JobObjectInfo> alter32,
>>>       Func<JobObjectInfo, JobObjectInfo> alter64)
>>>     {
>>>       JobObjectInfo jobObjectInfo;
>>>       IntPtr currentJob = IntPtr.Zero;
>>>       bool result;
>>>       int returnLength;
>>>
>>>       if (Is32Bit)
>>>       {
>>>         result = WinAPI.QueryInformationJobObject(
>>>           currentJobHandle,
>>>           JobObjectInfoClass.ExtendedLimitInformation,
>>>           out jobObjectInfo,
>>>           Marshal.SizeOf(typeof(ExtendedLimits32)),
>>>           out returnLength);
>>>
>>>         if (!result)
>>>         {
>>>           int error = Marshal.GetLastWin32Error();
>>>           throw new ApplicationException("Failed calling
>>> QueryInformationJobObject, error = " + error);
>>>         }
>>>
>>>         Trace.Assert(
>>>           returnLength == Marshal.SizeOf(typeof(ExtendedLimits32)),
>>>           "QueryInformationJobObject returned " + returnLength + " bytes
>>> instead of expected "
>>>           + Marshal.SizeOf(typeof(ExtendedLimits32)));
>>>         jobObjectInfo = alter32(jobObjectInfo);
>>>
>>>         result = WinAPI.SetInformationJobObject(
>>>           currentJobHandle,
>>>           JobObjectInfoClass.ExtendedLimitInformation,
>>>           ref jobObjectInfo,
>>>           Marshal.SizeOf(typeof(ExtendedLimits32)));
>>>
>>>         if (!result)
>>>         {
>>>           int error = Marshal.GetLastWin32Error();
>>>           var hresult = Marshal.GetHRForLastWin32Error();
>>>           throw new ApplicationException("Failed calling
>>> SetInformationJobObject, error = " + error
>>>             +" hresult = "+ hresult);
>>>         }
>>>       }
>>>       else
>>>       {
>>>         result = WinAPI.QueryInformationJobObject(
>>>           currentJobHandle,
>>>           JobObjectInfoClass.ExtendedLimitInformation,
>>>           out jobObjectInfo,
>>>           Marshal.SizeOf(typeof(ExtendedLimits64)),
>>>           out returnLength);
>>>
>>>         if (!result)
>>>         {
>>>           int error = Marshal.GetLastWin32Error();
>>>           throw new ApplicationException("Failed calling
>>> QueryInformationJobObject, error = " + error);
>>>         }
>>>
>>>         Trace.Assert(
>>>           returnLength == Marshal.SizeOf(typeof(ExtendedLimits64)),
>>>           "QueryInformationJobObject returned " + returnLength + " bytes
>>> instead of expected "
>>>             + Marshal.SizeOf(typeof(ExtendedLimits64)));
>>>
>>>         jobObjectInfo = alter64(jobObjectInfo);
>>>
>>>         result = WinAPI.SetInformationJobObject(
>>>           currentJobHandle,
>>>           JobObjectInfoClass.ExtendedLimitInformation,
>>>           ref jobObjectInfo,
>>>           Marshal.SizeOf(typeof(ExtendedLimits64)));
>>>
>>>         if (!result)
>>>         {
>>>           int error = Marshal.GetLastWin32Error();
>>>           throw new ApplicationException("Failed calling
>>> SetInformationJobObject, error = " + error);
>>>         }
>>>       }
>>>     }
>>>     #endregion
>>>
>>>     #region specific utility methods
>>>
>>>     /// <summary>
>>>     /// limits the memory usage of this process (and any children) to a
>>> fixed value
>>>     /// Also locks the processor affinity of all child processes to the
>>> same as the current process
>>>     /// </summary>
>>>     /// <param name="jobMemoryLimit">set to a non zero value in bytes to
>>> limit usage</param>
>>>     public static void StandardJobLimits(
>>>       ulong jobMemoryLimit)
>>>     {
>>>       if (IsCurrentProcessMemberOfAnyJob())
>>>         throw new InvalidOperationException("this process is already
>>> under the control of a job object!");
>>>       CreateNewJobForCurrentProcess();
>>>       SetJobMemoryLimit(jobMemoryLimit);
>>>       LinkAffinityToCurrent();
>>>     }
>>>
>>>     /// <summary>
>>>     /// limits the memory usage of this process (and any children) to a
>>> fixed value
>>>     /// </summary>
>>>     /// <param name="jobMemoryLimit">set to a non zero value in bytes to
>>> limit usage</param>
>>>     public static void SetJobMemoryLimit(
>>>       ulong jobMemoryLimit)
>>>     {
>>>       if (jobMemoryLimit > 0)
>>>       {
>>>         ChangeJob(
>>>           ji =>
>>>             {
>>>               ji.basicLimits32.LimitFlags |= LimitFlags.LimitJobMemory;
>>>               ji.extendedLimits32.JobMemoryLimit = (uint)jobMemoryLimit;
>>>               return ji;
>>>             },
>>>           ji =>
>>>             {
>>>               ji.basicLimits64.LimitFlags |= LimitFlags.LimitJobMemory;
>>>               ji.extendedLimits64.JobMemoryLimit = jobMemoryLimit;
>>>               return ji;
>>>             });
>>>       }
>>>     }
>>>
>>>     /// <summary>
>>>     /// locks the processor affinity of all child processes to the same
>>> as the current process
>>>     /// </summary>
>>>     public static void LinkAffinityToCurrent()
>>>     {
>>>       ChangeJob(
>>>         ji =>
>>>           {
>>>             ji.basicLimits32.LimitFlags |= LimitFlags.LimitAffinity;
>>>             ji.basicLimits32.Affinity =
>>> Process.GetCurrentProcess().ProcessorAffinity;
>>>             return ji;
>>>           },
>>>         ji =>
>>>           {
>>>             ji.basicLimits64.LimitFlags |= LimitFlags.LimitAffinity;
>>>             ji.basicLimits64.Affinity =
>>> System.Diagnostics.Process.GetCurrentProcess().ProcessorAffinity;
>>>             return ji;
>>>           });
>>>     }
>>>   #endregion
>>>   }
>>> }
>>>
>>>
>>> -----Original Message-----
>>> From: condor-users-bounces@xxxxxxxxxxx
>>> [mailto:condor-users-bounces@xxxxxxxxxxx] On Behalf Of Matthew Farrellee
>>> Sent: 24 June 2009 22:26
>>> To: Condor-Users Mail List
>>> Subject: Re: [Condor-users] Condor and processor affinity
>>>
>>> Ian Stokes-Rees wrote:
>>>> There were some posts at the start of the year from Ian Chesal and Matt
>>>> Hope about setting processor affinity in jobs.  Can anyone say if there
>>>> is some way to get Condor to do this automatically now?  And in any
>>>> case, can anyone comment on whether this is still important?  My
>>>> understanding was that kernel scheduling should handle this pretty
>>>> well.
>>>>
>>>> Ian
>>> http://www.cs.wisc.edu/condor/manual/v7.3/3_3Configuration.html#param:EnforceCpuAffinity
>>>
>>> Kernel scheduling won't keep you from using all cores on a node if you
>>> decide to fork a few times.
>>>
>>> Best,
>>>
>>>
>>> matt
>>> _______________________________________________
>>> Condor-users mailing list
>>> To unsubscribe, send a message to condor-users-request@xxxxxxxxxxx with
>>> a
>>> subject: Unsubscribe
>>> You can also unsubscribe by visiting
>>> https://lists.cs.wisc.edu/mailman/listinfo/condor-users
>>>
>>> The archives can be found at:
>>> https://lists.cs.wisc.edu/archive/condor-users/
>>>
>>> ----
>>> Gloucester Research Limited believes the information provided herein is
>>> reliable. While every care has been taken to ensure accuracy, the
>>> information is furnished to the recipients with no warranty as to the
>>> completeness and accuracy of its contents and on condition that any
>>> errors or omissions shall not be made the basis for any claim, demand or
>>> cause for action.
>>> The information in this email is intended only for the named recipient.
>>> If you are not the intended recipient please notify us immediately and
>>> do not copy, distribute or take action based on this e-mail.
>>> All messages sent to and from this email address will be logged by
>>> Gloucester Research Ltd and are subject to archival storage, monitoring,
>>> review and disclosure.
>>> Gloucester Research Limited, 5th Floor, Whittington House, 19-30 Alfred
>>> Place, London WC1E 7EA.
>>> Gloucester Research Limited is a company registered in England and Wales
>>> with company number 04267560.
>>> ----
>>>
>>> _______________________________________________
>>> Condor-users mailing list
>>> To unsubscribe, send a message to condor-users-request@xxxxxxxxxxx with
>>> a
>>> subject: Unsubscribe
>>> You can also unsubscribe by visiting
>>> https://lists.cs.wisc.edu/mailman/listinfo/condor-users
>>>
>>> The archives can be found at:
>>> https://lists.cs.wisc.edu/archive/condor-users/
>>
>
>

_______________________________________________
Condor-users mailing list
To unsubscribe, send a message to condor-users-request@xxxxxxxxxxx with a
subject: Unsubscribe
You can also unsubscribe by visiting
https://lists.cs.wisc.edu/mailman/listinfo/condor-users

The archives can be found at:
https://lists.cs.wisc.edu/archive/condor-users/