mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-26 08:03:03 +01:00
386 lines
10 KiB
Plaintext
386 lines
10 KiB
Plaintext
; The contents of this file are subject to the Interbase Public
|
||
; License Version 1.0 (the "License"); you may not use this file
|
||
; except in compliance with the License. You may obtain a copy
|
||
; of the License at http://www.Inprise.com/IPL.html
|
||
;
|
||
; Software distributed under the License is distributed on an
|
||
; "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
||
; or implied. See the License for the specific language governing
|
||
; rights and limitations under the License.
|
||
;
|
||
; The Original Code was created by Inprise Corporation
|
||
; and its predecessors. Portions created by Inprise Corporation are
|
||
; Copyright (C) Inprise Corporation.
|
||
;
|
||
; All Rights Reserved.
|
||
; Contributor(s): ______________________________________.
|
||
;
|
||
; PROGRAM: JRD Access Method
|
||
; MODULE: thread.mar
|
||
; DESCRIPTION: VMS Thread Manager
|
||
;
|
||
|
||
.title thread VMS Thread Manager
|
||
|
||
; Stack frame definition
|
||
|
||
.psect stf,abs
|
||
stf_handler: .long ; address of condition handler
|
||
stf_mask: .long ; misc flags and masks
|
||
stf_ap: .long ; saved ap (argument list pointer)
|
||
stf_fp: .long ; saved fp (frame pointer)
|
||
stf_pc: .long ; saved pc (program counter)
|
||
|
||
stack_size = 65536
|
||
|
||
; Thread control block
|
||
|
||
THREAD_ast = 1
|
||
|
||
.psect tcb,abs
|
||
tcb_fp: .long ; saved frame pointer
|
||
tcb_callback: .long ; callback routine to determine runnability
|
||
tcb_arg: .long ; arg to callback routine
|
||
tcb_next: .long ; next thread in system (linked in ring)
|
||
tcb_base: .long ; base frame for thread
|
||
tcb_stack: .long ; allocated stack
|
||
tcb_flags: .long ; task flags
|
||
tcb_length: .blkb 0
|
||
|
||
; Local thread data
|
||
|
||
.psect thread_data,noexe,long
|
||
|
||
threads: .address 0 ; currently scheduled thread
|
||
primary: .address 0 ; primary (i.e. main) thread
|
||
base_fp: .address 0 ; last frame pointer of "real" stack
|
||
ast_fp: .address 0 ; frame pointer for THREAD_completion_ast
|
||
ast_thread: .address 0 ; active ast thread
|
||
free: .address 0 ; any free threads
|
||
|
||
eol = 10
|
||
|
||
.if defined debug
|
||
debug_msg: .asciz /%x fp: %x, base: %x, stack: %x, next: %x/-
|
||
<eol> -
|
||
/ callback: %x, arg: %x, flags: %x/ <eol><eol>
|
||
.endc
|
||
|
||
.psect thread,exe,pic,nowrt,quad
|
||
.page
|
||
|
||
;THREAD_ast_active ()
|
||
;**************************************
|
||
;
|
||
; T H R E A D _ a s t _ a c t i v e
|
||
;
|
||
;*************************************
|
||
;
|
||
; Functional description
|
||
; Return 0 if AST thread is NOT active, otherwise TRUE.
|
||
;
|
||
;*************************************/
|
||
|
||
.entry THREAD_ast_active, ^m<>
|
||
|
||
movl ast_fp, r0
|
||
ret
|
||
|
||
;THREAD_completion_ast ()
|
||
;**************************************
|
||
;
|
||
; T H R E A D _ c o m p l e t i o n _ a s t
|
||
;
|
||
;*************************************
|
||
;
|
||
; Functional description
|
||
; Called as completion ast for any asynchronous system
|
||
; service.
|
||
;
|
||
;*************************************/
|
||
|
||
.entry THREAD_completion_ast, ^m<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>
|
||
|
||
movl threads, r0
|
||
tstl threads
|
||
beql 10$ ; thread not initialized -- don't bother
|
||
calls #0, completion
|
||
|
||
10$: ret
|
||
|
||
.entry completion, ^m<>
|
||
|
||
movl fp, ast_fp
|
||
clrl ast_thread
|
||
calls #0, THREAD_wait
|
||
ret
|
||
|
||
; THREAD_init ()
|
||
;**************************************
|
||
;
|
||
; T H R E A D _ i n i t
|
||
;
|
||
;*************************************
|
||
;
|
||
; Functional description
|
||
; Initialize thread package unless already initialized.
|
||
;
|
||
;*************************************/
|
||
|
||
.entry THREAD_init, ^m<>
|
||
|
||
; If this is the first time in, make up a thread block for the primary thread
|
||
|
||
tstl primary
|
||
bneq 10$ ; primary thread exists!
|
||
pushl #tcb_length ; size of thread control block
|
||
calls #1, G^gds__alloc ; allocate thread block
|
||
movl r0, primary ; we are the primary thread
|
||
movl r0, threads ; and also the only thread
|
||
movl r0, tcb_next(r0) ; so, link thread to itself
|
||
clrl tcb_stack(r0) ; no allocated stack
|
||
|
||
10$: ret
|
||
|
||
.if defined debug
|
||
; THREAD_print ()
|
||
;**************************************
|
||
;
|
||
; T H R E A D _ p r i n t
|
||
;
|
||
;*************************************
|
||
;
|
||
; Functional description
|
||
; Print out all thread blocks.
|
||
;
|
||
;*************************************/
|
||
|
||
.entry THREAD_print, ^m<r2>
|
||
|
||
movl threads, r2
|
||
beql 100$
|
||
|
||
10$: pushl tcb_flags(r2)
|
||
pushl tcb_arg(r2)
|
||
pushl tcb_callback(r2)
|
||
pushl tcb_next(r2)
|
||
pushl tcb_stack(r2)
|
||
pushl tcb_base(r2)
|
||
pushl tcb_fp(r2)
|
||
pushl r2
|
||
pushal debug_msg
|
||
calls #9, g^printf
|
||
movl tcb_next(r2), r2
|
||
cmpl r2, threads
|
||
bneq 10$
|
||
|
||
100$: ret
|
||
.endc
|
||
|
||
; THREAD_start (thread, arg, priority, flags)
|
||
; int (*thread)();
|
||
; long arg;
|
||
; int priority;
|
||
; int flags;
|
||
;**************************************
|
||
;
|
||
; T H R E A D _ s t a r t
|
||
;
|
||
;*************************************
|
||
;
|
||
; Functional description
|
||
; Start a new thread. Initialize thread package if this
|
||
; is first call.
|
||
;
|
||
;*************************************/
|
||
|
||
.entry THREAD_start, ^m<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>
|
||
|
||
; If this is the first time in, make up a thread block for the primary thread
|
||
|
||
movl primary, r1 ; is there already a primary thread
|
||
bneq 10$ ; primary thread exists!
|
||
calls #1, THREAD_init ; initialize package
|
||
movl primary, r1
|
||
|
||
; If this is the real stack, save the base frame
|
||
|
||
10$: cmpl r1, threads
|
||
bneq 20$ ; not primary thread
|
||
movl fp, base_fp ; save "real" frame pointer
|
||
|
||
; Find or allocate new thread block
|
||
|
||
20$: movl threads, r1
|
||
movl fp, tcb_fp (r1) ; save somewhere to return to
|
||
movl free, r2 ; check for free thread block
|
||
beql 30$ ; no free thread block, allocate one
|
||
movl tcb_next (r2), free ; unlink thread block from free list
|
||
brb 40$
|
||
|
||
30$: pushl #tcb_length ; size of thread control block
|
||
calls #1, G^gds__alloc ; allocate thread block
|
||
movl r0,r2 ; address of new thread control block
|
||
pushl #stack_size ; size of new stack
|
||
calls #1, G^gds__alloc ; allocate thread stack
|
||
movl r0, tcb_stack(r2) ; save address of new stack
|
||
|
||
40$: clrl tcb_callback(r2) ; thread is runnable
|
||
movl threads,r3 ; previous thread block
|
||
clrl tcb_callback(r3) ; current thread is still runnable
|
||
movl tcb_next(r3), tcb_next(r2) ; link into ring of threads
|
||
movl r2, tcb_next(r3)
|
||
movl 4(ap),r3 ; address of thread routine
|
||
movl 8(ap),r4 ; argument for thread
|
||
movl 16(ap), tcb_flags(r2) ; options
|
||
movl r2, threads ; currently executing thread
|
||
addl3 #stack_size, tcb_stack(r2), sp ; address of new stack
|
||
calls #0, thread_caller ; we will never come back
|
||
halt
|
||
|
||
.entry thread_caller, ^m<r2,r3,r4>
|
||
|
||
movl fp, tcb_base (r2) ; save base frame for stack
|
||
pushl r4 ; pass thru argument
|
||
calls #1, (r3) ; call thread, come back on thread exit
|
||
|
||
; Thread has existed. Unlink thread block from runnable threads,
|
||
; link it to free threads, and go reschedule
|
||
|
||
movl r2, r3
|
||
|
||
50$: movl tcb_next(r3), r3 ; get next thread in system
|
||
cmpl tcb_next (r3), r2
|
||
bneq 50$ ; not prior thread, continue search
|
||
|
||
movl tcb_next(r2), tcb_next (r3); ; unlink from active threads
|
||
movl free, tcb_next(r2) ; link into free threads
|
||
movl r2, free
|
||
movl r3, r2 ; start schedule with prior thread
|
||
brb schedule ; schedule next thread
|
||
|
||
; THREAD_wait (callback, arg)
|
||
; int (*callback)();
|
||
; long arg;
|
||
;**************************************
|
||
;
|
||
; T H R E A D _ w a i t
|
||
;
|
||
;*************************************
|
||
;
|
||
; Functional description
|
||
; Re-schedule thread. Don't resume this thread until iob
|
||
; has been posted. If no callback is given, just reschedule another
|
||
; thread.
|
||
;
|
||
;*************************************/
|
||
|
||
.entry THREAD_wait, ^m<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>
|
||
|
||
tstl ast_fp ; check to see if this is in AST mode
|
||
bneq ast_wait
|
||
|
||
movl threads, r2 ; pick up current thread block
|
||
bneq 10$ ; already initialized
|
||
calls #0, THREAD_init ; initialize thread package
|
||
movl threads, r2
|
||
|
||
10$:
|
||
movl 4(ap), tcb_callback (r2)
|
||
movl 8(ap), tcb_arg (r2)
|
||
movl fp, tcb_fp(r2) ; save current frame pointer
|
||
cmpl r2, primary
|
||
bneq schedule ; not primary thread
|
||
movl fp, base_fp ; primary thread -- save top frame
|
||
|
||
; Find next runnable thread. A thread is runnable if it either has
|
||
; no callback routine, or the callback return TRUE (non-zero)
|
||
|
||
schedule:
|
||
movl tcb_next (r2), r2 ; get next thread
|
||
|
||
; thread is a candidate to run -- see if it's runnable
|
||
|
||
10$: tstl tcb_callback (r2)
|
||
beql 25$ ; no callback -- thread is runnable
|
||
pushl tcb_arg (r2)
|
||
calls #1, @tcb_callback (r2) ; callback to determine runnability
|
||
tstl r0
|
||
bneq 25$ ; callback says runnable
|
||
|
||
; thread is not runnable -- see if we're done with scan
|
||
|
||
15$: cmpl r2, threads
|
||
bneq schedule ; haven't tried all threads yet
|
||
|
||
; nothing is runnable -- exit if we're in AST state, otherwise
|
||
; wait for something to happen
|
||
|
||
calls #0, G^sys$hiber ; wait for something to happen
|
||
brb schedule ; try, try again
|
||
|
||
; We've got a runnable thread -- set up frame point and off we go
|
||
|
||
25$: cmpl r2, primary
|
||
beql 30$ ; primary thread -- no stack adjustment
|
||
movl tcb_base(r2),r4 ; get base frame of thread
|
||
movl base_fp, stf_fp (r4) ; link thread to real stack
|
||
|
||
30$: movl r2, threads ; indicate current thread
|
||
movl tcb_fp(r2), fp ; restore saved frame
|
||
ret ; return to thread
|
||
|
||
ast_wait:
|
||
movl threads, r2 ; pick up current thread block
|
||
|
||
movl ast_thread, r3
|
||
beql ast_schedule ; we just entered AST mode
|
||
|
||
20$: movl 4(ap), tcb_callback (r3)
|
||
movl 8(ap), tcb_arg (r3)
|
||
movl fp, tcb_fp(r3) ; save current frame pointer
|
||
|
||
; Find next runnable thread. A thread is runnable if it either has
|
||
; no callback routine, or the callback return TRUE (non-zero)
|
||
|
||
ast_schedule:
|
||
movl tcb_next (r2), r2 ; get next thread
|
||
|
||
cmpl r2, threads
|
||
beql 15$ ; thread is already active
|
||
bitb #THREAD_ast, tcb_flags(r2)
|
||
beql 15$ ; thread doesn't run at AST level
|
||
|
||
; thread is a candidate to run -- see if it's runnable
|
||
|
||
10$: tstl tcb_callback (r2)
|
||
beql 25$ ; no callback -- thread is runnable
|
||
pushl tcb_arg (r2)
|
||
calls #1, @tcb_callback (r2) ; callback to determine runnability
|
||
tstl r0
|
||
bneq 25$ ; callback says runnable
|
||
|
||
; thread is not runnable -- see if we're done with scan
|
||
|
||
15$: cmpl r2, threads
|
||
bneq ast_schedule ; haven't tried all threads yet
|
||
|
||
; nothing is runnable -- exit
|
||
|
||
clrl ast_thread
|
||
movl ast_fp, fp ; get ready to return
|
||
clrl ast_fp
|
||
ret
|
||
|
||
; We've got a runnable thread -- if it's the one we came in on,
|
||
; just return (simple?)
|
||
|
||
25$: movl tcb_base(r2),r4 ; get base frame of thread
|
||
movl ast_fp, stf_fp(r4)
|
||
movl tcb_fp(r2), fp
|
||
movl r2, ast_thread
|
||
ret
|
||
|
||
|
||
.end
|