On Sun, 16 Jan 2000, Ron Martin wrote: > Both would require some recoding. I am planning to redo all my > mobiles anyway, so I may go with option 1. Not speaking in an official capacity: The formats of the database files use too many numbers for enumerated constants, anyway. E.g., 8 instead of "standing" -- why build the code system to rely upon the position of the numbers, then restrain the coder from changing those positions by adding an entire layer that relies upon the positions as they are? IHMO, this is such a large design flaw that it runs into the domain of "bugs". If CircleMUD is to be truly a clean-slate for which programmers to build their own Mud from, why are we preventing redesign and modification of the code with the stock world? To answer my own question, in a more official capacity: Backwards compability is important here and the 3.0 beta has gone on for 5 years now -- adding more time to that for the sake of properly expressing enumerated constants in the database (especially when C still doesn't have a proper way of expressing enumerated constants -- 'enum' works, but it throws everything into a global namespace, which forces you to prefix enumerated constants to avoid name collisions...Ugh) is ridiculous. Just out of curiousity: When is enough *enough*? That is, when is beta software sufficiently stable to where the developers say, "The other problems are with the design, and as much as patching the design with kludges emulates progress, real progress won't be made until we re-approach the problem from the beginning?" Anyway, I'm interested in what architecture people would really want from 4.0. I have some ideas that I've yet to toss to the rest of the team, but I might as well bring them up here for everyone to see. First, the big question, what language is CircleMUD 4.0 written in? My response? I dunno. If 4.0 goes the route of 3.0 and has much of the game mechanics and object handling in the kernel, then definitely C++ or another object oriented language. If, however, we have a sufficient database backend where the mechanics of the objects can be held indepedent of hard-coding (such as, say, would happen with Berkeley DB or SQL) and a sufficient virtual machine and scripting language for implementing the core mechanics, then perhaps ANSI C is the way to go. I know George ran a poll on this on the Ceramic Mouse some time back, but maybe someone will have a fresh take on the matter? What is more clear to me is that 4.0 should be multi-threaded. A while ago George posted (privately to the developers) a summary of various Event Models for the networking code. These included some threading models that I (or he) had tried, some we had talked about, the current single-threaded networking event model, and a SIGIO based model. I have one additional model that I think is superior to all those dealt with previously: kernel / | \ / | \ timed event | inactive thread service | expiration service | networking server | +---+---+---+---+---+---+---+---+---+---+ | | | | | | | | | | | +---+---+--- IO thread pool +---+---+---+ | ... --+-- ... temporary IO thread pool As you can see, there's three basic services running from this hypothetical 4.0 kernel, the timed (or "user") event service, which allows for the event-driven nature of the Mud; the inactive thread expiration service, which sets up an event with the user event service to check all the threads registered with it for activity (the registered threads should occassionally call some function to update their status with the expiration service -- not all threads need to be registered with this service), and if it hasn't been active for a while, the kernel either restarts the thread, kills it outright, or sends it a SIGUSR1 signal so that the thread can try to restart itself. Lastly, is the networking server service (:P). Basically, a new thread is spawned for the (blocking) TCP/IP server. All it does, after creating and binding the server socket, is call select() and wait indefinitely for some IO event[1]. When it gets some event, it tries to signal a thread in its IO thread pool that's waiting on the server's IO_event_condition. When the condition is signaled, one of the IO threads receives the information for the socket that has the IO event and what type of event it is. The IO thread then handles the event, leaving the server to go back to sleep. The IO event thread function probably looks something like this, void * netHandleSocketEvent (void *v) { netIOEvent_t *iov = (netIOEvent_t *) v; switch (iov->eventType) { case iovRead: helperPerformRead (iov->socket); break; case iovWrite: helperPerformWrite (iov->socket); break; case iovException: /* * process OOB data */ break; } return (NULL); } , where helperPerformRead() would read from iov->socket->fd, doing the appropriate line breaking (note that, unlike 3.0, it would not be in charge of performing ! or ^a^b substitutions; that would be handled higher-up, this would do just the base read and line-splitting). Since it's done in a separate thread, a blocking read is fine and it can call the command interpreter or whatever immediately after. You can determine what helperPerformWrite() would do... The temporary thread pool doesn't exist by default. IO threads are added if they're necessary because the default ("static") pool is filled. This pool is more aptly referred to as the "transient" IO thread pool, since the threads here can be removed by the CM kernel's inactive thread expiration service (whereas, the "static" pool is much more fixed in size, with reductions to it being made only rarely when the number of threads in the pool outnumbers the number of sockets we're handling, in which case it's pruned down to a happy number to keep down system load when CM4 is inactive). Services not shown include the database service and the virtual machine. (I remembered my footnotes for once!) [1] Note that select() returns almost instanteously because most sockets are available for writing all of the time. It might be better to have the server waiting on something like, . . . struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = SOME_TIME_HERE; while (!activity) { activity = !(condWait (srv->outputCond, &timeout)); if ((t = select (srv->topFD+1, &r, NULL, NULL, ¬ime)) < 0) { if (errno == EINTR) continue; logError ("select (%d)", srv->topFD+1); exit (1); } activity = (t > 0); } . . . Of course, the details are a bit sketchy, since I haven't done an implementation of this. We might be just as well doing a non-blocking write to a socket rather than have buffered output. <shrug> -dak +------------------------------------------------------------+ | Ensure that you have read the CircleMUD Mailing List FAQ: | | http://qsilver.queensu.ca/~fletchra/Circle/list-faq.html | +------------------------------------------------------------+
This archive was generated by hypermail 2b30 : 04/10/01 PDT