Jay O'Connor fed this fish to the penguins on Tuesday 02 December 2003
07:48 am:
The language in question was a VM language with it's own internal
process model. The process model was all inteneral to on eOS process
to it was a cooperative model of multi-threading
There's the first point of departure: Python threads (as long as you
aren't in a C language number crunching extension) are preemptive
scheduled, on something like a 10-20 byte-code interval. Cooperative
basically means /you/ had to handle the scheduling of threads; in
Python you don't, it happens automatically.
The language process model had both the ability to set process
priorities, as well as allow processes to sleep and cede control to
other processes.
Besides the built-in scheduler, invoking any type of blocking
operation (sleep, wait for a condition, etc.) will also trigger
scheduler swaps.
When a web request would come in, the handling of the request would be
forked as a seperate process so that the server could accept the next
request. Both the server process and the handling process(es) were at
the same priority level so in theory each process would run to
completion before allowing another process to run. For this reason,
Not a matter for Python... Let the main server create a new thread
with the connection information, and let it go back to waiting for
another connection request -- while it is waiting, the other thread(s)
take turns running.
both the server process and the handling processes would issue a
'yield' at periodic strategic points, allowing themselves to
temporarily halt and for the next process of equal priority that was
waiting to run. This allowed both the server to remain responsive and
all handling processes to run efficiently.
No need for an explicit yield; the scheduler will swap among ready
threads as needed (or as triggered by anything that causes a blocking
operation -- I'd expect even I/O requests to cause a suspend and thread
swap).
immediately take control. It's main job was to check the list of
handling processes for any that had been running too long (long
running processes in a web server meant that something had gone wrong)
and terminate them (freeing up the process and socket resources,
etc..). Then it (the cleanup process) would go back to sleep and let
the lower priority processes run.
Now that may be the tricky part -- though if you read the Windows
programming guides, force termination of threads is strongly
discouraged as there is pretty much no possibility of recovering
resources. If the thread has a possibility of running overly long, I'd
expect there is either a loop or an unsatisfied read. For the read
request, I'd try to code a time-out into the thread (which may then
loop a few times retrying the operation before aborting the thread --
from inside, not from outside). For the loop -- embed a non-blocking
test on some event/lock/queue which your manager task can can set.
Heck, define "running too long" in a time, and you could maybe even use
a timer-thread rather than a background manager. For each connection
you spawn the handler thread AND start a timer thread passing the
handler ID; when the timer expires let IT set the "abort" signal for
the handler, and then maybe do a thread join (wait for termination of
the handler) before the timer itself ends its existance.
At one point I attempted to translate this all into Python, but the
lack of the ability to set process priorities, or for processes to
I suspect process/thread priorities are OS dependent (on Windows the
PROCESS has a priority /class/, and each thread in the process has a
priority level relative to the base for the process). You likely have
to invoke the actual OS specific calls for that.
yield control to other processes, or an effective way to terminate
"Yield"ing takes place automatically. And for terminating, that is
really OS dependent -- I know of two OSs where one it is recommended
that one never force terminate; one is supposed to have the thread
terminate in response to some condition sent to it. (BTW: in Windows
nomenclature, multiple threads are all part of a single process,
sharing one memory space).
proceses,
kept me from doing this. The Python threading model seemed very
primitve compared to what I was used to in regards to how threads can
be controlled.
Whereas I view COOPERATIVE tasking as the primitive model (it's what
WfW3.11 used at the user interface level -- every program ran as a
cooperatively scheduled thread of the window manager).
Rather than being a knock on Python, I would prefer to know how to do
all this, if it can be done.
By throwing out any preconception of the threading model and starting
from scratch... Being preemptive, the task scheduling is a given, you
don't worry about it. The rest is a matter of determining a responsive
form of IPC, letting each thread handle its own termination.
--