README
1This is a source code distribution for QuickThreads. QuickThreads is a
2toolkit for building threads packages; it is described in detail in the
3University of Washington CS&E Technical report #93-05-06, available via
4anonymous ftp from `ftp.cs.washington.edu' (128.95.1.4, as of Oct. '94)
5in `tr/1993/05/UW-CSE-93-05-06.PS.Z'.
6
7This distribution shows basic ideas in QuickThreads and elaborates with
8example implementations for a gaggle of machines. As of October those
9machines included:
10
11 80386 faimly
12 88000 faimily
13 DEC AXP (Alpha) family
14 HP-PA family
15 KSR
16 MIPS family
17 SPARC V8 family
18 VAX family
19
20Configuration, build, and installation are described in INSTALL.
21
22Be aware: that there is no varargs code for the KSR.
23
24The HP-PA port was designed to work with both HP workstations
25and Convex SPP computers. It was generously provided by Uwe Reder
26<uereder@cip.informatik.uni-erlangen.de>. It is part of the ELiTE
27(Erlangen Lightweight Thread Environment) project directed by
28Frank Bellosa <bellosa@informatik.uni-erlangen.de> at the Operating
29Systems Department of the University of Erlangen (Germany).
30
31Other contributors include: Weihaw Chuang, Richard O'Keefe,
32Laurent Perron, John Polstra, Shinji Suzuki, Assar Westerlund,
33thanks also to Peter Buhr and Dirk Grunwald.
34
35
36Here is a brief summary:
37
38QuickThreads is a toolkit for building threads packages. It is my hope
39that you'll find it easier to use QuickThreads normally than to take it
40and modify the raw cswap code to fit your application. The idea behind
41QuickThreads is that it should make it easy for you to write & retarget
42threads packages. If you want the routine `t_create' to create threads
43and `t_block' to suspend threads, you write them using the QuickThreads
44`primitive' operations `QT_SP', `QT_INIT', and `QT_BLOCK', that perform
45machine-dependent initialization and blocking, plus code you supply for
46performing the portable operatons. For example, you might write:
47
48 t_create (func, arg)
49 {
50 stk = malloc (STKSIZE);
51 stackbase = QT_SP (stk, STKSIZE);
52 sp = QT_INIT (stakcbase, func, arg);
53 qput (runq, sp);
54 }
55
56Threads block by doing something like:
57
58 t_block()
59 {
60 sp_next = qget (runq);
61 QT_BLOCK (helper, runq, sp_next);
62 // wake up again here
63 }
64
65 // called by QT_BLOCK after the old thread has blocked,
66 // puts the old thread on the queue `onq'.
67 helper (sp_old, onq)
68 {
69 qput (onq, sp_old);
70 }
71
72(Of course) it's actually a bit more complex than that, but the general
73idea is that you write portable code to allocate stacks and enqueue and
74dequeue threads. Than, to get your threads package up and running on a
75different machine, you just reconfigure QuickThreads and recompile, and
76that's it.
77
78The QuickThreads `distribution' includes a sample threads package (look
79at stp.{c,h}) that is written in terms of QuickThreads operations. The
80TR mentioned above explains the simple threads package in detail.
81
82
83
84If you do use QuickThreads, I'd like to hear both about what worked for
85you and what didn't work, problems you had, insights gleaned, etc.
86
87Let me know what you think.
88
89David Keppel <pardo@cs.washington.edu>
90
README.MISC
1Here's some machine-specific informatin for various systems:
2
3m88k on g88.sim
4
5 .g88init:
6 echo (gdb) target sim\n
7 target sim
8 echo (gdb) ecatch all\n
9 ecatch all
10 echo (gdb) break exit\n
11 break exit
12 % vi Makefile // set CC and AS
13 % setenv MEERKAT /projects/cer/meerkat
14 % set path=($MEERKAT/bin $path)
15 % make run
16 % g88.sim run
17 (g88) run run N // where `N' is the test number
18
19
20m88k on meerkats, cross compile as above (make run)
21
22 Run w/ g88:
23 %g88 run
24 (g88) source /homes/rivers/robertb/.gdbinit
25 (g88) me
26 which does
27 (g88) set $firstchars=6
28 (g88) set $resetonattach=1
29 (g88) attach /dev/pp0
30 then download
31 (g88) dl
32 and run with
33 (g88) continue
34
35 Really the way to run it is:
36 (g88) source
37 (g88) me
38 (g88) win
39 (g88) dead 1
40 (g88) dead 2
41 (g88) dead 3
42 (g88) dl
43 (g88) cont
44
45 To rerun
46 (g88) init
47 (g88) dl
48
49 To run simulated meerkat:
50 (g88) att sim
51 <<then use normal commands>>
52
53 On 4.5 g88:
54 (g88) target sim memsize
55 instead of attatch
56 (g88) ecatch all # catch exception before becomes error
57
README.PORT
1Date: Tue, 11 Jan 94 13:23:11 -0800
2From: "pardo@cs.washington.edu" <pardo@meitner.cs.washington.edu>
3
4>[What's needed to get `qt' on an i860-based machine?]
5
6Almost certainly "some assembly required" (pun accepted).
7
8To write a cswap port, you need to understand the context switching
9model. Turn to figure 2 in the QT TR. Here's about what the assembly
10code looks like to implement that:
11
12 qt_cswap:
13 adjust stack pointer
14 save callee-save registers on to old's stack
15 argument register <- old sp
16 sp <- new sp
17 (*helper)(args...)
18 restore callee-save registers from new's stack
19 unadjust stack pointer
20 return
21
22Once more in slow motion:
23
24 - `old' thread calls context switch routine (new, a0, a1, h)
25 - cswap routine saves registers that have useful values
26 - cswap routine switches to new stack
27 - cswap routine calls helper function (*h)(old, a0, a1)
28 - when helper returns, cswap routine restores registers
29 that were saved the last time `new' was suspended
30 - cswap routine returns to whatever `new' routine called the
31 context switch routine
32
33There's a few tricks here. First, how do you start a thread running
34for the very first time? Answer is: fake some stuff on the stack
35so it *looks* like it was called from the middle of some routine.
36When the new thread is restarted, it is treated like any other
37thread. It just so happens that it's never really run before, but
38you can't tell that because the saved state makes it look like like
39it's been run. The return pc is set to point at a little stub of
40assembly code that loads up registers with the right values and
41then calls `only'.
42
43Second, I advise you to forget about varargs routines (at least
44until you get single-arg routines up and running).
45
46Third, on most machines `qt_abort' is the same as `qt_cswap' except
47that it need not save any callee-save registers.
48
49Fourth, `qt_cswap' needs to save and restore any floating-point
50registers that are callee-save (see your processor handbook). On
51some machines, *no* floating-point registers are callee-save, so
52`qt_cswap' is exactly the same as the integer-only cswap routine.
53
54I suggest staring at the MIPS code for a few minutes. It's "mostly"
55generic RISC code, so it gets a lot of the flavor across without
56getting too bogged down in little nitty details.
57
58
59
60Now for a bit more detail: The stack is laid out to hold callee-save
61registers. On many machines, I implemented fp cswap as save fp
62regs, call integer cswap, and when integer cswap returns (when the
63thread wakes up again), restore fp regs.
64
65For thread startup, I figure out some callee-save registers that
66I use to hold parameters to the startup routine (`only'). When
67the thread is being started it doesn't have any saved registers
68that need to be restored, but I go ahead and let the integer context
69switch routine restore some registers then "return" to the stub
70code. The stub code then copies the "callee save" registers to
71argument registers and calls the startup routine. That keeps the
72stub code pretty darn simple.
73
74For each machine I need to know the machine's procedure calling
75convention before I write a port. I figure out how many callee-save
76registers are there and allocate enough stack space for those
77registers. I also figure out how parameters are passed, since I
78will need to call the helper function. On most RISC machines, I
79just need to put the old sp in the 0'th arg register and then call
80indirect through the 3rd arg register; the 1st and 2nd arg registers
81are already set up correctly. Likewise, I don't touch the return
82value register between the helper's return and the context switch
83routine's return.
84
85I have a bunch of macros set up to do the stack initialization.
86The easiest way to debug this stuff is to go ahead and write a C
87routine to do stack initialization. Once you're happy with it you
88can turn it in to a macro.
89
90In general there's a lot of ugly macros, but most of them do simple
91things like return constants, etc. Any time you're looking at it
92and it looks confusing you just need to remember "this is actually
93simple code, the only tricky thing is calling the helper between
94the stack switch and the new thread's register restore."
95
96
97You will almost certainly need to write the assembly code fragment
98that starts a thread. You might be able to do a lot of the context
99switch code with `setjmp' and `longjmp', if they *happen* to have
100the "right" implementation. But getting all the details right (the
101helper can return a value to the new thread's cswap routine caller)
102is probaby trickier than writing code that does the minimum and
103thus doesn't have any extra instructions (or generality) to cause
104problems.
105
106I don't know of any ports besides those included with the source
107code distribution. If you send me a port I will hapily add it to
108the distribution.
109
110Let me know as you have questions and/or comments.
111
112 ;-D on ( Now *that*'s a switch... ) Pardo
113