From 7a653e51399b9bfe82f21e5b9bc6299a6d66fe1e Mon Sep 17 00:00:00 2001
From: Marco Maida <mmaida@mpi-sws.org>
Date: Thu, 7 Apr 2022 10:02:27 +0000
Subject: [PATCH] revise ideal uniprocessor schedule facts

- Slightly change `valid_nonpreemptive_readiness` so that `sched` is a
  parameter. This way, the proposition can be assumed to hold only for a
  certain schedule (making weaker assumptions).

- Modify `prev_job_nonpreemptive, to unconditionally get
  `jobs_must_be_ready_to_execute`.

- Revise lemmas accordingly.
---
 analysis/definitions/readiness.v              |   4 +-
 .../definitions/ideal_uni_scheduler.v         |  11 +-
 .../facts/ideal_uni/preemption_aware.v        | 178 ++++++++++++------
 implementation/facts/ideal_uni/prio_aware.v   |  25 +--
 4 files changed, 142 insertions(+), 76 deletions(-)

diff --git a/analysis/definitions/readiness.v b/analysis/definitions/readiness.v
index c54d8a99a..10ece80b7 100644
--- a/analysis/definitions/readiness.v
+++ b/analysis/definitions/readiness.v
@@ -41,8 +41,8 @@ Section ReadinessModelProperties.
       ready. Consequently, in a valid preemption-policy-compliant schedule, a
       nonpreemptive job must remain ready until at least the end of its
       nonpreemptive section. *)
-  Definition valid_nonpreemptive_readiness :=
-     forall sched j t,
+  Definition valid_nonpreemptive_readiness sched :=
+     forall j t,
         ~~ job_preemptable j (service sched j t) -> job_ready sched j t.
   
 End ReadinessModelProperties.
diff --git a/implementation/definitions/ideal_uni_scheduler.v b/implementation/definitions/ideal_uni_scheduler.v
index ba842ed21..9924b7e04 100644
--- a/implementation/definitions/ideal_uni_scheduler.v
+++ b/implementation/definitions/ideal_uni_scheduler.v
@@ -31,9 +31,7 @@ Section UniprocessorScheduler.
   (** ** Preemption-Aware Scheduler *)
 
   (** First, we define the notion of a generic uniprocessor scheduler that is
-      cognizant of non-preemptive sections, ... *)
-
-  (** ... so consider any preemption model. *)
+      cognizant of non-preemptive sections, so consider any preemption model. *)
   Context `{JobPreemptable Job}.
 
   Section NonPreemptiveSectionAware.
@@ -61,7 +59,8 @@ Section UniprocessorScheduler.
         match t with
         | 0 => false
         | S t' => if sched_prefix t' is Some j then
-                    ~~job_preemptable j (service sched_prefix j t)
+                   job_ready sched_prefix j t &&
+                   ~~job_preemptable j (service sched_prefix j t)
                   else
                     false
         end.
@@ -76,8 +75,8 @@ Section UniprocessorScheduler.
           choose_job t (jobs_backlogged_at arr_seq sched_prefix t).
     End JobAllocation.
 
-    (* A preemption-policy-aware ideal uniprocessor schedule is then produced
-       when using [allocation_at] as the policy for the generic scheduler. *)
+    (** A preemption-policy-aware ideal uniprocessor schedule is then produced
+        when using [allocation_at] as the policy for the generic scheduler. *)
     Definition np_uni_schedule : schedule PState := generic_schedule allocation_at idle_state.
 
   End NonPreemptiveSectionAware.
diff --git a/implementation/facts/ideal_uni/preemption_aware.v b/implementation/facts/ideal_uni/preemption_aware.v
index 693699313..43b2dc66d 100644
--- a/implementation/facts/ideal_uni/preemption_aware.v
+++ b/implementation/facts/ideal_uni/preemption_aware.v
@@ -1,6 +1,7 @@
 Require Export prosa.implementation.facts.generic_schedule.
 Require Export prosa.implementation.definitions.ideal_uni_scheduler.
 Require Export prosa.analysis.facts.model.ideal_schedule.
+Require Export prosa.model.task.sequentiality.
 Require Export prosa.model.schedule.limited_preemptive.
 
 (** * Properties of the Preemption-Aware Ideal Uniprocessor Scheduler *)
@@ -14,7 +15,7 @@ Require Import prosa.model.processor.ideal.
 Section NPUniprocessorScheduler.
 
   (** Consider any type of jobs with costs and arrival times, ... *)
