Yes, it is normal. If the class supports this use, clients do not need to synchronize the destruction; that is, they don’t need to check that all other methods of the object are finished before calling the destructor.
I would recommend that clients do not assume that they can do this unless explicitly documented. Clients really carry this load by default with standard library objects (§17.6.4.10 / 2).
There are times when this is normal; std::condition_variable destructor, for example, specifically allows current calls to the condition_variable::wait() method when ~condition_variable() run. It only requires that clients do not start wait () calls after ~condition_variable() run.
Perhaps it would be easier to require the client to synchronize access to the destructor - and the constructor, if important - like most other standard libraries. I would recommend doing this if possible.
However, there are certain patterns where it makes sense to free clients from the burden of complete synchronization of destruction. condition_variable The general pattern looks like one: consider using an object that handles possibly long-running requests. The user performs the following actions:
- Build Object
- The reason the object receives requests from other threads.
- The reason the object stops receiving requests: at the moment, some outstanding requests may be executed, but new ones cannot be called.
- Destroy the object. The destructor is blocked until all requests are completed, otherwise current requests may have a bad time.
An alternative would be to require clients to synchronize access. You can imagine step 3.5 above when the client calls the shutdown() method on an object that blocks, after which the client can destroy the object. However, this project has some disadvantages; this complicates the API and introduces additional state for the shutdown-but-valid object.
Think instead, it is possible that step (3) is blocked until all requests are completed. There are compromises ...
source share