| /* | |
| daemon.t version 1.00 (7 May 2001) | |
| Daemon priority sorting for TADS 2 | |
| Dan Schmidt <dfan@dfan.org> | |
| TADS 2 does not allow authors to specify exactly when daemons should run | |
| relative to each other. The ill effects of this can range from | |
| infelicitous (you lose control over the order in which some messages are | |
| printed) to quite annoying (you don't know for sure when the turncount() | |
| daemon will run, incrementing global.turnsofar). daemon.t allows you to | |
| specify a priority for each daemon, so that you can set exactly the | |
| order in which they should run. | |
| To use it, simply start up the timeline object at the beginning of time | |
| by inserting the following line into init(): | |
| notify (timeline, &run, 0); | |
| and replace all your calls to notify, unnotify, set/remdaemon, and | |
| set/remfuse with the following calls: | |
| - add_daemon (obj, func [, priority]) starts a daemon that will execute | |
| at the end of every turn, starting this one. If you want the daemon | |
| to be a global function, rather than the method of an object, set obj | |
| to nil. The priority argument is optional and defaults to | |
| timeline.default_priority (1000 by default) if not given. | |
| - add_fuse (obj, func, timeout [, priority]) starts a fuse; the specified | |
| function or method will be called timeout turns from now. As with | |
| add_daemon, the priority argument is optional, and obj may be nil to | |
| specify a global function. | |
| - rem_daemon (obj, func) removes the specified daemon. | |
| - rem_fuse (obj, func) removes the specified fuse. | |
| If any of these functions are called from within a daemon/fuse, they | |
| will not take effect until the following turn. | |
| The larger the priority number of a daemon or fuse, the later it runs. | |
| This may seem kind of unintuitive, but think of it in the 'take a number | |
| at the deli' sense; low numbers are served first. | |
| Daemons and fuses share the same priority queue, so a daemon with | |
| priority 10 executes before a fuse with priority 20 which executes | |
| before a daemon with priority 30. Daemons/fuses with the same priority | |
| happen to execute most-recently-added first, but don't count on that. | |
| TADS' setdaemon and setfuse builtins take an extra parameter, which is | |
| passed to the daemon or fuse function. daemon.t's add_daemon and | |
| add_fuse functions support this behavior as well; if the func | |
| parameter is a two-element list consisting of a symbol and a | |
| parameter, rather than just a symbol, then the first element of the | |
| list is taken to be the function to be called, and the second element | |
| is taken to be the parameter to pass to the function. | |
| If you use the two-element func option for add_daemon or add_fuse, you | |
| must also use it when removing, as with setdaemon/setfuse. | |
| Some examples: | |
| add_daemon (elephant, &stomp_around); | |
| causes elephant.stomp_around to be called every turn. | |
| add_daemon (elephant, &stomp_around, 100); | |
| has the same behavior, but elephant.stomp_around now executes with a | |
| priority of 100, rather than the default. | |
| rem_daemon (elephant, &stomp_around); | |
| stops elephant.stomp_around from being called any more. | |
| add_daemon (elephant. [&trumpet 3]); | |
| causes elephant.trumpet(3) to be called every turn. | |
| add_fuse (elephant, &go_berserk, 5, 50); | |
| causes elephant.go_berserk to be called 5 turns from now. It will | |
| execute with a priority of 50 (so, for example, it would execute | |
| before the stomp_around daemon). | |
| daemon.t will happily coexist with 'real' daemons, fuses, and | |
| notifies, but you won't have control over when the other ones execute. | |
| */ | |
| #pragma C+ | |
| // A sysdaemon is a method to be called on an object every turn. | |
| // They are kept as an explicit linked list since sorting actual | |
| // TADS lists is a pain in the neck. | |
| class sysdaemon: object | |
| pri = 0 // priority: 0 max, 32767 min | |
| obj = nil // object to have method called (or nil for function) | |
| func = nil // method/function to call | |
| timeout = 0 // when to call, or -1 for every turn | |
| next = nil // next daemon on priority-ordered list | |
| // Initialize | |
| ini (o, f, t, p) = { | |
| self.obj = o; | |
| self.func = f; | |
| self.timeout = t; | |
| self.pri = p; | |
| } | |
| ; | |
| null_daemon: sysdaemon pri=32767; // End of list sentinel | |
| head_daemon: sysdaemon pri=-1 next=null_daemon; // Beginning of list marker | |
| timeline: object | |
| head = head_daemon // head of our linked list of sysdaemons | |
| default_priority = 1000 // priority if not specified | |
| // If we're traversing the daemon list, we set self.running to true | |
| // and save off any pending add_daemons and rem_daemons. | |
| running = nil | |
| pending_adds = [] | |
| pending_rems = [] | |
| add (o, f, t, p) = { | |
| if (p <= head_daemon.pri || p >= null_daemon.pri) { | |
| "[BUG] priority out of range while adding daemon or fuse\n"; | |
| return; | |
| } | |
| if (self.running) { | |
| self.pending_adds += [[o, f, t, p]]; // save it off | |
| } else { | |
| local d = self.head; | |
| local newd = new sysdaemon; // create a new sysdaemon representing this call | |
| if (t == 0) t = -1; // to us, -1 means permanent | |
| newd.ini (o, f, t, p); | |
| // Insert into the sorted list | |
| while (1) { | |
| if (newd.pri <= d.next.pri) { | |
| newd.next = d.next; // splice it in | |
| d.next = newd; | |
| break; | |
| } | |
| d = d.next; // move down the list | |
| } | |
| } | |
| } | |
| rem (o, f) = { | |
| if (self.running) { | |
| self.pending_rems += [[o, f]]; // save it off | |
| } else { | |
| // Delete from the sorted list | |
| local flist = (datatype(f) == DTY_LIST); // f is list? | |
| local d = self.head; | |
| while (1) { | |
| if (d.next == null_daemon) { | |
| "[BUG] tried to remove nonexistent daemon\n"; | |
| return; | |
| } | |
| // '==' doesn't work for lists, thus the following annoyance | |
| if (d.next.obj == o && flist ? (d.next.func[1] == f[1] && d.next.func[2] == f[2]) | |
| : (d.next.func == f)) { | |
| local destroyed = d.next; // save a handle to the upcoming guy | |
| d.next = d.next.next; // splice it out | |
| delete destroyed; // and kill it | |
| break; | |
| } | |
| d = d.next; // move down the list | |
| } | |
| } | |
| } | |
| run = { | |
| // Traverse the sorted list, calling each daemon | |
| local prevd = self.head; // previous daemon called | |
| local d = prevd.next; // daemon to call | |
| self.running = true; // mark ourselves as traversing the list | |
| while (d != null_daemon) { | |
| local nextd = d.next; // daemon to call next round | |
| if (d.timeout <= 0) { // fuse expired, or it's permanent | |
| if (d.obj) { | |
| if (proptype(d, &func) == DTY_LIST) { | |
| (d.obj).(d.func[1])(d.func[2]); // method call w/ arg | |
| } else { | |
| (d.obj).(d.func); // method call | |
| } | |
| } else { | |
| if (proptype(d, &func) == DTY_LIST) { | |
| (d.func[1])(d.func[2]); // function call w/ arg | |
| } else { | |
| (d.func)(); // function call | |
| } | |
| } | |
| } | |
| if (d.timeout == 0) { // fuse expired | |
| prevd.next = d.next; // splice it out | |
| delete d; // and kill it | |
| } else if (d.timeout > 0) { | |
| --d.timeout; // fuse runs down | |
| } | |
| prevd = d; // move down the list | |
| d = nextd; | |
| } | |
| self.running = nil; // done with the list | |
| // Now handle any adds and rems that took place while we were traversing. | |
| { | |
| local i; | |
| for (i = 1; i <= length(self.pending_adds); ++i) { | |
| local p = self.pending_adds[i]; | |
| self.add (p[1], p[2], p[3], p[4]); | |
| } | |
| for (i = 1; i <= length(self.pending_rems); ++i) { | |
| local p = self.pending_rems[i]; | |
| self.rem (p[1], p[2]); | |
| } | |
| self.pending_adds = []; | |
| self.pending_rems = []; | |
| } | |
| } | |
| ; | |
| //////////////////////////////////////////////////////////// | |
| // | |
| // Now follows the global function interface | |
| add_daemon: function (obj, func, ...) | |
| { | |
| local pri = timeline.default_priority; | |
| if (argcount >= 3) { | |
| pri = getarg(3); | |
| } | |
| timeline.add (obj, func, 0, pri); | |
| } | |
| add_fuse: function (obj, func, t, ...) | |
| { | |
| local pri = timeline.default_priority; | |
| if (argcount >= 4) { | |
| pri = getarg(4); | |
| } | |
| timeline.add (obj, func, t, pri); | |
| } | |
| rem_daemon: function (obj, func) | |
| { | |
| timeline.rem (obj, func); | |
| } | |
| rem_fuse: function (obj, func) | |
| { | |
| timeline.rem (obj, func); | |
| } | |
Xet Storage Details
- Size:
- 8.76 kB
- Xet hash:
- d9728183713ae38ad475e879d8f61968f7eddd85f9b4d6d37ed4b2adc85179fa
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.