Можно ли вызывать allocator::deallocate() в другом порядке, чем вызов allocator::allocate()?

N3485 20.6.9.1 [allocator.members]/1 говорит:

Вызовы этих функций, которые выделяют или освобождают конкретную единицу памяти, должны происходить в едином общем порядке, и каждый такой вызов освобождения должен происходить до следующего выделения (если таковое имеется) в этом порядке.

Последнее требование меня смущает. Похоже, что стандарт говорит, что если выделить блок памяти (назовем его блоком a), а затем выделить другой блок (назовем его блоком b), то нельзя освободить блок a, пока он не освободит блок b.

Если это действительно то, что влечет за собой этот абзац, я не понимаю, как можно было бы реализовать что-то вроде роста vector с эффективным использованием пространства; потому что нельзя было выделить больший буфер, а затем освободить ранее выделенный (слишком маленький) буфер.

Это действительно то, что означает этот абзац, или я неправильно читаю этот раздел?


person Billy ONeal    schedule 09.12.2012    source источник
comment
Вы нашли, наверное, самый плохо написанный пункт std.   -  person curiousguy    schedule 28.05.2019


Ответы (1)


Вызовы этих функций, которые выделяют или освобождают определенную единицу памяти, должны выполняться в едином общем порядке, и каждый такой вызов освобождения должен выполняться до следующего выделения (если таковое имеется) в этом порядке.

Из того, что мне кажется, он только устанавливает отношения «происходит до» между выделениями и освобождениями (возможно, для предотвращения проблем параллелизма, возникающих из-за неправильной оптимизации компилятора). Это определенно не устанавливает отношения между a и b, где a и b — разные выделенные области.

Помните, что компиляторы следуют спецификации, а не человеческой логике. Спецификация помогает программистам компилятора запомнить все детали, о которых необходимо позаботиться (если она соответствует спецификации, это правильно). Вот почему спецификация содержит детали, которые можно было бы счесть очевидными. Одна из деталей заключается в том, что освобождение/выделение памяти представляет собой барьер памяти.

Сравнивать

reads -> deallocation -> allocation -> writes

с

reads -> deallocation
allocation -> writes

Без отношения «происходит до» два потока могут одновременно использовать одну и ту же область памяти (единицу хранения), как это видно из области памяти. С отношением «происходит до» весь доступ из освобождающего потока должен быть сброшен до того, как выделяющий поток сможет его использовать.

person John Dvorak    schedule 09.12.2012
comment
+1 -- О, может ли цель заключаться в том, что вы не можете вызвать allocate в двух разных потоках и получить один и тот же указатель в обоих? - person Billy ONeal; 09.12.2012
comment
@BillyONeal помните, что компиляторы следуют спецификации, а не человеческой логике. Спецификация помогает программистам компилятора запомнить все детали, о которых необходимо позаботиться (если она соответствует спецификации, это правильно). - person John Dvorak; 09.12.2012
comment
Я не понимаю, что вы имеете в виду под этим комментарием по отношению к этому ответу/вопросу/комментарию. Да, компиляторы следуют спецификации; но люди реализуют компилятор, поэтому люди (и в данном случае я) должны понимать, что происходит, чтобы правильно следовать спецификации. Нужно понимать, что здесь происходит, чтобы написать совместимый распределитель или контейнер, поддерживающий распределителя. - person Billy ONeal; 09.12.2012
comment
Я считаю, что цель состоит в том, чтобы предотвратить проблемы параллелизма, возникающие из-за задержки освобождения одного потока (что является просто записью в память) после выделения другого потока. - person John Dvorak; 09.12.2012
comment
@JohnDvorak Если освобождение задерживается, как может произойти выделение? - person curiousguy; 28.05.2019
comment
@curiousguy Если поток вызывает распределитель, чтобы освободить часть памяти, но затем использует эту память, как будто это не так, он столкнется с каким-то другим потоком, которому тем временем удалось захватить тот же фрагмент памяти. Поэтому вам нужна спецификация, которая запрещает компилятору делать именно это, сообщая потоку, что освобождение памяти еще не произошло. - person John Dvorak; 28.05.2019
comment
@JohnDvorak Может быть ... Я все еще интуитивно думаю, что дело распределителя - предоставить соответствующую гарантию, и ее вообще не нужно писать. - person curiousguy; 28.05.2019
comment
@curiousguy поведение распределителя (выделения не перекрываются) звучит как вещи, которые принадлежат спецификации, и поэтому должны быть записаны. В противном случае на него нельзя было бы положиться. - person John Dvorak; 28.05.2019
comment
@JohnDvorak Почему в спецификации даже используется концепция, связанная с потоком? Это всего лишь один поток. Это единственное место в std, где это происходит? - person curiousguy; 29.05.2019
comment
@JohnDvorak Я задал вопрос о состоянии гонки, создаваемом автоматическими объектами, и, похоже, все согласны с тем, что проблема не т существует. - person curiousguy; 29.05.2019