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

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/
>>
> 
>