peterchen
peterchen

Reputation: 41106

COM reference counting - codependent objects

I have two COM objects (let's call them Control and Job). Control is CoCreatable, Job objects are created by Control.NewJob().

Control has a method Control.Start(job) that makes the specified job the current job. It remains the current job as long as no other job is set.

Now for the client the following behavior appears reasonable for these particular controls:

Now, I have a classic circular reference here, the condition to release it would be both objects not having external references.

I can solve this scenario by mucking around with ATL's InternalRelease implementation, but this is pretty ugly and isolated.

Any suggestions? Existing solutions?

Upvotes: 1

Views: 129

Answers (2)

Hans Passant
Hans Passant

Reputation: 941505

as long as one of its Jobs exist, Control exists

No, fairly sure that this is the rule where you went wrong. Keep your eyes on the ball, the only reason you added the IControl::CreateJob() factory function is to give CJob (the implementation class, not the interface) a CControl* reference. This implies ownership, a particular IJob can only ever be associated with a particular Control instance. CControl should therefore keep a collection of CJobs that it owns.

It now becomes straight-forward:

  • The CControl instance is created as normal, there is only one AddRef() call, only the app owns the instance.
  • The IControl::CreateJob() implementation method creates a new CJob with the CJob(Control*) constructor, adds it to the collection and returns the IJob interface pointer. There is only one AddRef() call, only the client app owns the instance.
  • The CJob destructor calls a private CControl::RemoveJob() method to get itself removed from the collection, using the CControl* it got from its constructor if it is not null.
  • The CControl destructor iterates its collection, setting the CControl* that any remaining CJob maintains back to null. They are now un-owned.
  • The IControl::Start(IJob*) implementation method calls AddRef() to ensure the job stays alive. Release when the job is done or is replaced or the CControl object is destructed. Note that an error check is required, the method needs to iterate the collection to find the matching CJob object. So the client cannot start a job that was created by another Control instance. And allowing you to keep a CJob* as the CControl::currentJob private variable.

Upvotes: 2

Roman Ryltsov
Roman Ryltsov

Reputation: 69662

I don't think ATL has out of the box solution since the behavior in question is sensitive to specific requirements.

As long as the client has either a reference to Control or its CurrentJob, neither gets destroyed ("trivial": CurrentJob is a strong ref)

This requirement assumes that an external strong reference to Control exists from the side of the current Job, since control needs to stay alive with client to job reference only. That is, both control and job have strong references one to another. And then control+job combination needs to correctly handle release of all external references. For instance, this can be achieved the following way.

Job's (and the same way Control's?) CComObjectRootEx::InternalRelease is overridden and it checks whether the only external reference remained is the control's one. If this is the case, the job is initiating termination check - it calls certain control's method to check its references. If job's reference is the only reference control sees on itself, then both release references one to the other (and terminate).

Upvotes: 1

Related Questions