One of the more common myths around the software development community these days is that if you have an implementation A, and you wrap an API style B around it, that as far as the adherents of B are concerned, it should be all great, and that any arguing on their part is evil religious bigotry.
This ignores the core strength of open-source software: the open source.
Why does access to the source matter? The common (mis)conception is that increases people spotting bugs. Well, sorry to tell you, but random people do not frequently fix your code. The print out of the source code of your favorite app isn't likely to hit the best seller lists any time soon.
When it does matter, however, is if the library of service you rely on does something unexpected. When that happens in proprietary settings, you can either fork over a fat check for a support contract, or try lots of random things until you find one that works. Hopefully.
With source access, you don't have to do it. You can read the code, step through it, study the comments. Often times, that leads you straight to the answer. Assuming, of course, the code is actually readable.
And readable does not just mean "good" (though the lack of that hurts, a lot). It also means consistent with the style of code you yourself write, the style of libraries you are working with. It means that you shouldn't have to spend 2 weeks studying something esoteric for the code to be comprehensible. And it means, that if the implementation is written in an entirely different style than the wrapper around it and the code using it, it's code is of practically no use. So, the best one can do, is go hunt for help. .
So remember: no matter how you wrap it, it's still the same source-code. And if it does something you don't understand - and it will - you are either going have to waste a lot of time reading that code, or waste a lot of time seeking and getting help.
P.S. And, yes, this is related to the latest entries from Aaron Seigo. You just have to pick appropriate values of A and B.
I was wondering a bit about the reports that SVN blame is slow, so I did a bit of an experiment.
[Maksim@nest khtml]$ time cvs ann khtml_part.cpp > /dev/null Annotations for khtml_part.cpp *************** 0.04user 0.00system 0:02.93elapsed 1%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+470minor)pagefaults 0swaps
[Maksim@nest khtml]$ time svn blame khtml_part.cpp > /dev/null 4.73user 3.30system 6:17.41elapsed 2%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (21major+4577minor)pagefaults 0swapsNow, khtml_part.cpp is atypical with its 1019 revisions, but other files have more (e.g. konq_mainwindow.cc has 1407). And while more typical things like 100-commit files take more reasonable 40 seconds with SVN, the comparison with 2 seconds it takes CVS does show that SVN isn't quite there yet. Now, this stuff is being worked on. But I think people should be aware of stuff like this when they say newer is better.
Consider this scenario. A new product is announced. On balance, it seems like a result of competent engineering. It has some new ideas, some of them are even good. It has some design mistakes, too. In short, it's probably something worth using in some circumstances, yet nothing in any way groundbreaking or innovative, and hence in no way guaranteed nearly universal adoption.
Yet the creators do not despair. They chose a nice, catchy name, preferably short but high-tech sounding. A an aggressive marketing campaign commences, helped by enthusiasm of ill-informed third parties that trumpet a new, ground-breaking innovation in screaming headline on front pages of trade magazines. Soon, the new technology enters the mass conscience as a fundamental advance, a revolution that will bring huge benefits to everyone and everything --- even though it's really just some competent engineering, rehashing an idea that arose decades ago.
Sounds like something out of corporate world? Surely, as you were reading this, a name of a certain company out of Washington State, and a suite of technologies named after a TLD came to your mind. And surely, this doesn't happen in OSS world, right?
Well, if you though that, you were wrong. In fact, as I wrote the paragraphs above I had exactly OSS in mind. It is happening more and more every day. Let's name some names: D-BUS, various uses of SVG, to a smaller extent, HAL, and to a very limited extent, COMPOSITE1. People treat these solid projects as some sorts of great breakthroughs. Users demand developers to support these "groundbreaking" technologies --- without even being clear about benefits, if any. Journals publish articles that are quite frankly embarrassing in their lack of background knowledge. The only bright spot seems to be that the developers are generally limiting themselves to usual overenthusiasm about their work.
Come on, folks. No one invented RISC here. And there is no shame in that. Major breakthroughs like that are extraordinarily rare, and often require decades of hard work to fully refine. If there is one truly original refinement of a core idea in some project, that's more than enough (and probably a highly compelling reason to use something). None are necessary. In open source, we are generally engineers, not researchers. Doing a good job, and getting the little details right is what counts. But I think we also share with the ideals of scientific pursuit the belief in honesty and transparency. And giving one's work an appropriately modest stature is a part of that.
And to our users: you honor us by using our work. Please, also honor us by trusting our technical judgment. Don't ask us to use library X or technology Y. Tell us what you need. May be those things are the best way to it. May be they are not. But ultimately, it's up to the people who do the work to decide. And, please, stop treating things as breakthroughs and revolutions when they're not. The world will be better without the geek equivalent of screaming teenage girls seeing their idol.
Well, I haven't blogged for a while, since I don't have much hacking to report on, but I suppose I can check in if only to somewhat interrupt the stream of the intense
Enter vs. Ctrl-Enter Google Search Bar debates (although let me point out that there is a GUI for configuring what plugins to load in HEAD, done by this plugin's author).
As most of you know, I started grad school @ Cornell a few weeks ago. Thus far, I am mostly taking things gently: trying to prepare for the qualifying exams and to generally get adjusted. Hence, I am only taking 2 "real" courses, both targetting the exams, and only one of them is at the graduate level (formal PL semantics and type theory); the other is a senior-level course on numerical analysis. In the "imaginary" courses component, I also attend a couple of seminars, and sit in for an AI class as a way of pacing my review for yet-another qualifying exam (there are 5).
Probably the most stressful part is my TA role. The class is an intermediate Java/intro to datastructures thing, so it's quite huge, with about 200 students, and around 20 staff members. Needless to say, w/that many students, one quickly finds out just how differently people can read a supposedly clear and unambiguous assignment (which gets particularly muddled when two staff members give contradictory answers!). The most difficult part for me is teaching sections -- i.e. trying to go over parts of the material with smaller (20-30 people) groups of people, because:
As usual, I there are TaeKwonDo classes to keep me sane, though. The workouts are quite intense, although I suppose they would feel a bit easier if my feet weren't all banged up from climbing a portion of Ithaca's East Hill on my way to the campus every morning. My shoes simply aren't made for ~35 degree inclines. I am sort of enjoying the wide range of techniques covered here, too, since it really hits on my weak spots; but that also makes it more difficult to get "integrated", because, well, it is sort of hard to reshape your reflexes after so many years. Seems like a great bunch of people, too, although probably a bit more "instense" than what I am used to due to so much focus on competitions.
I did quite a bit more hacking on largeimagelib over the last few days. First of all, I converted the core to use tiles and not stripes, which helps the performance a lot when the viewport is smaller than the scaled image (but which may hurt a bit when downsampling images). A double-buffer was also added to reduce flicker — so now one can scroll around huge images at small zoom factors very smoothly. This wasn't quite enough when scaling huge images down a lot, however, as rescaling down 100 million pixels can take some time. So I implemented support for time limits for painting (rescaling is lazy, and hence done when painting, of course only for drawn regions). When the drawing code runs over the time limit, it stops doing expensive processing, and only shows tiles that can be fetched from the pixmap for the rest of the region, using black rectangles as a filler. It then generates a deferred update event for the still-unpainted range, using the same mechanism as in progressive loading. The end result is that if the image can't be scaled quickly, the app remains responsive, and the user can see it being scaled piecewise; and the application does not need to worry too much about it, either (I had to add one parameter to the draw call in the test kpart — that's it). This works very well — I can open a 13,000x13,000 JPEG of Mars, and scale it, scroll it, etc., however I want, without annoying "stuck" states. The only problem is that sometimes the timeout seems to trigger on smaller images, causing some flashing, so I may need to tweak it or to make the logic a bit more complicated.
I also implemented much of a progressive PNG loader, using libPNG, in a few hours this evening. It seems to work on all images I tried it with, although PNG supports a lot more types than are commonly in use; the code also needs testing on big-endian machines. The big missing piece is support for interlaced images. This needs some core tweaking in order to let the loader pull out partial scanlines from the image to merge in the updates. This is actually pretty easy, but it involves the tile locking code, and I try not to touch incremental algorithms late in the evening. Maybe I'll add it tomorrow or over the weekend.
One of the reasons making bug-free software is hard is that when you make a change, sometimes the effect isn't what you quite expect... Now, one of the problems in KDE3.2.x is that with some compiler flags, the MMX and SSE2 versions of some graphics routines don't work quite right, since they make assumptions which are no longer true of newer gcc versions. Fixing those things is rather painful (oy, debugging inline asm, oy), and so after assigning the bug to myself I've been procrastinating for quite a bit, partly because I can't even test the SSE2 versions, and partly because I was secretly hoping that someone else (Fredrik?) may fix it. Since there seemed to be a bit of a spike in the reports of the problems lately, I decided to do the reasonable thing, and to disable these routines in advance of 3.3 Beta 1, until they get sorted out. Seems like a sane and low-risk decision, doesn't it?
Well, a few days later, and people are reporting similar-sounding problems to what I thought I just "fixed" (which of course worked before and after on my machine). And, what's more surprising, some of them have not had those problems before. So in other words, a very conservative change that was supposed to fix a bug, actually introduced that very same bug, perhaps even for more people.
So I look into the C++ versions of routines, and find something like this:This piece of code may look right at first sight, but it actually has a major problem. There are some things about program behavior that C++ does not specify. For example, if you call a function, there is no guarantee about the order in which the parameters are evaluated. Now, most of the time it doesn't matter. But sometimes to get the parameter, one has to call a function, and that function may changed some memory, or output to console, or draw to screen. It's usually said that the expression has side-effects. To be more concrete, consider the example:
What should this output? It turns out, this can output either 123 or 213, since C++ permits the compiler to call get(1) and get(1) in any order. So what does this have to do with the original code? Well, that code has pretty much the same problem. What the person who wrote the code expected was that it would perform the calculation on the contents of data, then update that location, and update the pointer. Except, there is nothing requiring things to happen in quite that order. The compiler may chose to generate code that does the increment before doing the work on the right hand side. Which is likely what it did for people who were having problems with this. The fix?
So, to those still reading: please do not use ++variable in expressions where variable is accessed anywhere except through that operator, or you may face a confusing "but it works for me, why does it fail for so many people" bug.
A couple days ago I was on the IRC channel for a rather nice open-source game (probably the best open-source game in existence, in fact; the only reason I am not plugging it here is because I am afraid that doing so would cause other KDEers to spend way too much time playing it, at the cost of their hacking productivity, which is what happened to me). At that time, the main developer, who uses Windows, added some rudimentary clipboard support to the game, and the implementation for it on Windows, which took all of about 20 lines of code. I decided to contribute an X11 implementation because I knew roughly how things should work, having debugged a few clipboard-related problems in KDE apps, and felt it would be a nice way to make a contribution to a program I like while improving my knowledge. About 330 lines of C++ code later (no, they didn't take me 2 days, just a few hours), I figure I'll give a bit of the technical overview here, since I think it would be nice if more people knew how those things work, particularly since qclipboard_x11.cpp is not the best place to figure things out on the spot.
Now, the most important thing to know about clipboard in X is, well, that there is no clipboard in the normal sense of the word. There is no place where applications can store things, and other can pick them up. Rather, X provides a set of communication primitives that let one implement something similar, and a document called the ICCCM has a section describing the usual conventions for using those primitives for copy-paste. This is why my code, and even more so Qt clipboard code is so complicated compared to doing similar things on Window or Mac OS --- it can not just call a few functions, but must implement a protocol. Let me introduce some of these primitives:
Notice that there is no common place where data is stored. An immediate consequence of this is that if the application where data was copied from exits, the clipboard contents are lost (since there is no-one to ask for them), unless some other program (like klipper) requests them and saves a copy. It may also seem that the choice of CLIPBOARD above is kind of arbitrary, and that other values may be used instead. This is indeed the case, and two values are currently used --- modern X application distingush between the "clipboard" and the "selection", and the clipboard uses CLIPBOARD, while the selection uses PRIMARY. A rather annoying consequence of this scheme is that when someone pastes, the application must actually wait for an event --- Qt not doing that carefully enough is what caused the infamous bug 61412
An another obvious question about the above summary is, well, how does the requesting application know what format to request the data in? Does it have to try out a lot of different formats? Well, there is a special format called TARGETS, in response to which clipboard owners can reply with the list of formats they support. Of course, if that feature is not supported by the application, the requestor may have to try out a few things. Which can get kind of messy, since there are at least 4 separate formats available for just plain text.
There are further complications, of course. X11 is network-transparent, or rather, it is really a distributed system, so weird things can happen if one is not careful. Imagine if you have a remote window over a fairly high-latency link, and some local ones. You copy some text from the remote application, but then decide you don't really want it, and copy some text from a local application instead. Now, both applications get your copy request, and both send requests to X saying "I want to control CLIPBOARD". The problem is, since the link to the later window is quite slow, if you're a really fast clicker/typer, it's conceivable that the remote message can arrive after the local one, thus having earlier data effectively overwrite later one. This is solved by having each keypress/mouse click have an associated timestamp. When requesting ownership of selection, the timestamp for the click/key press that caused it is provided to X. This way, if an earlier request arrives later, it can be discarded. Thus, timestamps are used to prevent various race conditions. Further, there are also some size limitations for properties. This means that there are additional sub-protocols used to transfer large amounts of data.
Well, that's about all I know. I hope this is clear enough to explain how things work, and to imply why sometimes they doesn't work quite as well as we wish they would.
Spent part of today doing a bit of hacking on largeimagelib. Started of by merging in some improvements Dirk Mueller made to KHTML's JPEG loader, to avoid buffering lots of data it when dealing w/non-progressive JPEGs. This means huge JPEGs can now be loaded with capped memory usage. (Of course, the same change makes the lib a lot less important, since after it KHTML became much, much, better at handling huge images even w/QMovie).
I also implemented a little KPart w/zoom buttons using the library. Was a nice crash course in KParts, and my first experience using KIO in fully async mode. It works nicely, including progressive resizing, scales to large images reasonably well (although it's understandably quite sluggish with 35,000 x 35,000 ones, but it does not nearly kill your machine like KView would, and does not force rescale down by 1/8th or so inside the loader like KHTML cleverly does), but needs a lot of work to remove the insane amount of flicker. I also almost feel like reworking some of the core to switch to a tiled mode and not a scan-line based one. I think I know how to implement it sensibly now, and that should help a lot with swap management, and make the cache units have far more consistent sizes. The holy grail, of course, would be to be able to do asynchronous/non-blocking page in, and perhaps even prefetching, but I am not so sure I want to deal with the portability of async I/O.
The next task will probably be adding more loaders. PNG should be fairly easy, I think. MNG should help test animations and to see whether largeimagelib can really fix QMovie's pains w/those. The real challenge, though, will be animated GIF. I am not sure the core is capable enough for it, and I really wish I understood how to use libungif.
|Earlier Entries >>|