Wednesday, July 16, 2008

Gtk+ 3.0: The Things That Make You Go "Hmmm?"

Lots of discussion about Gtk+ 3.0 lately, unfortunately there's no real discussion going on (heh).

There's the camp that is excited about API/ABI breakage and there's the camp that isn't so enthused.

I have to take the side of the "not so enthused", alongside Miguel de Icaza[1][2], Havoc Pennington and Morten Welinder: it's crazy insane to go breaking API/ABI for the sake of cleaning up the API without adding new functionality that could not be achieved by simply extending the current 2.x series.

There are definitely things that can be done to the current gtk code base without having to break the current API/ABI to achieve them, so in my humble opinion that's what should be done. Granted, eventually there will have to be a Gtk+ 3.0 which breaks API/ABI in order to add some new functionality that isn't possible to implement in 2.x, but that road can be crossed when we get there. No sense breaking it now if it isn't absolutely needed.

Even more worrying is the lack of promise to not break API/ABI again after 3.0. In fact, I've read on one of the mailing lists or somewhere that they plan on breaking API/ABI every 4-5 years after 3.0, ripping out any API's they marked as deprecated in the meantime. This is crazy. One-Flew-Over-the-Cukoo's-Nest crazy (or, if you prefer, I'm-Cukoo-For-Cocoa-Puffs crazy).

For large applications (Gnumeric, Evolution, Mozilla, Eclipse, etc), porting from 1.2 to 2.0 was a huge PITA but we all did it because (besides being young and stupid) 2.0 made a long-term commitment to maintain API/ABI compatibility going forward in addition to the fact that it added a ton of much-needed functionality (accessibility, UTF-8, model/view, and really good text layout w/ support for RTL and CJK).

If 2.0 was just 1.2 + sealed structs and the removal of some deprecated APIs, no one would have bothered with 2.0.

To respond to a point on Tim Janik's blog:

3.0 will ABI-incompatibly remove all deprecated and private APIs. Of course, the above described deprecation scheme only scales well if deprecated APIs are really removed from the code base at some point.

One reason that ISV's are typically on-board with Microsoft is that Microsoft has gone to great lengths to maintain backward compatibility with their old APIs. Joel Spolsky talks about it a bit in How Microsoft Lost the API War (specifically, the section The Two Forces at Microsoft).

The Windows testing team is huge and one of their most important responsibilities is guaranteeing that everyone can safely upgrade their operating system, no matter what applications they have installed, and those applications will continue to run, even if those applications do bad things or use undocumented functions or rely on buggy behavior that happens to be buggy in Windows n but is no longer buggy in Windows n+1.

I'm not going to argue that Gtk/GNOME should go to these extensive lengths to make sure buggy apps continue to work, but there's no reason that G*_DISABLE_DEPRECATED can't simply mean "we're not going to bugfix these anymore, what you see is what you get". You don't have to ever remove them, especially since it likely doesn't require much maintenance to keep them around. The port to Gtk+ 3.0 would require some work to make sure all the internals of things like GtkCList use accessor methods to anything that they use internally, but after that the job should be done and over with.

(ABI might not be a bit hard, but you could maintain API - thus meaning a simple recompile would work)

On the *giggle* *giggle* You should be using Mono/Gtk# *giggle* *giggle*-side, any application built against Gtk# 2.x will still work with Gtk# 3.x w/o any changes :-)

Oh, and Gtk# has DataBinding support (which, btw, does not require API/ABI breakage to Gtk+ nor sealed structs like some people seem to think on this morning's #gnome-hacker's discussion *cough* *cough*).

PulseAudio: I Told You So

To those who told me that my PulseAudio problems were my fault and/or my distro's fault, you were wrong[1][2][3][4][5]*. I told you so.

The first 2 bugs were fixed by me last week (libgnome and libesd) and simply worked around the 4th bug linked above. On that note, I released esound-0.2.39 with my fixes (along with a number of other patches that had been sitting in bugzilla god-knows how long collecting dust).

The 3rd bug is just a crash in the pavucontrol which is apparently fixed in a new version (fairly minor annoyance since by the time I tried to use pavucontrol, pulseaudio was already deadlocked iirc).

The 4th bug (which was the most serious of the ones I've been experiencing) was a deadlock in the pulseaudio daemon (it is supposedly fixed now, but it is not in any public release yet). Just because you didn't experience it, doesn't mean the bug was my fault or my distro's fault ;-)

