Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add plumbing for performing custom activity code on actor disposal #15622

Merged
merged 1 commit into from Sep 24, 2018

Conversation

reaperrr
Copy link
Contributor

It appears that if an actor dies (or is disposed directly) while an activity is active, the actor will be disposed before the activities' TickOuter can tick one last time, so OnLastRun is never triggered.
As #15543 shows, this can lead to some nasty bugs.

This PR enforces a proper activity end on actor death/disposal.

/// <summary>
/// Used only by Actor.Dispose() to make sure the activity finishes properly on death/disposal of actor
/// </summary>
public virtual void ForceCancel(Actor self)
Copy link
Member

@pchote pchote Sep 17, 2018

Choose a reason for hiding this comment

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

I think it would be better to call this OnActorDispose instead, and leave the default implementation doing nothing.

The individual activity subclasses can then choose to override this with their own cleanup logic, avoiding any side-effects that might otherwise happen by blindly calling OnLastRun without knowing what the activities might be doing inside there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can do that, but I'm not convinced that OnLastRun being skipped on actor death/disposal is intentional.
Pinging @obrakmann just in case he has time to chime in on this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Tbh, I didn't quite think of modifying third-party state in the OnLastRun method, so the thought hasn't even crossed my mind. But I do agree with Paul here that unconditionally running OnLastRun might not be such a hot idea, and it should be up the activities themselves to decide what needs cleaning up after an actor gets disposed of.

@reaperrr
Copy link
Contributor Author

Ok, somehow can't get the fix to work anymore no matter what I try, I need to debug that first.

@pchote
Copy link
Member

pchote commented Sep 17, 2018

My first thought is the need to be careful about parent/child activities.

@reaperrr
Copy link
Contributor Author

Yes, the problem was that ResupplyAircraft chains Repair as ChildActivity.

This PR should be good as-is, I didn't need to make any further changes to this PR's code to fix/work around the problem in #15543 (which I've also updated now, so it serves as testcase).

public virtual bool Cancel(Actor self, bool keepQueue = false)
{
// Just a safe-guard in case this activity was already successfully cancelled
if (IsCanceled)
Copy link
Member

Choose a reason for hiding this comment

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

This will cause problems if you cancel an activity that takes a few ticks to complete (e.g. Move), queue a new activity, and then call Cancel again before the original activity has completed. Expected behaviour: second activity is cancelled, actual behaviour: it won't be.

@reaperrr
Copy link
Contributor Author

Updated.
That Cancel change was only a left-over from my old ForceCancel approach anyway.

// If CurrentActivity isn't null, run OnActorDispose in case some cleanups are needed.
// This should be done before the FrameEndTask to avoid dependency issues.
if (CurrentActivity != null)
CurrentActivity.RootActivity.OnActorDispose(this);
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to worry about propagating this to child activities, or do we assume that the activities will handle this themselves if they use them? My gut feeling is that this probably should be automatic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I lean towards agreeing that it should be automatic, that would require changing the protection level of ChildActivity, though.
Personally I wouldn't really mind, just wanted to point it out in case there are some potential issues with that I'm not aware of.

Copy link
Member

Choose a reason for hiding this comment

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

One option is to define a OnActorDisposeOuter on Activity (it should have internal visibility so that mod code can't see it), which calls OnActorDisposeOuter on the child activity if it exists and then calls OnActorDispose on itself. We use this pattern in other places, notably widgets. We may also want to propagate this through the NextActivity chain too?

If this isn't going to work then I think a reasonable compromise is to add a comment to the default OnActorDispose definition explaining that it must be manually chained through by any activities that use child activities.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Appears to work fine, so I'd like to go with it.

Not sure what you mean with propagating through the NextActivity chain, though?

Copy link
Member

Choose a reason for hiding this comment

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

I suppose calling OnActorDisposeOuter on NextActivity (and that activity will call it on its NextActivity and so on, hence "through the chain").

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But do we really want or need that?

When the actor is disposed, NextActivity hasn't even started yet, or it wouldn't be "Next" anymore. I don't see why we would want to run the dispose-specific code on activities that haven't even started yet, since that would mean OnFirstRun hasn't run, either. I'm not even sure if the ctor is run before the activity becomes 'current'.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Leaving changes to NextActivity for the future and opened #15632 instead, as discussed on IRC.

@reaperrr reaperrr changed the title Fix Activity.OnLastRun being skipped when actor dies Add plumbing for performing custom activity code on actor disposal Sep 20, 2018
This allows activities to perform necessary cleanups on actor
death/disposal, for example by running OnLastRun directly,
which would otherwise be skipped when the actor dies or is disposed
through other means.
@reaperrr
Copy link
Contributor Author

Updated.

@abcdefg30 abcdefg30 merged commit 9bcb754 into OpenRA:bleed Sep 24, 2018
@abcdefg30
Copy link
Member

Changelog

@reaperrr reaperrr deleted the fix-act-onlastrun branch October 26, 2018 19:57
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.

None yet

4 participants