Wednesday, May 14, 2008

ASM Java Bytecode Tool

ASM looks like it might be useful. Groovy uses it.

And it even has a plugin for Eclipse.

jSuneido - implementing classes & more

So far, so good. I've written about 850 lines of Java that equates to about 2000 lines of C++ in two and a bit days. I've been porting the basic Suneido data types.

I'm lucky that Java is pretty close to C++, and Suneido's byte code is pretty close to Java byte code.

I'm gradually getting an idea of how to implement Suneido on top of Java and the JVM. Here's how I envision Suneido classes being compiled:
class SuClass { // ultimate base class - static, not generated
public SuValue invoke(int method, SuValue ... args) {
return invoke2(method, args);
}
public SuValue invoke2(int method, SuValue[] args) {
if (method == DEFAULT)
throw SuException("method not found");
return invoke2(DEFAULT, args);
}
}

class xxx extends SuClass { // generated
public SuValue invoke2(int method, SuValue[] args) {
switch (method) {
case 8346:
return mymethod(massage(6, args, 4746, 9846, 3836));
...
default:
return super.invoke2(method, args);
}
}
public SuValue mymethod(SuValue[] locals) {
if (locals[2] == null) locals[2] = FALSE;
invoke(8347, locals[1]); // call a method in "this"
locals[4] = globals[123].invoke(7472, locals[0], locals[2]);
locals[3] = new SuValue(
JavaClass.a_static_method(locals[1].string()));
}
...
}
Note: I'm showing this as Java source code, but I plan to compile directly to Java byte code.

The explanation:
  • int method is an index into the Suneido symbol table - since it's faster to dispatch on int instead of string (and it also makes the compiled code smaller)
  • a variable number of arguments are received into the args array
  • a switch is used to dispatch to the correct method, in this example 8346 is the symbol index of "mymethod"
  • Suneido's extra argument passing features are implemented by "massage" which also allocates an array for the local variables of the method, the "6" is the size of this array, the remaining (variable number) arguments are symbol indexes for the methods parameter names (required to handle named/keyword arguments)
  • if the method is not found in the current class, invoke2 is called on the parent class, if this ends up at the ultimate base class (SuClass) then Suneido's Default method-not-found will be called, if this also ends up back at SuClass then an exception is thrown
  • methods receive a "locals" array containing the arguments in the first part of the array
  • default argument values are compiled to code, in this example the default value of the third argument is false
  • classes and functions are stored in the globals array
  • blocks are compiled into separate methods, context is passed via the locals array (this is one of the reasons for using the locals array instead of native Java local variables)
  • Java classes can be called directly, with the appropriate conversions
This should be relatively fast - method dispatch is just a couple of extra function calls and a switch - no reflection or table lookup. Of course, methods deep inside class hierarchies will take longer due to the "chaining".

So far I'm using unchecked exceptions. Partly because it's simpler and partly because that's what I'm used to from C++. The traditional advice (e.g. from Sun) is to use checked exceptions, but this is starting to be questioned.

I'm using the standard Java indent/curly style. It hasn't been a problem but it's a bit of a transition after 25 years of C and C++ (and Suneido) using the Whitesmiths style.

I did figure out how to configure the Home and End keys to be beginning and end of line in Eclipse. That's one thing I miss on the Mac. The "standard" appears to be Apple + left/right arrow, but I find that more awkward, it doesn't appear to be supported everywhere, and those keys are sometimes used for other purposes like switching OS X virtual desktops.

I notice that C++0x is proposing another approach to the problem I talked about in my last post. I suggested replacing:
Type var = new Type(...);
with:
Type var = new(...);
For C++0x they are proposing:
auto var = new Type(...);
This is better than my suggestion because it also allows things like:
auto var = func(...);
where the type of var is taken from the type of the return value of func.

Tuesday, May 13, 2008

jSuneido - good news, bad news

I just had a depressing thought. Suneido consists of about 60,000 lines of C++ code. Yesterday I wrote 200 lines of Java. Even if the Java is half the size of the C++ (?) that's 150 straight days of programming. I can probably do better than 200 lines once I get more up to speed on Java. But some of the code is going to be a lot harder to port than what I did yesterday. It's obviously do-able, but it's not going to be "quick" even if it's "easy" (which it probably won't be).

I'm starting to find some resources e.g. the golden spike by John Rose @ Sun and the JVM Languages Google Group. (there's some interesting stuff if you go back to the beginning of the group) It's good to know there's information out there (although these days that's almost a given) but the complexity of some of it is a little daunting.

Another C++ feature that doesn't port straight across is operator overloading. Not a big deal other than recognizing when it's used in the C++ code.

A Java annoyance - the duplication in statements like this bugs me:

HashMap hashmap = new HashMap();

C++ is similar but you notice it less because you can allocate on the stack with just:

HashMap hashmap;

And you can use typedef to make abbreviations so you can write:

Map hashmap = new Map;

Hah! Two days programming in Java and I'm already redesigning the language! Here's my proposed shortcut (syntactic sugar):

HashMap hashmap = new; // or new(args...)

On the positive side, writing a multi-threaded socket server seems a lot simpler in Java than in C++. It didn't hurt that one of the books I found at the bookstore was TCP/IP Sockets in Java.

Monday, May 12, 2008

More on jSuneido

When I wrote the jSuneido post a few days ago it was just a vague idea. But since then it's been buzzing around in my head and it seems more and more like something that should at least be investigated.

I think there are good "business" reasons for doing it, but I have to admit it's the technical challenge that excites me as much as anything!

Of course, the overwhelming temptation is to use this opportunity to "fix" parts of Suneido that in hindsight seem less than ideal. But if it's only the server part of Suneido, then it will have to stay compatible with the existing client, so that should stop me from getting too carried away!

I went through my library for Java books but they're all fairly out of date so Friday I ordered some new ones from Amazon and Chapters, including a couple on the JVM. I was out of town for the weekend but Sunday on the way home I stopped at the local bookstore and picked up a few more.

A lot of my questions revolve around how to "map" Suneido onto Java and the JVM. Luckily, Suneido is not as "dynamic" as some dynamic languages. For example, it doesn't allow adding/removing methods on classes on the fly as Ruby does. (This was a deliberate choice - Suneido's predecessor had this ability and I felt it led to more harm than good.)

