# Fair (to both) readers and writers.
#
# Fair in the sense that as soon as a writer arrives (while database is
# being read, say) then no more newly arriving readers are allowed to
# start reading.  Instead the newly arriving readers must wait until
# after that writer has written.  That writer will then sweep into
# reading the database those readers that had to wait.
# However a finishing writer will sweep into reading the
# database ALL waiting readers, even those readers that arrived after
# a writer that arrived after the current writer that is just finishing.
#
resource server
   import MPDanimator
   op Start_Read(int i ), End_Read(int i )
   op Start_Write(int i ), End_Write(int i )
body server()
   write("readers and writers server is alive")
   int  Readers = 0
   int  Writers = 0
   int  Waiting_Readers = 0
   int  Waiting_Writers = 0
   sem OK_to_read = 0
   sem OK_to_write = 0
   sem mutex = 1

   proc Start_Read(i)  {
      P(mutex)
      if (Waiting_Writers == 0 and Writers == 0) {
	 Readers++
	 write("  age=", age(), "reader", i,
	    "has begun reading, Readers==", Readers)
	 V(mutex)
      } else {
	 Waiting_Readers++
	 write("age=", age(), "reader", i,
	    "waiting to read, Waiting_Readers==", Waiting_Readers)
	 V(mutex)
	 P(OK_to_read)
      }

# Move the reader to be inside the database.
      call A_stepjumpto(3000+i, 7000+i, 3, 100)

   }

   proc End_Read(i)  {
      P(mutex)
      Readers--
      write("   age=", age(), "reader", i, "finished reading, Readers=",
	 Readers)
      if (Readers == 0 and Waiting_Writers > 0) {
	 V(OK_to_write)
	 Writers++
	 Waiting_Writers--
      }
      V(mutex)

# Move the reader back to its original position.
# Change consumers's symbol to an outline black circle.
      call A_stepjumpto(3000+i, 5000+i, 3, 100)
      call A_fill(3000+i, "outline")
      call A_color(3000+i, "black")

   }

   proc Start_Write(i)  {
      P(mutex)
      if (Readers == 0 and Writers == 0) {
	 Writers++
	 write("      age=", age(), "WRITER", i,
	    "has begun Writing, Writers==", Writers, "(better be 1)")
	 V(mutex)
      } else {
	 Waiting_Writers++
	 write("age=", age(), "WRITER", i,
	    "waiting to Write, Waiting_Writers==", Waiting_Writers)
	 V(mutex)
	 P(OK_to_write)
      }

# Move the writer to be inside the database.
      call A_stepjumpto(2000+i, 7000+i, 3, 100)

   }

   proc End_Write(i)  {
      Writers--
      write("   age=", age(), "WRITER", i, "has finished Writing, Writers=",
	 Writers, "(better be 0)")

# Move the writer back to its original position.
# Change the writer's symbol to an outline black circle.
      call A_stepjumpto(2000+i, 4000+i, 3, 100)
      call A_fill(2000+i, "outline")
      call A_color(2000+i, "black")

      if (Waiting_Readers > 0) {
	 write("  age=", age(), "cascading in", Waiting_Readers, "readers")
	 while (Waiting_Readers > 0) {
	    V(OK_to_read)
	    Readers++
	    Waiting_Readers--
	 }
      }
   }
end server
