8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-29 06:43:03 +01:00
firebird-mirror/src/jrd/thread.mar

386 lines
10 KiB
Plaintext
Raw Normal View History

2001-05-23 15:26:42 +02:00
; 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