Skip to content

Instantly share code, notes, and snippets.

@rajibchy
Created June 5, 2021 16:07
Show Gist options
  • Select an option

  • Save rajibchy/760ac9c3264ae79d311524638f680789 to your computer and use it in GitHub Desktop.

Select an option

Save rajibchy/760ac9c3264ae79d311524638f680789 to your computer and use it in GitHub Desktop.
Thread-safe Concurrent Process Manager
/// <![CDATA[copyright]]>
/// Copyright (c) 2018, Sow ( https://safeonline.world, https://www.facebook.com/safeonlineworld). (https://github.com/RKTUXYN) All rights reserved.
/// Copyrights licensed under the New BSD License.
/// See the accompanying LICENSE file for terms.
/// <![CDATA[copyright]]>
/// <![CDATA[Author]]>
/// By Rajib Chy
/// On 5/27/2021 7:56:04 PM
/// <![CDATA[Author]]>
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Diagnostics;
using System.Globalization;
namespace Sow.Framework {
public abstract class ProcInfo {
public string Index { get; set; }
public string LibDirectory { get; set; }
public string WorkingDirectory { get; set; }
public bool ReadStandardOutput { get; set; }
public string FileName { get; set; }
public string Arguments { get; set; }
}
public interface IProcManager<TProcInfo> {
Action<TProcInfo, string> OnData { get; set; }
Action<TProcInfo, string> OnError { get; set; }
Action<TProcInfo> OnExit { get; set; }
Action<Exception> OnInternalError { get; set; }
bool IsRunning( string index );
void KillProcess( string index );
void OpenProcess( TProcInfo procInfo );
decimal GetProcMemoryUsages( string index );
void Dispose( );
}
public static class ProcHelp {
public static void RunGC( ) {
GC.Collect( );
GC.WaitForPendingFinalizers( );
}
public static Process GetProc( int pid ) {
if ( pid < 0 ) return null;
Process[] processlist = Process.GetProcesses( );
return processlist.FirstOrDefault( pr => pr.Id == pid );
}
public static decimal GetAppMemoryUsages( int id ) {
Process proc = GetProc( id );
if ( proc == null ) return -1;
return GetAppMemoryUsages( proc );
}
/// <summary>
/// If argument proc is null then return the current app memory allocation MB
/// </summary>
/// <param name="proc"></param>
/// <returns>decimal</returns>
public static decimal GetAppMemoryUsages( Process proc = null ) {
if ( proc == null ) {
proc = Process.GetCurrentProcess( );
}
proc.Refresh( );
decimal memory = ( ( decimal )proc.WorkingSet64 ) / ( 1024 * 1024 );
proc.Dispose( );
return Math.Round( memory, 2 );
}
public static string GetAppMemoryUsages( string procName ) {
try {
ProcessStartInfo ps = new ProcessStartInfo( "tasklist" ) {
Arguments = "/fi \"IMAGENAME eq " + procName + ".*\" /FO CSV /NH",
RedirectStandardOutput = true,
CreateNoWindow = true,
UseShellExecute = false
};
using ( Process p = Process.Start( ps ) ) {
if ( p.WaitForExit( 1000 ) ) {
var s = p.StandardOutput.ReadToEnd( ).Split( '\"' );
return s[ 9 ].Replace( "\"", "" );
}
}
} catch { }
return "Unable to get memory usage";
}
public static void RestartApp(string arguments ) {
// "D:\project\sow-trade-plus-hook\native\Workstation\TradeFix\bin\Release\TradeFix.exe" www.google.com
if(string.IsNullOrEmpty( arguments ) ) {
string[] args = Environment.CommandLine.Split( new char[] { ' ' } );
arguments = string.Empty;
if ( args.Length > 0 ) {
arguments = args[ 1 ];
}
}
using ( Process proc = new Process( ) ) {
proc.StartInfo.FileName = System.Reflection.Assembly.GetEntryAssembly( ).Location;
proc.StartInfo.WorkingDirectory = Directory.GetCurrentDirectory( );
proc.StartInfo.Arguments = arguments;
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.ErrorDialog = false;
proc.StartInfo.UseShellExecute = false;
proc.Start( );
proc.WaitForExit( 100 );
}
}
}
public class ProcManager<TProcInfo> : IProcManager<TProcInfo> where TProcInfo : ProcInfo {
readonly ConcurrentStorage<string, int> _procs;
readonly ConcurrentStorage<string, TProcInfo> _procInfos;
readonly ConcurrentStorage<string, Thread> _threadProc;
public Action<TProcInfo, string> OnData { get; set; }
public Action<TProcInfo, string> OnError { get; set; }
public Action<TProcInfo> OnExit { get; set; }
public Action<Exception> OnInternalError { get; set; }
public ProcManager( ) {
_procs = new ConcurrentStorage<string, int>( );
_threadProc = new ConcurrentStorage<string, Thread>( );
_procInfos = new ConcurrentStorage<string, TProcInfo>( );
}
public decimal GetProcMemoryUsages( string index ) {
_procInfos.TryGet( index, out TProcInfo procInfo );
if ( procInfo == null ) return -1;
decimal memory = -1;
if ( _procs.TryGet( index, out int id ) ) {
memory = ProcHelp.GetAppMemoryUsages( id );
}
return memory;
}
private void UpdateProcInfo( TProcInfo procInfo ) {
if ( string.IsNullOrEmpty( procInfo.LibDirectory ) ) {
procInfo.LibDirectory = procInfo.WorkingDirectory;
}
if ( string.IsNullOrEmpty( procInfo.Index ) ) {
procInfo.Index = Guid.NewGuid( ).ToString( "N" );
}
}
private void FireAndForget( TProcInfo procInfo ) {
using ( Process proc = new Process( ) ) {
proc.StartInfo.FileName = Path.Combine( procInfo.LibDirectory, procInfo.FileName );
proc.StartInfo.WorkingDirectory = procInfo.WorkingDirectory;
proc.StartInfo.Arguments = procInfo.Arguments;
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.ErrorDialog = false;
proc.StartInfo.UseShellExecute = false;
proc.Start( );
_procs.TryAdd( procInfo.Index, proc.Id );
proc.WaitForExit( 1000 );
}
}
public void OpenProcess( TProcInfo procInfo ) {
UpdateProcInfo( procInfo ); KillProcess( procInfo.Index );
_procInfos.TryAdd( procInfo.Index, procInfo );
if ( procInfo.ReadStandardOutput ) {
ReadStandardOutput( procInfo );
} else {
FireAndForget( procInfo );
}
return;
}
public bool IsRunning( string index ) {
_procInfos.TryGet( index, out TProcInfo procInfo );
if ( procInfo == null ) return false;
_procInfos.TryRemove( index );
if ( !_procs.ContainsKey( index ) ) return false;
if ( procInfo.ReadStandardOutput == true ) {
if ( !_threadProc.ContainsKey( index ) ) return false;
}
if ( _procs.TryGet( index, out int id ) ) {
if ( ProcHelp.GetProc( id ) != null ) return true;
}
return false;
}
public void KillProcess( string index ) {
if ( !_procInfos.TryGet( index, out TProcInfo procInfo ) ) return;
if ( procInfo == null ) return;
_ = _procInfos.TryRemove( index );
if ( _procs.TryGet( index, out int id ) ) {
try {
Process process = ProcHelp.GetProc( id );
if ( process != null ) {
process.Kill( );
}
} catch ( Exception e ) {
OnInternalError?.Invoke( e );
} finally {
_procs.TryRemove( index );
}
}
if ( procInfo.ReadStandardOutput == false ) return;
if ( _threadProc.TryGet( index, out Thread th ) ) {
try {
th.Abort( 100 ); th.Join( );
} catch ( ThreadAbortException e ) {
OnInternalError?.Invoke( e );
} finally {
_threadProc.TryRemove( index );
}
}
}
protected void ReadStandardOutput( TProcInfo procInfo ) {
Thread thread = new Thread( new ParameterizedThreadStart( Spwan ) ) {
IsBackground = true, Priority = ThreadPriority.Normal, Name = procInfo.FileName
};
thread.SetApartmentState( ApartmentState.STA );
_threadProc.TryAdd( procInfo.Index, thread );
thread.Start( procInfo );
}
protected void Spwan( object _procInfo ) {
TProcInfo procInfo = ( TProcInfo )_procInfo;
try {
using ( Process process = new Process {
StartInfo = new ProcessStartInfo {
Arguments = procInfo.Arguments,
WorkingDirectory = procInfo.WorkingDirectory,
FileName = Path.Combine( procInfo.LibDirectory, procInfo.FileName ),
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
}
} ) {
process.OutputDataReceived += ( sender, args ) => OnData.Invoke( procInfo, args.Data );
process.ErrorDataReceived += ( sender, args ) => OnData.Invoke( procInfo, args.Data );
process.Start( );
_procs.TryAdd( procInfo.Index, process.Id );
process.BeginOutputReadLine( );
process.BeginErrorReadLine( );
process.WaitForExit( ); //you need this in order to flush the output buffer
}
} catch ( Exception e ) {
OnInternalError?.Invoke( e );
} finally {
OnExit?.Invoke( procInfo );
}
}
protected void Clean<TKey, TValue>( ConcurrentStorage<TKey, TValue> storage, Action<TKey> next ) {
if ( storage.Count > 0 ) {
storage.Each( ( key, proc ) => next( key ) );
storage.Clear( );
}
}
public void Dispose( ) {
Clean( _procInfos, KillProcess );
_threadProc.Clear( ); _procs.Clear( );
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment