Without a GIL, you not only have to make sure that your entire VM + runtime are re-entrant, but that every library you call into is, too. You can call it "lazy," but it's a fairly standard + pragmatic way to safely layer multi-threading atop arbitrary C libraries.
I don't know about Tcl, but Perl's approach is anything but successful. I have a Perl app of about 40000 lines. It takes 15 seconds to create a single thread, and after it's done, Perl segfaults. Every Perl thread duplicates the AST, making memory usage bloat to ridiculous proportions. I found out that forking new processes is faster and uses less memory.
Perl 5.6.2 all the way up until 5.8.8. On Windows XP, Fedora Linux and Ubuntu Linux. Same results everywhere. Perl threads only work for apps less than 2000 lines. Go beyond that they'll blow up.
Add to that the fact that, without the GIL, there becomes a need to define what qualifies as an atomic operation. Since Ruby didn't have a standard bytecode until recently, this ends up being an implementation-specific nightmare (even with the YARV bytecode, it's still an implementation-specific issue not likely to be resolved soon). In other words, without GIL, what's thread-safe in MRI might not be in JRuby or vice versa.
Ok - well I figured that this could be done on a much finer-grained, case-by-case basis (?), but I clearly haven't touched on the issue in enough detail.
In fact, it seems, well, extremely lazy -- but I'm not sure I understand the thinking behind it. Are there pros/cons to having a GIL approach?