CyberTiggyr Flez: An event system for Lisp

Gene Michael Stover

created Wednesday, 13 November 2002
updated Thursday, 4 December 2003

Copyright © 2002, 2003 by Gene Michael Stover. All rights reserved. Permission to copy, store, & view this document unmodified & in its entirety is granted.


Contents

1 Introduction

1.1 What is Flez?

It's a library for writing programs that are based on an event-driven architecture. Flez is written in Common Lisp. Programs which use it will probably be written in Common Lisp. Flez should be appropriate for use in simulations & games.

1.2 Why does Flez Exist?

Several reasons:

1.3 Why is it called Flez?

It's a library for event-driven programs, so I thought about calling it Evie, but I already used that name for my simulated evolution library. What's like ``Evie'' but different? Well, F follows E, & Flez has the same number of letters as Evie.

If you don't like (or don't believe that explanation1), then maybe F.L.E.Z. stands for Fabulous Lisp Event Zystem.

In any case, the library is called Flez.

1.4 Who is Responsible?

Gene Michael Stover wrote Flez. The same bloke maintains it.

2 To Do

  1. Finish example for flez-cancel-if.

  2. Finish the library & its tests.

  3. While writing the programs in demo.lisp, I learned of the dangers when the time values turn into floating point numbers. It's important to leave them as integers. They will become bignums, but fine. The problem isn't in Flez because Flez only does addition to compute new time values; it doesn't know or care about the actual types of the time values. It is the client who must ensure that time values are integers. The decision is the client's. Some clients on some systems might prefer floating point time values.

  4. Learned that internal run time (get-internal-run-time) does not increase during sleep, but internal real time does.

  5. Finish reference chapter.

  6. Re-start the tutorial chapter.

  7. Consider adding a ``filter'' feature so entities have an opportunity to react to messages as they are posted into the event queue.

3 Setup

3.1 Obtaining

3.2 Installation

  1. Unpack the distribution.

    Either way, you'll have a directory called flez-3/ in the current directory.

  2. ``cd flez-3''

  3. To run the test programs, you will need to edit logical-pathnames.lisp.in. In that file, find the translations for locigal host CL-LIBRARY & edit them for your site. (If you don't do that, you can still compile & install Flez, but the test programs will fail, so omit the ``check'' from the make command in the next step.)

  4. ``./configure; make all check install''

4 Event-Driven Program Architectures

I guess this section is a tutorial. I'm also trying to convince people that event-driven architectures are worthwhile. (Think ``Threads bad.'' Say it again: ``Threads baaaad!'')2

4.1 Event Loops

It's easy to use Flez in its most basic form. Here are some examples.

Assume you've already created all your classes & objects.3 Also, you have a global function, done-p, which returns truth (non-nil) when it's time for the program to exit the event loop & quit. Then to run your program, just use a loop like the one in Figure 1.

Figure 1: Simple loop for a game or other program that takes real time into account
\begin{figure}\begin{verbatim}(defun done-p ()
''Returns non-nil when it's ti...
...)
(loop until (done-p)
do (cybertiggyr-flez:tick)))\end{verbatim}
\end{figure}

That's about the most basic event loop you need. Its argument is an instance of class cybertiggyr-flez:flez. Each time it calls Flez's tick function, Flez processes all events that are ready. Those events might create & post other events, which Flez will queue until the next time you call tick.

The simple loop has a couple of deficiencies.

The first deficiency is that it is a busy loop. It calls Flez's tick function even if no events are ready. If we don't need to process asynchronous inputs, it would be more efficient to sleep until it's time for the next event. A loop with the well-behaved sleep behaviour is in Figure 2.

Figure 2: A non-busy loop for a game or other program that takes real time into account
\begin{figure}\begin{verbatim}(defun sleeping-basic-loop ()
(loop until (done...
...lez:tick)
(sleep cybertiggyr-flez:seconds-to-next)))\end{verbatim}
\end{figure}

Flez's seconds-to-next function returns the number of seconds until the next event. As sleeping-basic-loop shows, your program can use that value to sleep until it's time for the event, thereby doing a better job of sharing the CPU with other programs.

If your program needs to handle asynchronous input (or output), you'll need to use some function other than sleep. You'll need a Lisp equivalent of Unix's select function to poll some I/O channels & return when a channel is ready or on a timeout. I won't give an example of that there.