The 5th bug was found by Geoff Norton, Rolf Kvinge, and myself today while trying to get moonlight to work well with PulseAudio after much head scratching wondering why sound wasn't playing for certain short mp3's (turns out that it's reproducible with any mp3 player if you try to play a short enough audio stream where the decoded stream is less than ~22 kilobytes iirc, but we didn't notice that until just after filing the bug).

So the good news is that the PulseAudio devs have fixed the more serious issues I've found so far, but I'm still annoyed by the attitude of the people pushing PulseAudio of "well, we are forcing people to use PulseAudio so that bugs get found by the users".

For normal applications, this is less annoying... but when it is something as low-level/fundamental as PulseAudio (or heaven forbid, the linux kernel), it should be QA'd thoroughly before dumping it on the users.

Anywho, hopefully openSUSE/Ubuntu/Fedora will push my patches and the PA dev's patches soonish, so that should really help minimize more unnecessary user suffering.

* there are other bugs that I filed as well, but I'm too lazy to go digging for them since the "My Bugs" link on the PulseAudio Trac doesn't actually work.

Sunday, July 13, 2008

Good News on the Evolution Front

Novell has just recently (end of last week) announced that Evolution will no longer require copyright assignment. This is awesome news that I've been hoping would happen for a quite a while now.

Evolution's license is also changing to be dual-licensed under the LGPLv2 and LGPLv3.

Thanks to Michael Meeks and Srini for making this happen!

Thursday, July 10, 2008

PulseAudio Again...

Just had another exciting PulseAudio crash where my sound card got continually spammed until my ears bled and then it spammed the sound card some more.

Seriously though, my coworkers were a bit annoyed by my laptop blaring really loud and obnoxious noise for the past hour (made worse because my keyboard volume control keys do not actually work with pulseaudio which is apparently a known issue with Lenovo T61 laptops I'm told).

However, this did provide me with a most excellent opportunity to test the patches I posted in my previous blog entry about my pain and suffering at the hands of PulseAudio.

Yet again, I was unable to launch any new apps - though instead of blocking in esd_open_sound(), they blocked in esd_send_auth(), so I went and patched that up to use non-blocking IO, build new packages, installed them, and tested and HALLELUJAH! they work!

Yes, ladies and gentlemen, I can now actually use my system even when PulseAudio takes a proverbial dump on my desktop.

I've submitted my patches upstream:

  • bug #542391: libesd should not block if the daemon dies/hangs/whatever
  • bug #542296: gnome_sound needs to handle pa hangs more gracefully

Unfortunately, these patches do not solve the root problem (pulseaudio suckage), but at least they allow me to get work done when pulseaudio craps out on me (which only happens every few hours if I'm lucky).

Oh, and if you are on 32bit x86 openSUSE 11 systems, you can grab some packages I made up at http://www.gnome.org/~fejj/pulseaudio/. I've also uploaded the raw patches in case anyone wants to push them in their own distro packages.

Sex With Dead People Deemed Illegal

Ok, so as I'm riding the MBTA in to work this morning reading my Metro newspaper, I come across the article "Court: Sex with corpses is illegal"

Apparently until yesterday's ruling, sex with dead people was technically legal in the state of Wisconsin.

The last sentence of the article was, in my humble opinion, the most hilarious sentence I've ever read in my life:

In yesterday's 5-2 decision, the high court said Wisconsin law makes sex acts with dead people illegal because they are unable to give consent.

Yes folks, it was a 5-2 ruling. So 2 people ruled in favor of allowing sex with dead people. W. T. F.

But to make it even more hilarious, the reason they decided it was not kosher was that the dead people could not give consent. L. O. L.

We live in a funny world...

Wednesday, July 9, 2008

More PulseAudio Problems

I reinstalled PulseAudio to try and resolve the plethora of problems I've been having with it (that and openSUSE 11 doesn't actually ship th esound daemon anymore, so if I want full audio support I have no choice but to get the pulse-esound-compat package working short of forking the distro and maintaining my own set of esound/gnome/etc packages which is not my idea of fun).

Unfortunately, this has resulted in countless hours of frustration. After having read and followed the instructions at http://www.pulseaudio.org/wiki/PerfectSetup, it turns out that is exactly how my system was already configured. So no help there.

I don't know what the deal is, but I constantly have problems with PulseAudio.

For example, earlier today I was having an IM conversation with a friend in Pidgin and it locked up. I restarted Pidgin, but I wasn't getting audio events anymore. *shrug* Oh well.

At some point later I navigated my firefox window over to youtube to link a friend to some video. No surprise here, I get no sound and in fact, the flash video pauses as soon as the sound was supposed to start (which is a few seconds into the video).

At this point I close firefox and decide to reload it, thinking maybe that will solve it. Nope, I was wrong. Instead, firefox hangs before any windows pop up. Great.

Since I got flamed last time I blogged about PulseAudio for issuing a `killall -9 pulseaudio` command (btw, pulseaudio appears to have a --kill command-line option), I figured I'd log out and log back in again instead... but this was not to be. Instead, my desktop locks up (presumably because of the logout.wav).

Ok... so I Ctrl+Alt+F1 to the console, login as root, `init 3; init 5`. Since my system is configured to auto-login, when X came back up it tried to log me in. Instead, I get a hang.

Good grief, I guess I have no option but to reboot so that's what I do.

In the interest of saving other people from this catastrophe, I grabbed libgnome out of svn and wrote up the following patch to prevent it from hanging whenever PulseAudio decides to misbehave (which it seems quite prone to do).

Index: libgnome/gnome-sound.c
===================================================================
--- libgnome/gnome-sound.c (revision 3750)
+++ libgnome/gnome-sound.c (working copy)
@@ -30,8 +30,12 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/types.h>
 #include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
 #include <time.h>
+#include <poll.h>
 
 #ifdef HAVE_ESD
 #include <esd.h>
@@ -413,6 +417,55 @@
 }
 #endif
 
