Quuxplusone marked 11 inline comments as done.
Quuxplusone added inline comments.


================
Comment at: include/experimental/memory_resource:427
+    static _LIBCPP_CONSTEXPR const size_t __default_buffer_capacity = 1024;
+    static _LIBCPP_CONSTEXPR const size_t __default_buffer_alignment = 16;
+
----------------
strager wrote:
> Nit: Why isn't `__default_buffer_alignment` set to `alignof(max_align_t)` 
> (which I think is 16)? I think `alignof(max_align_t)` is a good, portable 
> default.
Will users be more upset if `__default_buffer_alignment` is 16-instead-of-`???` 
on exotic platform `???`; or more upset if `__default_buffer_alignment` is 
//usually// 16 but then sometimes is `???`-instead-of-16 on exotic platforms? I 
tend to think that "usually 16, sometimes something else" is worse behavior 
software-engineering-wise than "always 16, even when something else might 
arguably be better on exotic platform `???`."


================
Comment at: include/experimental/memory_resource:431-432
+        __chunk_header *__next_;
+        char *__start_;
+        char *__cur_;
+        size_t __align_;
----------------
strager wrote:
> Nit: Would it make sense to use `std::byte` instead of `char`?
IMHO "strong no"; `char` is the standard core-language type that takes up one 
byte. `std::byte` is an enum type that we'd have to drag in from a header 
(cost), and otherwise behaves just like `char` (no benefit). But it's certainly 
a non-functional nit and I would not put up a fight if libc++ wanted it that 
way. :)


================
Comment at: include/experimental/memory_resource:433
+        char *__cur_;
+        size_t __align_;
+        size_t __allocation_size() {
----------------
strager wrote:
> > Eric suggests replacing size_t __align_ with uint8_t __log2_align_. I'm 
> > amenable to this idea, but I'd want to know what's the best way to compute 
> > the log2 of a user-supplied number.
> 
> Perhaps `std::log2p1` could help: 
> https://en.cppreference.com/w/cpp/numeric/log2p1
Yes, but that's C++2a; I imagine some fiddliness would be needed to use it in 
C++17 library code (which in turn is actually compiled as C++11 or '14, I 
forget).


================
Comment at: 
test/libcxx/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_in_geometric_progression.pass.cpp:32
+        assert(globalMemCounter.checkOutstandingNewEq(0));
+    }
+
----------------
strager wrote:
> Nit: This test is named allocate_in_geometric_progression. I don't see any 
> multiplication or anything happening in this test case. Did you mean to call 
> this test something else, like release_deletes_upstream_memory?
Prior to the resolution of [LWG 
3120](https://cplusplus.github.io/LWG/issue3120), this test had tested 
something else. I guess now it //should// be more like 
"release_resets_initial_buffer.pass.cpp".


================
Comment at: 
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_deallocate.pass.cpp:28
+        std::experimental::pmr::monotonic_buffer_resource 
mono1(std::experimental::pmr::new_delete_resource());
+        std::experimental::pmr::memory_resource & r1 = mono1;
+
----------------
strager wrote:
> Nit: What is the purpose of the `r1` variable? Why not use `mono1` everywhere 
> instead (and rename `mono1` to `r1` if you like `r1` better than `mono1` as a 
> name)?
`r1` has a different static type. For example, here it's testing that 
`r1.allocate(50)` actually makes a virtual call to 
`monotonic_buffer_resource::do_allocate`. But I guess it doesn't really matter; 
I could use `mono1` for everything just as well.


================
Comment at: 
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_exception_safety.pass.cpp:48-49
+    void *res = r1.allocate(64, 16);
+    assert(res == buffer);
+    assert(globalMemCounter.checkNewCalledEq(0));
+
----------------
strager wrote:
> Nit: These assertions look unrelated to exception safety to me. (The test is 
> named allocate_exception_safety.) I'd delete these assertions.
Depends on your test-overfitting philosophy. These assertions are still 
expected to pass 100% of the time — if they fail, then something is definitely 
wrong. And they're cheap to execute. So IMHO they might as well be there, as 
"self-documenting code."


================
Comment at: 
test/std/experimental/memory/memory.resource.monotonic.buffer/monotonic.buffer.mem/allocate_exception_safety.pass.cpp:67-68
+
+    upstream.which = std::experimental::pmr::new_delete_resource();
+    res = r1.allocate(last_new_size, 16);
+    assert(res != buffer);
----------------
strager wrote:
> Question about the intended behavior of `monotonic_buffer_resource`:
> 
> If line 68 instead allocated 1 byte, and the byte could fit in the original 
> heap allocation (line 51), should `monotonic_buffer_resource` return a byte 
> from that original resource? In other words, should the following test pass?
> 
> ```
> repointable_resource upstream(std::experimental::pmr::new_delete_resource());
> std::experimental::pmr::monotonic_buffer_resource mono1(&upstream);
> 
> globalMemCounter.reset();
> mono1.allocate(1, 1);
> const size_t first_new_size = globalMemCounter.last_new_size;
> bool mono1_has_spare_capacity = first_new_size == 1;
> 
> upstream.which = std::experimental::pmr::null_memory_resource();
> try {
>     mono1.allocate(first_new_size, 1); // Force allocation from upstream.
>     assert(false);
> } catch (const std::bad_alloc&) {
>     // we expect this
> }
> 
> globalMemCounter.reset();
> mono1.allocate(1, 1);
> if (mono1_has_spare_capacity) {
>   assert(globalMemCounter.checkNewCalledEq(0));
> } else {
>   assert(globalMemCounter.checkNewCalledEq(1));
> }
> ```
> 
> Similarly, if `monotonic_buffer_resource` was given a buffer, and that buffer 
> still has spare capacity, should that capacity be reused? In other words, 
> should the following test pass?
> 
> ```
> int first_allocation_size = 20;
> int second_allocation_size = 30;
> 
> auto *upstream = std::experimental::pmr::null_memory_resource();
> char buffer[64];
> assert(sizeof buffer >= first_allocation_size + second_allocation_size);
> std::experimental::pmr::monotonic_buffer_resource mono1(buffer, sizeof 
> buffer, upstream);
> 
> globalMemCounter.reset();
> void *first_allocation = mono1.allocate(first_allocation_size, 1);
> assert(first_allocation == &buffer[0]);
> 
> try {
>     mono1.allocate(sizeof buffer, 1); // Force allocation from upstream.
>     assert(false);
> } catch (const std::bad_alloc&) {
>     // we expect this
> }
> 
> void *second_allocation = mono1.allocate(second_allocation_size, 1);
> assert(second_allocation == &buffer[second_allocation_size]);
> ```
> 
> I think these scenarios would be useful to test, in addition to the scenario 
> in your test here.
> If [the request] could fit in the original heap allocation, should 
> `monotonic_buffer_resource` return a byte from that original allocation?
> Similarly, if `monotonic_buffer_resource` was given a buffer, and that buffer 
> still has spare capacity, should that capacity be reused?

In both cases, the Standard says "absolutely not": 
[mem.res.monotonic.buffer.mem#5](http://eel.is/c++draft/mem.res.monotonic.buffer.mem#5).
 I can add these tests.



Repository:
  rCXX libc++

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D47111/new/

https://reviews.llvm.org/D47111



_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to