sleeping-basic-loop has one more problem. What if the event queue is empty? Flez allows you to schedule repeating events, but what if you didn't do that & the event queue emptied because none of the most recently executed events generated more events? Then your program would loop forever. So we need to make the event loop recognize an empty event queue & exit when it happens. Figure 3 shows such a loop.

Figure 3: A non-busy loop that also recognizes an empty event queue as a time to quit.
\begin{figure}\begin{verbatim}(defun best-basic-loop ()
(loop until (or (done...
...ick flez)
(sleep cybertiggyr-flez:seconds-to-next)))\end{verbatim}
\end{figure}

best-basic-loop is the best I'm going to show now. It does not take into account asynchronous input, so it wouldn't be good for a game, but it could be great for a simulation.

When the event queue is empty, cybertiggyr-flez:seconds-to-next returns 0, so if the event queue is empty after the last tick, the sleep function won't take any time, & then the until exit clause of the loop will be true, & the loop will exit.

4.2 Events

So much for event loops. What about events themselves?

Flez knows about two kinds of events: singular & repeating. A singular event happens just once & then is discarded. A repeating event is automatically reschedule to occur again after each use. Repeating events are more complex & won't be covered in this basic section.

A singular event has two main parts: a time & a closure. (It also has a tag, but that's not for basic use.)

You schedule an event by calling Flez's post function. post needs to know the event's closure & the time until the event executes. The closure argument is required; without a closure, you don't have an event. The time argument is optional & defaults to ``soonest''. (An event will never be executed during the tick in which it is queued. See Section 7.1 for a discussion.)

The time units are your own business. If you are calling Flez's tick function without any arguments, it uses the internal real time, which are seconds, so the delays you give Flez must be seconds. You could also use your own clock, especially if you are using simulation time.

5 More Detailed Uses

You must write your own event loop, but it's easy. There are two basic kinds of loops: real time & simulation time.


5.1 Simulation Time

In simulation time, you control the clock. You might think of the time in seconds (or whatever), but remember that those seconds are not the same as real seconds.

With simulation time, you don't need to call sleep in your event loop because you probably want the simulation to run as quickly as possible. What's more & as I already said, even if your simulation's time is in seconds, those seconds are not the same as real seconds, so calling sleep on them is not applicable.

6 API Reference

This section is a reference manual for the Application Programmer's Interface (API) of CyberTiggyr Flez.

6.1 Package cybertiggyr-flez

6.1.1 Description

Package cybertiggyr-flez contains all of the symbols for CyberTiggyr Flez.

The symbols it exports are

6.1.2 Examples

[1]> (load "/home/gene/lib/lisp/cybertiggyr/tigris.lisp")
;; Loading file /home/gene/lib/lisp/cybertiggyr/tigris.lisp ...
;; Loading of file /home/gene/lib/lisp/cybertiggyr/tigris.lisp is finished.
T
[2]> (load "src/flez.lisp")
;; Loading file src/flez.lisp ...
;; Loading of file src/flez.lisp is finished.
T
[3]> (use-package 'cybertiggyr-flez)
T
[4]>


6.2 Function CREATE-FLEZ

6.2.1 Syntax

create-flez $\Rightarrow$ object

6.2.2 Arguments and Values

None.

6.2.3 Description

Creates a Flez, which is currently implemented as a structure, & returns the new instance.

6.2.4 Examples

[3]> (use-package 'cybertiggyr-flez)
T
[4]> (create-flez)
#S(CYBERTIGGYR-FLEZ::FLEZ
   :HEAP
   #S(CYBERTIGGYR-TIGRIS:HEAP
      :LESS-FN
      #<CLOSURE CYBERTIGGYR-FLEZ::MESSAGE-LESS-P
        (CYBERTIGGYR-FLEZ::X CYBERTIGGYR-FLEZ::Y)
        (DECLARE (SYSTEM::IN-DEFUN CYBERTIGGYR-FLEZ::MESSAGE-LESS-P))
        (BLOCK CYBERTIGGYR-FLEZ::MESSAGE-LESS-P
         (LET
          ((CYBERTIGGYR-FLEZ::WHEN-X
            (CYBERTIGGYR-FLEZ::MESSAGE-WHEN CYBERTIGGYR-FLEZ::X))
           (CYBERTIGGYR-FLEZ::WHEN-Y
            (CYBERTIGGYR-FLEZ::MESSAGE-WHEN CYBERTIGGYR-FLEZ::Y)))
          (OR (< CYBERTIGGYR-FLEZ::WHEN-X CYBERTIGGYR-FLEZ::WHEN-Y)
           (AND (= CYBERTIGGYR-FLEZ::WHEN-X CYBERTIGGYR-FLEZ::WHEN-Y)
            (< (CYBERTIGGYR-FLEZ::MESSAGE-SERIAL CYBERTIGGYR-FLEZ::X)
             (CYBERTIGGYR-FLEZ::MESSAGE-SERIAL CYBERTIGGYR-FLEZ::Y))))))>
      :ORDER 2 :A #(NIL) :MAX-COUNT 0)
   :NOW NIL :SERIAL 0 :CANCELLED-COUNT 0)
[5]> (setq fz (create-flez))
#S(CYBERTIGGYR-FLEZ::FLEZ
   :HEAP
   #S(CYBERTIGGYR-TIGRIS:HEAP
      :LESS-FN
      #<CLOSURE CYBERTIGGYR-FLEZ::MESSAGE-LESS-P
        (CYBERTIGGYR-FLEZ::X CYBERTIGGYR-FLEZ::Y)
        (DECLARE (SYSTEM::IN-DEFUN CYBERTIGGYR-FLEZ::MESSAGE-LESS-P))
        (BLOCK CYBERTIGGYR-FLEZ::MESSAGE-LESS-P
         (LET
          ((CYBERTIGGYR-FLEZ::WHEN-X
            (CYBERTIGGYR-FLEZ::MESSAGE-WHEN CYBERTIGGYR-FLEZ::X))
           (CYBERTIGGYR-FLEZ::WHEN-Y
            (CYBERTIGGYR-FLEZ::MESSAGE-WHEN CYBERTIGGYR-FLEZ::Y)))
          (OR (< CYBERTIGGYR-FLEZ::WHEN-X CYBERTIGGYR-FLEZ::WHEN-Y)
           (AND (= CYBERTIGGYR-FLEZ::WHEN-X CYBERTIGGYR-FLEZ::WHEN-Y)
            (< (CYBERTIGGYR-FLEZ::MESSAGE-SERIAL CYBERTIGGYR-FLEZ::X)
             (CYBERTIGGYR-FLEZ::MESSAGE-SERIAL CYBERTIGGYR-FLEZ::Y))))))>
      :ORDER 2 :A #(NIL) :MAX-COUNT 0)
   :NOW NIL :SERIAL 0 :CANCELLED-COUNT 0)
[6]> (type-of fz)
CYBERTIGGYR-FLEZ::FLEZ
[7]> (flez-count fz)
0
[8]>

6.2.5 Affected By

Whatever. I mean, None.

6.2.6 Exceptional Situations

None that I know.

6.2.7 See Also

Whatever.

6.2.8 Notes

None.

6.3 Function FLEZ-CANCEL

6.3.1 Syntax

flez-cancel flez tag $\Rightarrow$ (values)

6.3.2 Arguments and Values

flez
An instance of Flez, created by create-flez,

tag
The tag of the events to remove. Remove all events whose tag is tag.

6.3.3 Description

Remove from the event queue all events whose tag slot is EQUAL to tag. It is not an error if flez is empty. Does not remove other events from the Flez.

6.3.4 Examples

[8]> (setq fz (create-flez))
#S(CYBERTIGGYR-FLEZ::FLEZ
   ; Lots of details removed for clarity
   )

;; Make some events with a tag of A
[11]> (dotimes (i 3) (flez-post fz #'(lambda (flex when) nil) :tag 'a))
NIL

;; Make some events with a tag of B.
[12]> (dotimes (i 2) (flez-post fz #'(lambda (flez when) nil) :tag 'b))
NIL

;; See... there are 5 events in the Flez.
[13]> (flez-count fz)
5

;; Remove the A events.  The two B events will
;; remain.
[14]> (flez-cancel fz 'a)
NIL
[15]> (flez-count fz)
2

;; Removing the two B events.
[16]> (flez-cancel fz 'b)
NIL

;; The Flez is now empty.
[17]> (flez-count fz)
0
[18]>

6.3.5 Affected By

Nothing.

6.3.6 Exceptional Situations

None.

6.3.7 See Also

Nothing.

6.3.8 Notes

None.

6.4 Function FLEZ-CANCEL-IF

6.4.1 Syntax

flez-cancel-if flez fn $\Rightarrow$ (values)

6.4.2 Arguments and Values

flez
An instance of Flez, created by create-flez,

fn
A function of one argument. That argument is the tag of a message. Fn should return true if & only if the message should be removed from the queue.

6.4.3 Description

Remove from the event queue all events for which FN returns true (non-NIL). It is not an error if flez is empty. Does not remove other events from the Flez.

6.4.4 Examples

[8]> (setq fz (create-flez))
#S(CYBERTIGGYR-FLEZ::FLEZ
   ; Lots of details removed for clarity
   )

;; Make some events with a tag of A
[11]> (dotimes (i 3) (flez-post fz #'(lambda (flex when) nil) :tag (list i 'a)))
NIL

;; Make some events with a tag of B.
[12]> (dotimes (i 2) (flez-post fz #'(lambda (flez when)
nil) :tag (list 'b i)))
NIL

;; See... there are 5 events in the Flez.
[13]> (flez-count fz)
5

;; Remove the A events.  The two B events will
;; remain.
[14]> (flez-cancel fz #'(lambda (tag) (member 1 tag))
NIL
[15]> (flez-count fz)
2

;; Removing the two B events.
[16]> (flez-cancel fz 'b)
NIL

;; The Flez is now empty.
[17]> (flez-count fz)
0
[18]>

6.4.5 Affected By

Nothing.

6.4.6 Exceptional Situations

None.

6.4.7 See Also

Nothing.

6.4.8 Notes

None.


6.5 Function FLEZ-CLEAR

6.5.1 Syntax

flez-clear flez $\Rightarrow$ flez

6.5.2 Arguments and Values

flez
A FLEZ event queue

6.5.3 Description

Removes any & all items from the FLEZ. Affects the FLEZ by removing all the items from it. Returns the FLEZ. The FLEZ will be empty.

6.5.4 Examples

[1]> (load "/home/gene/lib/lisp/cybertiggyr/tigris.lisp")
;; Loading file /home/gene/lib/lisp/cybertiggyr/tigris.lisp ...
;; Loading of file /home/gene/lib/lisp/cybertiggyr/tigris.lisp is finished.
T
[2]> (load "src/flez.lisp")
;; Loading file src/flez.lisp ...
;; Loading of file src/flez.lisp is finished.
T
[3]> (use-package 'cybertiggyr-flez)
T
[4]> (defvar *fz* (create-flez))
*FZ*
[5]> (dotimes (i 5) (flez-post *fz* #'(lambda (flez when) (print i))))
NIL
[6]> (flez-count *fz*)
5
[7]> (flez-clear *fz*)
0
[8]> (flez-count *fz*)
0
[9]>

6.5.5 Affected By

Whatever. I mean, None.

6.5.6 Exceptional Situations

None that I know.

6.5.7 See Also

Whatever.

6.5.8 Notes

None.


6.6 Function FLEZ-COUNT

6.6.1 Syntax

flez-count flez $\Rightarrow$ count

6.6.2 Arguments and Values

flez
A FLEZ event queue
count
A non-negative integer

6.6.3 Description

Returns the number of items in the Flez event queue. An empty Flez contains zero items. If you insert one item into the Flez & do not call flez-tick, do not cancel the event, & do not call flez-clear on it, FLEZ-COUNT on that Flez will return 1.

FLEZ-COUNT should never return a negative number.

6.6.4 Examples

[1]> (load "/home/gene/lib/lisp/cybertiggyr/tigris.lisp")
;; Loading file /home/gene/lib/lisp/cybertiggyr/tigris.lisp ...
;; Loading of file /home/gene/lib/lisp/cybertiggyr/tigris.lisp is finished.
T
[2]> (load "src/flez.lisp")
;; Loading file src/flez.lisp ...
;; Loading of file src/flez.lisp is finished.
T
[3]> (use-package 'cybertiggyr-flez)
T
[4]> (defvar *fz* (create-flez))
*FZ*
[5]> (dotimes (i 5) (flez-post *fz* #'(lambda (flez when) (print i))))
NIL
[6]> (flez-count *fz*)
5
[7]> (flez-clear *fz*)
0
[8]> (flez-count *fz*)
0
[9]>

6.6.5 Affected By

Whatever. I mean, None.

6.6.6 Exceptional Situations

None that I know.

6.6.7 See Also

Whatever.

6.6.8 Notes

None.


6.7 Function FLEZ-EMPTY-P

6.7.1 Syntax

flez-empty-p flez $\Rightarrow$ bool

6.7.2 Arguments and Values

flez
A FLEZ event queue

bool
A generalized Boolean value (nil means false, anything else means true)

6.7.3 Description

Returns true if the Flez event queue is empty. A Flez is empty if it contains exactly zero items.

FLEZ-EMPTY-P is probably faster than ``(zerop (flez-count flez))''.

6.7.4 Examples

[1]> (load "/home/gene/lib/lisp/cybertiggyr/tigris.lisp")
;; Loading file /home/gene/lib/lisp/cybertiggyr/tigris.lisp ...
;; Loading of file /home/gene/lib/lisp/cybertiggyr/tigris.lisp is finished.
T
[2]> (load "src/flez.lisp")
;; Loading file src/flez.lisp ...
;; Loading of file src/flez.lisp is finished.
T
[3]> (use-package 'cybertiggyr-flez)
T
[4]> (defvar *fz* (create-flez))
*FZ*
[5]> (flez-empty-p *fz*)
T
[6]> (dotimes (i 5) (flez-post *fz* #'(lambda (flez when) (print i))))
NIL
[7]> (flez-empty-p *fz*)
NIL
[8]> (flez-empty-p (flez-clear *fz*))
T
[9]>

6.7.5 Affected By

Whatever. I mean, None.

6.7.6 Exceptional Situations

None that I know.

6.7.7 See Also

Whatever.

6.7.8 Notes

None.


6.8 Function FLEZ-NEXT-WHEN

6.8.1 Syntax

flez-next-when flez $\Rightarrow$ time

6.8.2 Arguments and Values

flez
A FLEZ event queue
time
A number

6.8.3 Description

Returns the time at which the next event is scheduled to execute. If the Flez event queue is empty, returns NIL.

6.8.4 Examples

[1]> (load (translate-logical-pathname "CL-LIBRARY:CYBERTIGGYR;TIGRIS.LISP"))
;; Loading file /home/gene/lib/lisp/cybertiggyr/tigris.lisp ...
;; Loading of file /home/gene/lib/lisp/cybertiggyr/tigris.lisp is finished.
T
[2]> (load (translate-logical-pathname "CL-LIBRARY:CYBERTIGGYR;FLEZ.LISP"))
;; Loading file /home/gene/lib/lisp/cybertiggyr/flez.lisp ...
;; Loading of file /home/gene/lib/lisp/cybertiggyr/flez.lisp is finished.
T
[3]> (use-package 'cybertiggyr-flez)
T
[4]> (defvar *fz* (create-flez))
*FZ*
[5]> (flez-post *fz* #'(lambda (flez when) nil) :when 42)
#S(CYBERTIGGYR-FLEZ::MESSAGE :WHEN 42 :SERIAL 1
   :CLOSURE #<CLOSURE :LAMBDA (FLEZ WHEN) NIL> :TAG NIL :CANCELLED NIL)
[6]> (flez-next-when *fz*)
42
[7]> (flez-post *fz* #'(lambda (flez when) nil) :delay 17)
#S(CYBERTIGGYR-FLEZ::MESSAGE :WHEN 17 :SERIAL 2
   :CLOSURE #<CLOSURE :LAMBDA (FLEZ WHEN) NIL> :TAG NIL :CANCELLED NIL)
[8]> (flez-next-when *fz*)
17
[9]>

6.8.5 Affected By

Whatever. I mean, None.

6.8.6 Exceptional Situations

None that I know.

6.8.7 See Also

Whatever.

6.8.8 Notes

None.


6.9 Function FLEZ-NOW

6.9.1 Syntax

flez-now flez $\Rightarrow$ time

6.9.2 Arguments and Values

flez
A FLEZ event queue
time
A number

6.9.3 Description

Return the time of the currently executing event or the most recently executed event.

6.9.4 Examples



6.9.5 Affected By

Whatever. I mean, None.

6.9.6 Exceptional Situations

None that I know.

6.9.7 See Also

Whatever.

6.9.8 Notes

None.


6.10 Function FLEZ-POST

6.10.1 Syntax

flez-post flez closure &key (delay 0) when tag $\Rightarrow$ flez

6.10.2 Arguments and Values

flez
A FLEZ event queue

closure
The event, a closure, a function of two arguments

delay
Time, after when or the current time of the Flez, at which to execute the event

when
Time at which the event should be executed. If not specified, defaults to the current time of the Flez

tag
A user-specified value by which the event might be identified later

6.10.3 Description

Schedules an event in a Flez event queue for execution at a specific time.

Closure is the event. It's a function of two arguments, like this:

#'(lambda (flez when)
    (format t "~&I am an event.")
    (format t "~&The event queue is ~A." flez)
    (format t "~&The current game time is ~A." when))

The time at which the event will execute is $when + delay$, where $when$ is the keyword argument from flez-post or is the current time (flez-now) of the Flez if that keyword argument was not specified. $Delay$ is the keyword argument from flez-post or is zero if that keyword argument was not specified. Both $when$ & $delay$ should be numbers, but their scale & range is irrelevant to Flez except that smaller values should occur before larger ones. In other words, Flez doesn't know or care whether the time scale is microseconds, seconds, fortnights, or whatever, nor does it care whether they are expressed as integers or floating point as long as smaller numbers indicate earlier times.

The optional tag can be used to identify the event in case it should be deleted from the Flez later. It may be an atom, a list, the closure itself, or whatever. The tag value doesn't matter to flez-post; it might only matter to flez-cancel or to flez-cancel-if.

6.10.4 Examples

;; I need an example here.

6.10.5 Affected By

Whatever. I mean, None.

6.10.6 Exceptional Situations

None that I know.

6.10.7 See Also

Whatever.

6.10.8 Notes

None.


6.11 Function FLEZ-TASK

6.11.1 Syntax

flez-task flez closure first period tag tag $\Rightarrow$ nil

6.11.2 Arguments and Values

flez
A FLEZ event queue

closure
The event, a closure, a function of two arguments (flez & when)

fist
Time at which to execute the event the first time

period
Time interval between executing the event.

tag
A user-specified value by which the event might be identified later. Tags are important for tasks because that's the only way to cancel a task.

6.11.3 Description

Schedules an event in a Flez event queue so that it is executed at first & then every period ticks after that until or unless cancelled.

Closure is the event. It's a function of two arguments, like this:

#'(lambda (flez when)
    (format t "~&I am an event.")
    (format t "~&The event queue is ~A." flez)
    (format t "~&The current game time is ~A." when))

The tag is not optional for tasks because it is the only way to cancel them. If you insist that you won't need to cancel the task, it can be nil.

6.11.4 Examples

;; I need an example here.

6.11.5 Affected By

Whatever. I mean, None.

6.11.6 Exceptional Situations

None that I know.

6.11.7 See Also

Whatever.

6.11.8 Notes

None.


6.12 Function FLEZ-TICK

6.12.1 Syntax

flez-tick flez &optional time-now (count 0) $\Rightarrow$ crap

6.12.2 Arguments and Values

flez
A Flez event queue

time-now
The current time, which is not necessarily the real current time. If your program runs in real time, then it is the real current time, but if your program has its own time continuum, you specify that time as the time-now. Defaults to (flez-next-when flez).

count
If count is positive, it is the maximum number of events to execute. If count is zero, flez-tick executes all events that are ready to execute at time-now. It is an error if count is negative. Defaults to 0.

6.12.3 Description

Executs events that are ready. Doesn't return a useful value; maybe it should.

6.12.4 Examples

;; I need an example here.

6.12.5 Affected By

Whatever. I mean, None.

6.12.6 Exceptional Situations

None that I know.

6.12.7 See Also

Whatever.

6.12.8 Notes

None.

7 Rationale


7.1 Why are events never executed in the same tick they were posted?

An event that is posted is never executed during the tick in which it is posted, even if it is scheduled for ``now'' or a time in the past. Why?

It's to avoid endless loops. (Flez can't truly prevent endless loops, but it can take measures to avoid them.)

If an event was queued immediately when it was posted, it would be possible, even easy, for a single call to Flez's tick to become an endless loop. So while tick is active, Flez places new events in a temporary queue. Just before tick returns, it moves the events from the temporary queue into the real queue.

If you post an event while not inside a tick, the event is placed in the real queue immediately.

When you ask Flez how many items are in the queue, it reports the size of the real queue & the size of the temporary queue. So from the outside of tick, you can't distinguish between the real queue & the temporary queue.

8 Programmer's Log

These are my notes as a programmer while creating & maintaining Flez.

8.1 Wednesday, 13 November 2002

Created the project by copying from library/src/skeleton/ to this directory.

Created src/flez.lisp & some test programs.

8.2 Sunday, 9 March 2003

Picking up where I left-off months ago.

Created doc/flez.tex, which is this file. Wrote a lot in it.

9 Sunday, 10 August 2003

Damn few notes, eh.

Gene Michael Stover 2008-04-20