This may be a total obvious error for some, but I can't seem to find out why this segmentation fault happens.
I do understand that segmentation faults occur when accessing an address that my program should not access.
First, I have this base-class called "UiObject":
class UiObject {
private:
public:
UiObject();
virtual void render(char * imageBuffer) = 0;
};
Then, I am deriving multiple other classes from the UiObject class: For example, the ProgressBar class:
class ProgressBar : public UiObject {
private:
int x,y; // Position der ProgressBar auf dem Bildschirm (oben links) (Pixeln)
int width,height; // Breite und Höhe in Pixeln
public:
int maxProgress; // Maximal zu erreichner Fortschritt
long progress; // Fortschritt aktuell
ProgressBar(); // Constructor
void render(char * framebuffer); // Rendermethode
};
Now, these objects are managed and rendered by an UiManager. The function renderDisplay() is a thread created with std::thread:
void UiManager::renderDisplays() {
printf("[RENDER] Render thread has been started\n[RENDER] (%x)\n", this);
while(!this->shouldStop) {
// Removed code that manages the timing of this function (running approximately 60FPS)
// Rendering:
for(Display d: displayArray) {
char * framebuffer = d.getFrameBuffer();
printf("Framebuffer: %x\n", framebuffer);
printf("uiArray: %x\n", uiArray);
printf("&uiArray: %x\n", &uiArray);
for(UiObject* pObject : uiArray) {
printf("[RENDER] (%x) Rendering Object...", pObject);
pObject->render(framebuffer);
}
d.renderDisplay();
}
}
}
As you can see, for every added Display I am retrieving the framebuffer. I'm opening the /dev/fbX files so i can draw directly onto the screen. This all works fine. d.getFramebuffer() returns a char * pointer so i can manipulate the pixels, which is then passed into the render(char * framebuffer) function of any UiObject.
However, this function call is creating a segfault, and I can't understand why. There are sometimes when it works and just runs until the thread should stop, and sometimes it crashes immidiately after the first render-function call.
I add the UiObjects to the UiManager using this:
ProgressBar pBar;
pBar.maxProgress = 60*15;
pBar.progress = 1;
uiArray.push_back(&pBar);
The UiManager has this as its class-definition:
class UiManager {
private:
char ttyfd; // TTY File Descriptor.
std::thread uiManagerThread;
std::chrono::system_clock::time_point timeA;
std::chrono::system_clock::time_point timeB;
std::chrono::duration<double, std::milli> work_time;
public:
std::vector<Display> displayArray;
std::vector<UiObject*> uiArray;
bool shouldStop;
bool displayFPS;
UiManager();
void stop();
void renderDisplays();
void addDisplay(Display d);
void startThread();
void stopThread();
};
Now I'm wondering why this is.
According to the cppreference https://en.cppreference.com/w/cpp/language/abstract_class and their documentation about abstract classes, I'm not allowed to have the =0; in my UiObject class. If I remove it, I will get compiler warnings during linking with the message "undefined reference to vtable for UiObject".
What have I done wrong?
I suspect that the std::vector and the for-loop are not quite working in the UiManager.
Im using g++ on debian as my compiler.
My console output:
[RENDER] Render thread has been started
[RENDER] (6ea609f0)
Framebuffer: e09f0010
uiArray: e09eed20
&uiArray: 6ea60a30
./run.sh: Zeile 21: 25018 Speicherzugriffsfehler ./a.out
It also does not even jump into the ProgressBar render routine. I also commented out the contents of the render-function, therefore i can only suspect that specific function call.
= 0
for an abstract base class is correct.
However,
ProgressBar pBar;
//...
uiArray.push_back(&pBar);
is not.
pBar
will be destroyed once the scope in which it is declared is left and then trying to dereference the dangling pointer later in renderDisplays
is undefined behavior.
You need to create the object with new
, or better store std::unique_ptr<UiObject>
in the vector and create objects with std::make_unique<ProgressBar>()
.
Also, UiObject
needs a virtual destructor: virtual ~UiObject() = default;
Otherwise destroying objects through the base class pointer will cause undefined behavior.
Then there seems to be also no synchronization between the access to uiArray
in the two threads.
Accessing uiArray
for modification and read in two threads without synchronization is a data race and undefined behavior.
You need to add a mutex and lock it when accessing uiArray
in both threads.