Texture object locking
Neil Roberts
neil at linux.intel.com
Tue Jun 10 11:40:34 PDT 2014
Jason wrote:
> Kristian and I were looking at this today, and there seems to be a
> substantial race in the way that we are doing texture locking.
Yes, it does look like this is a problem. I also noticed another
dodgy-looking pattern when implementing the GL_ARB_clear_texture
extension. Whenever it enters a meta function that operates on a
texture image it unlocks the texture object and then does stuff
directly using the texture image pointer. Presumably that pointer
could also be freed at any point. Take a look at
copytexsubimage_using_blit_framebuffer in meta.c for an example.
I suppose in practice though it's probably not all that likely that an
application will go and delete textures in another thread without the
other threads expecting it so I don't know whether it's a major
concern.
I knocked up the following patch to Weston's simple-egl example to try
and get it to crash when deleting textures from multiple threads and
sure enough it does segfault.
Regards,
- Neil
------- >8 --------------- (use git am --scissors to automatically chop here)
Subject: Hack to test multi-threaded deleting textures
---
clients/simple-egl.c | 125 +++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 112 insertions(+), 13 deletions(-)
diff --git a/clients/simple-egl.c b/clients/simple-egl.c
index 2097b4c..6e592b4 100644
--- a/clients/simple-egl.c
+++ b/clients/simple-egl.c
@@ -39,6 +39,7 @@
#include <GLES2/gl2.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <pthread.h>
#include "xdg-shell-client-protocol.h"
@@ -100,6 +101,13 @@ struct window {
int fullscreen, opaque, buffer_size, frame_sync;
};
+struct thread_data {
+ EGLDisplay dpy;
+ EGLSurface surface;
+ EGLContext ctx;
+ int thread_num;
+};
+
static const char *vert_shader_text =
"uniform mat4 rotation;\n"
"attribute vec4 pos;\n"
@@ -117,15 +125,25 @@ static const char *frag_shader_text =
" gl_FragColor = v_color;\n"
"}\n";
+static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+};
+
static int running = 1;
+#define N_THREADS 63
+#define N_TEXTURES 512
+#define TEX_SIZE 128
+
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static uint64_t running_threads;
+static GLuint textures[N_TEXTURES];
+
static void
init_egl(struct display *display, struct window *window)
{
- static const EGLint context_attribs[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_NONE
- };
const char *extensions;
EGLint config_attribs[] = {
@@ -748,13 +766,101 @@ usage(int error_code)
exit(error_code);
}
+static GLuint
+create_texture(void)
+{
+ GLuint tex;
+ GLubyte data[TEX_SIZE * TEX_SIZE * 4];
+
+ glGenTextures(1, &tex);
+ glBindTexture(GL_TEXTURE_2D, tex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, data);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ return tex;
+}
+
+static void *
+thread_func(void *data)
+{
+ struct thread_data *thread_data = data;
+ int i;
+
+ eglMakeCurrent(thread_data->dpy,
+ thread_data->surface,
+ thread_data->surface,
+ thread_data->ctx);
+
+ while (true) {
+ /* Wait until this thread is told to run */
+ pthread_mutex_lock(&mutex);
+ while (!(running_threads & (1ULL << thread_data->thread_num)))
+ pthread_cond_wait(&cond, &mutex);
+ pthread_mutex_unlock(&mutex);
+
+ /* Delete all of the textures */
+ for (i = 0; i < N_TEXTURES; i++)
+ glDeleteTextures(1, textures + i);
+
+ /* Report that our thread is no longer running */
+ pthread_mutex_lock(&mutex);
+ running_threads &= ~(1ULL << thread_data->thread_num);
+ pthread_cond_broadcast(&cond);
+ pthread_mutex_unlock(&mutex);
+ }
+
+ return data;
+}
+
+static void
+delete_thread_test(struct display *display)
+{
+ pthread_t thread;
+ struct thread_data thread_data[N_THREADS];
+ int i;
+
+ for (i = 0; i < N_THREADS; i++) {
+ thread_data[i].dpy = display->egl.dpy;
+ thread_data[i].surface = display->window->egl_surface;
+ thread_data[i].ctx = eglCreateContext(display->egl.dpy,
+ display->egl.conf,
+ display->egl.ctx,
+ context_attribs);
+ thread_data[i].thread_num = i;
+ pthread_create(&thread,
+ NULL, /* attr */
+ thread_func,
+ thread_data + i);
+ }
+
+ pthread_mutex_lock(&mutex);
+
+ while (true) {
+ /* Wait until all of the threads have finished trying
+ * to delete the textures */
+ while (running_threads)
+ pthread_cond_wait(&cond, &mutex);
+
+ /* Create some textures to delete */
+ for (i = 0; i < N_TEXTURES; i++)
+ textures[i] = create_texture();
+
+ /* Set all the threads running */
+ running_threads = (1ULL << N_THREADS) - 1;
+ pthread_cond_broadcast(&cond);
+ }
+
+ pthread_mutex_unlock(&mutex);
+}
+
int
main(int argc, char **argv)
{
struct sigaction sigint;
struct display display = { 0 };
struct window window = { 0 };
- int i, ret = 0;
+ int i;
window.display = &display;
display.window = &window;
@@ -800,14 +906,7 @@ main(int argc, char **argv)
sigint.sa_flags = SA_RESETHAND;
sigaction(SIGINT, &sigint, NULL);
- /* The mainloop here is a little subtle. Redrawing will cause
- * EGL to read events so we can just call
- * wl_display_dispatch_pending() to handle any events that got
- * queued up as a side effect. */
- while (running && ret != -1) {
- wl_display_dispatch_pending(display.display);
- redraw(&window, NULL, 0);
- }
+ delete_thread_test(&display);
fprintf(stderr, "simple-egl exiting\n");
--
1.9.3
More information about the wayland-devel
mailing list