19 de Septiembre, 2009

Perl, GTK2 y threads

Aunque en realidad es un problema que me he encontrado con GTK cuando he querido hacer tareas que requieren cierto tiempo, Perl añade alguna pega más a la solución.

El problema surge por el funcionamiento de las aplicaciones GTK, que se basan en un bucle en el que se van procesando eventos (se hace click, se pulsa una tecla, X11 pide que se redibuje la ventana, etc), y si nos entretenemos demasiado, los eventos se amontonan sin procesar (lo más notable es que no se redibuja la ventana, por ejemplo).

Ni que decir tiene que esto es muy poco atractivo para el usuario, así que se nos proponen varias soluciones, como por ejemplo: Glib::Idle o Glib::Timeout.

En el primer caso podemos indicar una función que se ejecutará cada vez que el bucle de GTK no tenga nada que hacer, y en segundo podemos hacer que la función se ejecute cada n unidades de tiempo.

Así que si podemos desmenuzar la tarea a realizar de forma que se pueda hacer en varios pasos relativamente rápidos, con estas dos propuestas podemos evitar el problema (aunque el interfaz seguirá sin responder en realidad, pero no se notará :P).

Esto es, por ejemplo, lo que he hecho en la carga de las imágenes de Nautilus Flickr Uploader cuando se genera la lista de vista previa con thumbnails (versiones reducidas de la imágenes a subir). No es óptimo y depende de lo grandes que sean la imágenes y de la potencia de nuestra máquina, pero en general es aceptable.

Cuando intentamos reproducir el esquema al subir las fotos, vemos que no es viable :(. Los ficheros se cargan en Flickr de forma que el proceso no se puede interrumpir (uso LWP::UserAgent, y no me da muchas posibilidades), y normalmente es demasiado tiempo... con lo que el interfaz se congela nuevamente (no se procesan eventos en absoluto).

En este caso lo que sería ideal sería hacer dos cosas a la vez, y lo lógico es pensar en threads. Pero meterse ahí tiene algunas pegas (el documento puede no estar muy actualizado).

En primer lugar hay que descartar completamente cualquier tipo de acción contra el interfaz de GTK desde un hilo que no sea el que se encarga del bucle de GTK, algo que me parece razonable.

Además, por lo que he leído (y no sé si sigue aplicando en Perl v5.10.0 :P), cuando se crea un hilo, se duplica el intérprete, y se clonan todos los objetos... incluidos los relacionados con GTK, así que si termina el hilo en segundo plano, puede ser fatal para el interfaz gráfico de nuestra aplicación :(.

Las soluciones son simples, pero no siempre fáciles de aplicar:

  • Crear los hilos antes de crear ningún objeto GTK (es algo que me han recomendado desde varias fuentes). Ojo que no hablo del soporte de threads de la Glib, ya que no hay ni rastro en los bindings para Perl (?).
  • Crear los hilos cuando nos haga falta, pero asegurarnos de que siguen ahí hasta que acaba la aplicación (los podemos reutilizar, y dormirlos cuando no nos hagan falta). Este caso me vale muy bien, porque una vez que subo las fotos, arranco el navegador por defecto y ya no importa demasiado en interfaz GTK ni los hilos ;).

Por lo tanto me he quedado con el segundo esquema, usando memoria compartida para pasar la información de entrada/salida al hilo que sube las fotos (y nunca toca nada del interfaz), y una función llamada con Glib::Timeout cada 250 milisegundos que se encarga de actualizar el interfaz y de ir pasando fotos al thread (primero usaba Glib::Idle, pero se desperdiciaba mucha CPU).

El resultado: las fotos suben, y el bucle de GTK no se bloquea, con lo que la experiencia de usuario mejora considerablemente ;).

Otra vez la documentación podría ser mejor, pero me parece que he dado con una buena solución, aunque no esté seguro de que sea la mejor forma de hacer las cosas. La próxima versión del programa (la 0.03), incluirá esta mejora :).

Actualización: se me ha pasado comentarlo, pero ya he publicado la versión. El enlace es a la página en freshmeat del proyecto, donde cualquiera se puede suscribir a las actualizaciones del invento ;).

Anotación por Juan J. Martínez, clasificada en: gtk, perl, nautilus-flickr-uploader.

Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.

Algunas anotaciones relacionadas: