Исключительная безопасность на арене памяти - PullRequest
1 голос
/ 21 декабря 2011

Я пишу простой распределитель памяти и сталкиваюсь с небольшой проблемой с безопасностью исключений.Ситуация такова, когда вы выделяете объект, который сам вызывает распределитель.Задача пула памяти - выделить несколько объектов одновременно, а затем удалить их все при уничтожении пула.

{
    MemoryArena m;
    std::string* ptr = m.Allocate<std::string>();
    // use ptr whatever
    // Cleaned up when pool is destroyed
}

Но это становится довольно неприятным, когда он используется несколько раз.Если внутреннее распределение очищено, то оно может быть использовано впоследствии - не плохое предположение, так как это определение пула никогда не удалять объекты, пока не истечет его время жизни.рассмотрим:

struct X {
    X(MemoryArena* ptr, std::string*& ref) {
        ref = ptr->Allocate<std::string>();
        throw std::runtime_error("hai");
    }
};
MemoryArena m;
std::string* ptr;
m.Allocate<X>(&m, ptr);
// ptr is invalid- even though it came from the arena 
// which hasn't yet been destroyed

Но если внутреннее распределение не не очищено, внешнее распределение также не может быть очищено, потому что арена памяти распределяет их линейно, как на оборудованиистека, поэтому я утечки памяти.Поэтому я либо нарушаю семантику, рано уничтожая объект, либо теряю память.

Есть предложения по решению этой проблемы?

Ответы [ 2 ]

2 голосов
/ 21 декабря 2011

Может быть, это только пример кода, к которому это относится, но я не думаю, что пользователь должен предполагать, что ptr является действительным, когда конструктор X выбрасывает.С таким же успехом его можно было бы сгенерировать до назначения ref.

Так что я бы сказал, очистите внутренний объект, если можете (то есть, если передняя часть арены в настоящее время находится в конце внутреннего объекта).Хорошо, распределение с арены становится недействительным, что ненормально, но это распределение, которое так или иначе никогда бы не вышло в реальный мир.

Возможно, вы могли бы сделать это явным, с концепцией«мягкое» распределение.Не гарантировано, что жить вечно, потому что, будучи «мягким», его можно вернуть обратно на арену.Тогда конструктор X будет делать что-то вроде:

SoftPtr<std::string> tmp(ptr->SoftAllocate<std::string>());
stuff_that_might_throw(); 
ref = tmp.release();

Выполнение деструктора SoftPtr без предварительного вызова release подразумевает, что никакая ссылка на объект не была открыта.Он вызывает функцию MemoryArena, которая выполняет что-то вроде:

  • уничтожает объект
  • проверяет, является ли это самым последним выделением с арены
    • если это так, вычтите размер из указателя текущей позиции арены
    • , если нет, ничего не делайте (утечка памяти)

Таким образом, любое количество выделений может быть "отменено" при условии, что это сделано в обратном порядке.

0 голосов
/ 21 декабря 2011

По самой семантике пулов памяти и, как вы сами заявили в вопросе, может быть освобожден только пул в целом, а не отдельные объекты.Тем не менее, вы хотите сделать именно это.

Можно снабдить распределитель brk-подобными функциями для получения и установки следующего адреса выделения.Это дает вам низкоуровневый механизм, который вы можете использовать, чтобы построить на нем все, что захотите.

...