?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE: Program Control - Job Local Queues' ??
MODULE pmm$manage_local_queues;




{   PURPOSE:
{     The purpose of this module is to provide the interfaces to NOS/VE job
{     local queues (See: NOS/VE Program Management - Program Communications).
{     With the exception of pmm/p$status_queues_defined, this module contains
{     all knowledge of job local queues.

{   DESIGN:
{     The procedures contained in this module have an execution bracket of 1, 3
{     and a call bracket of 13.  All XDCL'd procedures are gated.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc osd$virtual_address
*copyc ose$heap_full_exceptions
*copyc oss$job_paged_literal
*copyc oss$task_private
*copyc ost$caller_identifier
*copyc ost$heap
*copyc ost$wait
*copyc pmd$local_queues
*copyc pme$local_queue_exceptions
*copyc pmt$queue_limits
*copyc pmt$queue_status
*copyc tmc$wait_times
?? POP ??
*copyc clp$validate_name
*copyc mmp$fetch_segment_attributes
*copyc mmp$verify_access
*copyc osp$clear_job_signature_lock
*copyc osp$disestablish_cond_handler
*copyc osp$establish_condition_handler
*copyc osp$initialize_sig_lock
*copyc osp$set_job_signature_lock
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc osp$test_sig_lock
*copyc osp$verify_system_privilege
*copyc pmp$continue_to_cause
*copyc pmp$exit
*copyc pmp$get_executing_task_gtid
*copyc pmp$get_task_id
*copyc pmp$long_term_wait
*copyc pmp$ready_task

*copyc osv$task_shared_heap
*copyc pmv$queue_definition_table
?? OLDTITLE ??
?? NEWTITLE := 'disconnect_task_from_queue', EJECT ??

{ PURPOSE:
{   Disconnect the task from the queue.
{
{ NOTE:
{   This procedure expects the connection lock to be set on entry and
{   on exit the connection lock is unlocked (cleared).

  PROCEDURE disconnect_task_from_queue
    (    queue: ^pmt$queue_specification;
     VAR status: ost$status);

?? NEWTITLE := 'delete_task_from_wait_list', EJECT ??

{ PURPOSE:
{   Delete the task from the wait list of the queue.

    PROCEDURE delete_task_from_wait_list
      (    queue: ^pmt$queue_specification);

      VAR
        next_waiting_task: ^pmt$queued_task,
        task: ost$global_task_id,
        waiting_task: ^^pmt$queued_task;

      pmp$get_executing_task_gtid (task);
      waiting_task := ^queue^.control.waiting_task_queue.dequeue;
      WHILE (waiting_task^ <> NIL) DO
        IF (waiting_task^^.task = task) THEN
          next_waiting_task := waiting_task^^.next_task;
          FREE waiting_task^ IN osv$task_shared_heap^;
          waiting_task^ := next_waiting_task;
          queue^.control.waiting_task_queue.number_waiting_tasks :=
                queue^.control.waiting_task_queue.number_waiting_tasks - 1;
          IF (waiting_task^ = NIL) THEN
            queue^.control.waiting_task_queue.enqueue := waiting_task;
          IFEND;
        ELSE
          waiting_task := ^waiting_task^^.next_task;
        IFEND;
      WHILEND;

    PROCEND delete_task_from_wait_list;
?? OLDTITLE, EJECT ??
?? EJECT ??

    VAR
      connected_task: ^^pmt$queue_connected_task,
      ignore_status: ost$status,
      local_task_id: pmt$task_id,
      next_connected_task: ^pmt$queue_connected_task;

    status.normal := TRUE;
    connected_task := ^queue^.control.connected_task_list;
    IF (connected_task^ = NIL) THEN
      osp$clear_job_signature_lock (queue^.control.connection_lock);
      osp$set_status_condition (pme$unknown_queue_identifier, status);
      RETURN;
    IFEND;

    pmp$get_task_id (local_task_id, ignore_status);
    WHILE (connected_task^ <> NIL) AND (connected_task^^.task <> local_task_id) DO
      connected_task := ^connected_task^^.next_connected_task;
    WHILEND;
    IF (connected_task^ <> NIL) THEN
      next_connected_task := connected_task^^.next_connected_task;
      FREE connected_task^ IN osv$task_shared_heap^;
      connected_task^ := next_connected_task;
      osp$set_job_signature_lock (queue^.control.waiting_task_lock);
      osp$clear_job_signature_lock (queue^.control.connection_lock);
      IF (queue^.control.waiting_task_queue.number_waiting_tasks > 0) THEN
        delete_task_from_wait_list (queue);
      IFEND;
      osp$clear_job_signature_lock (queue^.control.waiting_task_lock);
    ELSE
      osp$clear_job_signature_lock (queue^.control.connection_lock);
      osp$set_status_condition (pme$unknown_queue_identifier, status);
    IFEND;

  PROCEND disconnect_task_from_queue;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] pmp$await_nonempty_queue', EJECT ??
*copyc pmh$await_nonempty_queue

  PROCEDURE [XDCL] pmp$await_nonempty_queue
    (    qid: pmt$queue_connection;
         requestor_ring: ost$ring;
     VAR nonempty_queue: boolean;
     VAR status: ost$status);

?? NEWTITLE := 'set_await_message', EJECT ??

{ PURPOSE:
{   Add an entry to the queue's wait list for this task.

    PROCEDURE set_await_message
      (    queue: ^pmt$queue_specification;
       VAR status: ost$status);

      VAR
        ignore_status: ost$status,
        q_limits: pmt$queue_limits,
        waiting_task: ^pmt$queued_task,
        x_global_task_id: ost$global_task_id;

      status.normal := TRUE;
      pmp$get_executing_task_gtid (x_global_task_id);
      osp$set_job_signature_lock (queue^.control.waiting_task_lock);
      osp$clear_job_signature_lock (queue^.control.message_queue_lock);
      waiting_task := queue^.control.waiting_task_queue.dequeue;
      WHILE (waiting_task <> NIL) AND (waiting_task^.task = x_global_task_id) DO
        waiting_task := waiting_task^.next_task;
      WHILEND;
      IF (waiting_task = NIL) THEN
        pmp$get_queue_limits (q_limits, ignore_status);
        IF (queue^.control.waiting_task_queue.number_waiting_tasks < q_limits.maximum_messages) THEN
          ALLOCATE queue^.control.waiting_task_queue.enqueue^ IN osv$task_shared_heap^;
          queue^.control.waiting_task_queue.enqueue^^.next_task := NIL;
          queue^.control.waiting_task_queue.enqueue^^.task := x_global_task_id;
          queue^.control.waiting_task_queue.enqueue := ^queue^.control.waiting_task_queue.enqueue^^.next_task;
          queue^.control.waiting_task_queue.number_waiting_tasks :=
                queue^.control.waiting_task_queue.number_waiting_tasks + 1;
        ELSE
          osp$set_status_abnormal (pmc$program_management_id, pme$maximum_waiting_tasks, queue^.name, status);
        IFEND;
      IFEND;
      osp$clear_job_signature_lock (queue^.control.waiting_task_lock);

    PROCEND set_await_message;
?? OLDTITLE, EJECT ??

    VAR
      caller: ost$caller_identifier,
      connected_task: ^pmt$queue_connected_task,
      ignore_status: ost$status,
      local_task_id: pmt$task_id;

    status.normal := TRUE;
    IF (pmv$queue_definition_table.queues = NIL) OR (qid < 1) OR
          (qid > UPPERBOUND (pmv$queue_definition_table.queues^)) THEN
      osp$set_status_condition (pme$unknown_queue_identifier, status);
      RETURN;
    IFEND;

    osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
    IF (pmv$queue_definition_table.queues^ [qid].definition = NIL) THEN
      osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
      osp$set_status_condition (pme$unknown_queue_identifier, status);
      RETURN;
    IFEND;

    IF (requestor_ring > pmv$queue_definition_table.queues^ [qid].definition^.usage_bracket) THEN
      osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
      osp$set_status_abnormal (pmc$program_management_id, pme$usage_bracket_error,
            pmv$queue_definition_table.queues^ [qid].definition^.name, status);
      RETURN;
    IFEND;

    osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.connection_lock);
    osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
    connected_task := pmv$queue_definition_table.queues^ [qid].definition^.control.connected_task_list;
    pmp$get_task_id (local_task_id, ignore_status);
    WHILE (connected_task <> NIL) AND (connected_task^.task <> local_task_id) DO
      connected_task := connected_task^.next_connected_task;
    WHILEND;
    osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.
          connection_lock);
    IF (connected_task <> NIL) THEN
      osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.
            message_queue_lock);
      IF (pmv$queue_definition_table.queues^ [qid].definition^.control.message_queue.number_messages > 0) THEN
        nonempty_queue := TRUE;
        osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.
              message_queue_lock);
      ELSE
        set_await_message (pmv$queue_definition_table.queues^ [qid].definition, status);
      IFEND;
    ELSE
      osp$set_status_condition (pme$unknown_queue_identifier, status);
    IFEND;

  PROCEND pmp$await_nonempty_queue;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] pmp$connect_queue', EJECT ??
*copyc pmh$connect_queue

  PROCEDURE [XDCL, #GATE] pmp$connect_queue
    (    name: pmt$queue_name;
     VAR qid: pmt$queue_connection;
     VAR status: ost$status);

?? NEWTITLE := 'connect_task_to_queue', EJECT ??

{ PURPOSE:
{   Add task's id to the connected task list for the queue.

    PROCEDURE connect_task_to_queue
      (    queue: ^pmt$queue_specification;
       VAR connected {input, output} : boolean;
       VAR status: ost$status);

      VAR
        connected_task: ^pmt$queue_connected_task,
        ignore_status: ost$status,
        local_task_id: pmt$task_id,
        number_connected: pmt$connected_tasks_per_queue,
        queue_limits: pmt$queue_limits;

      status.normal := TRUE;
      pmp$get_task_id (local_task_id, ignore_status);
      connected_task := queue^.control.connected_task_list;
      number_connected := 1;
      WHILE connected_task <> NIL DO
        IF (connected_task^.task = local_task_id) THEN
          osp$set_status_abnormal (pmc$program_management_id, pme$task_already_connected, queue^.name,
                status);
          connected := TRUE;
          RETURN;
        IFEND;
        number_connected := number_connected + 1;
        connected_task := connected_task^.next_connected_task;
      WHILEND;

      pmp$get_queue_limits (queue_limits, ignore_status);
      IF (number_connected < queue_limits.maximum_connected) THEN
        ALLOCATE connected_task IN osv$task_shared_heap^;
        connected_task^.task := local_task_id;
        connected_task^.next_connected_task := queue^.control.connected_task_list;
        queue^.control.connected_task_list := connected_task;
        connected := TRUE;
      ELSE
        osp$set_status_abnormal (pmc$program_management_id, pme$maximum_tasks_connected, queue^.name, status);
      IFEND;

    PROCEND connect_task_to_queue;
?? OLDTITLE, EJECT ??

    VAR
      caller: ost$caller_identifier,
      connect_name: ost$name,
      connect_status: ost$status,
      connected: boolean,
      ignore_status: ost$status,
      q_index: pmt$queue_connection,
      valid_name: boolean;

    #CALLER_ID (caller);
    status.normal := TRUE;
    connect_status.normal := TRUE;
    connected := FALSE;
    qid := LOWERVALUE (pmt$queue_connection);

  /connect_queue/
    BEGIN
      IF (pmv$queue_definition_table.queues = NIL) THEN
        osp$set_status_abnormal (pmc$program_management_id, pme$unknown_queue_name, name, connect_status);
        EXIT /connect_queue/;
      IFEND;

      clp$validate_name (name, connect_name, valid_name);
      IF NOT valid_name THEN
        osp$set_status_abnormal (pmc$program_management_id, pme$incorrect_queue_name, name, connect_status);
        EXIT /connect_queue/;
      IFEND;

      FOR q_index := 1 TO UPPERBOUND (pmv$queue_definition_table.queues^) DO
        osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [q_index].removal_lock);
        IF (pmv$queue_definition_table.queues^ [q_index].definition <> NIL) AND
              (pmv$queue_definition_table.queues^ [q_index].definition^.name = connect_name) THEN
          IF (caller.ring <= pmv$queue_definition_table.queues^ [q_index].definition^.usage_bracket) THEN
            osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [q_index].definition^.control.
                  connection_lock);
            connect_task_to_queue (pmv$queue_definition_table.queues^ [q_index].definition, connected,
                  connect_status);
            osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [q_index].definition^.control.
                  connection_lock);
            IF NOT connect_status.normal THEN
              connect_status := connect_status;
            IFEND;
            IF connected THEN
              qid := q_index;
            IFEND;
          ELSE
            osp$set_status_abnormal (pmc$program_management_id, pme$usage_bracket_error, connect_name,
                  connect_status);
          IFEND;
          osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [q_index].removal_lock);
          EXIT /connect_queue/;
        IFEND;
        osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [q_index].removal_lock);
      FOREND;
      osp$set_status_abnormal (pmc$program_management_id, pme$unknown_queue_name, connect_name,
            connect_status);
    END /connect_queue/;
    IF NOT connect_status.normal THEN
      status := connect_status;
    IFEND;

  PROCEND pmp$connect_queue;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] pmp$define_queue', EJECT ??
*copyc pmh$define_queue

  PROCEDURE [XDCL, #GATE] pmp$define_queue
    (    name: pmt$queue_name;
         removal_bracket: ost$ring;
         usage_bracket: ost$ring;
     VAR status: ost$status);

    VAR
      define_status: ost$status;

?? NEWTITLE := 'initialize_local_queues', EJECT ??

{ PURPOSE:
{   Initialize the task's local queue table.

    PROCEDURE initialize_local_queues;

      VAR
        ignore_status: ost$status,
        q_index: pmt$queues_per_job,
        q_limits: pmt$queue_limits;

      pmp$get_queue_limits (q_limits, ignore_status);
      ALLOCATE pmv$queue_definition_table.queues: [1 .. q_limits.maximum_queues] IN osv$task_shared_heap^;
      FOR q_index := 1 TO UPPERBOUND (pmv$queue_definition_table.queues^) DO
        pmv$queue_definition_table.queues^ [q_index].definition := NIL;
        osp$initialize_sig_lock (pmv$queue_definition_table.queues^ [q_index].removal_lock);
      FOREND;

    PROCEND initialize_local_queues;
?? OLDTITLE, EJECT ??

    VAR
      caller: ost$caller_identifier,
      define_name: ost$name,
      free_entry: boolean,
      free_index: pmt$queues_per_job,
      ignore_status: ost$status,
      q_index: 0 .. (pmc$max_queues_per_job + 1),
      queue_definition: ^pmt$queue_specification,
      unlocked: ost$signature_lock,
      valid_name: boolean;

    #CALLER_ID (caller);
    status.normal := TRUE;
    define_status.normal := TRUE;

    clp$validate_name (name, define_name, valid_name);
    IF NOT valid_name THEN
      osp$set_status_abnormal (pmc$program_management_id, pme$incorrect_queue_name, name, status);
    ELSEIF (usage_bracket < removal_bracket) THEN
      osp$set_status_abnormal (pmc$program_management_id, pme$usage_lt_removal_bracket, define_name, status);
    ELSEIF (caller.ring > removal_bracket) THEN
      osp$set_status_abnormal (pmc$program_management_id, pme$caller_gt_removal_bracket, define_name, status);
    ELSE { Parameters are valid
      ALLOCATE queue_definition IN osv$task_shared_heap^;
      osp$initialize_sig_lock (unlocked);
      queue_definition^.name := define_name;
      queue_definition^.removal_bracket := removal_bracket;
      queue_definition^.usage_bracket := usage_bracket;
      queue_definition^.control.connection_lock := unlocked;
      queue_definition^.control.connected_task_list := NIL;
      queue_definition^.control.message_queue_lock := unlocked;
      queue_definition^.control.message_queue.number_messages := 0;
      queue_definition^.control.message_queue.enqueue := ^queue_definition^.control.message_queue.dequeue;
      queue_definition^.control.message_queue.dequeue := NIL;
      queue_definition^.control.waiting_task_lock := unlocked;
      queue_definition^.control.waiting_task_queue.number_waiting_tasks := 0;
      queue_definition^.control.waiting_task_queue.enqueue :=
            ^queue_definition^.control.waiting_task_queue.dequeue;
      queue_definition^.control.waiting_task_queue.dequeue := NIL;
      osp$set_job_signature_lock (pmv$queue_definition_table.definition_lock);
      IF (pmv$queue_definition_table.queues = NIL) THEN
        initialize_local_queues;
      IFEND;
      free_entry := FALSE;

    /preset_table/
      FOR q_index := 1 TO UPPERBOUND (pmv$queue_definition_table.queues^) DO
        IF (pmv$queue_definition_table.queues^ [q_index].definition <> NIL) THEN
          IF (pmv$queue_definition_table.queues^ [q_index].definition^.name = define_name) THEN
            osp$set_status_abnormal (pmc$program_management_id, pme$queue_already_defined, define_name,
                  define_status);
            EXIT /preset_table/;
          IFEND;
        ELSEIF NOT free_entry THEN
          free_index := q_index;
          free_entry := TRUE;
        IFEND;
      FOREND /preset_table/;
      IF free_entry THEN
        osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [free_index].removal_lock);
        pmv$queue_definition_table.queues^ [free_index].definition := queue_definition;
        osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [free_index].removal_lock);
      ELSE
        FREE queue_definition IN osv$task_shared_heap^;
        IF define_status.normal THEN
          osp$set_status_abnormal (pmc$program_management_id, pme$maximum_queues_defined, define_name,
                define_status);
        IFEND;
      IFEND;
      osp$clear_job_signature_lock (pmv$queue_definition_table.definition_lock);
      IF NOT define_status.normal THEN
        status := define_status;
      IFEND;
    IFEND;

  PROCEND pmp$define_queue;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] pmp$disconnect_queue', EJECT ??
*copyc pmh$disconnect_queue

  PROCEDURE [XDCL, #GATE] pmp$disconnect_queue
    (    qid: pmt$queue_connection;
     VAR status: ost$status);

    VAR
      caller: ost$caller_identifier,
      disconnect_status: ost$status,
      ignore_status: ost$status;

    #CALLER_ID (caller);
    status.normal := TRUE;
    disconnect_status.normal := TRUE;
    IF (pmv$queue_definition_table.queues = NIL) OR (qid < 1) OR
          (qid > UPPERBOUND (pmv$queue_definition_table.queues^)) THEN
      osp$set_status_condition (pme$unknown_queue_identifier, status);
    ELSE
      osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
      IF (pmv$queue_definition_table.queues^ [qid].definition = NIL) THEN
        osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
        osp$set_status_condition (pme$unknown_queue_identifier, disconnect_status);
      ELSEIF (caller.ring > pmv$queue_definition_table.queues^ [qid].definition^.usage_bracket) THEN
        osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
        osp$set_status_condition (pme$usage_bracket_error, disconnect_status);
      ELSE { QID is valid.
        osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.
              connection_lock);
        osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
        disconnect_task_from_queue (pmv$queue_definition_table.queues^ [qid].definition, disconnect_status);
      IFEND;
      IF NOT disconnect_status.normal THEN
        status := disconnect_status;
      IFEND;
    IFEND;

  PROCEND pmp$disconnect_queue;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] pmp$disconnect_task_from_queues', EJECT ??
*copyc pmh$disconnect_task_from_queues

  PROCEDURE [XDCL] pmp$disconnect_task_from_queues;

    VAR
      qid: pmt$queue_connection,
      ignore_status: ost$status;

    IF (pmv$queue_definition_table.queues <> NIL) THEN
      FOR qid := 1 TO UPPERBOUND (pmv$queue_definition_table.queues^) DO
        osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
        IF (pmv$queue_definition_table.queues^ [qid].definition <> NIL) THEN
          osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.
                connection_lock);
          osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
          disconnect_task_from_queue (pmv$queue_definition_table.queues^ [qid].definition, ignore_status);
        ELSE
          osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
        IFEND;

      FOREND;
    IFEND;

  PROCEND pmp$disconnect_task_from_queues;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] pmp$get_queue_limits', EJECT ??
*copyc pmh$get_queue_limits

  PROCEDURE [XDCL, #GATE] pmp$get_queue_limits
    (VAR queue_limits: pmt$queue_limits;
     VAR status: ost$status);

{   **** the contents of this procedure are temporary until it is defined how
{   **** maximums are determined.

    status.normal := TRUE;
    queue_limits.maximum_queues := UPPERVALUE (pmt$queues_per_job);
    queue_limits.maximum_connected := UPPERVALUE (pmt$connected_tasks_per_queue);
    queue_limits.maximum_messages := UPPERVALUE (pmt$messages_per_queue);

  PROCEND pmp$get_queue_limits;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] pmp$receive_queue_message', EJECT ??
*copyc pmh$receive_queue_message

  PROCEDURE [XDCL, #GATE] pmp$receive_queue_message
    (    qid: pmt$queue_connection;
         wait: ost$wait;
     VAR message: pmt$message;
     VAR complete: boolean;
     VAR status: ost$status);

?? NEWTITLE := 'rqm_condition_handler', EJECT ??

{ PURPOSE:
{   Handle the interactive break, job resource, block exit, and job
{   recovery conditions.

    PROCEDURE rqm_condition_handler
      (    condition: pmt$condition;
           condition_descriptor: ^pmt$condition_information;
           stack_frame_save_area_p: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      IF (condition.selector = jmc$job_resource_condition) OR
            (condition.selector = ifc$interactive_condition) OR
            ((condition.selector = pmc$user_defined_condition) AND
            (condition.user_condition_name = osc$job_recovery_condition_name)) THEN
        pmp$continue_to_cause (pmc$execute_standard_procedure, handler_status);
        EXIT pmp$receive_queue_message;
      ELSEIF (condition.selector = pmc$block_exit_processing) THEN
        pmp$remove_await_nonempty_queue (qid);
      IFEND;

    PROCEND rqm_condition_handler;
?? OLDTITLE ??
?? NEWTITLE := 'dequeue_message', EJECT ??

{ PURPOSE:
{   Remove a message from the queue.

    PROCEDURE dequeue_message
      (    queue: ^pmt$queue_specification;
           wait: ost$wait;
       VAR message: pmt$message;
       VAR dequeue_status: ost$status);

?? NEWTITLE := 'wait_for_message', EJECT ??

{ PURPOSE:
{   Wait for a message.

      PROCEDURE wait_for_message
        (    queue: ^pmt$queue_specification;
         VAR status: ost$status);

        VAR
          ignore_status: ost$status,
          interactive_descriptor: ^pmt$established_handler,
          q_limits: pmt$queue_limits,
          task_waiting: boolean,
          waiting_task: ^pmt$queued_task,
          x_global_task_id: ost$global_task_id;

        status.normal := TRUE;
        pmp$get_executing_task_gtid (x_global_task_id);
        pmp$get_queue_limits (q_limits, ignore_status);
        osp$set_job_signature_lock (queue^.control.waiting_task_lock);
        osp$clear_job_signature_lock (queue^.control.message_queue_lock);
        IF (queue^.control.waiting_task_queue.number_waiting_tasks < q_limits.maximum_messages) THEN
          ALLOCATE queue^.control.waiting_task_queue.enqueue^ IN osv$task_shared_heap^;
          queue^.control.waiting_task_queue.enqueue^^.next_task := NIL;
          queue^.control.waiting_task_queue.enqueue^^.task := x_global_task_id;
          queue^.control.waiting_task_queue.enqueue := ^queue^.control.waiting_task_queue.enqueue^^.next_task;
          queue^.control.waiting_task_queue.number_waiting_tasks :=
                queue^.control.waiting_task_queue.number_waiting_tasks + 1;
          osp$clear_job_signature_lock (queue^.control.waiting_task_lock);
          task_waiting := TRUE;
          WHILE task_waiting DO
            osp$set_job_signature_lock (queue^.control.waiting_task_lock);
            IF (queue^.control.waiting_task_queue.number_waiting_tasks > 0) THEN
              waiting_task := queue^.control.waiting_task_queue.dequeue;
              WHILE (waiting_task <> NIL) AND (waiting_task^.task <> x_global_task_id) DO
                waiting_task := waiting_task^.next_task;
              WHILEND;
              task_waiting := (waiting_task <> NIL);
            ELSE
              task_waiting := FALSE;
            IFEND;
            osp$clear_job_signature_lock (queue^.control.waiting_task_lock);
            IF task_waiting THEN
              osp$establish_condition_handler (^rqm_condition_handler, TRUE);
              pmp$long_term_wait (tmc$infinite_wait, tmc$infinite_wait);
              osp$disestablish_cond_handler;
            IFEND;
          WHILEND;
        ELSE
          osp$clear_job_signature_lock (queue^.control.waiting_task_lock);
          osp$set_status_abnormal (pmc$program_management_id, pme$maximum_waiting_tasks, queue^.name, status);
        IFEND;

      PROCEND wait_for_message;
?? OLDTITLE, EJECT ??

      VAR
        dequeued: ^pmt$queued_message,
        dequeued_message: pmt$queued_message,
        ignore_status: ost$status,
        message_dequeued: boolean,
        queued_message: ^^pmt$queued_message;


{*
{*    VAR
{*      s: 0 .. (pmc$max_segs_per_message + 1),
{*      queued_segments: ^array [*] OF mmt$queued_segment,
{*      d: pmt$segments_per_message,
{*      pva: ^cell;

      dequeue_status.normal := TRUE;
      message_dequeued := FALSE;
      WHILE NOT message_dequeued AND dequeue_status.normal DO
        osp$set_job_signature_lock (queue^.control.message_queue_lock);
        IF (queue^.control.message_queue.number_messages > 0) THEN
          queued_message := ^queue^.control.message_queue.dequeue;
          dequeued := queued_message^;
          dequeued_message := queued_message^^;
          queued_message^ := queued_message^^.dequeue_thread;
          IF (queued_message^ = NIL) THEN
            queue^.control.message_queue.enqueue := queued_message;
          IFEND;
          queue^.control.message_queue.number_messages := queue^.control.message_queue.number_messages - 1;
          osp$clear_job_signature_lock (queue^.control.message_queue_lock);
          CASE dequeued_message.message.contents OF
          = pmc$message_value =
            message := dequeued_message.message;
          = pmc$passed_segments, pmc$shared_segments =

{ **** R1 restriction - passed/shared segments are not supported by NOS/VE
{ Release 1.  The following documents the algorithm for dequeing queued
{ segments.  Note:  a field (segments) will be added to pmt$queued_message
{ which will point to an array of queued segment images that have been
{ constructed on behalf of pmp$send_to_queue by segment management (MMP$).
{ Segments contained in message_queue.segments^ are added to the requestor's
{ address space, via MMP$ADD, assuming of course that the requestor can access
{ all queued segments.
{
{ The MMP$ADD interface is not yet defined, therefore, some manipulation of
{ segment pointer types (e.g., message_heap_pointer) may be required when
{ constructing dequeued_message.segments.  The MMP$ADD request will return
{ abnormal status for a segment which unaccessible to the requestor - its
{ dequeue_message's responsibility to delete any segments already added to the
{ requestor's address space and to delete any remaining segments in
{ message_queue.segments^ when an unaccessible segment is detected.

{*          queued_segments := queue^.control.message_queue.segments;
{*          s := 1;
{*          WHILE (s <= UPPERBOUND (queued_segments^) AND dequeue_status.normal DO
{*            mmp$add_queued_segment (queued_segment^[s], dequeued_message.segments[s],
{*              dequeue_status);
{*            IF dequeue_status.normal THEN
{*              s := s + 1;
{*            ELSE
{*              {delete segments already added to requestor's address space
{*             FOR d := 1 TO (s - 1) DO
{*               CASE dequeued_message.segments[d].segment_type OF
{*                 =pmc$message_pointer=
{*                   mmp$delete_segment (dequeued_message.segments[d].segment_pointer,
{*                     ignore_status);
{*                 =pmc$message_heap_pointer=
{*                   pva := #LOC (dequeued_message.segments[d].segment_heap_pointer^);
{*                   mmp$delete_segment (pva, ignore_status);
{*                 =pmc$message_sequence_pointer=
{*                   pva := #LOC (dequeued_message.segments[d].segment_sequence_pointer^);
{*                   mmp$delete_segment (pva, ignore_status);
{*               CASEND;
{*             FOREND;
{*               {delete queued segments not yet added to requestor's address space
{*             FOR d := s to UPPERBOUND (queued_segments^) DO
{*               mmp$delete_queued_segment (queued_segment^[d], ignore_status);
{*             FOREND;
{*              osp$set_status_abnormal (pmc$program_management_id,
{*                pme$error_segment_privilege, '', dequeue_status);
{*           IFEND;
{*         WHILEND;
{*         FREE queued_segments IN osv$task_shared_heap^;

            ;
          CASEND;
          FREE dequeued IN osv$task_shared_heap^;
          message_dequeued := TRUE;
        ELSEIF (wait = osc$wait) THEN
          wait_for_message (queue, dequeue_status);
        ELSE
          osp$clear_job_signature_lock (queue^.control.message_queue_lock);
          message.contents := pmc$no_message;
          message_dequeued := TRUE;
        IFEND;
      WHILEND;

    PROCEND dequeue_message;
?? OLDTITLE, EJECT ??

    VAR
      caller: ost$caller_identifier,
      connected_task: ^pmt$queue_connected_task,
      ignore_status: ost$status,
      local_task_id: pmt$task_id,
      receive_status: ost$status;

    #CALLER_ID (caller);

    status.normal := TRUE;
    osp$verify_system_privilege;
    complete := FALSE;
    message.contents := pmc$no_message;
    receive_status.normal := TRUE;
    IF (pmv$queue_definition_table.queues = NIL) OR (qid < 1) OR
          (qid > UPPERBOUND (pmv$queue_definition_table.queues^)) THEN
      osp$set_status_condition (pme$unknown_queue_identifier, status);
      RETURN;
    IFEND;

    osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
    IF (pmv$queue_definition_table.queues^ [qid].definition = NIL) THEN
      osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
      osp$set_status_condition (pme$unknown_queue_identifier, status);
      RETURN;
    IFEND;

    IF (caller.ring > pmv$queue_definition_table.queues^ [qid].definition^.usage_bracket) THEN
      osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
      osp$set_status_abnormal (pmc$program_management_id, pme$usage_bracket_error,
            pmv$queue_definition_table.queues^ [qid].definition^.name, status);
      RETURN;
    IFEND;

    osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.connection_lock);
    osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
    connected_task := pmv$queue_definition_table.queues^ [qid].definition^.control.connected_task_list;
    pmp$get_task_id (local_task_id, ignore_status);
    WHILE (connected_task <> NIL) AND (connected_task^.task <> local_task_id) DO
      connected_task := connected_task^.next_connected_task;
    WHILEND;
    osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.
          connection_lock);
    IF (connected_task = NIL) THEN
      osp$set_status_condition (pme$unknown_queue_identifier, status);
    ELSE
      dequeue_message (pmv$queue_definition_table.queues^ [qid].definition, wait, message, receive_status);
      complete := TRUE;
      IF NOT receive_status.normal THEN
        status := receive_status;
      IFEND;
    IFEND;

  PROCEND pmp$receive_queue_message;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] pmp$remove_await_nonempty_queue', EJECT ??
*copyc pmh$remove_await_nonempty_queue

  PROCEDURE [XDCL] pmp$remove_await_nonempty_queue
    (    qid: pmt$queue_connection);

    VAR
      ignore_status: ost$status,
      next_waiting_task: ^pmt$queued_task,
      queue: ^pmt$queue_specification,
      task: ost$global_task_id,
      waiting_task: ^^pmt$queued_task;

    queue := pmv$queue_definition_table.queues^ [qid].definition;
    waiting_task := ^queue^.control.waiting_task_queue.dequeue;
    pmp$get_executing_task_gtid (task);
    osp$set_job_signature_lock (queue^.control.waiting_task_lock);
    WHILE (waiting_task^ <> NIL) DO
      IF (waiting_task^^.task = task) THEN
        next_waiting_task := waiting_task^^.next_task;
        FREE waiting_task^ IN osv$task_shared_heap^;
        waiting_task^ := next_waiting_task;
        queue^.control.waiting_task_queue.number_waiting_tasks :=
              queue^.control.waiting_task_queue.number_waiting_tasks - 1;
        IF (waiting_task^ = NIL) THEN
          queue^.control.waiting_task_queue.enqueue := waiting_task;
        IFEND;
      ELSE
        waiting_task := ^waiting_task^^.next_task;
      IFEND;
    WHILEND;
    osp$clear_job_signature_lock (queue^.control.waiting_task_lock);

  PROCEND pmp$remove_await_nonempty_queue;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] pmp$remove_queue', EJECT ??
*copyc pmh$remove_queue

  PROCEDURE [XDCL, #GATE] pmp$remove_queue
    (    name: pmt$queue_name;
     VAR status: ost$status);

    VAR
      caller: ost$caller_identifier,
      ignore_status: ost$status,
      lock_status: ost$signature_lock_status,
      q_index: pmt$queues_per_job,
      remove_name: ost$name,
      remove_status: ost$status,
      valid_name: boolean;

    #CALLER_ID (caller);
    status.normal := TRUE;
    remove_status.normal := TRUE;

  /remove_queue/
    BEGIN
      IF (pmv$queue_definition_table.queues = NIL) THEN
        osp$set_status_abnormal (pmc$program_management_id, pme$unknown_queue_name, remove_name,
              remove_status);
        EXIT /remove_queue/;
      IFEND;

      clp$validate_name (name, remove_name, valid_name);
      IF NOT valid_name THEN
        osp$set_status_abnormal (pmc$program_management_id, pme$incorrect_queue_name, name, remove_status);
        EXIT /remove_queue/;
      IFEND;

      FOR q_index := 1 TO UPPERBOUND (pmv$queue_definition_table.queues^) DO
        osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [q_index].removal_lock);
        IF (pmv$queue_definition_table.queues^ [q_index].definition <> NIL) AND
              (pmv$queue_definition_table.queues^ [q_index].definition^.name = remove_name) THEN
          IF (caller.ring > pmv$queue_definition_table.queues^ [q_index].definition^.removal_bracket) THEN
            osp$set_status_abnormal (pmc$program_management_id, pme$removal_bracket_error, remove_name,
                  remove_status);
          ELSEIF (pmv$queue_definition_table.queues^ [q_index].definition^.control.connected_task_list <>
                NIL) THEN
            osp$set_status_abnormal (pmc$program_management_id, pme$tasks_connected_to_queue, remove_name,
                  remove_status);
          ELSE
            osp$test_sig_lock (pmv$queue_definition_table.queues^ [q_index].definition^.control.
                  connection_lock, lock_status);
            IF lock_status <> osc$sls_not_locked THEN
              osp$set_status_abnormal (pmc$program_management_id, pme$tasks_connected_to_queue, remove_name,
                    remove_status);
            ELSEIF (pmv$queue_definition_table.queues^ [q_index].definition^.control.message_queue.enqueue =
                  ^pmv$queue_definition_table.queues^ [q_index].definition^.control.message_queue.dequeue)
                  THEN
              FREE pmv$queue_definition_table.queues^ [q_index].definition IN osv$task_shared_heap^;
            ELSE
              osp$set_status_abnormal (pmc$program_management_id, pme$nonempty_queue, remove_name,
                    remove_status);
            IFEND;
          IFEND;
          osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [q_index].removal_lock);
          EXIT /remove_queue/;
        IFEND;
        osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [q_index].removal_lock);
      FOREND;
      osp$set_status_abnormal (pmc$program_management_id, pme$unknown_queue_name, remove_name, remove_status);
    END /remove_queue/;
    IF NOT remove_status.normal THEN
      status := remove_status;
    IFEND;

  PROCEND pmp$remove_queue;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] pmp$send_to_queue', EJECT ??
*copyc pmh$send_to_queue

  PROCEDURE [XDCL, #GATE] pmp$send_to_queue
    (    qid: pmt$queue_connection;
         message: pmt$message;
     VAR status: ost$status);

?? NEWTITLE := 'validate_message', EJECT ??

{ PURPOSE:
{   Ensure a legitimate message.

    PROCEDURE validate_message
      (    caller: ost$caller_identifier;
           message: pmt$message;
       VAR status: ost$status);

{ validate does not return if an error is detected - status is set to reflect
{ the detected error and a nonlocal exit performed.

      CONST
        hardware = 1,
        software = 2;

      VAR
        pva: ^cell,
        s: pmt$segments_per_message,
        segment_attributes: [STATIC, oss$task_private] array [hardware .. software] of
              mmt$attribute_descriptor := [[mmc$kw_hardware_attributes, $mmt$hardware_attribute_set []],
              [mmc$kw_software_attributes, $mmt$software_attribute_set []]],
        access: mmt$va_access_mode,
        ignore_status: ost$status;

      status.normal := TRUE;
      CASE message.contents OF
      = pmc$message_value =
        ;
      = pmc$passed_segments, pmc$shared_segments =

{ **** R1 restricition - the following code prevents a user from passing or
{ sharing segments in NOS/VE Release 1.  The statments upto the next comment
{ maybe deleted when the restriction is removed.

        osp$set_status_condition (pme$pass_share_prohibited, status);
        RETURN;

{ **** end R1 restriction.

{       IF (message.number_of_segments >= LOWERVALUE (pmt$segments_per_message)) AND
{             (message.number_of_segments <= UPPERVALUE (pmt$segments_per_message)) THEN
{         FOR s := 1 TO message.number_of_segments DO
{           CASE message.segments [s].kind OF
{           = pmc$message_pointer =
{             pva := ^message.segments [s].pointer;
{             IF (message.contents = pmc$passed_segments) THEN
{               access := mmc$va_read;
{             ELSE
{               access := mmc$va_write;
{             IFEND;
{             IF NOT mmp$verify_access (caller, pva, access) THEN
{               osp$set_status_condition (pme$incorrect_segment_message, status);
{               RETURN;
{             IFEND;
{           = pmc$message_heap_pointer =
{             pva := ^message.segments [s].heap_pointer;
{             IF (message.contents = pmc$passed_segments) THEN
{               access := mmc$va_read;
{             ELSE
{               access := mmc$va_write;
{             IFEND;
{             IF NOT mmp$verify_access (caller, pva, access) THEN
{               osp$set_status_condition (pme$error_segment_privilege, status);
{               RETURN;
{             IFEND;
{           = pmc$message_sequence_pointer =
{             pva := ^message.segments [s].sequence_pointer;
{             IF (message.contents = pmc$passed_segments) THEN
{               access := mmc$va_read;
{             ELSE
{               access := mmc$va_write;
{             IFEND;
{             IF NOT mmp$verify_access (caller, pva, access) THEN
{               osp$set_status_condition (pme$error_segment_privilege, status);
{               RETURN;
{             IFEND;
{           ELSE
{             osp$set_status_condition (pme$incorrect_queued_seg_type, status);
{             RETURN;
{           CASEND;
{           mmp$fetch_segment_attributes (pva, segment_attributes, ignore_status);
{           IF (mmc$ha_binding IN segment_attributes [hardware].hardware_attri_set) OR
{                 (mmc$ha_execute IN segment_attributes [hardware].hardware_attri_set) THEN
{             osp$set_status_condition (pme$incorrect_segment_message, status);
{             RETURN;
{           IFEND;
{           IF (mmc$sa_stack IN segment_attributes [software].software_attri_set) THEN
{             osp$set_status_condition (pme$error_segment_privilege, status);
{             RETURN;
{           IFEND;
{         FOREND;
{       ELSE
{         osp$set_status_condition (pme$error_number_of_segments, status);
{         RETURN;
{       IFEND;

      ELSE
        osp$set_status_condition (pme$incorrect_message_type, status);
        RETURN;
      CASEND;

    PROCEND validate_message;
?? OLDTITLE ??
?? NEWTITLE := 'enqueue_the_message', EJECT ??

{ PURPOSE:
{   Add the message to the queue's message list.

    PROCEDURE enqueue_the_message
      (    message: ^pmt$message;
           queue: ^pmt$queue_specification;
           caller_ring: ost$ring;
       VAR enqueue_status: ost$status);

      VAR
        enqueue_message: ^pmt$queued_message,
        ignore_status: ost$status,
        limits: pmt$queue_limits;

{*      VAR
{*        s: pmt$segments_per_message,
{*        pva: ^cell;

      enqueue_status.normal := TRUE;
      pmp$get_queue_limits (limits, ignore_status);
      osp$set_job_signature_lock (queue^.control.message_queue_lock);
      IF (queue^.control.message_queue.number_messages < limits.maximum_messages) THEN
        ALLOCATE enqueue_message IN osv$task_shared_heap^;
        enqueue_message^.dequeue_thread := NIL;
        enqueue_message^.message := message^;
        pmp$get_task_id (enqueue_message^.message.sender_id, ignore_status);
        enqueue_message^.message.sender_ring := caller_ring;
        CASE message^.contents OF
        = pmc$message_value =

{*          queue^.control.message_queue.segments := NIL;

          ;
        = pmc$passed_segments =

{ **** R1 restriction - passed/shared segments are not supported by NOS/VE
{ Release 1.  The following documents the alogorithm for enqueueing queued
{ segments.  Note:  a field (segments) will be added to pmt$queued_message
{ which will point to an array of queued segments constructed by
{ enqueue_message via segment management (MMP$).  pmp$receive_from_queue on
{ behalf of its requestor will dequeue the segments adding them to the
{ requestor's address space.
{
{ The MMP$ENQUEUE_SEGMENT interface is not yet defined, therefore, some
{ manipulation of a segment pointer (e.g., message_heap_pointer) prior to
{ calling MMP$ENQUEUE_SEGMENT may be required.

{         ALLOCATE enqueue_message^.segments: [1 .. message^.number_of_segments] IN
{            osv$task_shared_heap^;
{         FOR s := 1 TO message^.number_of_segments DO
{           mmp$enqueue_segment (message.segments[s], enqueue_message^.segments^[s],
{                 enqueue_status);
{           CASE message^.segments[s].segment_type OF
{             =pmc$message_pointer=
{               pva := #LOC (message^.segments[s].segment_pointer^);
{             =pmc$message_pointer=
{               pva := #LOC (message^.segments[s].segment_heap_pointer^);
{             =pmc$message_sequence_pointer=
{               pva := #LOC (message^.segments[s].segment_sequence_pointer^);
{           CASEND;
{           mmp$delete_segment (pva, ignore_status);
{         FOREND;

          ;
        = pmc$shared_segments =

{ SEE **** R1 restriction above.

{         ALLOCATE enqueue_message^.segments: [1 .. message^.number_of_segments] IN
{           osv$task_shared_heap^;
{         IF (enqueue_message^.segments <> NIL) THEN
{           FOR s := 1 TO message^.number_of_segments DO
{             mmp$enqueue_segment (message.segments[s], enqueue_message^.segments[s],
{               enqueue_status);
{           FOREND;
{         ELSE
{           FREE enqueue_message IN osv$task_shared_heap^;
{           osp$set_status_abnormal (pmc$program_management_id,
{             ose$task_shared_full, 'pmp$send_to_queue', enqueue_status);
{           pmp$exit (status);
{         IFEND;

          ;
        CASEND;
        IF enqueue_status.normal THEN
          queue^.control.message_queue.enqueue^ := enqueue_message;
          queue^.control.message_queue.enqueue := ^enqueue_message^.dequeue_thread;
          queue^.control.message_queue.number_messages := queue^.control.message_queue.number_messages + 1;
        IFEND;
      ELSE
        osp$set_status_abnormal (pmc$program_management_id, pme$maximum_queued_messages, queue^.name,
              enqueue_status);
      IFEND;
      osp$clear_job_signature_lock (queue^.control.message_queue_lock);

    PROCEND enqueue_the_message;
?? OLDTITLE ??
?? NEWTITLE := 'ready_waiting_tasks', EJECT ??

{ PURPOSE:
{   Ready tasks waiting for a message on this queue.

    PROCEDURE ready_waiting_tasks
      (    queue: ^pmt$queue_specification);

      VAR
        ignore_status: ost$status,
        next_waiting_task: ^pmt$queued_task,
        waiting_task: ^^pmt$queued_task;

      osp$set_job_signature_lock (queue^.control.waiting_task_lock);
      waiting_task := ^queue^.control.waiting_task_queue.dequeue;
      WHILE waiting_task^ <> NIL DO
        pmp$ready_task (waiting_task^^.task, ignore_status);
        next_waiting_task := waiting_task^^.next_task;
        FREE waiting_task^ IN osv$task_shared_heap^;
        waiting_task^ := next_waiting_task;
        IF (waiting_task^ = NIL) THEN
          queue^.control.waiting_task_queue.enqueue := waiting_task;
        IFEND;
      WHILEND;
      queue^.control.waiting_task_queue.number_waiting_tasks := 0;
      osp$clear_job_signature_lock (queue^.control.waiting_task_lock);

    PROCEND ready_waiting_tasks;
?? OLDTITLE, EJECT ??

    VAR
      local_task_id: pmt$task_id,
      connected_task: ^pmt$queue_connected_task,
      connected: boolean,
      caller: ost$caller_identifier,
      ignore_status: ost$status,
      send_status: ost$status;

    #CALLER_ID (caller);
    status.normal := TRUE;
    send_status.normal := TRUE;

  /send_to_queue/
    BEGIN
      validate_message (caller, message, send_status);
      IF NOT send_status.normal THEN
        EXIT /send_to_queue/;
      IFEND;

{validate_message does not return if an error in message is detected

      IF (pmv$queue_definition_table.queues = NIL) OR (qid < 1) OR
            (qid > UPPERBOUND (pmv$queue_definition_table.queues^)) THEN
        osp$set_status_condition (pme$unknown_queue_identifier, send_status);
        EXIT /send_to_queue/;
      IFEND;

      osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
      IF (pmv$queue_definition_table.queues^ [qid].definition = NIL) THEN
        osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
        osp$set_status_condition (pme$unknown_queue_identifier, send_status);
        EXIT /send_to_queue/;
      IFEND;

      IF (caller.ring > pmv$queue_definition_table.queues^ [qid].definition^.usage_bracket) THEN
        osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
        osp$set_status_abnormal (pmc$program_management_id, pme$usage_bracket_error,
              pmv$queue_definition_table.queues^ [qid].definition^.name, send_status);
        EXIT /send_to_queue/;
      IFEND;

      osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.
            connection_lock);
      osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
      connected_task := pmv$queue_definition_table.queues^ [qid].definition^.control.connected_task_list;
      IF (connected_task = NIL) THEN
        osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.
              connection_lock);
        osp$set_status_condition (pme$unknown_queue_identifier, send_status);
        EXIT /send_to_queue/;
      IFEND;

      pmp$get_task_id (local_task_id, ignore_status);
      connected := FALSE;
      WHILE (connected_task <> NIL) AND (connected_task^.task <> local_task_id) DO
        connected_task := connected_task^.next_connected_task;
      WHILEND;
      osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.
            connection_lock);
      IF (connected_task = NIL) THEN
        osp$set_status_condition (pme$unknown_queue_identifier, send_status);
        EXIT /send_to_queue/;
      IFEND;
      connected := TRUE;
      enqueue_the_message (^message, pmv$queue_definition_table.queues^ [qid].definition, caller.ring,
            send_status);
      IF send_status.normal THEN
        ready_waiting_tasks (pmv$queue_definition_table.queues^ [qid].definition);
      IFEND;
    END /send_to_queue/;
    IF NOT send_status.normal THEN
      status := send_status;
    IFEND;

  PROCEND pmp$send_to_queue;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] pmp$status_queue', EJECT ??
*copyc pmh$status_queue

  PROCEDURE [XDCL, #GATE] pmp$status_queue
    (    qid: pmt$queue_connection;
     VAR counts: pmt$queue_status;
     VAR status: ost$status);

    VAR
      caller: ost$caller_identifier,
      connected: boolean,
      connected_task: ^pmt$queue_connected_task,
      ignore_status: ost$status,
      local_task_id: pmt$task_id,
      message: ^pmt$queued_message,
      q_status: ost$status;

    #CALLER_ID (caller);
    status.normal := TRUE;
    counts.connections := 0;
    q_status.normal := TRUE;

  /status_queue/
    BEGIN
      IF (pmv$queue_definition_table.queues = NIL) OR (qid < 1) OR
            (qid > UPPERBOUND (pmv$queue_definition_table.queues^)) THEN
        osp$set_status_condition (pme$unknown_queue_identifier, q_status);
        EXIT /status_queue/;
      IFEND;

      osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
      IF (pmv$queue_definition_table.queues^ [qid].definition = NIL) THEN
        osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
        osp$set_status_condition (pme$unknown_queue_identifier, q_status);
        EXIT /status_queue/;
      IFEND;

      IF (caller.ring > pmv$queue_definition_table.queues^ [qid].definition^.usage_bracket) THEN
        osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
        osp$set_status_abnormal (pmc$program_management_id, pme$usage_bracket_error,
              pmv$queue_definition_table.queues^ [qid].definition^.name, q_status);
        EXIT /status_queue/;
      IFEND;

      osp$set_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.
            connection_lock);
      osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].removal_lock);
      connected_task := pmv$queue_definition_table.queues^ [qid].definition^.control.connected_task_list;

      pmp$get_task_id (local_task_id, ignore_status);
      connected := FALSE;
      counts.connections := 0;
      WHILE (connected_task <> NIL) DO
        counts.connections := counts.connections + 1;
        connected := connected OR (connected_task^.task = local_task_id);
        connected_task := connected_task^.next_connected_task;
      WHILEND;
      osp$clear_job_signature_lock (pmv$queue_definition_table.queues^ [qid].definition^.control.
            connection_lock);
      IF connected THEN
        counts.messages := pmv$queue_definition_table.queues^ [qid].definition^.control.message_queue.
              number_messages;
        counts.waiting_tasks := pmv$queue_definition_table.queues^ [qid].definition^.control.
              waiting_task_queue.number_waiting_tasks;
      ELSE
        osp$set_status_condition (pme$unknown_queue_identifier, q_status);
      IFEND;
    END /status_queue/;
    IF NOT q_status.normal THEN
      status := q_status;
    IFEND;

  PROCEND pmp$status_queue;
?? OLDTITLE ??
MODEND pmm$manage_local_queues;