-  Context {Job : JobType} {JC : JobCost Job} {JA : JobArrival Job}.
+  Context {Job : JobType} `{JobCost Job} `{JobArrival Job}.
 
   (** ... in the context of an ideal uniprocessor model. *)
   Let PState := ideal.processor_state Job.
@@ -61,9 +62,9 @@ Section NPUniprocessorScheduler.
       elim: t => [|t _]; first by apply H_non_idling.
       rewrite /allocation_at /prev_job_nonpreemptive.
       elim: (sched t) => [j'|]; last by apply H_non_idling.
-      rewrite if_neg.
-      elim: (job_preemptable j' (service sched j' t.+1)); first by apply H_non_idling.
-      by discriminate.
+      move=> SCHED.
+      destruct (job_ready sched j' t.+1 && ~~ job_preemptable j' (service sched j' t.+1)) => //.
+      by apply (H_non_idling t.+1).
     Qed.
 
     (** As a stepping stone, we observe that the generated schedule is idle at
@@ -110,63 +111,116 @@ Section NPUniprocessorScheduler.
       sequence and only ready jobs are scheduled. *)
   Section Validity.
 
-    (** Any reasonable job selection policy will not create jobs "out of thin
-        air," i.e., if a job is selected, it was among those given to choose
-        from. *)
-    Hypothesis H_chooses_from_set:
-      forall t s j, choose_job t s = Some j -> j \in s.
-
-    (** For the schedule to be valid, we require the notion of readiness to be
-        consistent with the preemption model: a non-preemptive job remains
-        ready until (at least) the end of its current non-preemptive
-        section. *)
-    Hypothesis H_valid_preemption_behavior:
-      valid_nonpreemptive_readiness RM.
-
-    (** First, we show that if a job is chosen from the list of backlogged jobs, then it must be ready. *)
-    Lemma choose_job_implies_job_ready:
-      forall t j,
-      choose_job t.+1 (jobs_backlogged_at arr_seq (schedule_up_to policy idle_state t) t.+1) = Some j
-      -> job_ready schedule j t.+1.
+    (** First, any reasonable job selection policy will not create jobs "out 
+        of thin air," i.e., if a job is selected, it was among those given
+        to choose from. *)
+    Hypothesis H_chooses_from_set: forall t s j, choose_job t s = Some j -> j \in s.
+    
+    (** Second, for the schedule to be valid, we require the notion of readiness 
+        to be consistent with the preemption model: a non-preemptive job remains
+        ready until (at least) the end of its current non-preemptive section. *)
+    Hypothesis H_valid_preemption_behavior: valid_nonpreemptive_readiness RM schedule.
+    
+    (** Finally, we assume the readiness model to be non-clairvoyant. *)
+    Hypothesis H_nonclairvoyant_readiness: nonclairvoyant_readiness RM.
+    
+    (** For notational convenience, recall the definition of a prefix of the
+        schedule based on which the next decision is made. *)
+    Let sched_prefix t :=
+      if t is t'.+1 then schedule_up_to policy None t' else empty_schedule idle_state.
+
+    (** We begin by showing that any job in the schedule must come from the arrival 
+        sequence used to generate it. *)
+    Lemma np_schedule_jobs_from_arrival_sequence:
+      jobs_come_from_arrival_sequence schedule arr_seq.
+    Proof.
+      move=> j t; rewrite scheduled_at_def /schedule /np_uni_schedule /generic_schedule.
+      elim: t => [/eqP | t'  IH /eqP].
+      - rewrite schedule_up_to_def  /allocation_at  /prev_job_nonpreemptive => IN.
+        move: (H_chooses_from_set _ _ _ IN).
+        by apply backlogged_job_arrives_in.
+      - rewrite schedule_up_to_def  /allocation_at.
+        case: (prev_job_nonpreemptive  (schedule_up_to _ _ t') t'.+1) => [|IN].
+        + by rewrite -pred_Sn => SCHED; apply IH; apply /eqP.
+        + move: (H_chooses_from_set _ _ _ IN).
+          by apply backlogged_job_arrives_in.
+    Qed.
+
+    (** Next, we show that any job selected by the job selection policy must 
+        be ready. *)
+    Theorem chosen_job_is_ready:
+      forall j t,
+        choose_job t (jobs_backlogged_at arr_seq (sched_prefix t) t) == Some j ->
+        job_ready schedule j t.
     Proof.
-      move=> t j IN.
-      move: (H_chooses_from_set _ _ _ IN).
-      rewrite mem_filter /backlogged => /andP [/andP [READY _]  _].
-      rewrite -(H_nonclairvoyant_job_readiness (schedule_up_to policy idle_state t) schedule j t.+1) //.
-      by apply schedule_up_to_identical_prefix.
+      move=> j t /eqP SCHED.
+      apply H_chooses_from_set in SCHED.
+      move: SCHED; rewrite mem_filter => /andP [/andP[READY _] IN].
+      rewrite (H_nonclairvoyant_readiness _ (sched_prefix t) j t) //.
+      move=> t' LEQt'.
+      rewrite /schedule /np_uni_schedule /generic_schedule
+              schedule_up_to_def /sched_prefix.
+      destruct t => //=.
+      rewrite -schedule_up_to_def.
+      by apply (schedule_up_to_prefix_inclusion policy).
     Qed.
 
-    (** Next, we show that, under these natural assumptions, the generated schedule is valid. *)
+    (** Starting from the previous result we show that, at any instant, only
+        a ready job can be scheduled. *)
+    Theorem jobs_must_be_ready:
+      jobs_must_be_ready_to_execute schedule.
+    Proof.
+      move=> j t SCHED.
+      rewrite scheduled_at_def /schedule /uni_schedule /np_uni_schedule
+              /allocation_at /generic_schedule schedule_up_to_def //= in SCHED.
+      destruct (prev_job_nonpreemptive _) eqn:PREV.
+      { destruct t => //; rewrite //= in SCHED, PREV.
+        destruct (schedule_up_to) => //.
+        move: PREV => /andP [READY _].
+        move: SCHED=> /eqP SCHED.
+        injection SCHED => EQ; rewrite -> EQ in *.
+        erewrite (H_nonclairvoyant_readiness _ _ j t.+1); [by apply READY| |by done].
+        move=> t' LT.
+        rewrite /schedule /np_uni_schedule /generic_schedule //=.
+        rewrite /allocation_at //=.
+        by apply (schedule_up_to_prefix_inclusion policy). }
+      { by apply chosen_job_is_ready. }
+    Qed.
+
+    (** Finally, we show that the generated schedule is valid. *)
     Theorem np_schedule_valid:
       valid_schedule schedule arr_seq.
     Proof.
-      split; move=> j t; rewrite scheduled_at_def /schedule /np_uni_schedule /generic_schedule.
-      { elim: t => [/eqP | t'  IH /eqP].
-        - rewrite schedule_up_to_def  /allocation_at  /prev_job_nonpreemptive => IN.
-          move: (H_chooses_from_set _ _ _ IN).
-          by apply backlogged_job_arrives_in.
-        - rewrite schedule_up_to_def  /allocation_at.
-          case: (prev_job_nonpreemptive  (schedule_up_to _ _ t') t'.+1) => [|IN];
-                first by rewrite -pred_Sn => SCHED; apply IH; apply /eqP.
-          move: (H_chooses_from_set _ _ _ IN).
-          by apply backlogged_job_arrives_in. }
-      { elim: t => [/eqP |t'  IH /eqP].
-        - rewrite schedule_up_to_def  /allocation_at  /prev_job_nonpreemptive => IN.
+      rewrite /valid_schedule; split; first by apply np_schedule_jobs_from_arrival_sequence.
+      move=> j t; rewrite scheduled_at_def /schedule /np_uni_schedule /generic_schedule.
+      elim: t => [/eqP |t'  IH /eqP].
+      { rewrite schedule_up_to_def  /allocation_at  /prev_job_nonpreemptive => IN.
+        move: (H_chooses_from_set _ _ _ IN).
+        rewrite mem_filter /backlogged => /andP [/andP [READY _]  _].
+        now rewrite -(H_nonclairvoyant_job_readiness (empty_schedule idle_state) schedule j 0). }
+      { rewrite schedule_up_to_def  /allocation_at /prev_job_nonpreemptive.
+        have JOB: choose_job t'.+1 (jobs_backlogged_at arr_seq (schedule_up_to policy idle_state  t') t'.+1)
+                  = Some j
+                  -> job_ready schedule j t'.+1.
+        { move=> IN.
           move: (H_chooses_from_set _ _ _ IN).
           rewrite mem_filter /backlogged => /andP [/andP [READY _]  _].
-          by rewrite -(H_nonclairvoyant_job_readiness (empty_schedule idle_state) schedule j 0).
-        - rewrite schedule_up_to_def  /allocation_at /prev_job_nonpreemptive.
-          case: (schedule_up_to _ _ t' t') => [j' | IN]; last by apply choose_job_implies_job_ready.
-          destruct (~~ job_preemptable j' _) eqn:NP => [EQ|IN]; last by apply choose_job_implies_job_ready.
-          apply H_valid_preemption_behavior.
-          injection EQ => <-.
-          move: NP.
-          have <-: (service (schedule_up_to policy idle_state t') j' t'.+1
-                    = service (fun t : instant => schedule_up_to policy idle_state t t) j' t'.+1) => //.
-          rewrite /service.
-          apply equal_prefix_implies_same_service_during => t /andP [_ BOUND].
-          by rewrite (schedule_up_to_prefix_inclusion _  _ t t'). }
-    Qed.
+          rewrite -(H_nonclairvoyant_job_readiness (schedule_up_to policy idle_state t') schedule j t'.+1) //.
+          rewrite /identical_prefix /schedule /np_uni_schedule /generic_schedule => t'' LT.
+          now rewrite (schedule_up_to_prefix_inclusion _  _  t'' t') //. }
+        case: (schedule_up_to _ _ t' t') => [j' | IN]; last by apply JOB.
+        destruct (job_ready _ _ _ && ~~ job_preemptable j' _) eqn:NP => [EQ|IN]; last by apply JOB.
+        apply H_valid_preemption_behavior.
+        injection EQ => <-.
+        move: NP.
+        have <-: (service (schedule_up_to policy idle_state t') j' t'.+1
+                  = service (fun t : instant => schedule_up_to policy idle_state t t) j' t'.+1) => //.
+        rewrite /service.
+        apply equal_prefix_implies_same_service_during => t /andP [_ BOUND].
+        now rewrite (schedule_up_to_prefix_inclusion _  _ t t').
+        rewrite //=.
+        by move=> /andP [? ?]. }
+    Qed. 
 
   End Validity.
 
@@ -210,7 +264,8 @@ Section NPUniprocessorScheduler.
                 service (schedule_up_to policy idle_state t) j t.+1) => //.
       rewrite /service.
       apply equal_prefix_implies_same_service_during => t' /andP [_ BOUND].
-      by rewrite (schedule_up_to_prefix_inclusion _ _ t' t).
+      rewrite (schedule_up_to_prefix_inclusion _ _ t' t) //. 
+      by move=> /andP [? ?].
     Qed.
 
   End PreemptionTimes.
@@ -226,6 +281,11 @@ Section NPUniprocessorScheduler.
       forall j,
         arrives_in arr_seq j ->
         job_cannot_become_nonpreemptive_before_execution j.
+    
+    (** Second, for the schedule to be valid, we require the notion of readiness 
+        to be consistent with the preemption model: a non-preemptive job remains
+        ready until (at least) the end of its current non-preemptive section. *)
+    Hypothesis H_valid_preemption_behavior: valid_nonpreemptive_readiness RM schedule.
 
     (** Given such a valid preemption model, we establish that the generated
         schedule indeed respects the preemption model semantics. *)
@@ -233,7 +293,7 @@ Section NPUniprocessorScheduler.
       schedule_respects_preemption_model arr_seq schedule.
     Proof.
       move=> j.
-      elim => [| t' IH]; first by rewrite service0 => ARR /negP NP; move: (H_valid_preemption_function j ARR).
+      elim => [| t' IH];[by rewrite service0=>ARR /negP ?;move:(H_valid_preemption_function j ARR)|].
       move=> ARR NP.
       have: scheduled_at schedule j t'.
       { apply contraT => NOT_SCHED.
@@ -242,8 +302,12 @@ Section NPUniprocessorScheduler.
         by move /negP in NOT_SCHED. }
       rewrite !scheduled_at_def /schedule/np_uni_schedule/generic_schedule => /eqP SCHED.
       rewrite -SCHED (schedule_up_to_prefix_inclusion _ _ t' t'.+1) // np_job_remains_scheduled //.
-      rewrite /prev_job_nonpreemptive SCHED (identical_prefix_service _ schedule) //.
-      by apply schedule_up_to_identical_prefix.
+      rewrite /prev_job_nonpreemptive SCHED.
+      rewrite (identical_prefix_service _ schedule); last by apply schedule_up_to_identical_prefix.
+      apply /andP; split => //.
+      rewrite (H_nonclairvoyant_job_readiness _ schedule _ t'.+1) //.
+      - by apply H_valid_preemption_behavior.
+      - by apply schedule_up_to_identical_prefix.
     Qed.
 
   End PreemptionCompliance.
diff --git a/implementation/facts/ideal_uni/prio_aware.v b/implementation/facts/ideal_uni/prio_aware.v
index 62b928314..e50693862 100644
--- a/implementation/facts/ideal_uni/prio_aware.v
+++ b/implementation/facts/ideal_uni/prio_aware.v
@@ -19,12 +19,11 @@ Section PrioAwareUniprocessorScheduler.
   Hypothesis H_consistent_arrival_times: consistent_arrival_times arr_seq.
 
   (** ... a non-clairvoyant readiness model, ... *)
-  Context {RM: JobReady Job (ideal.processor_state Job)}.
+  Context {RM : JobReady Job (ideal.processor_state Job)}.
   Hypothesis H_nonclairvoyant_job_readiness: nonclairvoyant_readiness RM.
 
-  (** ... a preemption model that is consistent with the readiness model, ... *)
-  Context `{JobPreemptable Job}.
-  Hypothesis H_valid_preemption_behavior: valid_nonpreemptive_readiness RM.
+  (** ... a preemption model, ... *)
+  Context `{JobPreemptable Job}.      
 
   (** ... and reflexive, total, and transitive JLDP priority policy. *)
   Context `{JLDP : JLDP_policy Job}.
@@ -33,9 +32,14 @@ Section PrioAwareUniprocessorScheduler.
   Hypothesis H_transitive: transitive_priorities.
 
   (** Consider the schedule generated by the preemption-policy- and
-      priority-aware ideal uniprocessor scheduler. *)
+      priority-aware ideal uniprocessor scheduler... *)
   Let schedule := uni_schedule arr_seq.
 
+  (** ...and assume that the preemption model is consistent with the 
+      readiness model. *)
+  Hypothesis H_valid_preemption_behavior:
+    valid_nonpreemptive_readiness RM schedule.
+
   (** First, we note that the priority-aware job selection policy obviously
      maintains work-conservation. *)
   Corollary uni_schedule_work_conserving:
@@ -54,7 +58,7 @@ Section PrioAwareUniprocessorScheduler.
   Proof.
     apply np_schedule_valid => //.
     move=> t jobs j.
-    now apply supremum_in.
+    by apply supremum_in.
   Qed.
 
   (** Third, the schedule respects also the preemption model semantics. *)
@@ -77,7 +81,7 @@ Section PrioAwareUniprocessorScheduler.
     Qed.
 
   End PreemptionCompliance.
-
+  
   (** Now we proceed to the main property of the priority-aware scheduler: in
       the following section we establish that [uni_schedule arr_seq] is
       compliant with the given priority policy whenever jobs are
@@ -88,9 +92,7 @@ Section PrioAwareUniprocessorScheduler.
         policy and a prefix of the schedule based on which the next decision is
         made. *)
     Let policy := allocation_at arr_seq choose_highest_prio_job.
-    Let prefix t := if t is t'.+1
-                    then schedule_up_to policy idle_state t'
-                    else empty_schedule idle_state.
+    Let prefix t := if t is t'.+1 then schedule_up_to policy idle_state t' else empty_schedule idle_state.
 
     (** To start, we observe that, at preemption times, the scheduled job is a
         supremum w.r.t. to the priority order and the set of backlogged
@@ -120,7 +122,7 @@ Section PrioAwareUniprocessorScheduler.
       move=> j1 j2 t ARRIVES PREEMPT BACK_j1 SCHED_j2.
       case: (boolP (scheduled_at (uni_schedule arr_seq) j1 t)) => [SCHED_j1|NOT_SCHED_j1].
       { have <-: j1 = j2 by apply (ideal_proc_model_is_a_uniprocessor_model j1 j2 (uni_schedule arr_seq) t).
-        now apply H_reflexive_priorities. }
+        by apply H_reflexive_priorities. }
       { move: BACK_j1.
         have ->: backlogged (uni_schedule arr_seq) j1 t = backlogged (prefix t) j1 t.
         { apply backlogged_prefix_invariance' with (h := t) => //.
@@ -139,3 +141,4 @@ Section PrioAwareUniprocessorScheduler.
   End Priority.
 
 End PrioAwareUniprocessorScheduler.
+
-- 
GitLab