CIS307: Spring 97: Homework 2: Using Unix System Services

Given February 11, due March 5 by 10pm.
We have processes STORE_MANAGER, RAND_PROC1, RAND_PROC2 created(forked) by the parent process HMW_MAIN. The code of STORE_MANAGER will be in file table_manager.c. The code for RAND_PROC1 and RAND_PROC2 will be in file rand_proc.c. And the code of the parent process HMW_MAIN will be in hmw_main.c. These files will be compiled separately and linked together [See Hints] to form the image hmw2. To run the program you will then call hmw2 with as command line parameters the names of two files [for convenience in the following we assume these names are LOG.DAT and INIT.DAT].

STORE_MANAGER receives messages from RAND_PROC1 and RAND_PROC2 through the pipe C_BOX. It responds to messages from RAND_PROC1 by placing the responses in the pipe RAND_BOX1. It responds to messages from RAND_PROC2 by placing the responses in the pipe RAND_BOX2. Each message as it is sent or received, it is time-stamped and logged in the file 'LOG.DAT' by the STORE_MANAGER and by the RAND_PROCi processes. The LOG.DAT file is opened by HMW_MAIN before creating the other processes.

The STORE_MANAGER keeps in a TABLE TABLE_SIZE [say, 10] pairs (TABLE_ID, TABLE_ELEM). You will load an initial content for TABLE from the 'INIT.DAT' file listed below. TABLE_ID and TABLE_ELEM are strings of, respectively, 10 [TABLE_ID_SIZE] and 7 [TABLE_ELEM_SIZE] characters. TABLE_ID represents the name of a stock [for example "IBM "] and TABLE_ELEM represents the corresponding value in the form ddddddd [for example " 16375"].
The TABLE carries out two operations TABLE_UPDATE and TABLE_READ.

   function TABLE_UPDATE (WHO : TABLE_ID;
    			    WHAT : TABLE_ELEM) return INTEGER;
    {It updates the value of WHO to be WHAT. It returns 0 iff successful}
    {i.e. if WHO was a TABLE_ID in the table}
    {Note that WHO denotes the pair with first element equal to WHO.}

   function TABLE_READ (WHO : TABLE_ID;
    			  out WHAT  : TABLE_ELEM) return INTEGER;
    {It retrieves the value of WHO and stores it in WHAT. 
     It returns 0 iff successful, i.e. if WHO was a TABLE_ID in the table}

Here is an important complication: TABLE_READ and TABLE_UPDATE are slow operations [assume that they involve IO], taking a random time (say, between 1 and 3 seconds). During this time the STORE_MANAGER should be able to work on requests involving other TABLE_IDs.
To deal with this problem we assume that each operation is executed by the STORE_MANAGER as two operations: for TABLE_READ, TABLE_READ_START and TABLE_READ_END, and for TABLE_UPDATE, TABLE_UPDATE_START and TABLE_UPDATE_END. You decide what should be the parameters to use for these operations.

Here is how the STORE_MANAGER should behave:

   STORE_MANAGER:
       initialization;
       loop
	    wait for the completion of an operation or the arrival
	       of a new request;
            if it is a new request then
	       log the message and start the corresponding operation;
            else if it is a completion then
	       complete the operation, send the response, and log it;
	end loop
        conclusion;

STORE_MANAGER receives messages that consist of, in sequence:

	o An Origin Code [the process id of the sending process, as a
          five digit number]
	o A message code ['U' for TABLE_UPDATE, 'R' for TABLE_READ] 
	o The  operands required by the corresponding operation [hence for 
	  TABLE_UPDATE, the 'U' will be followed by a TABLE_ID value [it
	  is TABLE_ID_SIZE characters] 
	  and by a TABLE_ELEM value [it is TABLE_ELEM_SIZE characters], 
	  while for TABLE_READ, the 'R' will be followed only by TABLE_ID.] 

The STORE_MANAGER responses consist of the character representing the operation requested, followed by the result of the operation ['0' for success, '1' for failure], followed by the values involved in the operation [both TABLE_ID and TABLE_ELEM].

RAND_PROC1 and RAND_PROC2 have the same form [but use different seeds for the random number generator]:

   RAND_PROCi:
       initialization;
       loop
	    generate a random TABLE_READ or TABLE_UPDATE request;
	    [in both operations the TABLE_ID should be selected 
	     at random from a list maintained at RAND_PROCi; in the 
             TABLE_UPDATE operation TABLE_ELEM should be created using 
	     a random number generator]
	    send the request and log it;
	    wait for the corresponding response and log it;
	    wait some random time [say, between 0 and 2 seconds];
	end loop
        conclusion;
In writing RAND_PROCi you should represent the sending of a message and the receiving of the corresponding response as taking place within calls to the following functions:
   function TABLE_UPDATE (WHO : TABLE_ID;
    			   WHAT  : TABLE_ELEM) return INTEGER;
    {It updates the value of WHO to be WHAT. It returns 0 iff successful}
    {Note that WHO denotes the pair with first element equal to WHO.}
   function TABLE_READ (WHO : TABLE_ID;
    			  var WHAT  : TABLE_ELEM) return INTEGER;
    {It retrieves the value of WHO and stores it in WHAT. 
     It returns 0 iff successful}
I have used the same name and form for the functions used in STORE_MANAGER to access TABLE, and in processes RAND_PROC1 and RAND_PROC2 to communicate with STORE_MANAGER. I did it intentionally to stress that through communication the TABLE is as if it were local to its users. [The fact that you are using the same name should not give origin to problems because they occur in different files and should be local to those files.]

HMW_MAIN, after starting the other processes, in a loop prompts the users to ask if they want to see statistical information from RAND_PROC1 and RAND_PROC2.

   HMW_MAIN:
       pid_t pids[3];
       initialization, including opening LOG.DAT;
       if ((pids[0]=fork())<0) {perror("fork"); exit(1);}
       else if (pids[0]==0) STORE_MANAGER();
       if ((pids[1]=fork())<0) {perror("fork"); exit(1);}
       else if (pids[1]==0) RAND_PROC1();
       if ((pids[2]=fork())<0) {perror("fork"); exit(1);}
       else if (pids[2]==0) RAND_PROC2();
       loop
	   Prompt the User;
	   If the answer is "1" then
		send a signal to RAND_PROC1 [RAND_PROC1 
		will write to the screen the number of Read and 
		Update messages that it has executed [different 
		numbers represents the Reads started and completed, 
		and the Updates started and completed]].
	   else if the answer is "2", then
		the same is done with RAND_PROC2
	   else 
		send signals to RAND_PROC1, RAND_PROC2 and
		STORE_MANAGER requesting that they terminate;
		wait that they all exit and exit the loop;
	end loop
        conclusion, including closing LOG.DAT;
RAND_PROC1 and RAND_PROC2 should respond within callbacks to the signals from HMW_MAIN.

The INIT.DAT file is:

       IBM   16375
     LASMO    1280
      NOVA    3950
     HONDA    6125
   DIGITAL    7650
    DISNEY    7725
   CHEVRON    6950
   BORDERS    4490
    FINOVA    7275
  HALLWOOD    2700

ingargiola@cis.temple.edu