I was having some issues with new/deletes occurring on different threads (TI F28379D) that were causing the program to lock if an interrupt occurred in the middle of allocating or deleting. I didn't want to wrap every instance of new/delete with a macro that disables and enables interrupts, as it felt like a lot of duplicate code. My solution was to create two functions that have the appropriate macros around the new/delete.
void *create_array_pointer(uint16_t count, size_t size, char *variable_name) {
//create the array (inside where interrupts are disabled
uint16_t interrupt_settings = __disable_interrupts();
void *ptr = new uint16_t[count*size];
__restore_interrupts(interrupt_settings);
check_allocation_throw_error(ptr, variable_name);
return ptr;
}
void delete_array_pointer(void *ptr) {
uint16_t interrupt_settings = __disable_interrupts();
delete[] ptr;
ptr = NULL;
__restore_interrupts(interrupt_settings);
}
This works for primitive types. However, I realized today that this is a bad solution for c++ classes that have default constructors/destructors, as the constructor or destructor will never be called automatically.
Is there some way to make sure the constructors/destructors are called, without going back to wrapping every call?
I am assuming you are in an environment where you can't use containers or smart pointers. Otherwise I would recommend returning std::vector
or std::unique_ptr
.
You can use templates to retain the type of the objects you are creating whilst still being generic.
Also you may want to consider a RAII
way to manage your interrupts as they will be exception safe as well as being generally safer and easier:
template<typename T>
void check_allocation_throw_error(T*, char const*) {}
// This will disable interrupts on creation and re-enable them
// when it goes out of cope. This means if you reach the end
// of the function OR if an exception is thrown the interrupts
// will be re-enabled regardless.
class interrupt_disabler
{
public:
interrupt_disabler(): settings(__disable_interrupts()) {}
~interrupt_disabler() { __restore_interrupts(settings); }
private:
interrupt_disabler(interrupt_disabler const&) {}
interrupt_disabler& operator=(interrupt_disabler const&) { return *this; }
uint16_t settings;
};
template<typename T>
T* create_array_of(uint16_t count, char const* variable_name)
{
T* ptr = 0;
// create a scope where interrupts are disabled
{
interrupt_disabler lock(); // enabled at end of scope OR if new throws
ptr = new T[count];
}
// interrupts enabled automatically here
check_allocation_throw_error(ptr, variable_name);
return ptr;
}
template<typename T>
void delete_array(T* ptr)
{
interrupt_disabler lock(); // enabled at end of scope
delete[] ptr;
}
struct MyType {}; // any old type
int main()
{
MyType* mytype = create_array_of<MyType>(10, "wibble");
delete_array(mytype);
}