public async Task <ExecutionOutputResult> ExecuteAsync( string sessionId, byte[] assemblyBytes, byte[] outputBufferBytes, bool includePerformance, CancellationToken cancellationToken ) { // Note that _containers are never accessed through multiple threads for the same session id, // so atomicity is not required within same session id using var allocationCancellation = CancellationFactory.ContainerAllocation(cancellationToken); if (_containerPool.GetSessionContainer(sessionId) is not { } container) { if (_crashSuspensionManager.GetSuspension(sessionId, outputBufferBytes) is {} suspension) { return(suspension); } try { container = await _containerPool.AllocateSessionContainerAsync(sessionId, _cleanupWorker.QueueForCleanup, allocationCancellation.Token); } catch (OperationCanceledException ex) { throw new ContainerAllocationException("Failed to allocate container within 5 seconds.", _containerPool.LastContainerPreallocationException ?? ex); } await _dockerClient.Containers.RenameContainerAsync(container.ContainerId, new ContainerRenameParameters { NewName = _containerNameFormat.GenerateSessionContainerName(sessionId) }, cancellationToken); } var result = await _executionProcessor.ExecuteInContainerAsync( container, assemblyBytes, outputBufferBytes, includePerformance, cancellationToken ); if (!result.IsOutputReadSuccess) { var containerCrashed = false; try { var response = await _dockerClient.Containers.InspectContainerAsync(container.ContainerId, cancellationToken); containerCrashed = !response.State.Running; } catch (DockerContainerNotFoundException) { containerCrashed = true; } if (containerCrashed) { _containerPool.RemoveSessionContainer(sessionId); } return(_crashSuspensionManager.SetSuspension(sessionId, result)); } return(result); }
private async Task <ActiveContainer> CreateAndStartContainerAsync(CancellationToken cancellationToken) { var containerName = _containerNameFormat.GeneratePreallocatedName(); _logger.LogDebug($"Allocating container {containerName}"); var containerId = (await _dockerClient.Containers.CreateContainerAsync(new CreateContainerParameters { Name = containerName, Image = "mcr.microsoft.com/dotnet/runtime:5.0", Cmd = new[] { @"c:\\app\SharpLab.Container.exe" }, AttachStdout = true, AttachStdin = true, OpenStdin = true, StdinOnce = true, NetworkDisabled = true, HostConfig = new HostConfig { Isolation = "process", Mounts = new[] { new Mount { Source = AppDomain.CurrentDomain.BaseDirectory, Target = @"c:\app", Type = "bind", ReadOnly = true } }, Memory = 100 * 1024 * 1024, CPUQuota = 50000, AutoRemove = true } }, cancellationToken)).ID; MultiplexedStream?stream = null; ActiveContainer container; try { stream = await _dockerClient.Containers.AttachContainerAsync(containerId, tty : false, new ContainerAttachParameters { Stream = true, Stdin = true, Stdout = true }, cancellationToken); await _dockerClient.Containers.StartContainerAsync(containerId, new ContainerStartParameters(), cancellationToken); container = new ActiveContainer(containerId, stream); var outputBuffer = ArrayPool <byte> .Shared.Rent(2048); try { var result = await _warmupExecutionProcessor.ExecuteInContainerAsync( container, _warmupAssemblyBytes, outputBuffer, includePerformance : false, cancellationToken ); if (!result.IsOutputReadSuccess) { throw new Exception($"Warmup output failed for container {containerName}:\r\n" + Encoding.UTF8.GetString(result.Output.Span) + Encoding.UTF8.GetString(result.OutputReadFailureMessage.Span)); } } finally { ArrayPool <byte> .Shared.Return(outputBuffer); } _logger.LogDebug($"Allocated container {containerName}"); } catch { _containerCleanup.QueueForCleanup(containerId, stream); throw; } return(container); }