15.7 Race Conditions

The code for “swap” has a number of interesting features. In particular it displays in microcosm the problems of race conditions when several processes are running together.

Consider the following scenario:

No swapping is taking place when process A initiates a swapping operation. Denoting “swbuf.b_flags” by simply “flags”, we have initially

  flags == null

Process A is not delayed at line 5204, initiates its i/o operation and goes to sleep at line 5215. We now have

  flags == B_BUSY | B_PHYS | rdflg

which was set at line 5206.

Suppose now while the i/o operation is proceeding, process B also initiates a swapping operation. It too begins to execute “swap”, but finds the “B_BUSY” flag set, so it sets the “B_WANTED” flag (5203) and goes to sleep also (5204). We now have

  flags == B_BUSY | B_PHYS | rdflg | B_WANTED

At last the i/o operation completes. Process C takes the interrupt and executes “rkintr”, which calls (5471) “iodone” which calls (5301) “wakeup” to awaken process A and process B. “iodone” also sets the “B_DONE” flag and resets the “B_WANTED” flag so that

  flags == B_BUSY | B_PHYS | rdflg | B_DONE

What happens next depends on the order in which process A and process B are reactivated. (Since they both have the same priority, “PSWP”, it is a toss-up which goes first.)

Case (a):

Process A goes first. “B_DONE” is set so no more sleeping is needed. “B_WANTED” is reset so there is no one to “wakeup”. Process A tidies up (5219), and leaves “swap” with

  flags == B_PHYS | rdflg | B_DONE

Process B now runs and is able to initiate its i/o operation without further delay.

Case (b):

Process B goes first. It finds “B_BUSY” on, so it turns the “B_WANTED” flag back on, and goes to sleep again, leaving

  flags == B_BUSY | B_PHYS | rdflg |
           B_DONE | B_WANTED

Process A starts again as in Case (a), but this time finds “B_WANTED” on so it must call “wakeup” (5217) in addition to its other chores. Process B finally wakes again and the whole chain completes.

Case (b) is obviously much less efficient than case (a). It would seem that a simple change to line 5215 to read

  sleep (fp, PSWP-1);

would cost virtually nothing and ensure that Case (b) never occurred!

The necessity for the raising of processor priority at various points should be studied: for example if line 5201 was omitted and if process B had just completed line 5203 when the “i/o complete” interrupt occurred for Process A’s operation, then “iodone” would turn off “B_WANTED” and perform “wakeup” before process B went to sleep ... forever! A bad scene.