Skip to content

Commit 40759ba

Browse files
committed
Refactored for cyclomatic complexity, added docs.
1 parent 34c995b commit 40759ba

1 file changed

Lines changed: 163 additions & 82 deletions

File tree

src/Autofac/Core/Registration/DefaultRegisteredServicesTracker.cs

Lines changed: 163 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,12 @@ protected override async ValueTask DisposeAsync(bool disposing)
264264
// Do not call the base, otherwise the standard Dispose will fire.
265265
}
266266

267+
/// <summary>
268+
/// Filters registration sources to skip a single source.
269+
/// </summary>
270+
/// <param name="sources">The source sequence to scan.</param>
271+
/// <param name="exclude">The source to exclude.</param>
272+
/// <returns>Sources that are not the excluded instance.</returns>
267273
[MethodImpl(MethodImplOptions.AggressiveInlining)]
268274
private static IEnumerable<IRegistrationSource> ExcludeSource(IEnumerable<IRegistrationSource> sources, IRegistrationSource exclude)
269275
{
@@ -276,6 +282,33 @@ private static IEnumerable<IRegistrationSource> ExcludeSource(IEnumerable<IRegis
276282
}
277283
}
278284

285+
/// <summary>
286+
/// Acquires the service info lock and records wait time when metrics are enabled.
287+
/// </summary>
288+
/// <param name="info">The service info lock target.</param>
289+
/// <param name="instrumentationService">Optional detail about the service for metrics.</param>
290+
/// <param name="lockTaken">Tracks whether the lock was acquired.</param>
291+
private static void EnterServiceInfoLock(ServiceRegistrationInfo info, string? instrumentationService, ref bool lockTaken)
292+
{
293+
if (AutofacMetrics.MetricsEnabled)
294+
{
295+
var wait = ValueStopwatch.StartNew();
296+
Monitor.Enter(info, ref lockTaken);
297+
AutofacMetrics.RecordLockContention("Service", instrumentationService, wait.ElapsedTicks);
298+
}
299+
else
300+
{
301+
Monitor.Enter(info, ref lockTaken);
302+
}
303+
}
304+
305+
/// <summary>
306+
/// Gets or creates an ephemeral service info entry used during pre-complete initialization.
307+
/// </summary>
308+
/// <param name="ephemeralSet">The ephemeral map for this initialization pass.</param>
309+
/// <param name="service">The service key.</param>
310+
/// <param name="info">The baseline service info to clone if needed.</param>
311+
/// <returns>An ephemeral service info entry for the service.</returns>
279312
private static ServiceRegistrationInfo GetEphemeralServiceInfo(Dictionary<Service, ServiceRegistrationInfo> ephemeralSet, Service service, ServiceRegistrationInfo info)
280313
{
281314
if (ephemeralSet.TryGetValue(service, out var ephemeral))
@@ -290,112 +323,59 @@ private static ServiceRegistrationInfo GetEphemeralServiceInfo(Dictionary<Servic
290323
return newCopy;
291324
}
292325

293-
private ServiceRegistrationInfo GetInitializedServiceInfo(Service service)
326+
/// <summary>
327+
/// Unwraps scope-isolated services and notes when isolation applies.
328+
/// </summary>
329+
/// <param name="service">The service to inspect.</param>
330+
/// <param name="isScopeIsolatedService">Set to <see langword="true"/> when the service is scope isolated.</param>
331+
/// <returns>The inner service to process.</returns>
332+
private static Service ResolveScopeIsolation(Service service, ref bool isScopeIsolatedService)
294333
{
295-
var createdEphemeralSet = false;
296-
var isScopeIsolatedService = false;
297-
298334
if (service is ScopeIsolatedService scopeIsolatedService)
299335
{
300336
// This is an isolated service query; use the wrapped service instead and
301337
// remember that fact for later.
302338
isScopeIsolatedService = true;
303-
service = scopeIsolatedService.Service;
339+
return scopeIsolatedService.Service;
304340
}
305341

342+
return service;
343+
}
344+
345+
/// <summary>
346+
/// Ensures the service info is initialized and returns it.
347+
/// </summary>
348+
/// <param name="service">The service being queried.</param>
349+
/// <returns>The initialized service info.</returns>
350+
private ServiceRegistrationInfo GetInitializedServiceInfo(Service service)
351+
{
352+
var createdEphemeralSet = false;
353+
var isScopeIsolatedService = false;
354+
355+
service = ResolveScopeIsolation(service, ref isScopeIsolatedService);
356+
306357
var info = GetServiceInfo(service);
307358
var instrumentationService = AutofacMetrics.MetricsEnabled ? service.ToString() : null;
308359
if (info.IsInitialized)
309360
{
310361
return info;
311362
}
312363

313-
if (!_trackerPopulationComplete)
314-
{
315-
// We need an ephemeral set for this pre-complete initialization.
316-
if (_ephemeralServiceInfo is null)
317-
{
318-
_ephemeralServiceInfo = new Dictionary<Service, ServiceRegistrationInfo>();
319-
createdEphemeralSet = true;
320-
}
321-
322-
info = GetEphemeralServiceInfo(_ephemeralServiceInfo, service, info);
323-
}
364+
info = GetServiceInfoForInitialization(service, info, ref createdEphemeralSet);
324365

325366
var succeeded = false;
326367
var lockTaken = false;
327368
try
328369
{
329-
if (AutofacMetrics.MetricsEnabled)
330-
{
331-
var wait = ValueStopwatch.StartNew();
332-
Monitor.Enter(info, ref lockTaken);
333-
AutofacMetrics.RecordLockContention("Service", instrumentationService, wait.ElapsedTicks);
334-
}
335-
else
336-
{
337-
Monitor.Enter(info, ref lockTaken);
338-
}
370+
EnterServiceInfoLock(info, instrumentationService, ref lockTaken);
339371

340372
if (info.IsInitialized)
341373
{
342374
return info;
343375
}
344376

345-
if (!info.IsInitializing)
346-
{
347-
BeginServiceInfoInitialization(service, info, _dynamicRegistrationSources);
348-
}
349-
350-
info.InitializationDepth++;
351-
352-
while (info.HasSourcesToQuery)
353-
{
354-
var next = info.DequeueNextSource();
355-
356-
// Do not query per-scope registration sources
357-
// for isolated services.
358-
if (isScopeIsolatedService && next is IPerScopeRegistrationSource)
359-
{
360-
continue;
361-
}
362-
363-
foreach (var provided in next.RegistrationsFor(service, _registrationAccessor))
364-
{
365-
// This ensures that multiple services provided by the same
366-
// component share a single component (we don't re-query for them)
367-
foreach (var additionalService in provided.Services)
368-
{
369-
var additionalInfo = GetServiceInfo(additionalService);
370-
if (additionalInfo.IsInitialized || additionalInfo == info)
371-
{
372-
continue;
373-
}
374-
375-
if (_ephemeralServiceInfo is not null)
376-
{
377-
// Use ephemeral info for additional services.
378-
additionalInfo = GetEphemeralServiceInfo(_ephemeralServiceInfo, service, info);
379-
}
380-
381-
if (!additionalInfo.IsInitializing)
382-
{
383-
BeginServiceInfoInitialization(additionalService, additionalInfo, ExcludeSource(_dynamicRegistrationSources, next));
384-
}
385-
else
386-
{
387-
additionalInfo.SkipSource(next);
388-
}
389-
}
390-
391-
AddRegistration(
392-
provided,
393-
preserveDefaults: true,
394-
originatedFromDynamicSource: true);
395-
}
396-
}
397-
398-
succeeded = true;
377+
// PopulateServiceInfo increments InitializationDepth; the decrement is paired in finally.
378+
succeeded = PopulateServiceInfo(service, info, isScopeIsolatedService);
399379
}
400380
finally
401381
{
@@ -421,8 +401,8 @@ private ServiceRegistrationInfo GetInitializedServiceInfo(Service service)
421401
Monitor.Exit(info);
422402
}
423403

424-
// This method was the entry point to an ephemeral service info initialization.
425-
// We need to discard it, so the next set of ephemeral service info is done from scratch.
404+
// This method was the entry point to an ephemeral initialization pass.
405+
// Discard the temporary map so later calls start with a clean slate.
426406
if (createdEphemeralSet)
427407
{
428408
_ephemeralServiceInfo?.Clear();
@@ -433,6 +413,102 @@ private ServiceRegistrationInfo GetInitializedServiceInfo(Service service)
433413
return info;
434414
}
435415

416+
/// <summary>
417+
/// Returns the appropriate service info for initialization, swapping to an ephemeral copy when needed.
418+
/// </summary>
419+
/// <param name="service">The service being queried.</param>
420+
/// <param name="info">The current service info.</param>
421+
/// <param name="createdEphemeralSet">Set to <see langword="true"/> when a new ephemeral set is created.</param>
422+
/// <returns>The service info to use for initialization.</returns>
423+
private ServiceRegistrationInfo GetServiceInfoForInitialization(Service service, ServiceRegistrationInfo info, ref bool createdEphemeralSet)
424+
{
425+
if (!_trackerPopulationComplete)
426+
{
427+
// We need an ephemeral set for this pre-complete initialization.
428+
if (_ephemeralServiceInfo is null)
429+
{
430+
_ephemeralServiceInfo = new Dictionary<Service, ServiceRegistrationInfo>();
431+
createdEphemeralSet = true;
432+
}
433+
434+
info = GetEphemeralServiceInfo(_ephemeralServiceInfo, service, info);
435+
}
436+
437+
return info;
438+
}
439+
440+
/// <summary>
441+
/// Populates service info by querying registration sources and adding derived registrations.
442+
/// </summary>
443+
/// <param name="service">The service being initialized.</param>
444+
/// <param name="info">The service info to populate.</param>
445+
/// <param name="isScopeIsolatedService"><see langword="true"/> when per-scope sources should be skipped.</param>
446+
/// <returns><see langword="true"/> when initialization completes.</returns>
447+
private bool PopulateServiceInfo(Service service, ServiceRegistrationInfo info, bool isScopeIsolatedService)
448+
{
449+
if (!info.IsInitializing)
450+
{
451+
BeginServiceInfoInitialization(service, info, _dynamicRegistrationSources);
452+
}
453+
454+
info.InitializationDepth++;
455+
456+
// Drain sources in-order; registrations can enqueue additional sources.
457+
while (info.HasSourcesToQuery)
458+
{
459+
var next = info.DequeueNextSource();
460+
461+
// Do not query per-scope registration sources
462+
// for isolated services.
463+
if (isScopeIsolatedService && next is IPerScopeRegistrationSource)
464+
{
465+
continue;
466+
}
467+
468+
foreach (var provided in next.RegistrationsFor(service, _registrationAccessor))
469+
{
470+
// This ensures that multiple services provided by the same
471+
// component share a single component (we don't re-query for them)
472+
foreach (var additionalService in provided.Services)
473+
{
474+
var additionalInfo = GetServiceInfo(additionalService);
475+
if (additionalInfo.IsInitialized || additionalInfo == info)
476+
{
477+
continue;
478+
}
479+
480+
if (_ephemeralServiceInfo is not null)
481+
{
482+
// Use ephemeral info for additional services.
483+
additionalInfo = GetEphemeralServiceInfo(_ephemeralServiceInfo, service, info);
484+
}
485+
486+
if (!additionalInfo.IsInitializing)
487+
{
488+
BeginServiceInfoInitialization(additionalService, additionalInfo, ExcludeSource(_dynamicRegistrationSources, next));
489+
}
490+
else
491+
{
492+
additionalInfo.SkipSource(next);
493+
}
494+
}
495+
496+
AddRegistration(
497+
provided,
498+
preserveDefaults: true,
499+
originatedFromDynamicSource: true);
500+
}
501+
}
502+
503+
return true;
504+
}
505+
506+
/// <summary>
507+
/// Seeds service info with middleware and registration sources.
508+
/// </summary>
509+
/// <param name="service">The service being initialized.</param>
510+
/// <param name="info">The service info to update.</param>
511+
/// <param name="registrationSources">Sources to query for registrations.</param>
436512
private void BeginServiceInfoInitialization(Service service, ServiceRegistrationInfo info, IEnumerable<IRegistrationSource> registrationSources)
437513
{
438514
// Add any additional service pipeline configuration from external sources.
@@ -444,6 +520,11 @@ private void BeginServiceInfoInitialization(Service service, ServiceRegistration
444520
info.BeginInitialization(registrationSources);
445521
}
446522

523+
/// <summary>
524+
/// Gets or creates the service info entry for a service key.
525+
/// </summary>
526+
/// <param name="service">The service key.</param>
527+
/// <returns>The service info entry.</returns>
447528
[MethodImpl(MethodImplOptions.AggressiveInlining)]
448529
private ServiceRegistrationInfo GetServiceInfo(Service service)
449530
{

0 commit comments

Comments
 (0)