Known Verb Issues
NOTE: This may become a non-issue if we switch to relational DB storage, since we can let the DB take care of locking.
The primary issue with all of this is that it's another application of the Halting Problem. We can't truly prevent someone from making a loop and filling memory, but we can make various limitations that would be acceptable in this context.
The issue here is properties. If someone owns an object, there's little to stop them from appending to an array over and over until memory is full. However, we have another consideration, which is that all verbs run in the course of a reactor iteration. What I'd like to do is a little trickery with threads; create a thread, running the verb inside, and wait for a maximum runtime of 1-2 seconds. This should be enough, because anything that runs longer should be creating a deferred. Some problems with this approach include the fact that it's really hard to just "stop" a thread. Also, we still need to cap the number of deferred events a verb can create, by way of some kind of counter.
The following has been copied from the LambdaMOO programmer's manual. This method of watching resources would not work for InnerSpace, but there's a couple of ideas worth investigating.
4.3 MOO Tasks
A task is an execution of a MOO program. There are five kinds of tasks in LambdaMOO:
- Every time a player types a command, a task is created to execute that command; we call these command tasks.
- Whenever a player connects or disconnects from the MOO, the server starts a task to do whatever processing is necessary, such as printing out ‘Munchkin has connected’ to all of the players in the same room; these are called server tasks.
- The ‘fork’ statement in the programming language creates a task whose execution is delayed for at least some given number of seconds; these are forked tasks.
- The suspend() function suspends the execution of the current task. A snapshot is taken of whole state of the execution, and the execution will be resumed later. These are called suspended tasks.
- The read() function also suspends the execution of the current task, in this case waiting for the player to type a line of input. When the line is received, the task resumes with the read() function returning the input line as result. These are called reading tasks.
The last three kinds of tasks above are collectively known as queued tasks or background tasks, since they may not run immediately.
To prevent a maliciously- or incorrectly-written MOO program from running forever and monopolizing the server, limits are placed on the running time of every task. One limit is that no task is allowed to run longer than a certain number of seconds; command and server tasks get five seconds each while other tasks get only three seconds. This limit is, in practice, rarely reached. The reason is that there is also a limit on the number of operations a task may execute.
The server counts down ticks as any task executes. Roughly speaking, it counts one tick for every expression evaluation (other than variables and literals), one for every ‘if’, ‘fork’ or ‘return’ statement, and one for every iteration of a loop. If the count gets all the way down to zero, the task is immediately and unceremoniously aborted. By default, command and server tasks begin with an store of 30,000 ticks; this is enough for almost all normal uses. Forked, suspended, and reading tasks are allotted 15,000 ticks each.
These limits on seconds and ticks may be changed from within the database, as can the behavior of the server after it aborts a task for running out; see the chapter on server assumptions about the database for details.
Because queued tasks may exist for long periods of time before they begin execution, there are functions to list the ones that you own and to kill them before they execute. These functions, among others, are discussed in the following section.