+#ifdef HAVE_ESD
+static int
+send_all (int fd, const char *buf, size_t buflen)
+{
+ struct pollfd pfd[1];
+ size_t nwritten = 0;
+ int flags, rv;
+ ssize_t n;
+ 
+ if ((flags = fcntl (fd, F_GETFL)) == -1)
+  return -1;
+ 
+ fcntl (fd, F_SETFL, flags | O_NONBLOCK);
+ 
+ pfd[0].events = POLLOUT;
+ pfd[0].fd = fd;
+ 
+ do {
+  do {
+   pfd[0].revents = 0;
+   rv = poll (pfd, 1, 100);
+  } while (rv == -1 && errno == EINTR);
+  
+  if (pfd[0].revents & POLLOUT) {
+   /* socket is ready for writing */
+   do {
+    n = write (fd, buf + nwritten, buflen - nwritten);
+   } while (n == -1 && errno == EINTR);
+   
+   if (n > 0)
+    nwritten += n;
+  } else if (pfd[0].revents & (POLLERR | POLLHUP)) {
+   /* we /just/ lost the esd connection */
+   esd_close (fd);
+   fd = -1;
+   break;
+  } else if (rv == -1 && errno == EBADF) {
+   /* socket is bad */
+   fd = -1;
+   break;
+  }
+ } while (nwritten < buflen);
+ 
+ if (fd != -1 && flags != -1)
+  fcntl (fd, F_SETFL, flags);
+ 
+ return fd;
+}
+#endif
 
 /**
  * gnome_sound_sample_load:
@@ -469,21 +522,23 @@
     * file, or event type, for later identification */
    s->id = esd_sample_cache (gnome_sound_connection, s->format, s->rate,
         size, (char *)sample_name);
-   write (gnome_sound_connection, s->data, size);
-   confirm = esd_confirm_sample_cache (gnome_sound_connection);
+   
+   gnome_sound_connection = send_all (gnome_sound_connection, (const char *) s->data, size);
+   if (gnome_sound_connection != -1)
+     confirm = esd_confirm_sample_cache (gnome_sound_connection);
+   
    if (s->id <= 0 || confirm != s->id)
      {
        g_warning ("error caching sample <%d>!\n", s->id);
        s->id = 0;
      }
-   g_free (s->data);
-   s->data = NULL;
  }
     }
 
   sample_id = s->id;
 
-  g_free(s->data); g_free(s);
+  g_free(s->data);
+  g_free(s);
 
   return sample_id;
 #else
@@ -521,9 +576,12 @@
 
   sample = gnome_sound_sample_load (buf, filename);
 
-  esd_sample_play(gnome_sound_connection, sample);
-  fsync (gnome_sound_connection);
-  esd_sample_free(gnome_sound_connection, sample);
+  if (gnome_sound_connection != -1 && sample > 0)
+    {
+      esd_sample_play(gnome_sound_connection, sample);
+      fsync (gnome_sound_connection);
+      esd_sample_free(gnome_sound_connection, sample);
+    }
 #endif
 }

This patch should hopefully help prevent the typical gnome apps from hanging when trying to play audio, but I don't really have any surefire way of testing that it actually works.

