?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Operating System : initialize tables' ??
MODULE osm$initialize_tables;

{ MODULE: osm$initialize_tables
{
{ PURPOSE: This module contains the routines to initialize
{          dispatcher tables at deadstart time and the
{          routine to expand the PTL.
{
{ NOTES:   This module contains the following procedures:
{            osp$initialize_ptl - allocates and initializes PTL.
{            osp$expand_ptl - expands PTL by tmc$ptl_increment entries.
{            osp$reset_ptl - re-initializes PTL and DCT
{

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc jmt$dispatching_priority
*copyc jmt$initiated_job_list_entry
*copyc ost$heap
*copyc tmt$dispatching_control_sets
?? POP ??
*copyc i#call_monitor
*copyc jmv$ijle_size
*copyc jmv$maximum_service_classes
*copyc jmv$service_classes
*copyc osp$clear_mainframe_sig_lock
*copyc osp$set_mainframe_sig_lock
*copyc osp$set_status_abnormal
*copyc osv$mainframe_pageable_heap
*copyc pmp$zero_out_table
*copyc pmv$quantum
*copyc tme$monitor_mode_exceptions
*copyc tmt$primary_task_list
*copyc tmt$rb_update_job_task_enviro
*copyc tmv$dispatch_priority_integer
*copyc tmv$dispatching_control_sets
*copyc tmv$ptl_p
*copyc tmv$dct

*copyc osv$mainframe_wired_heap

?? TITLE := 'osp$initialize_ptl', EJECT ??

  PROCEDURE [XDCL] osp$initialize_ptl;

{ This routine is called at deadstart time to initialize PTL.
{
{ INPUT: none.
{
{ OUTPUT: none.
{
{ NOTES: This routine does the following:
{          . initializes the ijl entry size (This must be done when the ptl is allocated--
{            before any task switch can occur--because the ijl entry size is used by task switch.)
{          . initializes the dispatching control sets and initializes the array containing the
{            allocated dispatching priority integer for each dispatching priority.  The dispatching
{            priority integer is used to determine which dispatching priority is highest when the
{            CPU has been allocated among the various dispatching priorties.  Dispatching priorities
{            which have a "minimum to satisfy" have a higher integer priority than those that do not.
{            NOTE:  System dispatching priorities are always considered to have "minimums to satisfy",
{            so they are always the highest priority.
{          . allocate the PTL.
{          . initialize all the PTL entries into a free queue.
{

    VAR
      dp: jmt$dispatching_priority,
      local_set: tmt$dispatching_control_sets,
      i: integer,
      ijl_size_ptr: ^array [1 .. * ] of jmt$initiated_job_list_entry,
      max_ptlo: ost$task_index;

    PUSH ijl_size_ptr: [1 .. 2];
    jmv$ijle_size := #OFFSET (^ijl_size_ptr^ [2]) - #OFFSET (ijl_size_ptr);

{ Initialize the dispatching controls and determine the dispatching priority integers.

    tmv$dispatching_control_sets.minimums_to_satisfy := $jmt$dispatching_priority_set [1, 2, 3, 4, 5, 6];
    tmv$dispatching_control_sets.maximums_exceeded := $jmt$dispatching_priority_set [];
    tmv$dispatching_control_sets.enforce_maximums := $jmt$dispatching_priority_set [];

    local_set := tmv$dispatching_control_sets;

    FOR dp := jmc$min_dispatching_priority TO jmc$max_dispatching_priority DO
      local_set.ready_tasks := $jmt$dispatching_priority_set [jmc$dp_conversion - dp];
      local_set.minimums_to_satisfy := local_set.minimums_to_satisfy * local_set.ready_tasks;
      local_set.ready_tasks := local_set.ready_tasks XOR local_set.minimums_to_satisfy;
      #UNCHECKED_CONVERSION (local_set, tmv$dispatch_priority_integer [dp]);
    FOREND;

{ Allocate the ptl and service_class_table.  Some service class table fields are used by task
{ switch and must be initialized before any task switches occur.

    ALLOCATE tmv$ptl_p: [0 .. tmc$initial_ptl_size] IN osv$mainframe_wired_heap^;
    pmp$zero_out_table (#LOC (tmv$ptl_p^), #SIZE (tmv$ptl_p^));

    ALLOCATE jmv$service_classes [jmc$system_service_class] IN osv$mainframe_wired_heap^;

    pmp$zero_out_table (jmv$service_classes [jmc$system_service_class],
          #SIZE (jmv$service_classes [jmc$system_service_class]^));

    jmv$service_classes [jmc$system_service_class]^.attributes.
          dispatching_control [jmc$min_dispatching_control].dispatching_timeslice.minor := pmv$quantum;
    jmv$service_classes [jmc$system_service_class]^.attributes.
          dispatching_control [jmc$min_dispatching_control].dispatching_timeslice.major := pmv$quantum;

{ Link all the PTL entries into a free queue.

    max_ptlo := UPPERBOUND (tmv$ptl_p^);
    FOR i := 1 TO max_ptlo DO
      tmv$ptl_p^ [i].ptl_thread := i + 1;
    FOREND;
    tmv$ptl_p^ [max_ptlo].ptl_thread := 0;

{ Initialize the free queue control block.

    tmv$dct [jmc$null_dispatching_priority].queue_head := 1;
    tmv$dct [jmc$null_dispatching_priority].queue_tail := max_ptlo;
  PROCEND osp$initialize_ptl;
?? TITLE := 'osp$expand_ptl', EJECT ??

*copy osh$expand_ptl

  PROCEDURE [XDCL] osp$expand_ptl
    (    unconditionally_expand: boolean;
     VAR status: ost$status);

{ NOTES: This procedure is called by deadstart job recovery, job initialization,
{        and task initialization.  It does the following:
{          . allocates a new expanded PTL.
{          . calls monitor to copy the old PTL into the new PTL.
{          . frees the old PTL.
{

    CONST
      minimum_free = 10;

    VAR
      count: integer,
      expand_ptl_lock: [STATIC, oss$mainframe_pageable] ost$signature_lock,
      increment: integer,
      new_ptl_p: ^tmt$primary_task_list,
      next_index: ost$task_index,
      old_max_ptlo: ost$task_index,
      old_ptl_p: ^tmt$primary_task_list,
      rb: tmt$rb_update_job_task_enviro;

    status.normal := TRUE;
    osp$set_mainframe_sig_lock (expand_ptl_lock);
    old_ptl_p := tmv$ptl_p;
    old_max_ptlo := UPPERBOUND (tmv$ptl_p^);
    increment := tmc$ptl_increment;

    IF old_max_ptlo = tmc$maximum_ptl THEN
      osp$set_status_abnormal ('TM', tme$ptl_full, '', status);
      osp$clear_mainframe_sig_lock (expand_ptl_lock);
      RETURN;
    ELSEIF old_max_ptlo + tmc$ptl_increment > tmc$maximum_ptl THEN
      increment := tmc$maximum_ptl - old_max_ptlo;
    IFEND;

{  Check to see if some entries just freed up.

    count := 0;
    IF (tmv$dct [jmc$null_dispatching_priority].queue_head <> 0) AND (NOT unconditionally_expand) THEN
      count := 1;
      next_index := tmv$dct [jmc$null_dispatching_priority].queue_head;
      WHILE (tmv$ptl_p^ [next_index].ptl_thread <> 0) AND (count < minimum_free) DO
        count := count + 1;
        next_index := tmv$ptl_p^ [next_index].ptl_thread;
      WHILEND;
    IFEND;

    IF count <> minimum_free THEN
      ALLOCATE new_ptl_p: [0 .. old_max_ptlo + increment] IN osv$mainframe_wired_heap^;
      pmp$zero_out_table (#LOC (new_ptl_p^), #SIZE (new_ptl_p^));
      rb.reqcode := syc$rc_update_job_task_enviro;
      rb.subcode := tmc$ujte_expand_ptl;
      rb.ptl_p := new_ptl_p;
      i#call_monitor (#LOC (rb), #SIZE (rb));
      FREE old_ptl_p IN osv$mainframe_wired_heap^;
    IFEND;

    osp$clear_mainframe_sig_lock (expand_ptl_lock);

  PROCEND osp$expand_ptl;
?? TITLE := 'osp$reset_ptl', EJECT ??

{ This procedure is called at deadstart time after all tasks that run and terminate
{ before job recovery have completed and before tasks that stay around have been
{ initiated.

  PROCEDURE [XDCL, #GATE] osp$reset_ptl;

    VAR
      index: ost$task_index,
      max_ptlo: ost$task_index;

    max_ptlo := UPPERBOUND (tmv$ptl_p^);
    FOR index := 2 TO max_ptlo DO
      tmv$ptl_p^ [index].ptl_thread := index + 1;
    FOREND;
    tmv$ptl_p^ [max_ptlo].ptl_thread := 0;

    tmv$dct [jmc$null_dispatching_priority].queue_head := 2;
    tmv$dct [jmc$null_dispatching_priority].queue_tail := max_ptlo;

  PROCEND osp$reset_ptl;

MODEND osm$initialize_tables;
