Title
Unclear behavior of monotonic_buffer_resource::release()
Status
c++23
Section
[mem.res.monotonic.buffer.mem]
Submitter
Arthur O'Dwyer

Created on 2018-06-10.00:00:00 last changed 4 months ago

Messages

Date: 2020-11-09.21:40:50

Proposed resolution:

This wording is relative to N4861.

  1. Modify [mem.res.monotonic.buffer.mem] as indicated:

    void release();
    

    -1- Effects: Calls upstream_rsrc->deallocate() as necessary to release all allocated memory. Resets current_buffer and next_buffer_size to their initial values at construction.

Date: 2020-11-09.00:00:00

[ 2020-11-09 Approved In November virtual meeting. Status changed: Tentatively Ready → WP. ]

Date: 2020-10-15.00:00:00

[ 2020-10-06; moved to Tentatively Ready after seven votes in favour in reflector poll ]

Date: 2020-10-15.00:00:00

[ 2020-10-03; Daniel comments and provides improved wording ]

The recent wording introduces the very generic term "state" without giving a concrete definition of that term. During reflector discussions different interpretations of that term were expressed. The revised wording below gets rid of that word and replaces it by the actually involved exposition-only members.

Date: 2018-08-23.00:00:00

[ 2018-08-23 Batavia Issues processing ]

We liked Pablo's wording from the reflector discussion. Status to Open.

Previous resolution [SUPERSEDED]:

This wording is relative to N4750.

  1. Modify [mem.res.monotonic.buffer.mem] as indicated:

    void release();
    

    -1- Effects: Calls upstream_rsrc->deallocate() as necessary to release all allocated memory. Resets *this to its initial state at construction.

Date: 2018-06-23.00:00:00

[ 2018-06-23 after reflector discussion ]

Priority set to 2

Previous resolution [SUPERSEDED]:

This wording is relative to N4750.

[Drafting note: The resolution depicted below would make MSVC's and my-proposed-libc++'s implementations both conforming.]

  1. Modify [mem.res.monotonic.buffer.mem] as indicated:

    void release();
    

    -1- Effects: Calls upstream_rsrc->deallocate() as necessary to release all allocated memory. Resets the state of the initial buffer.

    -2- [Note: The memory is released back to upstream_rsrc even if some blocks that were allocated from this have not been deallocated from this. This function has an unspecified effect on next_buffer_size.end note]

Date: 2018-06-16.17:41:11

The effects of monotonic_buffer_resource::release() are defined as:

Calls upstream_rsrc->deallocate() as necessary to release all allocated memory.

This doesn't give any instruction on what to do with the memory controlled by the monotonic_buffer_resource which was not allocated, i.e., what to do with the initial buffer provided to its constructor.

Boost.Container's pmr implementation expels its initial buffer after a release(). Arthur O'Dwyer's proposed pmr implementation for libc++ reuses the initial buffer after a release(), on the assumption that this is what the average library user will be expecting.

#include <memory_resource>

int main() 
{
  char buffer[100];
  {
    std::pmr::monotonic_buffer_resource mr(buffer, 100, std::pmr::null_memory_resource());
    mr.release();
    mr.allocate(60);  // A
  }
  {
    std::pmr::monotonic_buffer_resource mr(buffer, 100, std::pmr::null_memory_resource());
    mr.allocate(60);  // B
    mr.release();
    mr.allocate(60);  // C
  }
}

Assume that allocation "B" always succeeds.
With the proposed libc++ implementation, allocations "A" and "C" both succeed.
With Boost.Container's implementation, allocations "A" and "C" both fail.
Using another plausible implementation strategy, allocation "A" could succeed but allocation "C" could fail. I have been informed that MSVC's implementation does this.

Which of these strategies should be permitted by the Standard?

Arthur considers "A and C both succeed" to be the obviously most user-friendly strategy, and really really hopes it's going to be permitted. Requiring "C" to succeed is unnecessary (and would render MSVC's current implementation non-conforming) but could help programmers concerned with portability between different implementations.

Another side-effect of release() which goes underspecified by the Standard is the effect of release() on next_buffer_size. As currently written, my interpretation is that release() is not permitted to decrease current_buffer_size; I'm not sure if this is a feature or a bug.

Consider this test case (taken from here):

std::pmr::monotonic_buffer_resource mr(std::pmr::new_delete_resource());
for (int i=0; i < 100; ++i) {
  mr.allocate(1);  // D
  mr.release();
}

Arthur believes it is important that the 100th invocation of line "D" does not attempt to allocate 2100 bytes from the upstream resource.

History
Date User Action Args
2023-11-22 15:47:43adminsetstatus: wp -> c++23
2020-11-09 21:40:50adminsetmessages: + msg11555
2020-11-09 21:40:50adminsetstatus: ready -> wp
2020-10-06 07:55:39adminsetmessages: + msg11510
2020-10-06 07:55:39adminsetstatus: open -> ready
2020-10-04 12:55:39adminsetmessages: + msg11506
2018-08-27 14:22:44adminsetmessages: + msg10136
2018-08-27 14:22:44adminsetstatus: new -> open
2018-06-25 00:47:25adminsetmessages: + msg9986
2018-06-16 16:33:06adminsetmessages: + msg9920
2018-06-10 00:00:00admincreate