Using pthread results in failed asseration

1.question

It can works if implement the code in on_button_execute() directly.
But it will throw error if create a thread and implement code in the thread entry.

What happened ? The rookie need your help, please.

2. error message

Gtk:ERROR:../../../gtk/gtktextview.c:4748:gtk_text_view_validate_onscreen: assertion failed: (priv->onscreen_validated)
Bail out! Gtk:ERROR:../../../gtk/gtktextview.c:4748:gtk_text_view_validate_onscreen: assertion failed: (priv->onscreen_validated)

And sometimes it may also be:

(main:23234): Gtk-WARNING **: 23:19:02.356: Invalid text buffer iterator: either the iterator is uninitialized, or the characters/paintables/widgets in the buffer have been modified since the iterator was created.
You must use marks, character numbers, or line numbers to preserve a position across buffer modifications.
You can apply tags and insert marks without invalidating your iterators,
but any mutation that affects 'indexable' buffer contents (contents that can be referred to by character offset)
will invalidate all outstanding iterators
**
Gtk:ERROR:../../../gtk/gtktextview.c:4748:gtk_text_view_validate_onscreen: assertion failed: (priv->onscreen_validated)
Bail out! Gtk:ERROR:../../../gtk/gtktextview.c:4748:gtk_text_view_validate_onscreen: assertion failed: (priv->onscreen_validated)

3. source code

Input apt search ls and press Enter, which will output lots of text, then you can reproduce this error.

head.h:

#pragma  once

#include <gtkmm.h>
#include <iostream>
#include <stdlib.h>
#include <pthread.h>

class TextEditor : public Gtk::Window {
public:
	TextEditor();
protected:
	void on_button_execute();
	
	Gtk::Box m_vbox_top, m_hbox;
	Gtk::TextView m_TextView_output;
	Glib::RefPtr<Gtk::TextBuffer> m_refTextBuffer;
	Gtk::ScrolledWindow m_ScrolledWindow_output;
	Gtk::Entry m_Entry_cmdline;
	Gtk::Button m_Button_execute;
	pthread_t m_thread_execute;
	pthread_mutex_t m_mutex_execute;
private:
	static void *thread_entry (void *self_ptr);
};

TextEditor::TextEditor() :
	m_vbox_top (Gtk::Orientation::VERTICAL),
	m_hbox (Gtk::Orientation::HORIZONTAL),
	m_Button_execute ("execute")
{
	pthread_mutex_init (&m_mutex_execute, NULL);
	
	set_default_size (640, 480);
	set_child (m_vbox_top);
	set_default_widget (m_Button_execute);
	//m_vbox layout
	m_vbox_top.append (m_hbox);
	m_vbox_top.append (m_ScrolledWindow_output);
	
	//m_ScrolledWindow_output
	m_ScrolledWindow_output.set_child (m_TextView_output);
	m_ScrolledWindow_output.set_expand();
	
	//m_hbox layout
	m_hbox.append (m_Entry_cmdline);
	m_hbox.append (m_Button_execute);
	
	//m_Button_execute
	m_Button_execute.signal_clicked().connect (
	    sigc::mem_fun (*this, &TextEditor::on_button_execute)
	);
	
	//m_Entry
	m_Entry_cmdline.set_margin (10);
	m_Entry_cmdline.set_size_request (400, 20);
	m_Entry_cmdline.set_activates_default();
	
	//m_refTextBuffer
	m_refTextBuffer = Gtk::TextBuffer::create();
	
	//m_TextView_output
	m_TextView_output.set_buffer (m_refTextBuffer);
	m_TextView_output.set_expand();
	m_TextView_output.set_margin (10);
	m_TextView_output.set_editable (false);
	
}

void *TextEditor::thread_entry (void *self_ptr)
{
	auto self = reinterpret_cast<TextEditor *> (self_ptr);
	pthread_mutex_lock (&self->m_mutex_execute);
	
	const Glib::ustring cmd = self->m_Entry_cmdline.get_text();
	
	FILE *p_bash = popen (cmd.c_str(), "r");
	if (p_bash == NULL) {
		std:: cerr << "failed to create process\n";
		pthread_exit (NULL);
	}
	auto text_buf = self->m_refTextBuffer;
	text_buf->set_text ("");
	char result[4096];
	while (fgets (result, sizeof (result), p_bash) != NULL) {
		text_buf->insert (text_buf->end(), result);
	}
	pclose (p_bash);
	
	pthread_mutex_unlock (&self->m_mutex_execute);
	
	return NULL;
}

void TextEditor::on_button_execute()
{
	pthread_create (&m_thread_execute, NULL, TextEditor::thread_entry, this);
}

main.cpp:

#include "head.h"
#include <gtkmm/application.h>
int main (int argc, char **argv)
{
	auto app = Gtk::Application::create();
	return app->make_window_and_run<TextEditor> (argc, argv);
}

4. expectde results

change TextEditor::on_button_execute() as below and it can work.


void TextEditor::on_button_execute()
{
	//  pthread_create (&m_thread_execute, NULL, TextEditor::thread_entry, this);
	
	auto self = reinterpret_cast<TextEditor *> (this);
	pthread_mutex_lock (&self->m_mutex_execute);
	
	const Glib::ustring cmd = self->m_Entry_cmdline.get_text();
	
	FILE *p_bash = popen (cmd.c_str(), "r");
	if (p_bash == NULL) {
		std:: cerr << "failed to create process\n";
		pthread_exit (NULL);
	}
	auto text_buf = self->m_refTextBuffer;
	text_buf->set_text ("");
	char result[4096];
	while (fgets (result, sizeof (result), p_bash) != NULL) {
		text_buf->insert (text_buf->end(), result);
	}
	pclose (p_bash);
	
	pthread_mutex_unlock (&self->m_mutex_execute);
}

Hello, GTK is not thread safe. You should never call any GTK function from a separate thread. To post work back to the main thread you can use idle functions, see the gtkmm tutorial: Idle Functions.

If all you want to do is read a file, you can also use async methods in giomm. That way you can avoid implementing your own threading.

2 Likes

OK. Thank you. Iā€™m still stay in chapter 11 so I know less about gtk.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.