Statistics: (including the 8-point bonus)
mean 45.278 (815.000/18) stddev 6.305 median 45.000 midrange 40.000-50.000 # Average score 1 7.667 / 15 2a 6.889 / 7 b 4.778 / 7 c 5.611 / 7 3a 6.5 / 7 b 5.833 / 7
One way of accomplishing this is with three variables. One holds the latest observation (latest), another (latest_id) associates a unique identification number with the current observation, and the last (last_read) tracks the identification number of the last observation read by the displaying process.
We use a semaphore latest_lock to ensure that the correct identification number is associated with data sent or received.
Following is C code for using this technique.
void save_obs(int value) { int read_obs() { while(latest_id == last_read); down(latest_lock); down(latest_lock); latest = value; int ret = latest; is_good = true; is_good = false; up(latest_lock); up(latest_lock); return ret; } }
This implementation, however, involves some busy-waiting by read_obs(), bogging the CPU down when the user double-clicks the button. Design an approach that uses semaphores to eliminate this busy-waiting.
We'll use block_test and is_good_wait, both initially 1.
We'll use a boolean variable blocked to indicate whether a reader is blocked.
void save_obs(int value) { int read_obs() { down(block_test); if(!is_good) { blocked = true; up(block_test); down(is_good_wait); } else { up(block_test); } down(latest_lock); down(latest_lock); latest = value; int ret = latest; is_good = true; is_good = false; up(latest_lock); up(latest_lock); down(block_test); if(blocked) up(is_good_wait); up(block_test); return ret; } }
Say that a task is currently running (or ready to run), and a hardware interrupt is received that this task should handle. Since the task is currently running, it cannot receive the message about the interrupt yet. Instead, Minix uses the p_int_blocked flag to indicate that the task should receive a message about the interrupt the next time the task becomes ready to receive a message.
If there are more than two user processes in the ready state, the user process queue must include both of them. The p_nextready field of the first process in the ready queue points to the process table entry of the next process in the ready queue.
When a process blocks trying to receive a message from a second process, the ID of the second process (for which the first is waiting) is placed into the p_getfrom field. This way, when the second process finally sends a message to the first process, it can check the p_getfrom of the first process to see whether it was already waiting to receive a message. If so, then the message can be immediately delivered, with no further blocking.
When a process tries to send a message to a second process, and the second process isn't waiting for a message from that process yet, the process is inserted into the list of processes represented by p_callerq in the receiving process's process table entry. That way, when the second process requests to receive a message, it can look through the list of processes to see whether the desired process is already waiting to send to it.
This looks at the ready queue for server processes. If it is non-empty, then it selects that process to run next. Minix does this whenever it selects a new process to run - it has this particular code to give priority to server processes over user processes.
This rotates the user process ready queue, moving the first to the end of the line. This occurs when a user process's quantum expires, and the OS wants to switch to a new user process while keeping the old one in the ready queue (since it still wants to continue).