Table of Contents

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

loadTimeout TimeSpan?

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.

callTimeout TimeSpan?

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

dllPath string

The path to the DLL.

callingConvention CallingConvention

The calling convention of the functions.

Returns

TInterface

A proxy implementing TInterface that forwards every call to the shared worker.

Type Parameters

TInterface

The interface that defines the functions to map.

Exceptions

ObjectDisposedException

Thrown when the pool has already been disposed.

NotSupportedException

Thrown when TInterface uses a type that cannot cross a process boundary.