?? RIGHT := 110 ??
?? NEWTITLE := 'NOSVE AJL MANAGER MODULE' ??
MODULE jmm$ajl_manager;

{
{ This module governs the allocation and freeing of the ajl ordinals. It also
{ modifies the monitor segment table.
{
{ JMP$ASSIGN_AJL_ENTRY: This procedure is called by tmm$dispatcher (tmp$create_job)
{    when a job is created, jsm$monitor_mode_job_swapper when a job is being swapped
{    in, or when access to a job's job fixed is needed.  An entry is assigned if
{    needed, otherwise just the in_use count is incremented.  An error status condition
{    is returned if an entry is needed but not available.  When an entry is assigned,
{    several fields are initialized, and an entry is made into the monitor segment
{    table.  The variables jmv$max_ajl_ordinal_in_use and jmv$number_free_ajl_entries
{    are updated appropriately.  Jmv$number_free_ajl_entries may go negative down to the
{    number of cpus defined (must_assign is set and called for swapping io, for io
{    completion or for job fixed access).
{
{
{ JMP$ASSIGN_AJL_WITH_LOCK: Same as above but caller already has tmv$ptl_lock set.
{
{
{ JMP$FREE_AJL_ENTRY: This procedure decrements the in_use count.  When the count goes
{    to zero the ajl entry is freed and the monitor segment table entry is marked invalid.
{    The variables jmv$max_ajl_ordinal_in_use and jmv$number_of_free_ajl_entries are
{    updated appropriately.  This procedure is called by tmm$dispatcher (tmp$exit_job)
{    when a job is exited, by jsm$monitor_mode_job_swapper when a job is swapped out, or
{    when access to a job's job fixed is no longer needed.
{
{
{ JMP$FREE_AJL_WITH_LOCK: Same as above but caller already has tmv$ptl_lock set.
{
{
{ NOTE: The following are inline procedures.  They are used instead of the above
{    procedures when access to a job's job fixed is needed and there is reason to
{    believe there is already an ajl entry for the job.
{
{
{ JMP$LOCK_AJL: This procedure increments the in_use count. Jmp$assign_ajl_with_lock
{    is called if necessary with the MUST_ASSIGN parameter set to TRUE.
{
{
{ JMP$LOCK_AJL_WITH_LOCK: This procedure increments the in_use count but caller
{    already has tmv$ptl_lock_set.  Jmp$assign_ajl_with_lock is called if
{    necessary with the MUST_ASSIGN parameter set to TRUE.
{
{
{ JMP$UNLOCK_AJL: This procedure decrements the in_use count.  Jmp$free_ajl_with_lock
{    is called if necessary.
{
{
{ JMP$UNLOCK_AJL_WITH_LOCK: Same as above but caller already has tmv$ptl_lock set.
{

?? NEWTITLE := 'Global Declarations Referenced By This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc jmc$ajl_caller
*copyc jmc$null_ajl_ordinal
*copyc mtc$job_fixed_segment
*copyc osc$multiprocessor_constants
*copyc jme$job_scheduler_conditions
*copyc jmt$active_job_list
*copyc jmt$ajl_ordinal
*copyc jmt$ijl_ordinal
*copyc jmt$initiated_job_list_entry
*copyc syt$monitor_status
?? POP ??
*copyc jmf$ijle_p
*copyc jmp$set_sched_event_cond_ready
*copyc mmp$conditional_purge_all_s_map
*copyc mtp$error_stop
*copyc mtp$set_status_abnormal
*copyc tmp$clear_lock
*copyc tmp$set_lock
*copyc jmv$ajl_p
*copyc jmv$ijl_p
*copyc jmv$job_sched_events_selected
*copyc jmv$job_scheduler_event
*copyc mmv$multiple_page_maps
*copyc mtv$monitor_segment_table
*copyc osv$cpus_physically_configured
*copyc tmv$ptl_lock

{The following declaration is needed by mmp$conditional_purge_all_s_map

  VAR
    mtv$recovery_lock3: [XREF] boolean;

?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared By This Module', EJECT ??

  VAR
    jmv$number_free_ajl_entries: [XDCL, #GATE] integer,
    jmv$nr_free_ajl_entries_set_evt: [XDCL, #GATE] integer := 0,
    null_ajl: [STATIC] jmt$active_job_list_entry := [0, [0, 0], NIL, FALSE, 0],
    jmv$max_ajl_ordinal_in_use: [XDCL, #GATE] jmt$ajl_ordinal := 0,
    jmv$start_ajl_search_ordinal: [STATIC] jmt$ajl_ordinal := 0;

?? TITLE := ' JMP$ASSIGN_AJL_ENTRY ', EJECT ??

{  This procedure is called by tmm$dispatcher (tmp$create_job)
{  when a job is created, jsm$monitor_mode_job_swapper when a job is being swapped
{  in, or when access to a job's job fixed is needed.  An entry is assigned if
{  needed, otherwise just the in_use count is incremented.  An error status condition
{  is returned if an entry is needed but not available.  When an entry is assigned,
{  several fields are initialized, and an entry is made into the monitor segment
{  table.  The variables jmv$max_ajl_ordinal_in_use and jmv$number_free_ajl_entries
{  are updated appropriately.  Jmv$number_free_ajl_entries may go negative down to the
{  number of cpus defined (must_assign is set and called for swapping io, for io
{  completion or for job fixed access).

  PROCEDURE [XDCL] jmp$assign_ajl_entry
    (    asid: ost$asid,
         ijl_o: jmt$ijl_ordinal;
         caller: 0 .. 10(16);
         must_assign: boolean;
     VAR ajl_o: jmt$ajl_ordinal;
     VAR status: syt$monitor_status);

    VAR
      ajl_p: ^jmt$active_job_list_entry,
      ajlo: jmt$ajl_ordinal,
      ijle_p: ^jmt$initiated_job_list_entry,
      job_fixed_ste: ost$segment_descriptor,
      none_found: boolean;

    status.normal := TRUE;
    tmp$set_lock (tmv$ptl_lock{, mtc$ignore});

    ijle_p := jmf$ijle_p (ijl_o);
    IF ijle_p^.ajl_ordinal = jmc$null_ajl_ordinal THEN
      IF (jmv$number_free_ajl_entries > 0) OR (must_assign) THEN
        none_found := TRUE;

      /scan_ajl_for_free_entry/
        FOR ajlo := jmv$start_ajl_search_ordinal TO UPPERBOUND (jmv$ajl_p^) DO
          IF jmv$ajl_p^ [ajlo].in_use = 0 THEN
            ajl_o := ajlo;
            none_found := FALSE;
            EXIT /scan_ajl_for_free_entry/;
          IFEND;
        FOREND /scan_ajl_for_free_entry/;

        IF none_found THEN
          mtp$error_stop ('JM - No free ajl ordinal');
        IFEND;

      ELSE
        mtp$set_status_abnormal ('JM', jme$no_free_ajl_ordinals, status);
        tmp$clear_lock (tmv$ptl_lock);
        RETURN;
      IFEND;

      ajl_p := ^jmv$ajl_p^ [ajl_o];
      ajl_p^.ijl_ordinal := ijl_o;
      ajl_p^.ijle_p := ijle_p;
      ijle_p^.ajl_ordinal := ajl_o;

{ Purge the page-segment map if it has not been purged since the AJL entry was last used.

      mmp$conditional_purge_all_s_map (ajl_p^.time_freed);


{ Entry made into the monitor segment table for the job fixed segment of the job.

      job_fixed_ste := mtv$monitor_segment_table.st [mtc$job_fixed_segment].ste;
      job_fixed_ste.asid := asid;
      mtv$monitor_segment_table.st [ajl_o + mtc$job_fixed_segment].ste := job_fixed_ste;

      jmv$start_ajl_search_ordinal := ajl_o;

      IF jmv$max_ajl_ordinal_in_use < ajl_o THEN
        jmv$max_ajl_ordinal_in_use := ajl_o;
      IFEND;

      jmv$number_free_ajl_entries := jmv$number_free_ajl_entries - 1;

    ELSE
      ajl_p := ^jmv$ajl_p^ [ijle_p^.ajl_ordinal];
      ajl_o := ijle_p^.ajl_ordinal;
    IFEND;

    ajl_p^.in_use := ajl_p^.in_use + caller;
    tmp$clear_lock (tmv$ptl_lock);

  PROCEND jmp$assign_ajl_entry;

?? TITLE := ' JMP$ASSIGN_AJL_WITH_LOCK ', EJECT ??

{  This procedure is identical to jmp$assign_ajl_entry except the caller
{  already has tmv$ptl_lock set.

  PROCEDURE [XDCL] jmp$assign_ajl_with_lock
    (    asid: ost$asid,
         ijl_o: jmt$ijl_ordinal;
         caller: 0 .. 10(16);
         must_assign: boolean;
     VAR ajl_o: jmt$ajl_ordinal;
     VAR status: syt$monitor_status);

    VAR
      ajl_p: ^jmt$active_job_list_entry,
      ajlo: jmt$ajl_ordinal,
      ijle_p: ^jmt$initiated_job_list_entry,
      job_fixed_ste: ost$segment_descriptor,
      none_found: boolean;

    status.normal := TRUE;

    ijle_p := jmf$ijle_p (ijl_o);
    IF ijle_p^.ajl_ordinal = jmc$null_ajl_ordinal THEN
      IF (jmv$number_free_ajl_entries > 0) OR (must_assign) THEN
        none_found := TRUE;

      /scan_ajl_for_free_entry/
        FOR ajlo := jmv$start_ajl_search_ordinal TO UPPERBOUND (jmv$ajl_p^) DO
          IF jmv$ajl_p^ [ajlo].in_use = 0 THEN
            ajl_o := ajlo;
            none_found := FALSE;
            EXIT /scan_ajl_for_free_entry/;
          IFEND;
        FOREND /scan_ajl_for_free_entry/;

        IF none_found THEN
          mtp$error_stop ('JM - No free ajl ordinal');
        IFEND;

      ELSE
        mtp$set_status_abnormal ('JM', jme$no_free_ajl_ordinals, status);
        RETURN;
      IFEND;

      ajl_p := ^jmv$ajl_p^ [ajl_o];
      ajl_p^.ijl_ordinal := ijl_o;
      ajl_p^.ijle_p := ijle_p;
      ijle_p^.ajl_ordinal := ajl_o;

{ Purge the page-segment map if it has not been purged since the AJL entry was last used.

      mmp$conditional_purge_all_s_map (ajl_p^.time_freed);


{ Entry made into the monitor segment table for the job fixed segment of the job.

      job_fixed_ste := mtv$monitor_segment_table.st [mtc$job_fixed_segment].ste;
      job_fixed_ste.asid := asid;
      mtv$monitor_segment_table.st [ajl_o + mtc$job_fixed_segment].ste := job_fixed_ste;

      jmv$start_ajl_search_ordinal := ajl_o;

      IF jmv$max_ajl_ordinal_in_use < ajl_o THEN
        jmv$max_ajl_ordinal_in_use := ajl_o;
      IFEND;

      jmv$number_free_ajl_entries := jmv$number_free_ajl_entries - 1;

    ELSE
      ajl_p := ^jmv$ajl_p^ [ijle_p^.ajl_ordinal];
      ajl_o := ijle_p^.ajl_ordinal;
    IFEND;

    ajl_p^.in_use := ajl_p^.in_use + caller;

  PROCEND jmp$assign_ajl_with_lock;

?? TITLE := ' JMP$FREE_AJL_ENTRY ', EJECT ??

{  This procedure decrements the in_use count.  When the count goes
{  to zero the ajl entry is freed and the monitor segment table entry is marked invalid.
{  The variables jmv$max_ajl_ordinal_in_use and jmv$number_of_free_ajl_entries are
{  updated appropriately.  This procedure is called by tmm$dispatcher (tmp$exit_job)
{  when a job is exited, by jsm$monitor_mode_job_swapper when a job is swapped out, or
{  when access to a job's job fixed is no longer needed.

  PROCEDURE [XDCL] jmp$free_ajl_entry
    (    ijle_p: ^jmt$initiated_job_list_entry;
         caller: 0 .. 10(16));

    VAR
      ajl_ordinal: jmt$ajl_ordinal,
      in_use: integer,
      new_ajlo: jmt$ajl_ordinal;

    tmp$set_lock (tmv$ptl_lock{, mtc$ignore});
    ajl_ordinal := ijle_p^.ajl_ordinal;
    in_use := jmv$ajl_p^ [ajl_ordinal].in_use;
    in_use := in_use - caller;
    IF in_use < 0 THEN
      mtp$error_stop ('JM - AJL.in_use has gone negative.');
    ELSEIF in_use > 0 THEN
      jmv$ajl_p^ [ajl_ordinal].in_use := in_use;
    ELSE   { in_use = 0 }
      IF (ajl_ordinal = 0) THEN
        mtp$error_stop ('JM - trying to free system job ajl');
      IFEND;
      ijle_p^.ajl_ordinal := jmc$null_ajl_ordinal;
      mtv$monitor_segment_table.st [ajl_ordinal + mtc$job_fixed_segment].ste.vl := osc$vl_invalid_entry;
      jmv$ajl_p^ [ajl_ordinal] := null_ajl;
      jmv$ajl_p^ [ajl_ordinal].time_freed := #FREE_RUNNING_CLOCK (0);

{ increment number of free ajl ordinals

      jmv$number_free_ajl_entries := jmv$number_free_ajl_entries + 1;

{ if scheduler is waiting for an ajlo to be freed, then notify scheduler

      IF (jmv$number_free_ajl_entries > 0) AND
            (jmv$job_sched_events_selected [jmc$needed_ajlo_available]) AND
            (NOT jmv$job_scheduler_event [jmc$needed_ajlo_available]) THEN
        jmp$set_sched_event_cond_ready (jmc$needed_ajlo_available, jmv$number_free_ajl_entries >
              jmv$nr_free_ajl_entries_set_evt);
      IFEND;

{ update jmv$start_ajl_search_ordinal if necessary

      IF ajl_ordinal < jmv$start_ajl_search_ordinal THEN
        jmv$start_ajl_search_ordinal := ajl_ordinal;
      IFEND;

{ update jmv$max_ajl_ordinal_in_use if necessary

      IF ajl_ordinal = jmv$max_ajl_ordinal_in_use THEN
        new_ajlo := ajl_ordinal;
        WHILE (jmv$ajl_p^ [new_ajlo].in_use = 0) DO
          new_ajlo := new_ajlo - 1;
        WHILEND;
        jmv$max_ajl_ordinal_in_use := new_ajlo;
      IFEND;
    IFEND;

    tmp$clear_lock (tmv$ptl_lock);

  PROCEND jmp$free_ajl_entry;
?? TITLE := ' JMP$FREE_AJL_WITH_LOCK ', EJECT ??

{  This procedure is identical to jmp$free_ajl_entry except the caller
{  already has tmv$ptl_lock set.

  PROCEDURE [XDCL] jmp$free_ajl_with_lock
    (    ijle_p: ^jmt$initiated_job_list_entry;
         caller: 0 .. 10(16));

    VAR
      ajl_ordinal: jmt$ajl_ordinal,
      in_use: integer,
      new_ajlo: jmt$ajl_ordinal;

    ajl_ordinal := ijle_p^.ajl_ordinal;
    in_use := jmv$ajl_p^ [ajl_ordinal].in_use;
    in_use := in_use - caller;
    IF in_use < 0 THEN
      mtp$error_stop ('JM - AJL.in_use has gone negative.');
    ELSEIF in_use > 0 THEN
      jmv$ajl_p^ [ajl_ordinal].in_use := in_use;
    ELSE   { in_use = 0 }
      IF (ajl_ordinal = 0) THEN
        mtp$error_stop ('JM - trying to free system job ajl');
      IFEND;
      ijle_p^.ajl_ordinal := jmc$null_ajl_ordinal;
      mtv$monitor_segment_table.st [ajl_ordinal + mtc$job_fixed_segment].ste.vl := osc$vl_invalid_entry;
      jmv$ajl_p^ [ajl_ordinal] := null_ajl;
      jmv$ajl_p^ [ajl_ordinal].time_freed := #FREE_RUNNING_CLOCK (0);

{ increment number of free ajl ordinals

      jmv$number_free_ajl_entries := jmv$number_free_ajl_entries + 1;

{ if scheduler is waiting for an ajlo to be freed, then notify scheduler

      IF (jmv$number_free_ajl_entries > 0) AND
            (jmv$job_sched_events_selected [jmc$needed_ajlo_available]) AND
            (NOT jmv$job_scheduler_event [jmc$needed_ajlo_available]) THEN
        jmp$set_sched_event_cond_ready (jmc$needed_ajlo_available, jmv$number_free_ajl_entries >
              jmv$nr_free_ajl_entries_set_evt);
      IFEND;

{ update jmv$start_ajl_search_ordinal if necessary

      IF ajl_ordinal < jmv$start_ajl_search_ordinal THEN
        jmv$start_ajl_search_ordinal := ajl_ordinal;
      IFEND;

{ update jmv$max_ajl_ordinal_in_use if necessary

      IF ajl_ordinal = jmv$max_ajl_ordinal_in_use THEN
        new_ajlo := ajl_ordinal;
        WHILE (jmv$ajl_p^ [new_ajlo].in_use = 0) DO
          new_ajlo := new_ajlo - 1;
        WHILEND;
        jmv$max_ajl_ordinal_in_use := new_ajlo;
      IFEND;
    IFEND;

  PROCEND jmp$free_ajl_with_lock;

MODEND jmm$ajl_manager;
