Class EmitWorkerPool
- Namespace
- Utils.Reflection.Reflection.Emit
- Assembly
- Utils.Reflection.dll
Maps several native interfaces onto a single shared isolated Emit worker process, instead of the one-process-per-interface cost of Emit<TInterface>(string, CallingConvention, TimeSpan?, TimeSpan?).
public sealed class EmitWorkerPool : IDisposable
- Inheritance
-
EmitWorkerPool
- Implements
- Inherited Members
- Extension Methods
Remarks
Emit<TInterface>(string, CallingConvention, TimeSpan?, TimeSpan?) re-launches the current executable as a brand new
sandboxed worker process every time it is called — a full CLR startup per mapped interface. An
application mapping several distinct native DLLs (or the same DLL through several interfaces) pays
that cost once per Emit call. EmitWorkerPool starts the worker once, on the
first Emit<TInterface>(string, CallingConvention) call, and reuses it for every subsequent call on the same pool
instance: each interface gets its own handle on the shared worker
(LoadInterface(Type, string, CallingConvention, TimeSpan)), so calls on one interface are never misrouted to
another.
Trade-off — this is an opt-in, not the default. Sharing a worker process trades away some of the isolation between the interfaces mapped through it: a crash or a hostile/misbehaving interface loaded on the shared worker can take down every other interface loaded on the same pool, where Emit<TInterface>(string, CallingConvention, TimeSpan?, TimeSpan?)'s one-worker-per-interface default keeps failures contained to a single interface. Use a pool when the interfaces mapped through it come from a common trust boundary (for example, several DLLs from the same vendor/build) and the reduced process-spawn cost matters; keep using separate Emit<TInterface>(string, CallingConvention, TimeSpan?, TimeSpan?) calls when interfaces need to be isolated from each other as well as from the calling process.
Disposing a proxy returned by Emit<TInterface>(string, CallingConvention) releases only that interface's resources on the shared worker; the worker process itself, and any other interface loaded through this pool, keeps running. Dispose the pool itself to shut the worker process down.
When the shared worker becomes unhealthy (faulted, retired after too many abandoned calls, or externally killed), the pool automatically replaces it with a fresh worker the next time Emit<TInterface>(string, CallingConvention) is called. Proxies bound to the old worker remain invalid and will throw InvalidOperationException when invoked.
Constructors
EmitWorkerPool(TimeSpan?, TimeSpan?)
Creates an empty pool. The shared worker process is not started until the first Emit<TInterface>(string, CallingConvention) call.
public EmitWorkerPool(TimeSpan? loadTimeout = null, TimeSpan? callTimeout = null)
Parameters
loadTimeoutTimeSpan?Maximum time to wait for the shared worker's response to each Emit<TInterface>(string, CallingConvention) call's load request. Defaults to Utils.Reflection.Reflection.Emit.EmitWorkerProcess.DefaultLoadTimeout when null. When provided, must be a positive finite duration not exceeding approximately 24.9 days.
callTimeoutTimeSpan?Maximum time to wait for the shared worker's response to each native call forwarded through any proxy returned by this pool. Defaults to Utils.Reflection.Reflection.Emit.EmitWorkerProcess.DefaultCallTimeout when null. When provided, must be a positive finite duration not exceeding approximately 24.9 days.
Exceptions
- ArgumentOutOfRangeException
Thrown when either timeout is provided but is zero, negative, or exceeds the maximum supported range. Validation happens in the constructor so that callers discover invalid values before any worker process is spawned.
Methods
Dispose()
Shuts down the shared worker process, invalidating every proxy previously returned by Emit<TInterface>(string, CallingConvention) on this pool.
public void Dispose()
Emit<TInterface>(string, CallingConvention)
Maps dllPath to TInterface on this pool's shared
worker, starting the worker first if this is the first call on this pool instance, or replacing
a faulted worker with a fresh one if the previous worker became unhealthy.
public TInterface Emit<TInterface>(string dllPath, CallingConvention callingConvention) where TInterface : class, IDisposable
Parameters
dllPathstringThe path to the DLL.
callingConventionCallingConventionThe calling convention of the functions.
Returns
- TInterface
A proxy implementing
TInterfacethat forwards every call to the shared worker.
Type Parameters
TInterfaceThe interface that defines the functions to map.
Exceptions
- ObjectDisposedException
Thrown when the pool has already been disposed.
- NotSupportedException
Thrown when
TInterfaceuses a type that cannot cross a process boundary.