Commit 5bc35f9e by FritzFlorian

Add deconstructor calls to tasks.

Our stack is not calling deconstructors of its elements. This is problematic for e.g. the graph implementation where reference counted images are hold in tasks. To solve this for now we manually call the deconstructor after each tasks (we do so, because a generic, virtual deconstructor adds runtime costs to primitive tasks, requiring us to re-run all benchmarks; with this change we do not need to do this and as we re-work the scheduler anyways we postpone a clean implementation for then).
parent eca0dd4d
Pipeline #1331 passed with stages
in 4 minutes 55 seconds
......@@ -93,6 +93,9 @@ class scan_task : public pls::internal::scheduling::task {
}
}
}, fixed_strategy{1});
wait_for_all();
this->~scan_task();
}
};
......
......@@ -105,6 +105,10 @@ class function_node<inputs<I0, I...>, outputs<O0, O...>, F> : public node {
auto invocation = new(memory) invocation_memory{};
invocation->inputs_missing_ = num_in_ports;
};
void clean_up_instance_buffer(void *memory) const override {
auto invocation = reinterpret_cast<invocation_memory *>(memory);
invocation->~invocation_memory();
}
private:
multi_in_port_type in_port_;
......
......@@ -115,6 +115,10 @@ class graph<inputs<I0, I...>, outputs<O0, O...>> : public node {
void init_instance_buffer(void *memory) const override {
new(memory) invocation_memory{};
};
void clean_up_instance_buffer(void *memory) const override {
auto invocation = reinterpret_cast<invocation_memory *>(memory);
invocation->~invocation_memory();
}
private:
inputs_type inputs_;
......
......@@ -129,9 +129,22 @@ class graph<inputs<I0, I...>, outputs<O0, O...>>::run_graph_task : public pls::t
self_->get_invocation<invocation_memory>(invocation_)->output_buffer_ = output;
}
~run_graph_task() {
node *iterator = self_->node_list_start_;
for (int i = 0; i < self_->num_nodes_; i++) {
void* memory = invocation_.get_instance_buffer<void>(i);
iterator->clean_up_instance_buffer(memory);
iterator = iterator->direct_successor_;
}
}
void execute_internal() override {
PROFILE_WORK_BLOCK("Graph Invocation")
feed_inputs<0, I0, I...>{self_->inputs_, input_, invocation_}.run();
wait_for_all();
this->~run_graph_task();
PROFILE_END_BLOCK
}
};
......
......@@ -102,6 +102,10 @@ class merge_node : public node {
auto invocation = new(memory) invocation_memory{};
invocation->inputs_missing_ = INITIAL_STATE;
};
void clean_up_instance_buffer(void *memory) const override {
auto invocation = reinterpret_cast<invocation_memory *>(memory);
invocation->~invocation_memory();
}
private:
multi_in_port_type in_port_;
......
......@@ -25,6 +25,7 @@ class node {
virtual int instance_buffer_size() const = 0;
virtual void init_instance_buffer(void *memory) const = 0;
virtual void clean_up_instance_buffer(void *memory) const = 0;
virtual bool is_fully_connected() const = 0;
......
......@@ -60,6 +60,9 @@ class split_node : public node {
void init_instance_buffer(void *) const override {
// No need for memory, we simply forward entries without buffering
};
void clean_up_instance_buffer(void *memory) const override {
// No need for memory, we simply forward entries without buffering
}
private:
multi_in_port_type in_port_;
......
......@@ -86,6 +86,10 @@ class switch_node : public node {
auto invocation = new(memory) invocation_memory{};
invocation->inputs_missing_ = 2;
};
void clean_up_instance_buffer(void *memory) const override {
auto invocation = reinterpret_cast<invocation_memory *>(memory);
invocation->~invocation_memory();
}
private:
multi_in_port_type in_port_;
......
......@@ -18,6 +18,9 @@ class lambda_task_by_reference : public task {
protected:
void execute_internal() override {
function_();
wait_for_all();
this->~lambda_task_by_reference<Function>();
}
};
......@@ -31,6 +34,9 @@ class lambda_task_by_value : public task {
protected:
void execute_internal() override {
function_();
wait_for_all();
this->~lambda_task_by_value<Function>();
}
};
......
......@@ -13,6 +13,18 @@ namespace pls {
namespace internal {
namespace scheduling {
/**
* A task to be executed by the runtime system.
* Tasks are guaranteed to be executed exactly once.
*
* Override the execute_internal() method for your custom code.
*
* IMPORTANT:
* Tasks memory is re-used without calling the destructor.
* You must call it yourself at the end of execute_internal().
* This is done to not introduce any overhead of virtual function calls
* if no clean up is required.
*/
class task {
friend class scheduler;
......@@ -29,6 +41,10 @@ class task {
protected:
/*
* Must call the parent constructor.
*
* IMPORTANT:
* Tasks memory is re-used without calling the destructor.
* You must call it yourself at the end of execute_internal().
*/
explicit task();
......@@ -37,6 +53,9 @@ class task {
* Memory will be pushed onto the stack (in aligned memory, thus avoid many small chunks).
* MUST be called in constructor, never afterwards.
*
* Memory is fully self managed. Calling e.g. deconstructors when not needing objects
* anymore is the users responsibility (memory is simply re-used after the life time of the task ends).
*
* @param size Number of bytes to be allocated
* @return The allocated memory region
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment