Skip to content

Conversation

@imurashka
Copy link
Contributor

@imurashka imurashka commented Jan 12, 2026

Exception re-throw fix

In a previous PR I added an optimization that unwrapped a single dispose failure (case 1: throw exceptionList[0];). That was a bad approach because re-throwing a caught exception instance replaces the original stack trace with the dispose call stack. Keeping failures as inner exceptions preserves their original stack traces. Now it is removed

What changed in this PR

  • Log dispose exceptions to Unity: leaf exceptions thrown by individual IDisposable.Dispose() calls are now logged via Debug.LogException(...).
  • Aggregate/propagate leaf exceptions cleanly across nested composites: introduce ControllerDisposeAggregateException and always throw it when any dispose errors occur. When an inner composite throws this marker, the outer composite collects its InnerExceptions (leaf exceptions) and skips logging the marker again, preventing duplicate console entries in nested hierarchies.

Tests

  • Updated/added tests to expect ControllerDisposeAggregateException (including nested composite scenarios).
  • Added missing LogAssert.Expect(...) where dispose exceptions are now logged.
  • Added InternalsVisibleTo("Playtika.Controllers.Tests") for asserting the internal marker type.

@imurashka imurashka marked this pull request as draft January 12, 2026 16:53
@imurashka imurashka marked this pull request as ready for review January 12, 2026 17:33
{
disposable?.Dispose();
}
catch (AggregateException e)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add light optimization by returning from this method when disposables has 0 elements?
Also, I'm thinking about making _compositeDisposables in the ControllerBase to be null by default and pooled only during first attempt to add disposable. Wdyt?

Copy link
Contributor Author

@imurashka imurashka Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no simple ways to check the number of elements in an IEnumerable for the AddRange method. However, for internal _disposables object, seem like a suitable option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also did lazy ControllerCompositeDisposable creation in ControllerBase. Pleasy, take a look.

Copy link
Collaborator

@eldarmguseynov eldarmguseynov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the fix. What do you think about optimizations around disposables?

@imurashka
Copy link
Contributor Author

Thank you for the fix. What do you think about optimizations around disposables?

I will be honest with you, I didn't think about optimization here too much. Right now, it looks pretty optimized, as I see. Espesially after small tweaks you proposed in method DisposeMany and ControllerBase class.

@imurashka
Copy link
Contributor Author

@eldarmguseynov hello! Is there any chance that you will merge this anytime soon?

@perepechenko
Copy link
Collaborator

Related to https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1065 Dispose should not throw any exception.

As for the use of Debug.LogError, many projects use their own logger, and we avoided using Debug.LogError in the controllers core.

The idea of a lazy composite disposable object is good, and we can use pooling instead of new.

@eldarmguseynov what do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants