Zhanga Redux
The chronicles of the work and personal life of a boring software developer.
Why I still prefer Linux over OS X
Monday, June 14, 2010
I got a MacBook Pro a week ago and I'm actually surprised at how much I like it overall. Aside from being shiny, since it's Darwin under the hood, I've been getting along quite well with the command line and that makes the system feel familiar even though I haven't used a Mac since elementary school. And with a Skype client that works far better than the joke that is the Skype Linux client, I actually use this laptop quite a bit.
But I still like Linux (and KDE) more. There are just too many issues for me to think that OS X is as great as some people want me to believe.
First, consider the command-tab hotkey. In Windows/Linux, alt-tab will cycle through open windows. In OS X, command-tab cycles through open applications, and for some reason OS X hates tabbed windows so many applications (especially Finder, the file manager) like to open tons of windows. Command-tab only focuses the most recently-used window of any particular app. To switch windows within apps, you have to focus the app then use command-`, but that doesn't show the little popup, so you don't know in advance what you're about to switch to or how many times to hit `.
Linux has a feature called virtual desktops, which lets you spread out your windows so you can reduce the amount of alt-tabbing. Apple recently introduced it as "Spaces," but it's the same thing. The problem is that if I'm on desktop #1, and I push command-tab, I don't want to select an app on desktop #4. To me, and apparently the KDE team, that's the point of having them separated. On the Mac there's no way to make this work the way I want. For example, if I have 4 desktops and 2 windows on each (say browser/terminal, email/chat, etc) I want command-tab to immediately switch me to the only other window on that desktop. Nope, OS X would like to switch me to whatever app I used last, which is usually on some other desktop, so I have to hit tab 6 more times to get to where I want. (Windows doesn't come with this feature at all but I've been using WindowsPager, a free tool that implements behavior similar to that of KDE on Linux.)
What's more, OS X apparently doesn't think some windows are real windows. For example, the Skype call/webcam window. If I use Expose, that window magically disappears. If I use the Spaces feature to zoom out and view all my virtual desktops, that window disappears. Which means it's very difficult to find that window if I lose track of it (command-` doesn't find it either, and the Dock just switches to an app not a window), and also hard to move it from one virtual desktop to another. And there's no hotkey to move a window from one virtual desktop to another. The closest thing I know of is to click and hold a window's title bar, and while holding down the mouse button, hit a hotkey. And it only seems to work most of the time. Retarded.
I would have thought that a company which prides itself so much on its user interface would have gotten these things right. Seriously, if Linux can get these things right, so should OS X.
Unrelated problem — the OS X Mail app for email doesn't work with a SOCKS proxy. Apparently it never has... I found posts from 2001 asking about this. So I can't check my email from work using that. Thunderbird, and even MS Entourage work fine. At least in Linux, if an app doesn't do SOCKS I can use a wrapper like tsocks or proxychains, but for the life of me I can't get either of these to work in OS X 10.6. Seems the only thing that can get the built-in Mail app to work is Proxifier, which is $40, and aside from the money it causes other issues.
Also, the 80-90 C temperature when all I'm doing is Skype webcam is unacceptable. Apparently Apple thinks this is normal.
Skype fails to start on Fedora 13, nothing displayed and 100% CPU usage
Sunday, June 6, 2010
I upgraded to Fedora 13 (from 12) today and spent an hour figuring out why everything worked fine but Skype wouldn't start... symptoms included:
- 100% CPU usage and no disk activity immediately after launching Skype
- No window or any GUI element shows up, no sign that it's doing anything useful
- No console output
After a while, strace skype gave me a hint. I'm not really sure whose fault this is, but apparently somebody broke something somewhere and the fangsongti font is causing freetype to hang. This also causes the nspluginwrapper for 32-bit Flash to hang.
Fix: yum remove bitmap-fangsongti-fonts
I filed a bug report on Redhat's bugzilla but got denied.
Bing app with Navigation for Windows Mobile released!
Tuesday, May 11, 2010
The Bing app for Windows Mobile was released today. In addition to the existing search functionality, you can now get real-time driving directions with voice guidance (including street names and distances) if you have a GPS-enabled Windows Mobile 6 phone.
In addition to being free, this app offers some other advantages over other popular GPS navigation devices. For example, you don't need to download or pay for periodic map updates, as this happens automatically when Bing updates its map servers. The app also shows traffic conditions on the map and by default it generates routes that avoid traffic.
Obligatory screenshots follow...
This is the screen that you see when you open the app:
Here is the screen that shows for a couple of seconds while the app generates a route:
Here is the list view of your route:
Here is the map view:
You can download it by going to m.bing.com/download on your Windows Mobile device.
Here is the official announcement.
HttpWebRequest.Abort() in .NET Compact Framework 2 doesn't work when m_connection is null
Wednesday, March 31, 2010
Note: This post applies to CF 2.0 and below. It is fixed in later versions.
I literally spent days poring over our app's code to figure out what was going wrong. Eventually I gave up and blamed our failure on the Compact Framework code, and it seems I was correct. It appears that in CF 2.0, HttpWebRequest.Abort() is broken in some cases and causes very difficult-to-detect incorrectness in subsequent requests.
Create and send an HttpWebRequest:
HttpWebRequest r = (HttpWebRequest)WebRequest.Create("http://www.example.com/");
r.BeginGetResponse(new AsyncCallback(SomeFunction), null);
Now, sometime later but before the request is completed, try to abort the request:
r.Abort();
It's obvious what this code is supposed to do, and the API also clearly states what is supposed to happen — the request is cancelled. However, due to a CF bug, that doesn't always work correctly.
If you Abort() the request early enough, it is possible that the m_connection private member of the HttpWebRequest object has not been set. In this case, the Abort() call actually fails to abort the connection. In fact, the HttpWebRequest will appear to have been aborted, but its underlying Connection object (and its Socket object) will still be sitting around. Since the Connection objects used by HttpWebRequest are pooled, this is very bad. It leaves the Connection object in an unaborted state while its owning HttpWebRequest has been aborted — meaning when the Connection object is reused by a different request, it still has old state and will behave in unexpected ways.
Consider the following scenario:
- Client creates a new
HttpWebRequestand begins a request for HTTP resource A. - Someone calls
Abort()on that request beforem_connectionis set. - Abort will silently fail and the underlying
ConnectionandSocketwill be left in an invalid state. - Client creates a new
HttpWebRequestand begins a request for HTTP resource B. - Since
HttpWebRequests are not pooled,HttpWebRequestB is a different object from the now-defunctHttpWebRequestA. However, their underlyingConnectionobjects are pooled.HttpWebRequestB tries to get aConnectionfrom the pool, and may end up with theConnectionthat A left in an invalid state. HttpWebRequestB sends things through its underlyingConnectionand theSocketowned by theConnection. But theSocketis still connected to resource A.HttpWebRequestB thinks it successfully sent request B. The server returns content from A since that's what theSocket's associated with.HttpWebRequestB gets a response stream from the server. It thinks that this contains data from resource B, when in fact it is data from resource A.
So you end up with random data corruption. Not only that, but it's silent: it's very difficult to detect this condition, and most of the time the data looks legitimate. For example if you're downloading 100 images and image #50 actually contains the data that was supposed to go into image #49, well, tough luck because it looks like a legitimate image.
I spent forever trying to come up with solutions to this, but could only come up with ones that don't work:
Do not call
Abort()onHttpWebRequests whosem_connectionmember is null.Since this bug occurs iff
m_connection == nullwhen aborting, if we avoid doing that then this bug can never occur. The problem is thatAbort()is the only way to kill the request, and it may not be possible for your app to completely avoid aborting requests. In addition, since theConnectionobjects are pooled and there are very few of them in the pool, you can't leave these requests hanging around or your app will quickly run out of connections. (IfAbort()is called and this bug is triggered, theConnectionwill be returned to the pool and not block subsequent requests; it will just have unpredictable behavior next time.)When aborting, check
m_connection, and if it's null then store theConnectionwhich is about to become invalid into a data structure somewhere. When anHttpWebRequestreturns data, check to see whether it came from a brokenConnection; if so disregard the data and retry.This would work... if it was possible to get the
HttpWebRequest'sConnectionobject. But since the bug only occurs whenm_connection == null, and because theConnectionis actually created deep within the innards of the code, passed around as a local variable, and is not known to theHttpWebRequestuntil a callback some time later, it's not possible at abort time to get a reference to theConnectionobject, not even with reflection.Also, since the
HttpWebRequestaren't pooled, keeping track of those is not useful.Inherit from
HttpWebRequest. Tag each one with a unique ID, and when the response comes back check to see the ID is what you expectSince
HttpWebRequests aren't pooled this doesn't work. It's the underlyingConnectionwhich is pooled and left in an invalid state, not the request itself.Set a timeout on the underlying
Connectionor theSocketthat it owns so they can dispose themselves.Those two objects don't have such a thing as timeouts, at least not in CF, and even if they did there's no way to get a reference to those objects until
m_connectionis set (but the bug is gone by that point).HttpWebRequesthas a timeout but it is useless as it simply callsAbort()when the timer rings, so will still exhibit the buggy behavior.
The fix is to use CF 3.5, but I have to support 2.0. Still looking for a workaround...
Static classes in C# and Java
Sunday, February 28, 2010
While C# and Java are similar enough that one can often copy/paste code from one to the other with minor changes, sometimes the subtle differences will get you...
Both languages allow you to declare a class as static. They mean very different things, however.
In C#, a static class is pretty straightforward: it is a class with all static members. You can't instantiate a static class.
In Java, it's more complicated. A class can only be marked static if it is an inner class and not anonymous. It is a compile-time error to mark a top-level class as static, e.g. you can't create Hello.java with this as the content:
/* Java */
public static class Hello { /* ... */ }
If an inner class is not declared static, then it can only be instantiated within the context of its enclosing class:
/* Java */
public class Hello {
public class Inner {}
public static void main(String[] args) {
new Inner(); // compile-time error because there is no enclosing instance of Hello
new Hello().new Inner(); // ok
new Hello().createInner(); // ok
}
public void createInner() {
new Inner();
}
}
If an inner class is declared static, then it behaves as if it was declared as a top-level class:
/* Java */
public class Hello {
public static class Inner {}
public static void main(String[] args) {
new Inner(); // ok
new Hello().new Inner(); // can't do this with static class
new Hello().createInner(); // ok
}
public void createInner() {
new Inner();
}
}
In C#, there is no equivalent of enclosing instances or non-static inner classes. Inner classes behave the same as top-level classes:
/* C# */
public class Hello {
public class Inner {} // equivalent to declaring as static in Java
}



