Skip to content

chore: Improve closing on FairLmdSource #1465

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

Merged
merged 1 commit into from
Nov 27, 2023

Conversation

ChristianTackeGSI
Copy link
Member

Close is a virtual method. It's getting called internally in a scenario where only FairLmdSource specific resources should be closed.

Also the desctructor did not close.

This was triggered by a comment here: #1464 (review)


Checklist:

`Close` is a virtual method. It's getting called internally
in a scenario where only FairLmdSource specific resources
should be closed.

Also the destructor did not close.
@YanzhaoW
Copy link

Hi, thanks for the PR.

There are few problems:

  1. The overridden Closed function from Fairlmd should be called from its base class (dtor of bass class) instead of itself.
  2. The overridden Closed function should be a private member function. I don't understand why you have private Closelmd and a public Close instead of just making Close private.

@ChristianTackeGSI
Copy link
Member Author

The overridden Closed function from Fairlmd should be called from its base class (dtor of bass class) instead of itself.

That does not work.

Inside the base class destructor the object is of type base-class. So any calls to any virtual functions in the dtor go to the virtual methods of that base class (or its parents).

That's basically, why calling virtual functions in the dtor / ctor (there it's the same story) is a bad idea, because it does not do what you would think it should do.

There's even a clang-tidy warning for this: clang-analyzer-optin.cplusplus.VirtualCall. And yes, we have enabled this one in our CI. And yes, we should fix them…

The overridden Closed function should be a private member function. I don't understand why you have private Closelmd and a public Close instead of just making Close private.

Close is a public API of FairSource. So if we want to make it private, we should declare that a breaking change.

And IMHO it is okay for a FairSource to have Open and Close functions. The Close one can be virtual (yes, it would be nicer, if the virtual functions would be private/protected and the public interface would be another member functions, but we don't have that yet), so that FairRun can close the source when it's done, but doesn't have to destruct it yet.

So the idea is:

  • Each class can have a private (non-virtual!) helper function to cleanup resources / close the resources.
  • Call that helper from the dtor.
  • Override Close, call your helper
    And call the parent class' Close (if the parent class has one).
    (So yes, we probably should give the base class an empty "default" Close.)

@YanzhaoW
Copy link

YanzhaoW commented Nov 20, 2023

Inside the base class destructor the object is of type base-class. So any calls to any virtual functions in the dtor go to the virtual methods of that base class (or its parents).

You are right! I always thought calling a virtual function in dtor is same as in any other member functions. Glad I learned this new thing.

And IMHO it is okay for a FairSource to have Open and Close functions. The Close one can be virtual (yes, it would be nicer, if the virtual functions would be private/protected and the public interface would be another member functions, but we don't have that yet), so that FairRun can close the source when it's done, but doesn't have to destruct it yet.

Even though we can't call the Close in FairSource dtor, we could still call the Fair::Close in the dtor of its owner, as is also a better place to call it. As is a golden rule for the ownership, whoever owns it is responsible for how it's disposed (delete, close, log ext.).

So in my opinion, what's the correct way is to have a public non-virtual close member function in the base class (in our case, FairSource::Close()) and also another virtual private member function, for example, Close_imp(), which is called in Close. Then in the derived class, we only override this private member function Close_imp() (without overloading public method Close()). If we decides FairSource is owned by FairRun (I'm still not sure whether is FairRun or FairRooManager), in the dtor of FairRun, we call the public method FairSource::Close, which actually executes the Close_imp() from the true type of the "source" object.

A very simple illustration.

Copy link

@YanzhaoW YanzhaoW left a comment

Choose a reason for hiding this comment

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

Following my comment above.

The benefit of this way is that the close action is automatically handled by the base class. The developer who implements a new derived class just needs to override the Close_imp() without concerning where it should be called. And we have only one line of Close for all derived classes.

@@ -54,6 +54,7 @@ FairLmdSource::FairLmdSource(const FairLmdSource& source)

FairLmdSource::~FairLmdSource()
{
CloseLmd();
Copy link

@YanzhaoW YanzhaoW Nov 20, 2023

Choose a reason for hiding this comment

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

This step could be executed by its owner.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, no.

I don't think, the "owner" should be resposible for calling Close. It could be considered "being a good citizen", but it should not be strictly needed.

FairLmdSource mysource;
mysource.Open(…);
if (true)
  throw std::runtime_exception("Something else went wrong");
mysource.Close();

This should not leak resources! (See "RAII printciple").

So yes, This is some overhead for the developer of a FairSource/Sink. But that's how C++ is like.

Comment on lines 242 to +245
void FairLmdSource::Close()
{
CloseLmd();
}
Copy link

@YanzhaoW YanzhaoW Nov 20, 2023

Choose a reason for hiding this comment

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

This could be implemented in the base class FairSource:

void FairSource::Close() {Close_imp();}

Copy link
Member Author

Choose a reason for hiding this comment

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

Can we postpone this for another PR?

@@ -65,6 +65,9 @@ class FairLmdSource : public FairMbsSource

FairLmdSource& operator=(const FairLmdSource&);

private:
void CloseLmd();
Copy link

@YanzhaoW YanzhaoW Nov 20, 2023

Choose a reason for hiding this comment

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

This could be an overridden private virtual function.

Copy link
Member Author

Choose a reason for hiding this comment

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

Let's please postpone this for another PR as well.

Copy link
Contributor

@kresan kresan left a comment

Choose a reason for hiding this comment

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

Much cleaner closing now. Looks good.

@ChristianTackeGSI ChristianTackeGSI merged commit 46b14b6 into FairRootGroup:dev Nov 27, 2023
@ChristianTackeGSI ChristianTackeGSI deleted the pr/lmd branch November 27, 2023 10:21
@ChristianTackeGSI
Copy link
Member Author

ChristianTackeGSI commented Nov 27, 2023

Should we backport this to 18.8 (or even 18.6)?

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