Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 69 additions & 68 deletions JobObject.Net/Job.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,16 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Stormancer.JobManagement
{
public class Job : IDisposable
{
private const int JobObjectExtendedLimitInformation = 9;
private const int CREATE_SUSPENDED = 0x00000004;

private const int INFINITE = -1;
private const int GWL_STYLE = -16;
private const int WS_CHILD = 0x40000000;
private const int WS_POPUP = unchecked((int)0x80000000);
private const int HWND_MESSAGE = -3;







[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr CreateJobObject(IntPtr a, string lpName);

[DllImport("kernel32.dll")]
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);

[DllImport("kernel32.dll", SetLastError = true)]
Expand All @@ -36,13 +22,39 @@ public class Job : IDisposable
public Job()
{
handle = CreateJobObject(IntPtr.Zero, null);
if (handle == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Job object already exists.");
}

UpdateMemoryLimit(null, null, null);
}

~Job()
{
Dispose(false);
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

private void Dispose(bool disposing)
{
if (disposed)
return;

Interop.CloseHandle(handle);
handle = IntPtr.Zero;
disposed = true;
}

public void UpdateMemoryLimit(uint? minPrcWorkingSet, uint? maxPrcWorkingSet, uint? maxJobVirtualMemory)
{
if (disposed) throw new ObjectDisposedException("Job is already disposed.");

var flags = JOBOBJECT_BASIC_LIMIT_FLAGS.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
var basicLimits = new JOBOBJECT_BASIC_LIMIT_INFORMATION();
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
Expand All @@ -58,26 +70,30 @@ public void UpdateMemoryLimit(uint? minPrcWorkingSet, uint? maxPrcWorkingSet, ui

}

if(maxJobVirtualMemory.HasValue)
if (maxJobVirtualMemory.HasValue)
{
flags |= JOBOBJECT_BASIC_LIMIT_FLAGS.JOB_OBJECT_LIMIT_JOB_MEMORY;
extendedInfo.JobMemoryLimit = (UIntPtr)maxJobVirtualMemory.Value;
}


basicLimits.LimitFlags = (uint)flags;

extendedInfo.BasicLimitInformation = basicLimits;

int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);

if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length))
try
{
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to set extended job information.");
}
}
finally
{
Marshal.FreeHGlobal(extendedInfoPtr);
throw new Exception(string.Format("Unable to set extended information. Error: {0}", Marshal.GetLastWin32Error()));
}
Marshal.FreeHGlobal(extendedInfoPtr);

}

/// <summary>
Expand All @@ -86,69 +102,52 @@ public void UpdateMemoryLimit(uint? minPrcWorkingSet, uint? maxPrcWorkingSet, ui
/// <param name="value">The limit in total CPU percent.</param>
public void UpdateCpuRateLimit(double value)
{
if (disposed) throw new ObjectDisposedException("Job is already disposed.");

var cpuRateInfo = new JOBOBJECT_CPU_RATE_CONTROL_INFORMATION
{
ControlFlags = 1 | 4,
CpuRate = (uint)(value * 100)
};
var length = Marshal.SizeOf(typeof(JOBOBJECT_CPU_RATE_CONTROL_INFORMATION));
IntPtr cpuRateInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(cpuRateInfo, cpuRateInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.JobObjectCpuRateControlInformation, cpuRateInfoPtr, (uint)length))
try
{
Marshal.StructureToPtr(cpuRateInfo, cpuRateInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.JobObjectCpuRateControlInformation, cpuRateInfoPtr, (uint)length))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to set cpu rate job information.");
}
}
finally
{
Marshal.FreeHGlobal(cpuRateInfoPtr);
throw new Exception(string.Format("Unable to set cpu rate information. Error: {0}", Marshal.GetLastWin32Error()));

}
Marshal.FreeHGlobal(cpuRateInfoPtr);

}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

private void Dispose(bool disposing)
{
if (disposed)
return;

if (disposing) { }

Close();
disposed = true;
}

public void Close()
{
Interop.CloseHandle(handle);
handle = IntPtr.Zero;
}

public bool AddProcess(IntPtr processHandle)
public void AddProcess(IntPtr processHandle)
{
return AssignProcessToJobObject(handle, processHandle);
if (!AssignProcessToJobObject(handle, processHandle))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to add process to JobObject.");
}
}

public bool AddProcess(int processId)
public void AddProcess(int processId)
{
return AddProcess(System.Diagnostics.Process.GetProcessById(processId));
AddProcess(Process.GetProcessById(processId));
}

public bool AddProcess(System.Diagnostics.Process process)
public void AddProcess(Process process)
{
return AddProcess(process.Handle);
AddProcess(process.Handle);
}



}

#region Helper classes

[StructLayout(LayoutKind.Sequential)]
struct IO_COUNTERS
internal class IO_COUNTERS
{
public UInt64 ReadOperationCount;
public UInt64 WriteOperationCount;
Expand All @@ -168,7 +167,7 @@ internal enum JOBOBJECT_BASIC_LIMIT_FLAGS

}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_LIMIT_INFORMATION
internal class JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public Int64 PerProcessUserTimeLimit;
public Int64 PerJobUserTimeLimit;
Expand All @@ -182,15 +181,15 @@ struct JOBOBJECT_BASIC_LIMIT_INFORMATION
}

[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
internal class SECURITY_ATTRIBUTES
{
public UInt32 nLength;
public IntPtr lpSecurityDescriptor;
public Int32 bInheritHandle;
}

[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
internal class JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
Expand All @@ -200,13 +199,15 @@ struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
public UIntPtr PeakJobMemoryUsed;
}

struct JOBOBJECT_CPU_RATE_CONTROL_INFORMATION
[StructLayout(LayoutKind.Sequential)]
internal class JOBOBJECT_CPU_RATE_CONTROL_INFORMATION
{
public UInt32 ControlFlags;
public UInt32 CpuRate;

}
public enum JobObjectInfoType

internal enum JobObjectInfoType
{
AssociateCompletionPortInformation = 7,
BasicLimitInformation = 2,
Expand Down
Loading