Here are some highpoints:
  • Suneido exception => Java exception
  • Suneido class => Java class
  • Suneido function => Java class with static method
  • Suneido number => Java BigDecimal
  • Suneido instance members => Java HashMap
  • Suneido method call => custom method lookup (until Java invokedynamic is released)
Suneido has more argument passing options e.g. keyword parameters so this will have to be handled on top of the Java argument passing.

Suneido blocks (closures) will be one of the harder parts. The code itself can be private methods, but the challenge is to provide access to the local variables of the defining context.

I'm not sure how to implement calling Java code from Suneido code. But judging from Groovy, I assume it's do-able, presumably using Java reflection. For now, I'm not too worried about the reverse (calling Suneido code from Java).

I'm not sure whether I'll port my own regular expression code or use Java's. It depends on how compatible they are.

Another issue is how to "map" (port) my C++ code to Java. Things like reference variables and templates make this tricky in spots.

Sunday evening I installed Eclipse on my MacBook and started to get my feet wet. I have to say that starting a new project, and in Eclipse's "native" language, I had a much more positive experience than when I tried to use it for C++ recently. Of course, I fumbled around a bit as you would expect with an unfamiliar tool but overall it went pretty smoothly and I'm enjoying the Eclipse features.

I chose to start by implementing the Suneido data types. I've written about 200 lines of Java code (as reported by the Eclipse Metrics plugin) including jUnit tests. So far it's been a straightforward translation of the C++ code to Java.

Already the portability is nice, I was able to work in Mac OS X at home and then copy the project directly to Windows at work and continue working.

It's hard to say how big a project this is. Sometimes it doesn't seem too bad, other times it seems like a huge job. Time will tell I guess.

Friday, May 09, 2008

New Trick for SciTE

I just discovered that SciTE, the programming text editor that I use, has an autocomplete feature that I never knew about.

You turn it on by adding to the properties file:

autocompleteword.automatic=1

From then on, if you type a prefix of a word that has only one match within the current file it will suggest that word and you can hit Tab to accept it. This is really handy for long names.

