|
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license.
|
3 | 3 |
|
4 | 4 | using System;
|
| 5 | +using System.Collections.Concurrent; |
5 | 6 | using System.Collections.Generic;
|
6 | 7 | using System.IO;
|
7 | 8 | using System.Linq;
|
@@ -407,6 +408,7 @@ public ExecutionContext(MonoSDBHelper sdbAgent, int id, object auxData, PauseOnE
|
407 | 408 | AuxData = auxData;
|
408 | 409 | SdbAgent = sdbAgent;
|
409 | 410 | PauseOnExceptions = pauseOnExceptions;
|
| 411 | + Destroyed = false; |
410 | 412 | }
|
411 | 413 |
|
412 | 414 | public string DebugId { get; set; }
|
@@ -440,6 +442,8 @@ public ExecutionContext(MonoSDBHelper sdbAgent, int id, object auxData, PauseOnE
|
440 | 442 | internal int TempBreakpointForSetNextIP { get; set; }
|
441 | 443 | internal bool FirstBreakpoint { get; set; }
|
442 | 444 |
|
| 445 | + internal bool Destroyed { get; set; } |
| 446 | + |
443 | 447 | public DebugStore Store
|
444 | 448 | {
|
445 | 449 | get
|
@@ -486,4 +490,86 @@ public PerScopeCache()
|
486 | 490 | {
|
487 | 491 | }
|
488 | 492 | }
|
| 493 | + |
| 494 | + internal sealed class ConcurrentExecutionContextDictionary |
| 495 | + { |
| 496 | + private ConcurrentDictionary<SessionId, ConcurrentBag<ExecutionContext>> contexts = new (); |
| 497 | + public ExecutionContext GetCurrentContext(SessionId sessionId) |
| 498 | + => TryGetCurrentExecutionContextValue(sessionId, out ExecutionContext context) |
| 499 | + ? context |
| 500 | + : throw new KeyNotFoundException($"No execution context found for session {sessionId}"); |
| 501 | + |
| 502 | + public bool TryGetCurrentExecutionContextValue(SessionId id, out ExecutionContext executionContext, bool ignoreDestroyedContext = true) |
| 503 | + { |
| 504 | + executionContext = null; |
| 505 | + if (!contexts.TryGetValue(id, out ConcurrentBag<ExecutionContext> contextBag)) |
| 506 | + return false; |
| 507 | + if (contextBag.IsEmpty) |
| 508 | + return false; |
| 509 | + IEnumerable<ExecutionContext> validContexts = null; |
| 510 | + if (ignoreDestroyedContext) |
| 511 | + validContexts = contextBag.Where(context => context.Destroyed == false); |
| 512 | + else |
| 513 | + validContexts = contextBag; |
| 514 | + if (!validContexts.Any()) |
| 515 | + return false; |
| 516 | + int maxId = validContexts.Max(context => context.Id); |
| 517 | + executionContext = contextBag.FirstOrDefault(context => context.Id == maxId); |
| 518 | + return executionContext != null; |
| 519 | + } |
| 520 | + |
| 521 | + public void OnDefaultContextUpdate(SessionId sessionId, ExecutionContext newContext) |
| 522 | + { |
| 523 | + if (TryGetAndAddContext(sessionId, newContext, out ExecutionContext previousContext)) |
| 524 | + { |
| 525 | + foreach (KeyValuePair<string, BreakpointRequest> kvp in previousContext.BreakpointRequests) |
| 526 | + { |
| 527 | + newContext.BreakpointRequests[kvp.Key] = kvp.Value.Clone(); |
| 528 | + } |
| 529 | + newContext.PauseOnExceptions = previousContext.PauseOnExceptions; |
| 530 | + } |
| 531 | + } |
| 532 | + |
| 533 | + public bool TryGetAndAddContext(SessionId sessionId, ExecutionContext newExecutionContext, out ExecutionContext previousExecutionContext) |
| 534 | + { |
| 535 | + bool hasExisting = TryGetCurrentExecutionContextValue(sessionId, out previousExecutionContext, ignoreDestroyedContext: false); |
| 536 | + ConcurrentBag<ExecutionContext> bag = contexts.GetOrAdd(sessionId, _ => new ConcurrentBag<ExecutionContext>()); |
| 537 | + bag.Add(newExecutionContext); |
| 538 | + return hasExisting; |
| 539 | + } |
| 540 | + |
| 541 | + public void CreateWorkerExecutionContext(SessionId workerSessionId, SessionId originSessionId, ILogger logger) |
| 542 | + { |
| 543 | + if (!TryGetCurrentExecutionContextValue(originSessionId, out ExecutionContext context)) |
| 544 | + { |
| 545 | + logger.LogDebug($"Origin sessionId does not exist - {originSessionId}"); |
| 546 | + return; |
| 547 | + } |
| 548 | + if (contexts.ContainsKey(workerSessionId)) |
| 549 | + { |
| 550 | + logger.LogDebug($"Worker sessionId already exists - {originSessionId}"); |
| 551 | + return; |
| 552 | + } |
| 553 | + contexts[workerSessionId] = new(); |
| 554 | + contexts[workerSessionId].Add(context.CreateChildAsyncExecutionContext(workerSessionId)); |
| 555 | + } |
| 556 | + |
| 557 | + public void DestroyContext(SessionId sessionId, int id) |
| 558 | + { |
| 559 | + if (!contexts.TryGetValue(sessionId, out ConcurrentBag<ExecutionContext> contextBag)) |
| 560 | + return; |
| 561 | + foreach (ExecutionContext context in contextBag.Where(x => x.Id == id).ToList()) |
| 562 | + context.Destroyed = true; |
| 563 | + } |
| 564 | + |
| 565 | + public void ClearContexts(SessionId sessionId) |
| 566 | + { |
| 567 | + if (!contexts.TryGetValue(sessionId, out ConcurrentBag<ExecutionContext> contextBag)) |
| 568 | + return; |
| 569 | + foreach (ExecutionContext context in contextBag) |
| 570 | + context.Destroyed = true; |
| 571 | + } |
| 572 | + |
| 573 | + public bool ContainsKey(SessionId sessionId) => contexts.ContainsKey(sessionId); |
| 574 | + } |
489 | 575 | }
|
0 commit comments