Update 2008-07-10 10:42am I've now also written the following patch for libesd so that it doesn't hang for god knows how long when trying to open a connection to the sound server (in my case, pulse-esound-compat):

diff -up esound-0.2.38.orig/esdlib.c esound-0.2.38/esdlib.c
--- esound-0.2.38.orig/esdlib.c 2008-07-10 10:10:31.000000000 -0400
+++ esound-0.2.38/esdlib.c 2008-07-10 10:32:23.000000000 -0400
@@ -20,9 +20,11 @@
 #include <arpa/inet.h>
 #include <errno.h>
 #include <sys/wait.h>
+#include <poll.h>
 
 #include <sys/un.h>
 
+
 #ifndef SUN_LEN
 #define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path)  \
                      + strlen ((ptr)->sun_path))
@@ -408,6 +410,43 @@ int esd_resume( int esd )
     return ok;
 }
 
+static int
+connect_timeout (int sockfd, const struct sockaddr *serv_addr, size_t addrlen, int timeout)
+{
+ struct pollfd pfd[1];
+ int flags, rv;
+ 
+ if ((flags = fcntl (sockfd, F_GETFL)) == -1)
+  return -1;
+ 
+ fcntl (sockfd, F_SETFL, flags | O_NONBLOCK);
+ 
+ if (connect (sockfd, serv_addr, addrlen) == 0) {
+  fcntl (sockfd, F_SETFL, flags);
+  return 0;
+ }
+ 
+ if (errno != EINPROGRESS)
+  return -1;
+ 
+ pfd[0].fd = sockfd;
+ pfd[0].events = POLLIN | POLLOUT;
+ 
+ do {
+  pfd[0].revents = 0;
+  rv = poll (pfd, 1, timeout);
+ } while (rv == -1 && errno == EINTR);
+ 
+ if (pfd[0].revents & (POLLIN | POLLOUT)) {
+  /* success, we are now connected */
+  fcntl (sockfd, F_SETFL, flags);
+  return 0;
+ }
+ 
+ /* took too long / error connecting, either way: epic fail */
+ return -1;
+}
+
 /**
  * esd_connect_tcpip: make a TCPIP connection to ESD
  * @host: specifies hostname and port to connect to as "hostname:port"
@@ -512,7 +551,7 @@ esd_connect_tcpip(const char *host)
            goto error_out;
          }
 
-         if ( connect( socket_out, res->ai_addr, res->ai_addrlen ) != -1 ) 
+         if ( connect_timeout ( socket_out, res->ai_addr, res->ai_addrlen, 1000 ) != -1 ) 
            break;
 
          close ( socket_out );
@@ -596,9 +635,9 @@ esd_connect_tcpip(const char *host)
     socket_addr.sin_family = AF_INET;
     socket_addr.sin_port = htons( port );
   
-    if ( connect( socket_out,
-    (struct sockaddr *) &socket_addr,
-    sizeof(struct sockaddr_in) ) < 0 )
+    if ( connect_timeout ( socket_out,
+      (struct sockaddr *) &socket_addr,
+      sizeof(struct sockaddr_in), 1000 ) < 0 )
  goto error_out;
 
     }
@@ -650,8 +689,7 @@ esd_connect_unix(void)
     socket_unix.sun_family = AF_UNIX;
     strncpy(socket_unix.sun_path, ESD_UNIX_SOCKET_NAME, sizeof(socket_unix.sun_path));
   
-    if ( connect( socket_out,
-    (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix) ) < 0 )
+    if ( connect_timeout ( socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix), 100 ) < 0 )
  goto error_out;
   
     return socket_out;

Hopefully after these 2 patches get applied, my system at least won't become unusably hung when pulseaudio decides to crap out on me, but these patches doesn't solve the root of the problem :(

Monday, July 7, 2008

4th of July Fireworks from VMWare Office

I watched the 4th of July fireworks from the 11th Floor offices of VMWare in Cambridge this year with some friends.



dscn0265.jpg, originally uploaded by jstedfast.


dscn0343.jpg, originally uploaded by jstedfast.


dscn0349.jpg, originally uploaded by jstedfast.


dscn0400.jpg, originally uploaded by jstedfast.


dscn0401.jpg, originally uploaded by jstedfast.

Update: I should note that these photos were made possible thanks to Alan McGovern's 256 MB CF card he mailed me last week. So thanks, Alan! ;-)

Code Snippet Licensing

All code posted to this blog is licensed under the MIT/X11 license unless otherwise stated in the post itself.