Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

made ActorCell use nullable #7475

Merged
merged 8 commits into from
Jan 21, 2025

Conversation

Aaronontheweb
Copy link
Member

@Aaronontheweb Aaronontheweb commented Jan 20, 2025

Changes

This PR looks scarier than it is. I've implemented nullability on the ActorCell, the brains underneath each actor, in order to support some future changes we're going to make here. None of this should result in a behavior change or a breaking API change - practically speaking, given that most of these APIs are not called directly from user code.

Checklist

For significant changes, please ensure that the following have been completed (delete if not relevant):

@Aaronontheweb
Copy link
Member Author

I've also cleaned up A TON of TBD stuff in the XML-DOCS while I was at it. Didn't get everything, but got a lot of it.

Copy link
Member Author

@Aaronontheweb Aaronontheweb left a comment

Choose a reason for hiding this comment

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

Detailed my changes

Copy link
Member Author

Choose a reason for hiding this comment

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

API approval file - you should still look over it but there's nothing super scary here. Going to comment on the changes in the source files themselves.

@@ -36,10 +36,10 @@ private IReadOnlyCollection<IActorRef> Children
}

private ImmutableDictionary<string, FunctionRef> FunctionRefs => Volatile.Read(ref _functionRefsDoNotCallMeDirectly);
internal bool TryGetFunctionRef(string name, out FunctionRef functionRef) =>
internal bool TryGetFunctionRef(string name, out FunctionRef? functionRef) =>
Copy link
Member Author

Choose a reason for hiding this comment

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

All of these TryGetValue<TKey, TValue> methods have a signature of bool TryGetValue<TKey, TValue>(TKey key, out TValue? val)in the .NET standard libraries - so I've brought theTryGetFunctionRef` up to speed.

Copy link
Member Author

Choose a reason for hiding this comment

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

So a really useful over-arching point for all of these code changes in the review:

We're not changing what is actually returned - we're just accurately annotating it going forward.

Please bear that in mind - only input behavior has changed (i.e. explicitly handling null inputs where we weren't before), not any output behavior. Methods that could return null before could still return null, but it's just annotated now.

@@ -199,7 +199,7 @@ protected void UnreserveChild(string name)
/// </summary>
/// <param name="actor">TBD</param>
/// <returns>TBD</returns>
public ChildRestartStats InitChild(IInternalActorRef actor)
public ChildRestartStats? InitChild(IInternalActorRef actor)
Copy link
Member Author

Choose a reason for hiding this comment

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

ChildRestartStats will be null if we don't have a child by this name - should never happen but it has always been theoretically possible

@@ -318,7 +318,7 @@ public bool TryGetChildStatsByName(string name, out IChildStats child) //This
/// <summary>
/// Tries to get the stats for the child with the specified name. This ignores children for whom only names have been reserved.
/// </summary>
private bool TryGetChildRestartStatsByName(string name, out ChildRestartStats child)
private bool TryGetChildRestartStatsByName(string name, out ChildRestartStats? child)
Copy link
Member Author

Choose a reason for hiding this comment

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

Again, the TryGetValue pattern applied here

/// </summary>
public int NumberOfMessages { get { return Mailbox.NumberOfMessages; } }
public int NumberOfMessages { get { return Mailbox!.NumberOfMessages; } }
Copy link
Member Author

Choose a reason for hiding this comment

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

Mailbox can't be null by the time anyone calls this method - or else there's been a catastrophic failure.

@@ -216,7 +236,7 @@ public void Init(bool sendSupervise, MailboxType mailboxType)
}

SwapMailbox(mailbox);
Mailbox.SetActor(this);
Mailbox!.SetActor(this);
Copy link
Member Author

Choose a reason for hiding this comment

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

Mailbox can't be null by the time anyone calls this method - or else there's been a catastrophic failure.



Assert.Assert(instance != null, (string)(nameof(instance) + " != null"));
return instance!;
Copy link
Member Author

Choose a reason for hiding this comment

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

The assertion on the line above prevents this from ever actually returning null.

public static IActorRef GetCurrentSenderOrNoSender()
{
var current = Current;
return current != null ? current.Sender : ActorRefs.NoSender;
return current?.Sender ?? ActorRefs.NoSender;
Copy link
Member Author

Choose a reason for hiding this comment

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

Since Current can be null and Sender can separately be null (this has always been a possibility), we return ActorRefs.NoSender here - which, ironically, is null right now.

@@ -188,19 +187,18 @@ public bool Contains(IActorRef actor)
/// <param name="sb">TBD</param>
/// <param name="kvp">TBD</param>
/// <param name="index">TBD</param>
protected void ChildStatsAppender(StringBuilder sb, KeyValuePair<string, IChildStats> kvp, int index)
protected static void ChildStatsAppender(StringBuilder sb, KeyValuePair<string, IChildStats> kvp, int index)
Copy link
Member Author

Choose a reason for hiding this comment

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

Not a breaking change - moving to static would only change inheritors, not callers.

Copy link
Member Author

Choose a reason for hiding this comment

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

(and there could be no inheritors of this method before.)

* Add `NotNullWhen` compiler annotation attribute to all of the `bool TryX(out Y)` patterned functions
* Add assertions on all shebangs to make sure we document why the nullability check was overriden
@@ -177,13 +177,15 @@ public static TimeSpan Min(this TimeSpan @this, TimeSpan other)
/// <param name="enumerable">TBD</param>
/// <param name="item">TBD</param>
/// <returns>TBD</returns>
public static IEnumerable<T> Concat<T>(this IEnumerable<T> enumerable, T item)
#nullable enable
public static IEnumerable<T> Concat<T>(this IEnumerable<T>? enumerable, T item)
Copy link
Member Author

Choose a reason for hiding this comment

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

Good call

@@ -226,7 +226,9 @@ public static class ActorRefs
/// Use this value as an argument to <see cref="ICanTell.Tell"/> if there is not actor to
/// reply to (e.g. when sending from non-actor code).
/// </summary>
public static readonly IActorRef NoSender = null;
#nullable enable
public static readonly IActorRef? NoSender = null;
Copy link
Member Author

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@Arkatufus Arkatufus left a comment

Choose a reason for hiding this comment

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

LGTM

@Aaronontheweb Aaronontheweb merged commit 2ee5176 into akkadotnet:dev Jan 21, 2025
9 of 12 checks passed
@Aaronontheweb Aaronontheweb deleted the optional-actor-telemetry branch January 21, 2025 18:53
# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants