This is an archive of the discontinued LLVM Phabricator instance.

[libc++] NFCI: Implement make_shared as allocate_shared with std::allocator
ClosedPublic

Authored by ldionne on Dec 10 2020, 2:27 PM.

Details

Summary

This simplifies the implementation, and it appears to be equivalent since
make_shared was allocating memory with std::allocator anyway.

Diff Detail

Event Timeline

ldionne created this revision.Dec 10 2020, 2:27 PM
ldionne requested review of this revision.Dec 10 2020, 2:27 PM
Herald added a project: Restricted Project. · View Herald TranscriptDec 10 2020, 2:27 PM
Herald added a reviewer: Restricted Project. · View Herald Transcript

@glenjofe Taking a chance to tag you here since you've been involved in most changes to std::shared_ptr (and Peter Dimov does not appear to be on Phabricator). Was the intent that make_shared could be implemented via allocate_shared with the default allocator, or am I missing something subtle?

if you allocate memory with an allocator, you need to deallocate it with the same allocator. Not with operator delete

if you allocate memory with an allocator, you need to deallocate it with the same allocator. Not with operator delete

There's a good chance that this is a pre-existing problem.

if you allocate memory with an allocator, you need to deallocate it with the same allocator. Not with operator delete

There's a good chance that this is a pre-existing problem.

Indeed, however std::allocator is specified to use operator new and operator delete. Also, we already did allocate using std::allocator<>::allocate and our control block already deallocates using the provided allocator's deallocate function, so the allocate and deallocate calls are matched (both before and after the patch).

Indeed, however std::allocator is specified to use operator new and operator delete.

I don't think that applies to user-provided specializations of std::allocator

zoecarver accepted this revision.Dec 10 2020, 4:58 PM
zoecarver added a subscriber: zoecarver.

Thanks for the cleanup. This LGTM!

ldionne accepted this revision as: Restricted Project.Dec 11 2020, 9:01 AM

Indeed, however std::allocator is specified to use operator new and operator delete.

I don't think that applies to user-provided specializations of std::allocator

Good point, I hadn't thought about specializations. But even then, I think it's fine. User-provided specializations are allowed as long as they depend on a user-defined type, and they satisfy the requirements of the base template. Here, std::allocator::allocate clearly says:

Allocates n * sizeof(T) bytes of uninitialized storage by calling ::operator new(std::size_t)

IDK whether that's considered "a requirement", but it's close enough. In any case, if it is an issue, then it's a pre-existing condition because we were using std::allocator already, so I think this is really a NFC. I just hope we're not violating the intent of the standard, but as I think of it, I believe we couldn't change that behavior without an ABI break (because it would require changing the control block used by make_shared so that it doesn't use an allocator anymore.

This revision is now accepted and ready to land.Dec 11 2020, 9:01 AM

@glenjofe Taking a chance to tag you here since you've been involved in most changes to std::shared_ptr (and Peter Dimov does not appear to be on Phabricator). Was the intent that make_shared could be implemented via allocate_shared with the default allocator, or am I missing something subtle?

My implementation Boost's does: https://github.com/boostorg/smart_ptr/blob/develop/include/boost/smart_ptr/make_shared_array.hpp

i.e. Those boost::make_shared overloads just call boost::allocate_shared with boost::default_allocator

It would be nice to do specify std::make_shared overloads in the standard as just returning std::allocate_shared with std::allocator but we would need wording for that.

As pointed out users can specialize std::allocator and some even specialize std::allocator_traits.

@glenjofe Taking a chance to tag you here since you've been involved in most changes to std::shared_ptr (and Peter Dimov does not appear to be on Phabricator). Was the intent that make_shared could be implemented via allocate_shared with the default allocator, or am I missing something subtle?

My implementation Boost's does: https://github.com/boostorg/smart_ptr/blob/develop/include/boost/smart_ptr/make_shared_array.hpp

i.e. Those boost::make_shared overloads just call boost::allocate_shared with boost::default_allocator

It would be nice to do specify std::make_shared overloads in the standard as just returning std::allocate_shared with std::allocator but we would need wording for that.

As pointed out users can specialize std::allocator and some even specialize std::allocator_traits.

Thanks for the clarification of intent! I believe it's a reasonable implementation -- I can hardly imagine someone relying on std::allocator *not* being used by std::make_shared. Or at least, that would imply a very specific way of reading of the standard.