Исключение не уничтожается - PullRequest
0 голосов
/ 28 июня 2018

Я пытаюсь реализовать мою версию __cxa_allocate_exception и __cxa_free_exception, чтобы избежать выделения памяти на throw.

Итак, я реализовал пул памяти, который, кажется, работает нормально. Но при тестировании с вложенными исключениями деструктор исключений вызывался не во всех случаях, и поэтому __cxa_free_exception также не вызывался, в результате чего пул памяти со временем заполнялся.

См. Следующий пример кода:

class MyException {
public:
  MyException() {
    std::cout << "MyException constructed." << std::endl;
  }
  ~MyException() {
    std::cout << "MyException destroyed." << std::endl;
  }
};

void * __cxa_allocate_exception(size_t thrown_size)
{
  const auto mem = malloc(thrown_size); //Not part of the example
  std::cout << "allocate: " << mem <<  std::endl;
  return mem;
}

void __cxa_free_exception(void *thrown_object)
{
  std::cout << "free: " << thrown_object << std::endl;

  free(thrown_object); //Not part of the example.
}

void non_rec() {
  try {
    throw MyException();
  } catch(...) {
    try {
      throw MyException();
    } catch(...) {
      //...
    }
  }
}

int main() {
  while(true) {
    non_rec();
    std::cout << "-----------" << std::endl;
  }
}

Вывод этой программы:

allocate: 0x8cbc20
MyException constructed.
allocate: 0x8cc030
MyException constructed.
MyException destroyed.
free: 0x8cc030
MyException destroyed.
free: 0x8cbc20
-----------
allocate: 0x8cbc20
MyException constructed.
allocate: 0x8cc030
MyException constructed.
MyException destroyed.
free: 0x8cc030
-----------
allocate: 0x8cc030
MyException constructed.
allocate: 0x8cc440
MyException constructed.
MyException destroyed.
free: 0x8cc440
-----------
allocate: 0x8cc440
MyException constructed.
allocate: 0x8cc850
MyException constructed.
MyException destroyed.
free: 0x8cc850

В первый раз работает правильно. Но после этого два исключения создаются и распределяются в каждой итерации цикла, а освобождается и уничтожается только одно.

Я использую g ++ 5.4.0 в Ubuntu 16.04.

Ответы [ 2 ]

0 голосов
/ 28 июня 2018

Я думаю, что короткий ответ на это, что это не определено. В разделе 18.1.4 стандарта C ++ сказано:

Память для объекта исключения выделена неопределенным образом ...

MSVC, например, выделяет его в стеке . Удачи в этом.

Тем не менее, интересно выяснить, почему написанный код не работает (как и для других комментаторов, gcc сообщает о повреждении памяти, когда я пытаюсь его запустить), и, как говорит @AlanBirtles, ответ лежит здесь:

https://code.woboq.org/gcc/libstdc++-v3/libsupc++/eh_alloc.cc.html

Если вы посмотрите на реализацию __cxa_allocate_exception (строка 279), вы увидите, что она делает три вещи, которые вы не делаете:

  • выделяет дополнительное пространство для заголовка (частного) типа __cxa_refcounted_exception
  • обнуляет этот заголовок
  • возвращает указатель на первый байт после этого заголовка

Затем в __cxa_free_exception он разрешает настройку указателя перед его освобождением.

Так что достаточно легко заставить его работать, просто сделайте что-то вроде этого (или, может быть, вы можете проложить свой путь к объявлению __cxa_refcounted_exception, я думаю, что это где-то на этом сайте):

#define EXTRA 1024

extern "C" void * __cxa_allocate_exception(size_t thrown_size)
{
  void *mem = malloc (thrown_size + EXTRA);
  std::cout << "allocate: " << mem <<  " (" << thrown_size << ") " << std::endl;
  memset (mem, 0, EXTRA);
  return (char *) mem + EXTRA;
}

extern "C" void __cxa_free_exception(void *thrown_object)
{
  std::cout << "free: " << thrown_object << std::endl;
  char *mem = (char *) thrown_object;
  mem -= EXTRA;
  free (mem);
}

И когда я запускаю это в Wandbox , я получаю:

allocate: 0x1e4c990 (1) 
MyException constructed.
allocate: 0x1e4ddb0 (1) 
MyException constructed.
MyException destroyed.
free: 0x1e4e1b0
MyException destroyed.
free: 0x1e4cd90
-----------
allocate: 0x1e4c990 (1) 
MyException constructed.
allocate: 0x1e4ddb0 (1) 
MyException constructed.
MyException destroyed.
free: 0x1e4e1b0
MyException destroyed.
free: 0x1e4cd90
-----------
allocate: 0x1e4c990 (1) 
MyException constructed.
allocate: 0x1e4ddb0 (1) 
MyException constructed.
MyException destroyed.
free: 0x1e4e1b0
MyException destroyed.
free: 0x1e4cd90
-----------

Этот не работает с clang , поэтому они должны делать вещи по-другому. Как я уже сказал, это UB, так что будьте осторожны.

0 голосов
/ 28 июня 2018

Выделение правильного объема памяти аналогично libstdc ++ исправляет сбои для меня:

#include <iostream>
#include <cstdlib>
#include <exception>
#include <cstring>

class MyException {
public:
  MyException() {
    std::cout << "MyException constructed." << std::hex << (size_t)this << std::endl;
  }
  ~MyException() {
    std::cout << "MyException destroyed." << std::hex << (size_t)this << std::endl;
  }
};

const size_t __cxa_refcounted_exception_size = 16 * sizeof(size_t); // approx sizeof(__cxa_refcounted_exception)

void * __cxa_allocate_exception(size_t thrown_size)
{
  thrown_size += __cxa_refcounted_exception_size;
  const auto mem = malloc(thrown_size);
  std::cout << "allocate: " << mem <<  std::endl;
  memset (mem, 0, __cxa_refcounted_exception_size);
  return (void *)((char *)mem + __cxa_refcounted_exception_size);
}

void __cxa_free_exception(void *thrown_object)
{
  std::cout << "free: " << thrown_object << std::endl;
  char *ptr = (char *) thrown_object - __cxa_refcounted_exception_size;

  free(ptr);
}

void non_rec() {
  try {
    throw MyException();
  } catch(...) {
    try {
      throw MyException();
    } catch(...) {
      //...
    }
  }
}

int main() {
  for (int i=0;i<4;i++) {
    non_rec();
    std::cout << "-----------" << std::endl;
  }
}

Вы должны найти фактическое значение sizeof(__cxa_refcounted_exception_size) для вашей платформы, включив unwind-cxx.h .

...