Yeah, I know, this would be no trivial task. But modernizing MOO in
general is no trivial task anyway. MOO would be a hell of a lot
better, IMO, if it took advantage of more modern technologies (i.e.
LLVM for the parser like Tyler said, or a different database format
that can be trivially modified offline, or by using multithreading).
@Tyler, your ideas are nice; the database could be updated to use a
different system altogether. I in fact was poking around at various
relational and NoSQL DBMSs to see what I could find that would satisfy
MOO's requirements, especially Stunt. Unfortunately pretty much most
relational DBs don't qualify, since you'd need like 5 tables per
object, which would be a fucking outrageously huge database. Something
like etcd might work, since its key/value and you can have directories
in etcd, or even MongoDB.
The LLVM parser is going to be a huge, messy problem. One way to make
it as fast as possible (which would probably be even faster than MOO)
would be to make JIT compile it as soon as possible (preferably when
set_verb_code() is called), and then store the compiled machine code
in memory. That would defeat the hole purpose of JIT, -- compiling on
the fly, when its actually needed, -- but if your not going to be
modifying verbs for a while, then making the server recompile the verb
every time its executed seems inefficient. The only time I can see
this causing problems is when a verb has already begun execution and a
programmer edits it and recompiles it. (This especially applies to
forked tasks.) Will the old machine code be effectively "hotpatched"
by the new machine code? Will the old code be allowed to finish
executing in that round and then the new code would take over?
Also, one final issue that I've discovered while playing with stunt:
try/catch. This is in the server, not in MOO (that still works in MOO
with try/except, thank god), but try/catch in the server code doesn't.
This has made me discover a rather concerning bug that makes the
server crash hard: if you call open_network_connection() with a valid
host but a closed port, *** B O O M ***. And your server crashes,
instantly. No panic or anything. Or mine does, anyway. I've backtraced
it to this function in net_multi.cc:
#ifdef OUTBOUND_NETWORK
enum error
network_open_connection(Var arglist, server_listener sl)
{
int rfd, wfd;
const char *local_name, *remote_name;
enum error e;
e = proto_open_connection(arglist, &rfd, &wfd, &local_name, &remote_name);
if (e == E_NONE)
make_new_connection(sl, rfd, wfd,
local_name, remote_name, 1);
return e;
}
#endif
Furthermore, this problem seems to stem from proto_open_connection(),
in net_bsd_tcp.cc. So I went digging and found this portion of that
function that uses try/catch:
try {
id = set_timer(server_int_option("outbound_connect_timeout", 5),
timeout_proc, 0);
result = connect(s, (struct sockaddr *)&addr, sizeof(addr));
cancel_timer(id);
}
catch (timeout_exception& exception) {
result = -1;
errno = ETIMEDOUT;
reenable_timers();
}
The error seems to be from:
result = connect(s, (struct sockaddr *)&addr, sizeof(addr));
The confusing thing is why this doesn't work. The server is being
compiled in C++ mode (otherwise stunt wouldn't even run), so I'd
presume that try/catch handlers would be executed if available. I've
even added catch (std::exception &ex) and catch (...) to this code
before and no go. So it seems like all exception handlers are just
being skipped, as if the try/catch block wasn't even there, behavior
that I've never seen before in a C++ program. This is the code for
both the exception and timer_proc, also relevant to this error:
class timeout_exception: public std::exception
{
public:
timeout_exception() throw() {}
virtual ~timeout_exception() throw() {}
virtual const char* what() const throw() {
return "timeout";
}
};
static void
timeout_proc(Timer_ID id, Timer_Data data)
{
throw timeout_exception();
}
This same issue also happened to me when I added a bunch of hash
functions (like K12, M14, Sha512, and so on) to extensions.cc. Most of
those built-in functions needed try/catch since most of the functions
I called could've thrown std::runtime_error, and so I added catch for
those. But again, the same thing happened -- the exceptions were
skipped and abort() was called like I'd never defined the exception
handlers.