/// <summary> /// Construct a <see cref="SessionController"/> /// </summary> /// <param name="reattachInformation">The value of <see cref="reattachInformation"/></param> /// <param name="process">The value of <see cref="process"/></param> /// <param name="byondLock">The value of <see cref="byondLock"/></param> /// <param name="byondTopicSender">The value of <see cref="byondTopicSender"/></param> /// <param name="bridgeRegistrar">The <see cref="IBridgeRegistrar"/> used to populate <see cref="bridgeRegistration"/>.</param> /// <param name="chat">The value of <see cref="chat"/></param> /// <param name="chatTrackingContext">The value of <see cref="chatTrackingContext"/></param> /// <param name="assemblyInformationProvider">The <see cref="IAssemblyInformationProvider"/> for the <see cref="SessionController"/>.</param> /// <param name="logger">The value of <see cref="logger"/></param> /// <param name="startupTimeout">The optional time to wait before failing the <see cref="LaunchResult"/></param> /// <param name="reattached">If this is a reattached session.</param> public SessionController( ReattachInformation reattachInformation, IProcess process, IByondExecutableLock byondLock, ITopicClient byondTopicSender, IChatTrackingContext chatTrackingContext, IBridgeRegistrar bridgeRegistrar, IChatManager chat, IAssemblyInformationProvider assemblyInformationProvider, ILogger <SessionController> logger, uint?startupTimeout, bool reattached) { this.reattachInformation = reattachInformation ?? throw new ArgumentNullException(nameof(reattachInformation)); this.process = process ?? throw new ArgumentNullException(nameof(process)); this.byondLock = byondLock ?? throw new ArgumentNullException(nameof(byondLock)); this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); this.chatTrackingContext = chatTrackingContext ?? throw new ArgumentNullException(nameof(chatTrackingContext)); bridgeRegistration = bridgeRegistrar?.RegisterHandler(this) ?? throw new ArgumentNullException(nameof(bridgeRegistrar)); this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); this.chatTrackingContext.SetChannelSink(this); portClosedForReboot = false; disposed = false; apiValidationStatus = ApiValidationStatus.NeverValidated; released = false; rebootTcs = new TaskCompletionSource <object>(); primeTcs = new TaskCompletionSource <object>(); reattachTopicCts = new CancellationTokenSource(); synchronizationLock = new object(); _ = process.Lifetime.ContinueWith( x => { if (!disposed) { reattachTopicCts.Cancel(); } chatTrackingContext.Active = false; }, TaskScheduler.Current); LaunchResult = GetLaunchResult( assemblyInformationProvider, startupTimeout, reattached); logger.LogDebug("Created session controller. Primary: {0}, CommsKey: {1}, Port: {2}", IsPrimary, reattachInformation.AccessIdentifier, Port); }
/// <summary> /// Construct a <see cref="SessionController"/> /// </summary> /// <param name="reattachInformation">The value of <see cref="reattachInformation"/></param> /// <param name="metadata">The owning <see cref="Instance"/>.</param> /// <param name="process">The value of <see cref="process"/></param> /// <param name="byondLock">The value of <see cref="byondLock"/></param> /// <param name="byondTopicSender">The value of <see cref="byondTopicSender"/></param> /// <param name="bridgeRegistrar">The <see cref="IBridgeRegistrar"/> used to populate <see cref="bridgeRegistration"/>.</param> /// <param name="chat">The value of <see cref="chat"/></param> /// <param name="chatTrackingContext">The value of <see cref="chatTrackingContext"/></param> /// <param name="assemblyInformationProvider">The <see cref="IAssemblyInformationProvider"/> for the <see cref="SessionController"/>.</param> /// <param name="logger">The value of <see cref="logger"/></param> /// <param name="postLifetimeCallback">The <see cref="Func{TResult}"/> returning a <see cref="Task"/> to be run after the <paramref name="process"/> ends.</param> /// <param name="startupTimeout">The optional time to wait before failing the <see cref="LaunchResult"/></param> /// <param name="reattached">If this is a reattached session.</param> /// <param name="apiValidate">If this is a DMAPI validation session.</param> public SessionController( ReattachInformation reattachInformation, Api.Models.Instance metadata, IProcess process, IByondExecutableLock byondLock, ITopicClient byondTopicSender, IChatTrackingContext chatTrackingContext, IBridgeRegistrar bridgeRegistrar, IChatManager chat, IAssemblyInformationProvider assemblyInformationProvider, ILogger <SessionController> logger, Func <Task> postLifetimeCallback, uint?startupTimeout, bool reattached, bool apiValidate) { this.reattachInformation = reattachInformation ?? throw new ArgumentNullException(nameof(reattachInformation)); this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); this.process = process ?? throw new ArgumentNullException(nameof(process)); this.byondLock = byondLock ?? throw new ArgumentNullException(nameof(byondLock)); this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); this.chatTrackingContext = chatTrackingContext ?? throw new ArgumentNullException(nameof(chatTrackingContext)); if (bridgeRegistrar == null) { throw new ArgumentNullException(nameof(bridgeRegistrar)); } this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); portClosedForReboot = false; disposed = false; apiValidationStatus = ApiValidationStatus.NeverValidated; released = false; rebootTcs = new TaskCompletionSource <object>(); primeTcs = new TaskCompletionSource <object>(); initialBridgeRequestTcs = new TaskCompletionSource <object>(); reattachTopicCts = new CancellationTokenSource(); synchronizationLock = new object(); if (apiValidate || DMApiAvailable) { bridgeRegistration = bridgeRegistrar.RegisterHandler(this); this.chatTrackingContext.SetChannelSink(this); } else { logger.LogTrace( "Not registering session with {0} DMAPI version for interop!", reattachInformation.Dmb.CompileJob.DMApiVersion == null ? "no" : $"incompatible ({reattachInformation.Dmb.CompileJob.DMApiVersion})"); } async Task <int> WrapLifetime() { var exitCode = await process.Lifetime.ConfigureAwait(false); await postLifetimeCallback().ConfigureAwait(false); return(exitCode); } Lifetime = WrapLifetime(); LaunchResult = GetLaunchResult( assemblyInformationProvider, startupTimeout, reattached, apiValidate); logger.LogDebug( "Created session controller. CommsKey: {0}, Port: {1}", reattachInformation.AccessIdentifier, reattachInformation.Port); }