I've seen this feature in other editors but didn't realize it was available in SciTE. (I was aware that there was a facility for setting up API's but this is simpler and more generic.)

Note: SciTE is based on Scintilla which is the code editing component used by Suneido.

Thursday, May 08, 2008

Suneido Broken & Fixed

When I was making the changes to handle thread specific storage on ACE I encountered some "ugly" code.

The first thing was a lot of instances of:
proc->stack.top()
which got even worse when they became:
tss_proc()->stack.top()
(and similar for push, pop, etc.)

In interp.cpp I had defined inline functions for these, but I hadn't extended this to elsewhere. I moved them to interp.h and converted all the instances to:
TOP()
While doing this I noticed code like:
Value* sp = GETSP();
...
SETSP(sp);
This seemed like a perfect application for an automatic destructor. So I added this to interp.h:
struct KeepSp
{
KeepSp()
{ sp = GETSP(); }
~KeepSp()
{ SETSP(sp); }
Value* sp;
};
#define KEEPSP KeepSp keep_sp;
Using this, the code became simply:
KEEPSP
...
This was even better where I'd had:
Value* sp = GETSP();
Value result = ...
SETSP(sp);
return result;
because it could now be simplified to:
KEEPSP
return ...
The next thing that caught my eye was code like:
try
{
...
x->close();
}
catch
{
x->close();
throw ;
}
Another good application of automatic destructors. So I added this to std.h
template<class T> struct Closer
{
Closer(T t) : x(t)
{ }
~Closer()
{ x->close(); }
T x;
};
This let me rewrite the code to simply:
Closer<mytype> closer(x);
...
(without any try-catch)

I was feeling pretty good about this refactoring, until I found that one of my tests was broken. I reviewed all my changes to see if I'd made any inadvertant typos or other mistakes. It all looked good. I started tediously reversing my changes one at a time until I found where I'd broken the test. (Theoretically I should have compiled and run the tests after each little change, but unfortunately that takes a little too long to be tolerable.)

I found the spot, but it looked fine. It almost seemed like the KeepSp destructor wasn't getting run. At one point it seemed like it worked on Visual C++ so I started looking for bugs related to destructors with GCC 3.4.2 But I'd been mistaken, it was actually broken on Visual C++ as well.

I tried using GDB but it didn't really help. In the end I added some "prints" and that gave me the clue that there was an exception being thrown, but the problem wasn't that the destructor wasn't being run - the problem was that the destructor was being run. The old way the code had been written, the stack pointer wasn't being restored if there was an exception, now it was.

Why would that cause a problem? Looking at the interp.cpp code I saw a key comment - returns from a block (that actually return from the containing function) are implemented as exceptions and pass the return value on the stack. My new code that restored the stack pointer even when there was an exception was losing the block return value.

Hmmm... now what? Do I put the code back to the way it was? But it seems kind of ugly to depend on cleanup code not getting run when there's an exception. Instead I changed block returns to store the value in the proc rather than on the stack. Now all the tests pass.

Thank goodness for tests. Only one test (out of hundreds) failed. Without that test it could have been quite a while till I ran into this bug, and it would have been a lot harder to track down.

You could argue that if I'd just left well enough alone I wouldn't have run into the problem in the first place. After all, if it's not broken, don't fix it. I don't agree. Down that path lies chaos. You have to fight entropy. Otherwise each successive change makes the code slightly worse, until eventually it's too incomprehensible and fragile to dare touch.

The day was a bit of a roller coaster. I start off feeling pretty good about the cleanups. Then I got frustrated when I couldn't figure out why it was broken. Then when I thought I'd have to undo my changes I got depressed. I know, rationally it's pretty silly to get depressed about something like that. But I guess if I didn't care enough for it to be depressing, then I would never have gotten this far in the first place. Still, you'd think after 30 plus years of programming I'd be a little less emotional about it!

At this point I decided to take a break and go for a run. That usually improves my mood. And while I was running I figured out that I could make it work with the improvements. So between the endorphins and not having to undo my changes, suddenly I was in a good mood again. That's probably a good place to leave things for the day!

OpenOffice 3 beta

OpenOffice 3 beta is available. The big feature for me was that it finally runs native on Mac OS X (instead of via X windows) but there are a bunch of other features.

I've been using NeoOffice to get a Mac version, but it's nice that this is now standard. (Although it leaves the NeoOffice project with less of a reason for existence.)

Wednesday, May 07, 2008

jSuneido ?

As I work on multi-threading Suneido I wonder if I shouldn't be shifting direction.

Does it really make sense to implement your own VM these days? Wouldn't it make more sense to re-implement Suneido on top of an existing VM such as Java?

For example, jRuby (Ruby re-implemented on the Java VM) in some cases outperforms the original C version of Ruby.

And Groovy also demonstrates that it is possible to implement a highly dynamic language on top of the Java VM.

The JVM would handle low level issues like garbage collection and JIT and multi-threading and 64 bit. (Although you'd still have to deal with higher level multi-threading issues.)

It could provide access to Java libraries so we didn't have to continually "re-invent the wheel".

It would not solve the current issue of a non-portable Windows UI. As with my current project, it could be server only to start with.

It would allow portability to everywhere that the JVM is available.

I'd have to not only learn Java, but also learn JVM issues that a normal Java programmer doesn't have to worry about. That's a little intimidating.

Of course, the other big question is why not just use an existing language/database? The big reason now, is that we have a big (to us) application written with Suneido's language and database. The thought of re-writing or porting it all to another platform is way too scary.

Moving to something like the JVM that allowed interoperability with other JVM based languages (and other databases) might actually allow us to incrementally move to another language if we chose to.

Obviously, this wouldn't be a minor decision, but it is food for thought.

More Concurrency

While I was waiting for the iMac transfer I was working on my project to multi-thread Suneido. The next piece I decided to tackle are the global hash tables for symbols and global names.

One obvious solution is to simply use many-readers/single-writer locking.

That still leaves the question of how to implement it. Do I mix the locking into the existing code? Or do I write a "wrapper" class that handles the locking and then delegates to the existing class. Neither is ideal - mixing in the locking will complicate the code but a wrapper will add overhead (although probably minimal).

But I'm not completely happy with adding locking overhead to readers. Especially for these tables. Once the server is "warmed up" these tables will almost never be written to. It would be really nice to avoid any overhead for readers.

My next thought was lock-free hash tables. I've heard various noises about this so I thought it was worth seeing what was available. As far as I could find, the answer was: not much.

The Intel Threading Building Blocks have some concurrent C++ data structures. But Boost doesn't. And STL doesn't (yet).

The most interesting thing I came across was Cliff Click's work. If you've got an hour to spare, check out the video of him giving a presentation about it. I see from his blog that he's extended his work to other data structures. Apart from the impressive benchmarks, what I really like about his approach is that it's simple and relatively understandable. (Although I still have a hard time wrapping my head around re-ordering of memory accesses and the required "fencing".)

Unfortunately, his work is all in Java. The only mention of C++ is someone commenting on the blog that they'd like a C++ version. Part of me is really tempted to implement it. It seems like it wouldn't be that hard. Famous last words.

Side note: I ran across some references to the advantages of power-of-two size + AND hash tables versus prime size + MOD (more traditional and what I'm using). One of the reasons is that MOD is slow (relative to AND). But I wonder how much that really affects overall speed.

Another thought I had was that I could just give each thread their own tables. The downside is the extra memory and redundant work maintaining the tables. This approach is closer to using separate processes instead of threads.

That led me to consider two copies - one for reading and one for writing. The reading one would be lock free. The write copy would be protected by a mutex. To write you would lock the write copy, update it, swap the two copies using an atomic compare-and-swap, and then update the other copy to keep in sync. This seems simple to implement, and simple enough to be reasonably sure it's "correct". It uses double the memory, but only for the hash table of pointers, not for the data itself. (Both copies can point to the same data.)

Strangely, I haven't seen any mention of this approach. Perhaps because it's not general purpose - it would not scale to many writers. But this shouldn't matter for my purposes. Or maybe there is some flaw that I'm missing? (concurrency issues can be like that)

The writing bottleneck could slow down startup. If so, (and obviously I'd want to measure that) then it would be fairly easy to write out symbol tables from a "warmed up" server and use these during startup to initialize.

As far as the atomic compare-and-swap, that seems readily available: in Win32 it's InterlockedCompareExchange. ACE has ACE_Atomic_Op, although it doesn't appear to have compare-and-swap. As of version 4.1 GCC has built-in atomic operations.

Given all the attention on concurrency these days, it's surprising that there aren't more tools available.

Tuesday, May 06, 2008

New iMac

My new iMac arrived today. There's a bit of a story behind it's arrival.

Originally I was going to buy it through the local store (not an actual Apple store, but an Apple retailer and service center). Of course, they didn't have the model I wanted in stock. But they had some similar models coming in a few days and they could upgrade the memory and hard disk to what I wanted. Except the person that took down my phone number mumbled that he didn't think they could upgrade the hard drive, leaving me wondering whether they could or not.

A week passed and no word. Finally I phoned them to find out what was happening. "Soon", I was told. Given the uncertainty about getting the right configuration, I decided I might as well just order on-line.

But my credit card was declined!? It was paid off and I hadn't had any problems with it. I had let Apple use the credit card info on file - maybe it was out of date. I re-entered the credit card information. It was declined again. Finally I phoned the credit card company - they had declined it because they (or probably their software) thought it was "suspicious". I'm not sure why. I've purchased from Apple before. Sure, it was a larger amount but surely that doesn't mean it automatically gets declined? And couldn't they have contacted me? Oh well, no point expecting rational behavior from something like a bank.

One the positive side, while I was messing around with the credit card I got an email from Apple saying they had come out with upgraded models and since my order hadn't been shipped I'd be getting the new model. Lucky - I'd have been really annoyed if I got the old model just a few days before the new one came out.

I got a 24" (1920 x 1200) iMac with 3 ghz Core 2 Duo with 6 mb L2, 4 gb of memory and a 1 tb hard disk. A decent improvement over my 2.2 ghz Mac mini with 2 gb of memory and 160 gb disk

I pulled it out of the box, plugged it into the power and network, put batteries in the mouse and keyboard, and fired it up. I hesitated when the migration tool wanted Firewire, but I hadn't set the machines up side by side. I decided to use the secondary Ethernet choice. (wouldn't that be clearer if they called it "Network"?) This required installing CD and DVD Sharing software on the old machine.

Soon after it started it came up with an error about losing its connection. But when I re-tried it seemed fine. Not sure what caused it. It started off saying it was going to take 10 hours, but that estimate soon dropped and it ended up taking a couple of hours to transfer about 110 gb. That seemed a little slow. I searched on the web and found some people saying Firewire was faster. I wondered about starting over but the migration didn't even have a cancel button and I was a little nervous about "pulling the plug" while it was transferring so I let it go. The nice thing about having multiple machines is that when one is tied up you can still work on another (the MacBook).

The transfer finished and at first it seemed like everything was great. But then I started running into problems. Some of the System Preferences wouldn't open. iTunes refused to start (with an error 1000). Hmmm... I searched on the internet for migration problems. One person recommended running Software Update on the new machine before migrating (so you didn't get newer settings on older software) But unfortunately, it was a little too late for that.

After a few tries and a few system restarts (reminiscent of Windows!) I got Software Update to run and that seemed to clean up the problems.

I calibrated the monitor with my Huey Pro. This seemed to make a bigger difference than on my last monitor, making the colors quite a bit "warmer" than the default.

Then I noticed that I now had a Huey icon on the menu bar. Strange, that hadn't shown up on my old machine. Then after I restarted the machine it was gone. I got on the Huey web site to see if there were any software updates. In their support section I found that the automatic startup was broken and you have to manually install. Which means it hadn't been running on my old machine. Ouch. I'm not sure if this affected the entire calibration or just the ambient light adjustment. They hope to fix their software in 2008. I wonder how many other people have this problem?

Next problem was that it had installed my Epson R1800 printer with the Gutenprint drivers. This works ok for simple printing, but not so good with printing photographs from Lightroom. I downloaded the drivers from Epson and installed them. I thought this would create the printer but it didn't, just installed the drivers. So I added the printer and said "yes" I really wanted to install this printer a second time. But when I printed a test page, nothing came out. Here we go again.

I poked around and happened to right click on the printer. One of the options was "Reset printing system". That seemed like it might help so I chose it. I got a warning saying this would delete all my printer queues and pending jobs. That seemed alright so I clicked on OK. Oops! That deleted all the printers. I guess that's what they meant by "printer queues". I re-install the printer and run a test page again. Still no errors, but nothing comes out. I notice that it appears to be printing a Postscript file. But I don't think the Epson driver uses Postscript. I try printing from another program and it works fine. It was probably working all along. Sigh. Not a very good test page. I guess it's intended for Gutenprint drivers, but why use it for other drivers?

I also had problems getting my Parallels virtual machines to work. I thought I might have to re-install the software, but after hanging up a few times it decided to start working. Except that my virtual machines had lost their network adapter. But all I had to do was re-enable it.

Of course, I'd lost the twisted printer setup that let me print from Windows with the Epson driver. But I noticed I now had Apple Bonjour on Windows. I can't remember why I installed it, but I figured it was worth a try. My first attempt failed but after enabling Printer Sharing on OS X and sharing the printer I got it to work. Even the test page printed :-) Much easier than the previous setup.

I order the iMac with the new wireless keyboard. It's tiny! Especially next to the 24" iMac. It's taking a little getting used to but the feel is nice. I won't miss the numeric keypad, but I'll miss the larger arrow keys and Home/End/Page Up/Page Down/forward Delete etc. But it's essentially identical to my MacBook keyboard so it's not too bad.

And that pretty much took up my whole day. Still, probably the most painless computer switch I've done.

Monday, May 05, 2008

Old Lessons

One of the common suggestions for testing is to test around limits or boundaries. e.g. try 0 or 1 or -1. I was painfully reminded of this by a recent debugging session.

Some background. Originally Suneido memory mapped the entire database file into memory. That was simple and fast but limited the size of the database to less than 2 gb (the amount of address space a Win32 process gets).

When our customers' databases started to approach this size I had to change Suneido to memory map a limited number of segments of the database file and re-use address space in an LRU fashion.

Considering how major this change was it went amazingly well. I was quite nervous about weird bugs from things like dangling pointers caused by re-mapping. So I did quite a lot of testing before we rolled it out. And we kept a close eye on it at first.

This was quite a while ago and I figured everything was good. Until our client with the largest database started having problems. Their database was right around 4 gb. So I did some testing and sure enough when the database reached 4 gb MapViewOfFile would fail with "Access Denied". The "Access Denied" part threw me off since it sounded like some kind of security/permissions issue. I dug through MSDN trying to find some mention of a 4 gb limit but couldn't find anything.

As with so many of these things, the error turned out to be really stupid. Memory mapping a file with Win32 is a two step process, first CreateFileMapping, then MapViewOfFile. Since it was MapViewOfFile failing I hadn't paid much attention to the CreateFileMapping step.

Both API calls accept 64 bit file offsets as two 32 bit values. I was only passing the low 32 bit value to CreateFileMapping. Stupid! Of course, it worked up to 4 gb. But over 4 gb it wasn't passing the correct offset. Whereas I was passing both halves of the offset to MapViewOfFile. It was trying to map the correct offset, but was failing because the file mapping was for a different, incorrect offset. (A little confusing because the call with the wrong arguments was succeeding and the call with the right arguments was failing.)

Why hadn't I caught this with my testing? Because the old database size limit was under 2 gb so I tested with over 2 gb. The bigger the database the slower the testing so I never bothered going as big as 4 gb. Oops.

I should have realized that the limit for 32 bit values would be an obvious failure point and I should have tested up to and over 4 gb.

After I fixed this bug I was able to grow a database past 4 gb. For a minute I thought I was done. But a little more testing revealed that it wasn't accessing the data past 4 gb properly.

This was a little tougher to track down. I got fed up with waiting so long to create big databases and came up with a way to quickly expand the temporary databases used by the tests to over 4 gb. (I could have saved considerable time in the long run if I'd done this at the start!)

The problem turned out to be a place in the code where I mistakenly stored a 64 bit file offset in a 32 bit integer. Again, this worked up to 4 gb but not beyond.

After this fix all the tests ran successfully.

Next time maybe I'll remember to test around obvious limits and boundaries.

PS. Not that it's any excuse, but it seems odd that the compiler wouldn't give a warning about assigning a 64 bit value to a 32 bit variable. I'm using -Wall with GCC and the only warning I have turned off is -Wno-sign-compare. Maybe there are warnings I need to turn on that aren't included in -Wall. I should probably look into this.

PPS. Along the way I got an excellent demonstration of the effects of locality on multi-level storage. If I created a big database by doing: for each table, add 10000 records, then it was reasonably fast. If I turned it around to: 10000 times, for each table, add 1 record, then it was about 10 times slower. Dealing with one table at a time kept all the data and index nodes for that table together so they could all be mapped into memory. Looping through each table and adding one record to each causes the data and index nodes for all the tables to be mixed together over too large an area to be mapped all at once, causing continuous un-mapping and re-mapping.

Saturday, April 26, 2008

The Best UI is No UI

The other day I watched someone unlock their car with a remote key. No big deal, a common occurrence. But for some reason I noticed it and thought that's cool. Maybe because I never had a remote key. I went from a regular, mechanical, non-electronic key, to a Prius with a "Smartkey" system.

With the Prius, I don't need to touch the key. I walk up to the locked car and open the door. As long as I have my key in my pocket or backpack it's all automatic. In other words, no user interface. (I'm now spoiled - I resent having to pull my office key-card out of my pocket, and even worse to have to use a mechanical key on my house.)

Too often in software (and other areas) we think the ultimate "solution" to a problem is some clever UI, like a remote key. But people don't want a clever UI, they want to get some job done, and often the less UI the better - none if possible.

Similarly, the best solution for errors is to prevent them in the first place. (again, no UI) You can't lock your key in the Prius because it won't let you lock the car if the key is inside.

Thursday, April 24, 2008

Multi-Threading Suneido - Thread Specific Storage

Back to work on multi-threading Suneido. After frittering away my last session, I figured I'd "park downhill" and decided at the end of that session that the next thing I'd work on was replacing my home brew thread specific storage with ACE_TSS. With a clear task I wasted less time.

ACE_TSS is a template class used like: ACE_TSS<mystruct> mytss;

which is then accessed like: mytss->member

It overloads operator-> to do its stuff.

In order to keep ACE specific code as limited as possible I defined functions to access my thread specific storage. For example:

Proc*& tss_proc() { return tss->proc; }

The '&' is needed so the functions can be used as lvalues like:

tss_proc() = newvalue;

Unfortunately, this meant replacing all the instances of proc with tss_proc() but I could use the compiler to do the heavy lifting by removing proc and seeing where I got compile errors.

I could have kept the name as proc, but changing it made the transition clearer, and it doesn't hurt to make it clear in the code that it is thread-specific.

For my current, non-ACE version of Suneido I simply defined the functions to access my home brew thread specific storage.

In the process I discovered that part of the old thread specific storage code was broken. It hadn't caused problems because it was code to clear unused memory during garbage collection, so it just meant the garbage collection was a little less efficient. I fixed it for the current version, but I don't know how to implement it for real threads so it's a good thing it's not critical!

The only real problem I ran into was that the Boehm garbage collector does not appear to "see" the ACE thread specific storage. That's probably fixable but it would require digging into how ACE_TSS is implemented. And that would be platform specific. Yuck. Instead I was able to make the thread specific storage simply point to data that would be seen by the garbage collector. I'll just have to be careful I don't forget about this issue in the future.

As far as I can tell, it's all working, both the current version and the ACE version. Next I have to start tackling the crux of the problem - locking global data structures. I think I'll start with the symbol table and the global values table. These are fairly loosely coupled and shouldn't be too hard to handle. That should give me a little experience to help tackle the hard parts like the database.

PS. I started this session using NetBeans but I gave up part way through and went back to using Scite. This was partly because I haven't got NetBeans set up to build, which meant I had to compile in a separate command line window, which meant there was no easy way to click on the errors and go to that part of the source. (which I can do in Scite, although it's mostly a straight editor). I could have messed around with getting the build environment set up, but then I would have ended up like last time, with no real work accomplished.

Another thing I found disappointing in NetBeans is that it didn't seem to have the kind of powerful tools that I want/expect from an IDE. For instance, there didn't seem to be any way to find all the uses of a function or variable. Maybe I just missed it. But I wouldn't be surprised if it wasn't there, C++ tends to get second-class support in primarily Java IDE's. The Eclipse CDT used to be fairly minimal too, but I think they're supposed to have made significant improvements in the latest version. I'll have to go back and take another stab at trying to use it.

A final annoyance in NetBeans was that it didn't treat underscores as word characters. e.g. if you choose "Whole Words" and search for "proc" you find "tss_proc". I looked through the configuration options but couldn't find anywhere to change this. It seems odd because that's not C++ specific. If anyone knows how to "fix" this, let me know.

Firefox 3 Beta

I've been holding off on trying the Firefox 3 beta. I used to be an earlier adopter but now that so much of my computing life goes through my Firefox browser I'm a little more reluctant to live on the bleeding edge.

But beta 5 is supposed to be the final beta before the release candidate so I thought I'd give it a try - as long as I could install it alongside my current Firefox. On Windows I used Portable Firefox but I couldn't find anything similar for the Mac. Instead I created a second profile and have Firefox prompt for the profile when it starts up. This works but is a bit of a hassle and I'm afraid I'll choose the wrong profile at some point.

A pleasant surprise on the Mac is that it finally has a more Mac-like theme. e.g. buttons on web pages are rounded instead of ugly rectangles.

Unfortunately, it still doesn't show the favicons on the bookmark bar. I guess this is to be compatible with Safari, but that seems pretty silly to me. But I found the Mac bookmark toolbar favicons style for Stylish which solved this issue.

If you asked me if I needed a customizable browser my first response would be "no". But when I go back to a "bare" browser I realize how much I depend on the add-ons I use. Of course, not all the add-ons have Firefox 3 versions yet. dragdropupload (which I use all the time for email attachments) was available.

But the Google Firefox Browser Sync isn't, which was a real hassle because it meant I didn't have any of my bookmarks or passwords. I don't have many bookmarks since I keep the bulk of them on del.icio.us so it was easy to export from Firefox 2 and import into Firefox 3 (once on Windows and once on Mac). I haven't found a way to export and import my passwords.

Passwords was another pleasant change in Firefox 3. I have a hard time remembering all my passwords for all the web sites I use, especially since I try not to use the same password for all of them. On Firefox 2 after you enter a password a dialog pops up asking if you want to remember it. But at this point you don't know if it's right or not. Either you say "remember" and then if it's wrong end up storing a bad password. Or you say "don't remember", in which case, if it works, if you want to remember it, you have to log out and log back in. Either way it's not ideal. But in Firefox 3, the "remember" prompt is inserted in a bar at the top of the screen mode-lessly - meaning you can wait to see if the password is accepted before choosing to remember it or not. Great improvement.

The Delicious Bookmarks add-on for Firefox 3 is a beta version that requires you to join the Yahoo group before you can download it. No big deal, but more hassle than necessary.

Firefox 3 is supposed to be a lot faster than Firefox 2. I can't say I've really noticed a big difference, but I haven't timed anything.

I haven't had any real problems with the beta so far. I crashed once in a couple of days, which isn't any worse than Firefox 2. I'll continue testing for a bit longer, but it seems good enough to switch over.

Tuesday, April 22, 2008

Wireless Security

Sometimes it seems like I go out of my way to make things hard for myself!

I got home from work to find a note from Shelley that her laptop wireless wasn't working. Oh yeah, I hadn't got it working with the new Time Capsule.

I run the Airport Utility, change the Wireless Security to WEP, and try to set the password to what I was using previously (so I didn't have to mess with the laptops or other devices).

Nope, it will only take an exactly 13 character password. Okay, I make up a new password and go to connect Shelley's Windows laptop. But it won't work. I mess around with the settings, stupidly failing to record the original settings. No luck.

I search on the internet. Some people say that on Windows you have to use the hex version of the password (you get that from the AirPort Utility from Equivalent Network Password on the Base Station menu). I type in a 26 character hex password multiple times. No luck.

More digging reveals that this is 128 bit WEP. (More or less. It's also Transitional Network Security that is also WPA compatible. Whatever that means.) The older 40 bit WEP that I was using previously is actually available - you just have to turn off the new 802.11n :-( and then hold down the option key while you pull down the Radio Mode list. (how could I have missed that!)

Now I can enter the old 10 digit hex password (with a dollar sign). And now the Windows laptop connects.

I pull out the Chumby to make sure it can still connect. It can, but in the process I find that it does handle WPA! So I don't need to use WEP after all. Argh! (It must have been my old Squeezebox audio player that was WEP only, but I'm not using it any more - too much hassle to run its proprietary server.)

Back to the Airport Utility, re-enable 802.11n, set it back to the default WPA. Back to the Windows laptop, enter the new password, it connects fine. Back to the Chumby, enter the new password, it works fine. Same with my MacBook.

So I think I'm all good. I'm glad my Mac mini is hard wired to the Time Capsule. Trying to configure wireless through a wireless connection would have added even more fun to the process!

Monday, April 21, 2008

New Time Capsule

I just replaced my Linksys wireless router and Lacie network drive with a 1 tb Apple Time Capsule. My reasons for the change included:

- The Linksys router periodically "dies". Even my wife knows to unplug it, wait a few seconds, and plug it back in, but it's still a hassle. Since this fixes the problem I'm assuming it's the router, but I suppose it's possible it's something like the ISP DHCP. I'm crossing my fingers that a new router will fix the problem.

- The Lacie network drive was getting full.

- I was using a second Firewire Lacie drive for my Time Machine backups from my Mac mini. This drive was also running out of space.

- I wanted to make Time Machine backups of my MacBook, and the only way to do that without plugging in an external drive is with Time Capsule. (Time Machine won't work to a regular network drive. I'm not sure if that's just a marketing decision or whether there is some technical reason as well.)

- I could replace two boxes (router & network drive) with one - the less cables the better, if you ask me.

- I occasionally had range problems with wireless. I'm hoping the new router will help.

As usual, the packaging was slick and the hardware is attractive. I plugged it in, installed the software, and it worked.

At first I couldn't see the drive from the Windows machine but after I set the right workgroup it appeared.

The setup wizard only allowed WPA wireless security. I was still using WEP since I have some wireless devices that don't support WPA (like my Chumby). I realize WEP is minimal security but it's enough to stop my neighbors from accidentally using my network. I have used MAC address filtering in addition but it's a hassle when you add new devices and I always seem to end up turning it off. I see the regular configuration allows WEP but I haven't got around to changing it yet.

To keep things simple (at least I assume that's the reason), OS X only seems to offer a single "key" entry field. I'm never quite sure if this is hex or text or what. If you were Apple/Mac only it wouldn't matter - it just works. But when you're connecting to other things it can be confusing. I've seen references to using a dollar sign or 0x prefix to enter hex keys but it doesn't seem to be clearly documented (that I've seen).

Once I had it set up I switched my Mac mini Time Machine to go to the Time Capsule. Of course, the first initial backup is huge and takes a long time. And there's no way to move your existing Time Machine backups to the new drive.

I probably should have know better, but while that was chugging away I turned on Time Machine on my MacBook and pointed it at the Time Capsule. Of course, it also was a huge initial backup.

At the same time (I know, asking for trouble!) I was playing with the Time Machine options to exclude certain files and directories. Then I realized every time I changed the settings the initial backup started all over again!

Just to stress it a little more, I started copying the other files (pictures and music) from the old network drive to the new one. It took me a few minutes to get the network drive working again - it wouldn't show up until I re-ran the configuration utility. This copy ran for quite a while (an hour maybe?) and then aborted with an Error 50. I suspect my impromptu stress test uncovered some bug in the Time Capsule software.

When I went to bed the two Time Machine backups were still running. When I got up, the MacBook had finished. Unfortunately, I'd forgotten I had the Mac mini set to power off at night. When I started it up, it had to restart the backup from the very beginning! I realize this is just the initial backup, but given how long it takes, you'd think they would have made the software handle resuming a backup. This time, without all the other concurrent activity, the backup went smoothly and finished.

Or maybe I should say, more or less finished. For several more hours it popped up windows about backup up large quantities of files. I'm not sure what this was - I hadn't modified or added any significant amount of files. Why didn't it get this stuff on the initial backup? But eventually it seemed to settle down. The problem with this kind of system that just invisibly does stuff in the background is that you're never quite sure if it's working properly (at least if you're a cynical techie).

Once the backups were done I went back and copied the other data from the old network drive. Again, now that I wasn't trying to do too many things at once, it went smoothly. (Theoretically, if the software is "correct" it shouldn't matter how much stuff you do at once. But no software is "correct". As the saying goes, in theory, practice should be the same as theory, but in practice, it's not.)

So far so good. My only (minor) complaint is that, judging by the temperature of the case, the Time Capsule doesn't seem to go to "sleep" - even overnight, with no computers active (or even turned on). There's probably continuous activity on the internet side, but if nothing is awake on the LAN side, I would think it could still be smart enough to go to sleep.

One nice side benefit is that the Time Capsule hard drive is a lot quieter than the Lacie.

I know it's showing my age but I can't help continuing to be a little mind boggled by gigabytes of memory and terabytes of hard drive space. (I just listened to a podcast that said that in many cases algorithms had progress more than hardware and in these cases you'd be better off with a modern algorithm on old hardware than old algorithms on modern hardware. In theory that might be true in some cases, but modern software is never going to fit on old hardware. I can't even fit a single digital picture on a floppy disk, let alone something like Open Office.)

My next plan is to replace my Mac mini with a 24" iMac. I considered a quad core Mac Pro for not a lot more money, but I decided I'd prefer the reduced "clutter" of the iMac. And if I wanted an Apple monitor, then the Pro would end up quite a bit more expensive. When I bought the mini it was more in the nature of an experiment so I bought the cheapest Mac I could. Now that I've pretty much converted to Mac I want something a little "bigger" i.e. 4 gb ram, 1 tb disk. Nothing like RAW photos and virtual machine images to eat up disk space!

Tools, NetBeans, and Eclipse

I've been pushing my Ruby on Rails programmer to use some kind of IDE instead of just an editor. I suggested NetBeans since I'd read some good things about it. He installed it, but so far I haven't convinced him to switch. I can sympathize, when you've got a comfortable routine it's a hassle to change. And productivity tends to take a dip at first while you're learning new tools. But in the end, better tools can make a big difference. Many of the new tools we've added to Suneido have become so useful that I'd be really annoyed to lose them. (Even though we went years without them, and without knowing what we were missing.)

Soon after, I found myself working on the Suneido C++ source code with ... just an editor. Hmmm. I decided maybe I should give NetBeans a try myself. I also figured I should see what progress there had been on the C++ tools (CDT) in Eclipse. Both NetBeans and Eclipse have had many improvements. I downloaded, installed, and tried both. My conclusions were similar to Eclipse 3.3 or NetBeans 6.0.

As much as I like the concepts of Eclipse, it's got some awkward aspects (like "workspaces"). With NetBeans I was able to get the Suneido code loaded with little trouble. I ended up giving up on Eclipse. I'm sure I could have figured it out in the end (I have in the past) but I wasn't in the mood to spend the time.

It's interesting that both these IDE's are written in Java and run on Windows, Linux, and Mac.

Of course, in the end, I used up the time I had messing with IDE's and didn't make much progress on my code. Was this an excuse to goof off or was it an investment in better tools? Depends whether it pays off in the future I guess.

Tuesday, April 08, 2008

Too many heap sections

Recently, a couple of our clients with big databases (for us that's over 4 gb) have been unable to repair their database after crashing. (Why they crashed in the first place is another question - there may be hardware problems.) The repair aborts with "Too many heap sections". Luckily Thankfully, the software does automatic backups twice a day and the customer didn't lose much data. But the repair is still preferable as it generally only loses incomplete transactions that were in progress at the time of the crash.

This error comes from the Boehm memory manager/garbage collector we use. The repair process does keep a lot of information in memory and the amount of information is relative to the size of the database. The question was how to fix it? Did I need to rewrite the repair process to keep less information in memory, maybe use a temporary file? (Although that would make it slower.) Or was there something that could be adjusted in the Boehm code? There's also a newer version of the Boehm code - we're on 6.5 and the latest is 7.0

I searched on the web but didn't find any useful information about this error. Most mentions of it were pretty old. (I did find a reference to the Boehm code with the Mac OS X code - I wonder what part of OS X uses it?)

I searched the Boehm code for the error message and found it in several places (not very DRY). The error is caused when MAX_HEAP_SECTS is exceeded. I searched for where that is defined and the value seemed to depend on whether SMALL_CONFIG or LARGE_CONFIG (or neither) was defined. I wasn't specifically defining either, presumably leading to a medium setting.

I figured it was worth a try re-compiling with LARGE_CONFIG. I modified the makefile and re-built. Then I read a magazine while I tried running the repair process. (It takes a while to process a 4 gb database, even with a fast computer with lots of memory.) It reminded me of the "old" days when I'd have time to catch up on my reading while I compiled what today would be regarded as tiny C programs.

Damn, it still crashed with the same error. Oh well, should have known it wouldn't be that easy.

I went back to remove the setting from the makefile and glanced at the file name. Hey! I was editing the makefile for the MinGW version, but I'd been testing the VC7 version. Doh!

Try again, this time build the right version & test the version I build.

Eureka! It completed successfully.

Now we'll just have to test this version enough to be relatively sure that the change doesn't have any unwanted side effects. Maybe it will be that easy after all!

Sidenote: Considering the number of people and projects using the Boehm code (e.g. Mono) it seems odd that there isn't more documentation. I can understand Boehm not writing it, I'd rather he spent his time on the code. But you'd think someone along the way would have written some. Maybe no one else understands it well enough. Despite having written my own, I know I don't - I just treat it as a black box.

More Software Frustrations

Here's a few software things that have bugged me lately:

I'm currently reading Why Software Sucks by David Platt. He praises how Google senses the country you are connecting from and uses the language of that country e.g. Spanish in Mexico. The problem is, it does this even if I am logged in to my account. Just because I am in another country doesn't mean I've changed my language. It's possible to get back to English but it's annoying nonetheless.

Next, I'm on my bank web site paying some bills (a feature I do appreciate). A message shows up telling me I can now receive one of my bills electronically, rather than having it mailed to me. Great! I click on the link and get presented with a blank form, without the name of the company filled in, forcing me to pick it from a huge list. (Most of the list is not applicable, e.g. for other provinces.) Next there is a "Name" field - who's name? mine? but surely it knows my name? or the company name? but I just picked it from a list? I leave it blank and continue - it doesn't complain so I'm still not sure what it wanted. Next it tells me I have to sign up for an ePost account. It's free, but it's annoying to have to sign up for yet another on-line account and come up with yet another password. Can't the company send bills to my bank without every account holder having to sign up for ePost?

But the best is yet to come. I get through the ePost signup, only to be told that I have to contact the company personally, by phone, before I can complete the process. Yeah, right. I pay bills on the evenings and weekends, so I have to wait till business hours to phone. Then I can look forward to the "pleasure" of an automated phone system and being on hold forever. Then I'll likely talk to some poor customer service person who has never heard of sending bills electronically. No thanks. And they wonder why so many on line transactions are abandoned part way through.

The next incident was minor, but it's a good example of a GUI blooper. I was ordering books through Lulu (copies of Getting Real for my programmers). I'm entering my address and I get to the field for "State/Province". Except there are no provinces. That wouldn't surprise me except that for the prompt. I skip it and move to the next field, "Country", and enter Canada. The screen jumps around a little bit. Hmmm... sure enough, State/Province now lists provinces. This is a good trick, some programmer obviously applied some Javascript. But they didn't consider that people generally enter fields in order, pretty much guaranteeing that Canadian customers will be frustrated. And most people wouldn't notice the slight screen jump and figure out that they could go back to a previous field and it would now magically let them do what it wouldn't just a minute ago.

We run into this issue in our own software. The rule of thumb is that if a field "depends" on another field it should be "after" it in the normal entry order. Usually this can be handled simply by changing the order of the fields. But in this case, it seems "wrong" to put Country before State/Province. My suggestion would be to remove the fancy Javascript - just have a combined list. The programmer can apply their Javascript by filling in the country based on the State/Province choice.

We use Snagit for our screenshots. It's a good program. But it has one really annoying "feature". Quite frequently, when I start it up, it pops up a dialog saying "You have the most recent version of Snagit". I always have to read this a couple of times because the normal expectation is that it would pop up to tell you there's a newer version.

I haven't quite figured out the logic behind this one. Why do I need to be told I have the latest version? I don't think it comes up every time, so maybe it only comes up after it does a periodic check for a new version. (Of course, I only use it periodically, so it happens more often than not.) It may just be that the programmer thought that since they'd gone to all the work of checking for a new version there should be some recognition of this. Programmers are often strangely reluctant to just have the program quietly do it's stuff. Maybe we need pop up blockers for more than just our web browsers.

There is a link on the dialog to the release notes. So perhaps the purpose of the pop up is to give you a chance to read the release notes. But surely if that was the purpose it would only pop up once after installing a new version. How many times (if any) do I want to read the release notes?

Enough being critical, I'd better go and try to do better myself. It's always easier to criticize. (Why Software Sucks does actually suggest solutions or at least, better alternatives, to most of the things it complains about.)

Monday, April 07, 2008

MindMeister Online Mind Mapping

I came across MindMeister from a list of web applications that use Google Gears to run off-line. Check out the demo screencast for a quick overview. It's free for a basic account.

From a quick look MindMeister looks like a good example of a Web 2.0 application.

I haven't got into using mind mapping but some people swear by it.