Operating Systems 3rd Edition Nutt Solutions Manual instant download all chapter

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 55

Operating Systems 3rd Edition Nutt

Solutions Manual
Go to download the full and correct content document:
https://testbankdeal.com/product/operating-systems-3rd-edition-nutt-solutions-manual
/
More products digital (pdf, epub, mobi) instant
download maybe you interests ...

Operating Systems 3rd Edition Nutt Test Bank

https://testbankdeal.com/product/operating-systems-3rd-edition-
nutt-test-bank/

Operating Systems 3rd Edition Deitel Solutions Manual

https://testbankdeal.com/product/operating-systems-3rd-edition-
deitel-solutions-manual/

Operating Systems Design And Implementation 3rd Edition


Tanenbaum Solutions Manual

https://testbankdeal.com/product/operating-systems-design-and-
implementation-3rd-edition-tanenbaum-solutions-manual/

Understanding Operating Systems 7th Edition McHoes


Solutions Manual

https://testbankdeal.com/product/understanding-operating-
systems-7th-edition-mchoes-solutions-manual/
Understanding Operating Systems 8th Edition McHoes
Solutions Manual

https://testbankdeal.com/product/understanding-operating-
systems-8th-edition-mchoes-solutions-manual/

Modern Operating Systems 4th Edition Tanenbaum


Solutions Manual

https://testbankdeal.com/product/modern-operating-systems-4th-
edition-tanenbaum-solutions-manual/

Understanding Operating Systems 5th Edition McHoes


Solutions Manual

https://testbankdeal.com/product/understanding-operating-
systems-5th-edition-mchoes-solutions-manual/

Survey of Operating Systems 5th Edition Holcombe


Solutions Manual

https://testbankdeal.com/product/survey-of-operating-systems-5th-
edition-holcombe-solutions-manual/

Operating Systems Internals and Design Principles 9th


Edition Stallings Solutions Manual

https://testbankdeal.com/product/operating-systems-internals-and-
design-principles-9th-edition-stallings-solutions-manual/
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions

Chapter 9: High-Level Synchronization and Interprocess


Communication
Exercises

1. Here is one solution. Another one uses the monitor to encapsulate the resources rather
than just the entry and exit.

monitor sharedV {
public enter(int i) {set i busy;};
public exit(int i) {set i idle;};
};

p_0() {
...
enter(2);
access V_2;
exit(2)
...
}

p_1() {
...
enter(0);
access V_0
exit(0)
...
enter(2);
access V_2;
exit(2)
...
enter(0);
enter(2);
access V_0
access V_2;
exit(0)
exit(2)
}

p_2() {
...
enter(0);
access V_0
exit(0)
...
enter(1);
access V_1;
exit(1)
...
enter(0);
enter(1);
access V_0
access V_1;
exit(0)

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
exit(1)
}

p_3() {
...
enter(1);
access V_1;
exit(1)
...
}

2. Here is one solution.

semaphore s0 = 1, s1 = 1, s2 = 1;
...
P_simultaneous(...);
V_simultaneous(...);

p_0() {
...
P_simultaneous(s0, s1);
access V_0 & V_1;
V_simultaneous(s0, s1);
...
}

p_1() {
...
P_simultaneous(s1, s2);
access V_1 & V_2;
V_simultaneous(s0, s1);
...
}

p_2() {
...
P_simultaneous(s0, s2);
access V_0 & V_2;
V_simultaneous(s0, s1);
...
}

3. Here is one solution.

monitor semaphore {
private:
int count = 1; /* set to the initial value of the semaphore
*/
condition hold;
public:
P() {count--; if(count <= 0) hold.wait;};
V() {count++; hold.signal:};
};

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions

4. Condition variables are just signaling semaphores (such as the full/empty semaphores
in the bounded buffer problem). The solution to this problem requires that you know
how mutual exclusion is implemented in the monitor. Then the condition variable
wait code must enqueue the thread and release the mutual exclusion. Similarly, the
signal code obtains mutually exclusive access to the monitor and dequeues a thread.
You will need to provide some guidance as to the amount of detail you want as an
acceptable solution to this problem. Here is some pseudo code (that has only been
debugged “by eye”):

struct monitor_t {
private:
semaphore mutex = 1;
int cv = 0;
<ADT data structures>
...
public:
proc_i(...) {
P(mutex);
<processing for proc_i>
/* CV wait */
cv--;
while(cv < 0) {
enqueue(self, cv_list);
setState(self, blocked);
V(mutex);
yield(); /* Call the scheduler */
P(mutex);
}
/* CV signal */
cv++;
if(cv <= 0) {
pid = dequeue(cv_list);
setState(pid, ready);
V(mutex);
yield(); /* Call the scheduler */
P(mutex);
}
V(mutex);
};
...
};

5. The idea is to translate the solution for #4 into System V semaphores:

struct monitor_t {
private:
union semun {
int val;
struct semid_ds *buf;
ushort * array;
} arg;
arg.val = 1; /* Initial value of semaphore */
int mutex;
struct sembuf opns[1];

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
int cv = 0; /* You could use an event here, but it will be
* tricky releasing the mutex
*/
<ADT data structures>
...
public:
monitor_t() {
mutex = semget(MY_MONITOR, 1, 0666|IPC_CREATE);
if(mutex < 0)
{
fprintf(stderr, "Semaphore not available\n");
exit(0);
}
if(semctl(id, 0, SETVAL, arg) < 0)
{
fprintf( stderr, "Unable to set semaphore value\n");
}
/* Set up the sembuf structure. */
opns[0].sem_num = 0;
opns[0].sem_flg = 0;
};
proc_i(...) {
/* P(mutex) */
opns[0].sem_op = -1;
semop(id, opns, 1);
<processing for proc_i>
/* CV wait */
cv--;
while(cv < 0) {
enqueue(self, cv_list);
setState(self, blocked);
/* V(mutex) */
opns[0].sem_op = 1;
semop(id, opns, 1);
yield(); /* Call the scheduler */
/* P(mutex) */
opns[0].sem_op = -1;
semop(id, opns, 1);
}
/* CV signal */
cv++;
if(cv <= 0) {
pid = dequeue(cv_list);
setState(pid, ready);
/* V(mutex) */
opns[0].sem_op = 1;
semop(id, opns, 1);
yield(); /* Call the scheduler */
/* P(mutex) */
opns[0].sem_op = -1;
semop(id, opns, 1);
}
/* V(mutex) */
opns[0].sem_op = 1;
semop(id, opns, 1);
};
...

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
};

6. Here is the basic idea of a solution:

struct monitor_t {
private:
HANDLE mutex;
Char *mName = “mutexName”;
int cv = 0;
<ADT data structures>
...
public:
monitor_t() {
mutex = CreateMutex(NULL, FALSE, mName);
};
proc_i(...) {
WaitForSingleObject(mutex, INFINITE); /* P(mutex */
<processing for proc_i>
/* CV wait */
cv--;
while(cv < 0) {
enqueue(self, cv_list);
setState(self, blocked);
ReleaseMutex(mutex); /* V(mutex) */
yield(); /* Call the scheduler */
WaitForSingleObject(mutex, INFINITE); /* P(mutex) */
}
/* CV signal */
cv++;
if(cv <= 0) {
pid = dequeue(cv_list);
setState(pid, ready);
ReleaseMutex(mutex); /* V(mutex) */
yield(); /* Call the scheduler */
WaitForSingleObject(mutex, INFINITE); /* P(mutex) */
}
ReleaseMutex(mutex); /* V(mutex) */
};
...
};

7. Here is one solution.

monitor sleepy_barber {
private:
int number_of_customers = 0;
condition_variable haircut, sleepy;

public:
request_haircut {
number_of_customers = number_of_customers + 1;
if (number_of_customers == 1) sleepy.signal;
haircut.wait;

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions

take_customer {
if (number_of_customers == 0) sleepy.wait;
number_of_customers = number_of_customers - 1;
haircut.signal;
}

The barber is a cyclic process of the form:


while (TRUE) {
sleepy_barber.take_customer;
cut_hair
}

Each customer has the form:


...
sleepy_barber.request_haircut
get_haircut;
...

8. An asynchronous send operation can be used in any situation in which the sending
process wishes to transmit information to a sender, but it does not care when the
message is received. In the SOR example, the central process can assign work to an
equation solver using an asynchronous send. (The centralized process will ultimately
have to synchronize the completion of the equation solutions by all the solvers, but
the allocation of work need not have synchronization.)
Suppose two processes, p_0 and p_1, cooperate with one another so that they
generally operate independently and concurrently, but they occasionally share
information by p_0 sending information to p_1; with a synchronous send, p_0 can
procede after its send operation with the assurance that p_1 has received the
information. This communication paradigm is used in remote procedure call
(described in detail in Chapter 17).

9. If a set of processes are working on a common problem, i.e., the work has been
partitioned and delegated to various processes, then the run time will be reduced if all
the processes can execute at the same time. This follows because a fixed amount of
work is divided, then two or more processes perform parts of the work simultaneously
(in a system with multiple processors). Blocking receive operations tend to make a
process wait for one or more other processes to "catch up," reducing the effective
amount of concurrency in the execution. However, as we have seen in various
examples in Chapters 8 and 9, concurrent operation on shared information means that
it is necessary to incorporate a synchronization mechanism to manage sharing -- a
factor that considerably raises the complexity of the software.
10. Condition variables are used to suspend a process while it is logically in a critical
section. In a user thread package, the programmer controls the scheduling of threads
within an address space (process). Therefore, the programmer can determine
situations in which a thread is in a critical section, yet needs another critical section,
and thus can suspend the thread from executing until the needed critical section can

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions

become available. Notice that this is not generally possible at the process level, since
there is not parent to control the schedule and to suspend other processes.
11. The Mach C thread cthread_fork() is similar to UNIX fork() in that it creates a
new thread. It is different in that the thread is given a specific procedure where it will
begin execution (same address space, but a different location from the parent thread).
When the C thread terminates, there is no synchronization with the parent thread.
12. This solution (with minor edits) provided by Don Lindsay, Fall, 1995.

===========================================================================
===========================================================================
/* parent.c
*
* Example program by Don Lindsay
*
*
* This program uses the trapezoidal rule to integrate
* f(x) = 1/(x+1)
* on [0,2]. It does this by forking N processes, each of which
* does n/N trapezoids.
* The parent specifies tasks over N pipes, and recieves answers over one
* shared pipe.
* The parent reports the totalled area, and reports the elapsed integration
* time, ie excluding the setup time. The time is the average of many runs.
* This program must be run with N between 1 and 8, and n=64.
*
* Tested on Linux: portability unknown.
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>>
#include <errno.h>
#include <fcntl.h>
#include <math.h>

#define REPEAT (4000)


#define NUMSTEPS (64)
#define PATHNAME "./child"

#define PRINT if(debug)fprintf


#define PRINT2 if(debug>1)fprintf
extern char **environ;

#ifndef FALSE
#define FALSE 0
#define TRUE 1
#endif
typedef int Boolean;
/*typedef unsigned char u_char;*/

#define PIPE_IN (0)


#define PIPE_OUT (1)

int debug = 0;

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
int forkcount;
int pipes_to_child[8][2];
int pipe_from_child[2];

/****************************************************************
*
*/
void fatal( char *string )
{
fprintf( stderr, "FATAL ERROR: %s\\n", string );
exit(1);
}
/****************************************************************
*
*/
void system_fatal( char *string )
{
fprintf( stderr, "FATAL SYSTEM ERROR: ");
perror( string );
exit(1);
}
/****************************************************************
*
*/
void prompt()
{
fprintf( stderr, "TRY: parent N [-d]\\n" );
exit(1);
}
/****************************************************************
*
* Sets globals forkcount, debug
*/
void parse_my_args( int argc, char **argv )
{
if( argc < 2 || argc > 3 ) prompt();
if( argc == 3 ){
if( strcmp( argv[2], "-d" ) == 0 )
debug = 1;
else if( strcmp( argv[2], "-D" ) == 0 )
debug = 2;
else
prompt();
}
forkcount = atoi( argv[1] );
PRINT( stderr, "forkcount %d\\n", forkcount );
if( forkcount < 1 || forkcount > 8 ) fatal( "arg out of range" );
}
/****************************************************************
*
* Fork that many children.
* Leaves global pipes_to_child and pipe_from_child. These are the
* stdin and stdout of each child.
* Exits on error.
*/
void fork_solvers( int forkcount )
{
int i;
pid_t pid;
char *child_argv[2];
child_argv[0] = PATHNAME;
child_argv[1] = NULL;

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
if( pipe( pipe_from_child )) system_fatal( "Pipe" );
for( i = 0; i < forkcount; i++ ) {
if( pipe( pipes_to_child[i] )) system_fatal( "Pipe" );

PRINT( stderr, "about to fork\\n" );


pid = fork();
if( pid == 0 ) {
/* I am the i'th child. */

if( close(0))
system_fatal("Close1");
if( dup( pipes_to_child[i][PIPE_IN ])==-1)system_fatal("dup1");
if( close(1))
system_fatal("Close2");
if( dup(pipe_from_child[ PIPE_OUT ])==-1)system_fatal("dup2");

/* child's stdin and stdout are now the pipes */

execve( PATHNAME, child_argv, environ );


fprintf( stderr, "exec of \\"%s\\" failed:\\n", PATHNAME );
system_fatal( "Child" );
exit(1);
}
/* I am the parent */
if( pid == -1 ) {
system_fatal( "Fork Parent" );
}
}
}
/***************************************************************
*
* Expects globals pipes_to_child, pipe_from_child.
* Writes step requests to those pipes, and returns the sum of the results.
*/
float farm_out_work( int forkcount )
{
u_char out_buf;
float in_buf;
int in_buf_len = sizeof( float );
float area;
int i, child;

/* Try to get them all working by writing all before any reads */

for( child = 0, i = 1; i <= NUMSTEPS; child++, i++ ) {


if( child >= forkcount ) child = 0; /* wrap around */

out_buf = i;
if( write( pipes_to_child[child][PIPE_OUT], &out_buf,1)!=1)
system_fatal( "write" );
}
for( area = 0.0, i = 1; i <= NUMSTEPS; i++ ) {
if( read( pipe_from_child[PIPE_IN], &in_buf,in_buf_len)
!= in_buf_len )
system_fatal( "read pipe" );
PRINT( stderr, "parent: %d gets area = %g\\n", i, in_buf );
area += in_buf;
}
PRINT( stderr, "parent: %d gets area = %g\\n", i, in_buf );
return area;
}
/****************************************************************
*

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
*
*/
void kill_solvers( int forkcount )
{
int i;
char out_buf;
out_buf = 0;

for( i = 0; i < forkcount; i++ ) {


if( write( pipes_to_child[i][PIPE_OUT], &out_buf,1)!=1)
system_fatal( "kill write" );
}
}
/******************************************************************
*
* Returns system time in milliseconds.
*/
double get_sys_time()
{
struct timeval t; /* int fields tv_sec, tv_usec */
double time;
int result = gettimeofday( &t, 0 );
if( result ) {
fprintf( stderr, "error from gettimeofday\\n" );
perror( "" );
exit(1);
}
PRINT( stderr, "%d %d\\n", t.tv_sec, t.tv_usec );
time = t.tv_sec * 1.e6 + t.tv_usec; /* in microseconds */
return time / 1000;
}
/****************************************************************
*/
main( int argc, char **argv )
{
double start, stop;
int i;
float area;

parse_my_args( argc, argv ); /* get forkcount */


fork_solvers( forkcount );

start = get_sys_time();
/* Must integrate many times to get a measurable amount of time. */
for( i = 0; i < REPEAT; i++ ) {
area = farm_out_work( forkcount );
}
stop = get_sys_time();

fprintf( stdout, "area is %g\\n", area );


fprintf( stdout, "time per integration is %g ms.\\n",
(stop-start)/REPEAT );
kill_solvers( forkcount );
exit(0);
}
===========================================================================
===========================================================================
/* child.c - to be invoked by "parent", forked integrator.
*
* Example program by Don Lindsay
*
*
* This program is used to integrate (via trapezoids) the function

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
*/
#define FUNC(x) (1/(x+1.0))
#define NUMSTEPS (64)
#define INTERVAL_LO (0.0)
#define INTERVAL_HI (2.0)

/* Reads 1-byte integers, one at a time, from stdin.


* A value in the range 1..NUMSTEPS causes a single trapezoid
* to be computed, and its area written (as a binary float) to stdout.
* Zero means quit: all else is a fatal error.
* Each trapezoid represents a 1/NUMSTEPS part of the INTERVAL.
*
* Tested on Linux: portability unknown.
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

#define PRINT if(debug)fprintf


#define PRINT2 if(debug > 1)fprintf
extern char **environ;

typedef int Boolean;


/*typedef unsigned char u_char;*/

int debug = 0;
int forkcount;
/****************************************************************
*
*/
void fatal( char *string )
{
fprintf( stderr, "FATAL ERROR: %s\\n", string );
exit(1);
}
/****************************************************************
*
*/
void parse_args( int argc, char **argv )
{
if( argc != 1 ) {
fprintf( stderr, "Illegal arglist to child: %d\\n", argc );
exit(1);
}
}
/****************************************************************
*
*/
void system_fatal( char *string )
{
fprintf( stderr, "CHILD FATAL SYSTEM ERROR: ");
perror( string );
exit(1);
}

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
/****************************************************************
*
* Returns the area under f(x1)..f(x2) assuming f is linear between.
* Assumes x2 > x1.
* Expects macro FUNC.
*/
float trapezoid( float x1, float x2 )
{
float f1 = FUNC(x1);
float f2 = FUNC(x2);
return (x2-x1) * (f1+f2)/2.;
}
/****************************************************************
*
* Returns area of indexed trapezoid.
*/
float integrate( int index )
{
float x1, x2, deltax;

deltax = (INTERVAL_HI - INTERVAL_LO)/ NUMSTEPS;


x1 = INTERVAL_LO + index * deltax;
x2 = x1 + deltax;
return trapezoid( x1, x2 );
}
/****************************************************************
*/
main( int argc, char **argv )
{
int area_len = sizeof(float);
float area;
u_char buf;

parse_args( argc, argv );


for(;;) {
if( read( 0, &buf, 1 ) != 1 ) system_fatal( "read" );
PRINT( stderr, "child reads %d from pipe.\\n", buf );
if( buf == 0 ) exit(0);
if( buf > NUMSTEPS ) {
fprintf( stderr, "child: illegal %d read.\\n", buf );
exit(1);
}
area = integrate( buf-1 );
PRINT( stderr, "Child: area %d is %g\\n", buf, area );
if( write( 1, &area, area_len ) != area_len ) system_fatal( "write" );
}
}

13. Here is the quadrature problem using Mach C threads (with politically incorrect name
for the trapezoid solver code).

===========================================================================
===========================================================================
/* Trapezoidal Rule Quadrature using Mach C threads
*
* Use the following command to compile this file
* (assumed to be named trapezoid.c)
*
* trapezoid: trapezoid.o timer.o
* cc -g -o trapezoid trapezoid.o timer.o
*
* trapezoid.o: trapezoid.c

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
* cc -c trapezoid.c
*
*/

#include

/* #define TRUE 1 */
#define bottomRange 0.0
#define topRange 2.0
#define intervals 64
#define maxN 8

#define iLeft(i) 2*i+N


#define iRight(i) 2*i+N+1

/* Local function prototypes */


void solver(int);

/* Shared memory map


*
* Result from Solver 0
* Result from Solver 1
* ...
* Result from Solver N-1
* Left endpoint for Solver 0
* Right endpoint for Solver 0
* Left endpoint for Solver 1
* Right endpoint for Solver 1
* ...
* Left endpoint for Solver N-1
* Right endpoint for Solver N-1
*
* The memory descriptors follow ...
*/
float *shMem; /* Pointer to shared memory as floats */
char *cshMem; /* Pointer to shared memory as chars */

/* Locks for synchronization between solvers and master */


mutex_t resultLock[maxN];
mutex_t segLock[maxN];

extern int errno;


int N;

main (argc, argv)


int argc;
char *argv[];
{
cthread_t t_handle[maxN];
cthread_t cthread_fork();

float getTime();
float currentLeft, currentRight;
float length, result;
float startTime, stopTime;

int i, nAlive, solverNum;

unsigned lengthSM;

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions

/* Get a value of N from the command line */


if(argc == 2)
{
N = atoi(argv[1]);
if (N > maxN)
{
printf("Usage: N is too large\n");
exit(0);
};
}
else
{
printf("Usage: trapezoid N\n");
exit(0);
};

/* Initialize variables for the test */


result = 0.0;
length = (topRange-bottomRange)/(float)intervals;
currentLeft = bottomRange;
currentRight = bottomRange;
cthread_init(); /* Initialize the C threads package */

/* Create the shared memory */


lengthSM = 3*N*sizeof(float); /* See shared mem map above */
cshMem = malloc(lengthSM); /* Make the space be shared */
shMem = (float *) cshMem; /* Access the array as floats */

/* Create the N solvers */


for (i=0; i < N; i++)
{
shMem[iLeft(i)] = 0.0;
shMem[iRight(i)] = 0.0;
/* Setup locks on the buckets */
resultLock[i] = mutex_alloc();
mutex_lock(resultLock[i]);
segLock[i] = mutex_alloc();

/* Create the solver thread */


t_handle[i] = cthread_fork(solver, i);
};

nAlive = N;

/* This loop controls the dispatching of requests and processing of results


*/
startTime = getTime(); /* Starting time */
solverNum = 0;
while (nAlive > 0)
{
/* Wait for a message from a solver */
while (!mutex_try_lock(resultLock[solverNum]))
{
cthread_yield();
solverNum = (solverNum +1) % N;
};

/* Acquired lock */
result = result + shMem[solverNum];

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
/* Dispatch new work to the solver that just finished */
if (currentRight+length <= topRange)
{ /* Assign the next segment to the solver */
shMem[iLeft(solverNum)] = currentLeft;
shMem[iRight(solverNum)] = currentRight;
mutex_unlock(segLock[solverNum]);
currentLeft = currentRight;
currentRight = currentRight + length;
}
else
{ /* Eliminate the solver */
shMem[iLeft(solverNum)] = 1.0;
shMem[iRight(solverNum)] = 0.0;
mutex_unlock(segLock[solverNum]);
cthread_join(t_handle[solverNum]);
nAlive--;
};
};

/* All done -- Report the time & result for this iteration */
stopTime = getTime();
printf("%d processes required %6.2f seconds, result = %f\n",
N, stopTime-startTime, result);

exit(0);
}

/*------------ The Solver Thread Schema --------------- */

void solver(me)
int me;
{
float left, right;
float result;
int i;

/* Ask for initial work by writing a result of zero */


mutex_lock(segLock[me]); /* This should pass immediately */
shMem[me] = 0.0;
mutex_unlock(resultLock[me]);

left = 0.0;
right = 0.0;
while (TRUE)
{
/* Wait for a pair of endpoints */
mutex_lock(segLock[me]);
left = shMem[iLeft(me)];
right = shMem[iRight(me)];

/* Terminate if the left endpoint is greater than the right */


if (left > right) cthread_exit(0);

/* make the call measurable */


for (i=0;i < 1000;i++) cthread_yield();

shMem[me] = ((1.0/(left+1.0))+(1.0/(right+1.0)))/2.0*(right-
left);

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
mutex_unlock(resultLock[me]);
};

Here is the SOR problem using Mach C threads.

#include <stdio.h>
#include <cthreads.h>

#define MAX_N 4

/* Local function prototype */


void solver(int);

/* Shared memory among the threads */


mutex_t lock[MAX_N];
float A[MAX_N][MAX_N], b[MAX_N], x[MAX_N];

int N;

main(argc, argv)
int argc;
char *argv[];
{
FILE *data_fp, *fopen();
int i;
int tmp;
double double_eps;
float error, check();
float epsilon;
float solveX();
char data_filename[128];
char host_filename[128];

cthread_t t_handle[MAX_N];
cthread_t cthread_fork();

/* Get data from file; first, get the command line parameters ... */
if(argc == 3)
{
double_eps = atof(argv[1]);
epsilon = double_eps;
strcpy(data_filename, argv[2]);
}
else
{
printf("Usage: chaosMaster \n");
exit(0);
};

/* ... then read the data file */


printf("Opening %s ...\n", data_filename);
if((data_fp = fopen(data_filename, "r")) == NULL)
{
printf("Open on %s failed, exiting ...\n", data_filename);
exit(1);
};
fscanf(data_fp, "%d", &tmp);
N = tmp;
if(N > MAX_N)
{

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
printf("Degree %d is too large, halting ...\n", N);
exit(1);
};
for (i=0;i < N;i++)
fscanf(data_fp,"%f%f%f", &A[i][0], &A[i][1], &A[i][2]);
fscanf(data_fp,"%f %f %f", &b[0], &b[1], &b[2]);
fscanf(data_fp,"%f %f %f", &x[0], &x[1], &x[2]);
fclose(data_fp);

/* Echo the input */


printf("N = %d\n", N);
for (i=0;i < N;i++)
printf("A[%d][*] = %f %f %f\n", i, A[i][0], A[i][1],
A[i][2]);
printf("b[*] = %f %f %f\n", b[0], b[1], b[2]);
printf("x[*] = %f %f %f\n", x[0], x[1], x[2]);

/* Create the N solvers */


/* This is the code for dynamically defined (cthreads) N */
for (i=0; i < N; i++)
{
/* Initialize lock */
lock[i] = mutex_alloc();
t_handle[i] = cthread_fork(solver, i);
};

/* Solve the system */


error = 1000000.0;
while(error > double_eps)
{
/* Proceed only if all solvers are locked */
for (i=0; i < N; i++)
{
while(mutex_try_lock(lock[i]))
{ /* wasn't locked -- restore it*/
mutex_unlock(lock[i]);
cthread_yield();
};
};
error = check(A, x, b, N);
for (i=0; i < N; i++)
mutex_unlock(lock[i]);
cthread_yield();
};

printf("Solution x[*] = %f %f %f\n", x[0], x[1], x[2]);


printf(" With error factor = %f\n", error);
exit(0);
}

float check(A, x, b, n)
float A[][4], x[], b[];
int n;
{
int i, j;
float sum;
float error;

error = 0.0;
for(i=0; i < n; i++)
{

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
sum = 0.0;
for (j=0; j < n; j++)
sum = sum + A[i][j]*x[j];
error = error + (sum - b[i]);
};
if(error<0.0) error = -error;
return(error);
}

/*--------- The Solver Thread Schema -----------*/


void solver(me)
int me;
{
int j;

cthread_yield();
for(;;)
{
mutex_lock(lock[me]);
x[me] = b[me];
for (j=0;j < N; j++)
if(me!=j) x[me] = x[me] - A[me][j]*x[j];
x[me] = x[me]/A[me][me];
};
}

14. SOR with pipes

#include <stdio.h>
#include <signal.h>

#define MAX_N 4

int N;
double A[MAX_N][MAX_N], b[MAX_N];
int xout[MAX_N][2];
int xin[MAX_N][2];

int main(int argc, char *argv[]) {


double check(double [][MAX_N], double [], double [], int);
void solver(int);

FILE *data_fp, *fopen();


int i, j;
int status;
int solverPID[MAX_N];
double epsilon, newX;
double x[MAX_N];
char data_filename[128];

/*******************************************************/
/* Get the command line parameters ... */
if(argc == 3) {
sscanf(argv[1], "%lf", &epsilon);
strcpy(data_filename, argv[2]);
} else {
printf("Usage: sor \n");
exit(1);

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
};

/* Now read the data from the file */


printf("Epsilon = %lf, opening %s ...\n", epsilon, data_filename);
if((data_fp = fopen(data_filename, "r")) == NULL) {
printf("Open on %s failed, exiting ...\n", data_filename);
exit(1);
};
fscanf(data_fp, "%d", &N);
if(N > MAX_N) {
printf("Degree %d is too large, halting ...\n", N);
exit(1);
};
printf("N = %d\n", N);

/* Read the A matrix */


for (i=0; i < N; i++) {
for (j=0; j < N; j++) {
fscanf(data_fp,"%lf", &A[i][j]);
printf(" A[%d, %d] %lf", i, j, A[i][j]);
};
printf("\n");
};

/* Read the b vector */


for (j=0; j < N; j++) {
fscanf(data_fp,"%lf", &b[j]);
printf(" b[%d] %lf", j, b[j]);
};

/* Read the x vector guess */


printf("\n");
for (j=0; j < N; j++) {
fscanf(data_fp,"%lf", &x[j]);
printf(" x[%d] %lf", j, x[j]);
};

printf("\n");
fclose(data_fp);

/*******************************************************/
/* Create the N solvers */
for (i=0; i < N; i++) {
pipe(xin[i]); /* Pipe for solver[i] to return x[i]
*/
pipe(xout[i]); /* Pipe for solver[i] to get x[j] */
if((solverPID[i] = fork()) == 0) {;
solver(i); /* The i-th child runs this code */
exit(1); /* Never get here -- never returns */
};
};
/* The parent continues ... */

/*******************************************************/
/* Iteratively solve the system */
while(check(A, x, b, N) > epsilon) {
/* Distribute the newest x values*/
for (i=0; i < N; i++) {
for(j=0; j < N; j++)
if(j != i)

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
write(xout[i][1],
&x[j],sizeof(double));
};
/* Read x[i] from xin[i]. Since we cannot procede beyond
* this loop until ALL solvers have returned a value, we can
* use a blocking read here, i.e., we can wait for all in any order.
*/
for (i=0; i < N; i++)
read(xin[i][0], &x[i], sizeof(double));
};

/* Solution converged -- terminate the solvers */


for (i=0; i < N; i++) {
kill(solverPID[i], SIGTERM);
wait(&status);
};

/* Report the results */


printf("\nSolution with error factor = %lf:\n", check(A, x, b, N));
for (i=0; i < N; i++) {
printf(" x[%d] = %lf\n", i, x[i]);
};
printf("\n");
}

double check(double A[][MAX_N], double x[], double b[], int n) {


int i, j;
double sum, error;

error = 0.0;
for(i=0; i < n; i++) {
sum = 0.0;
for (j=0; j < n; j++)
sum = sum + A[i][j]*x[j];
error = error + (sum - b[i]);
};
if(error < 0.0) error = -error;
return(error);
}

/*--------- The Solver Schema -----------*/


void solver(int me) {
int i;
double x[MAX_N];

for(;;) {
/* Get new x values */
for(i=0; i < N; i++)
if(i != me)
read(xout[me][0], &x[i], sizeof(double));
x[me] = b[me];
for (i=0; i < N; i++)
if(i != me) x[me] = x[me] - A[me][i]*x[i];
x[me] = x[me]/A[me][me];
/* Return the new x[me] value */
write(xin[me][1], &x[me], sizeof(double));
};
}

15. This solution is provided by Sam Siewert, Spring, 1996.

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
Makefile:

#CFLAGS = -g -DDEBUG
CFLAGS = -g
CC = cc
SRC =
OBJ = ${SRC:.c=.o}
HDR =
MAINSRC = vt.c

.SUFFIXES: .c.o

vt: ${OBJ} ${MAINSRC} ${HDR}


${CC} ${CFLAGS} vt.c ${OBJ} -o $@

.c.o:
${CC} -c ${CFLAGS} $<

${OBJ}: ${HDR}

clean:
rm vt

Virtual Terminal Manager:

#include <signal.h>
#include <stdio.h>
#include <termio.h>
#include <unistd.h>
#include <fcntl.h>

#define TRUE 1
#define FALSE 0
#define NUMCOL 80
#define NUMLINES 25
#define NUM_VTS 2

struct vt_status_st {
int line;
int bufchars;
int total_lines_out;
int pid;
int running;
int inputchars;
int awaiting_newinput;
};

static struct vt_status_st vt_status[2];


static int active_vt = 0;
static int pipe_to_child_1[2];
static int pipe_to_child_2[2];
static int pipe_from_child_1[2];
static int pipe_from_child_2[2];

void vt_exit_handler()
{
int status;
int pid;

pid = wait(&status);

if(vt_status[0].pid == pid) {

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
fprintf(stderr, "vt0 exited\n");
close(pipe_from_child_1[0]);
close(pipe_to_child_1[1]);
vt_status[0].running = 0;
if(vt_status[1].running)
active_vt = 1;
else
exit(0);
}
else if(vt_status[1].pid == pid) {
fprintf(stderr, "vt1 exited\n");
close(pipe_from_child_2[0]);
close(pipe_to_child_2[1]);
vt_status[1].running = 0;
if(vt_status[0].running)
active_vt = 0;
else
exit(0);
}
else
fprintf(stderr, "somone else exited?\n");

void vt_abort()
{
write(STDERR_FILENO, "killing vts\n", 12);
fflush(stderr);

if(close(pipe_from_child_1[0]) == -1)
perror("close cp1 input");
if(close(pipe_to_child_1[1]) == -1)
perror("close pp1 output");
if(close(pipe_from_child_2[0]) == -1)
perror("close cp2 input");
if(close(pipe_to_child_2[1]) == -1)
perror("close pp2 output");

kill(vt_status[0].pid, SIGKILL);
kill(vt_status[1].pid, SIGKILL);

exit(-1);
}

main(int argc, char **argv)


{
int pid1, pid2, status, nbytes, i, j, val;
char buffer[NUMCOL];
char vt_buffer[NUM_VTS][NUMLINES][NUMCOL];
char vt_input_buffer[NUM_VTS][NUMCOL];
char c;
struct termio tiold, tinew;
int promptsynch, lastline;

/* clear screen to start */


system("clear");

/* terminal settings for character at a time input */


ioctl(STDIN_FILENO, TCGETA, &tiold);
tinew = tiold;
tinew.c_lflag &= ~ICANON;

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
tinew.c_lflag &= ~(ECHOCTL);
tinew.c_cc[VMIN] = 1;
tinew.c_cc[VTIME] = 0;

if(argc < 3 || argc > 4) {


fprintf(stderr, "usage: vt [-promptsynch]\n");
exit(-1);
}
else if(argc == 4)
promptsynch = 1;
else /* argc must equal 3 */
promptsynch = 0;

/* trap child exit signals */


signal(SIGCHLD, vt_exit_handler);

/* deal with vt manager abort */


signal(SIGINT, vt_abort);

if(pipe(pipe_to_child_1) < 0) {
perror("parent pipe 1");
exit(-1);
}

if(pipe(pipe_to_child_2) < 0) {
perror("parent pipe 1");
exit(-1);
}

if(pipe(pipe_from_child_1) < 0) {
perror("child pipe 1");
exit(-1);
}

if(pipe(pipe_from_child_2) < 0) {
perror("child pipe 2");
exit(-1);
}

pid1 = fork();
vt_status[0].pid = pid1;

if (pid1 == 0) {
#ifdef DEBUG
printf("This is child 1\n");
#endif

/* close write end of pipe_to_child_1 for child */


close(pipe_to_child_1[1]);

/* close read end of pipe_from_child_1 for child */


close(pipe_from_child_1[0]);

#ifdef DEBUG
write(pipe_from_child_1[1], "hello", 5);
nbytes = read(pipe_to_child_1[0], buffer, 5);
buffer[nbytes] = '\0';
printf("Child 1 hears parent %s\n", buffer);
#endif

/* so that write to pipe by VT manager sends data to execed stdin */


dup2(pipe_to_child_1[0], STDIN_FILENO);

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
/* so that read from pipe by VT manager gets data from execed stdout */
dup2(pipe_from_child_1[1], STDOUT_FILENO);

signal(SIGINT, SIG_IGN);
execlp(argv[1], argv[1], (char *)0);
}
else {
#ifdef DEBUG
printf("This is the parent\n");
#endif
pid2 = fork();
vt_status[1].pid = pid2;
if (pid2 == 0) {
#ifdef DEBUG
printf("This is child 2\n");
#endif

/* close write end of pipe_to_child_2 for child */


close(pipe_to_child_2[1]);

/* close read end of pipe_from_child_2 for child */


close(pipe_from_child_2[0]);

#ifdef DEBUG
write(pipe_from_child_2[1], "hello", 5);
nbytes = read(pipe_to_child_2[0], buffer, 5);
buffer[nbytes] = '\0';
printf("Child 2 hears parent %s\n", buffer);
#endif

/* so that write to pipe by VT manager sends data to execed stdin */


dup2(pipe_to_child_2[0], STDIN_FILENO);

/* so that read from pipe by VT manager gets data from execed stdout
*/
dup2(pipe_from_child_2[1], STDOUT_FILENO);

signal(SIGINT, SIG_IGN);
execlp(argv[2], argv[2], (char *)0);
}
else {

/* close write end of child_pipes for parent */


close(pipe_from_child_1[1]);
close(pipe_from_child_2[1]);

/* close read end of parent_pipes for parent */


close(pipe_to_child_1[0]);
close(pipe_to_child_2[0]);

#ifdef DEBUG
printf("This is the parent\n");
nbytes = read(pipe_from_child_1[0], buffer, 5);
buffer[nbytes] = '\0';
printf("Parent hears child 1 %s\n", buffer);
nbytes = read(pipe_from_child_2[0], buffer, 5);
buffer[nbytes] = '\0';
printf("Parent hears child 2 %s\n", buffer);
write(pipe_to_child_1[1], "hello", 5);
write(pipe_to_child_2[1], "hello", 5);

#endif

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
vt_status[0].line = 0;
vt_status[1].line = 0;
vt_status[0].total_lines_out = 0;
vt_status[1].total_lines_out = 0;
vt_status[0].bufchars = 0;
vt_status[1].bufchars = 0;
vt_status[0].inputchars = 0;
vt_status[1].inputchars = 0;
vt_status[0].running = 1;
vt_status[1].running = 1;
vt_status[0].awaiting_newinput = 1;
vt_status[1].awaiting_newinput = 1;

ioctl(STDIN_FILENO, TCSETA, &tinew);

/* Make sure writes to child blocking */


val = fcntl(pipe_to_child_1[1], F_GETFL, 0);
val &= ~O_NONBLOCK;
fcntl(pipe_to_child_1[1], F_SETFL, val);

val = fcntl(pipe_to_child_2[1], F_GETFL, 0);


val &= ~O_NONBLOCK;
fcntl(pipe_to_child_2[1], F_SETFL, val);

/* Main Loop for VT Manager */

for(;;) {

/* read program output if any */


if(vt_status[0].awaiting_newinput) {
#ifdef DEBUG
printf("reading vt0 output\n");
#endif
vt_status[0].bufchars = 0;
/* initial blocking read for prompt synch. if applicable */
if(promptsynch) {
nbytes = read(pipe_from_child_1[0],

&(vt_buffer[0][vt_status[0].line][vt_status[0].bufchars]),
1);
}

/* set pipe non-blocking in case there is no output */


val = fcntl(pipe_from_child_1[0], F_GETFL, 0);
val |= O_NONBLOCK;
fcntl(pipe_from_child_1[0], F_SETFL, val);

do {
if(nbytes > 0) {
if(vt_buffer[0][vt_status[0].line][vt_status[0].bufchars] == '\n')
{
#ifdef DEBUG
printf("vt0 read: %s", vt_buffer[0][vt_status[0].line]);
#endif
if(active_vt == 0) {
write(STDOUT_FILENO, vt_buffer[0][vt_status[0].line],
vt_status[0].bufchars+1);
}

vt_buffer[0][vt_status[0].line][vt_status[0].bufchars+1] = '\0';
vt_status[0].line = (vt_status[0].line+1) % NUMLINES;
vt_status[0].total_lines_out++;

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
vt_status[0].bufchars = 0;
}
else
vt_status[0].bufchars++;
}
nbytes = read(pipe_from_child_1[0],

&(vt_buffer[0][vt_status[0].line][vt_status[0].bufchars]), 1);
} while(nbytes > 0);
#ifdef DEBUG
printf("vt0 read: %s", vt_buffer[0][vt_status[0].line]);
#endif
val = fcntl(pipe_from_child_1[0], F_GETFL, 0);
val &= ~O_NONBLOCK;
fcntl(pipe_from_child_1[0], F_SETFL, val);
if(active_vt == 0) {
write(STDOUT_FILENO, vt_buffer[0][vt_status[0].line],
vt_status[0].bufchars);
}
vt_status[0].awaiting_newinput = 0;
} /* end awaiting new input */

if(vt_status[1].awaiting_newinput) {
#ifdef DEBUG
printf("reading vt1 output\n");
#endif
vt_status[1].bufchars = 0;
/* initial blocking read for prompt synch. if applicable */
if(promptsynch) {
nbytes = read(pipe_from_child_2[0],

&(vt_buffer[1][vt_status[1].line][vt_status[1].bufchars]),
1);
}

/* set pipe non-blocking in case there is no output */


val = fcntl(pipe_from_child_2[0], F_GETFL, 0);
val |= O_NONBLOCK;
fcntl(pipe_from_child_2[0], F_SETFL, val);

do {
if(nbytes > 0) {
if(vt_buffer[1][vt_status[1].line][vt_status[1].bufchars] == '\n')
{
#ifdef DEBUG
printf("vt1 read: %s", vt_buffer[1][vt_status[1].line]);
#endif
if(active_vt == 1) {
write(STDOUT_FILENO, vt_buffer[1][vt_status[1].line],
vt_status[1].bufchars+1);
}
vt_buffer[1][vt_status[1].line][vt_status[1].bufchars+1] = '\0';
vt_status[1].line = (vt_status[1].line+1) % NUMLINES;
vt_status[1].total_lines_out++;
vt_status[1].bufchars = 0;
}
else
vt_status[1].bufchars++;
}
nbytes = read(pipe_from_child_2[0],

&(vt_buffer[1][vt_status[1].line][vt_status[1].bufchars]), 1);
} while(nbytes > 0);

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
#ifdef DEBUG
printf("vt1 read: %s", vt_buffer[1][vt_status[1].line]);
#endif
val = fcntl(pipe_from_child_2[0], F_GETFL, 0);
val &= ~O_NONBLOCK;
fcntl(pipe_from_child_2[0], F_SETFL, val);
if(active_vt == 1) {
write(STDOUT_FILENO, vt_buffer[1][vt_status[1].line],
vt_status[1].bufchars);
}
vt_status[1].awaiting_newinput = 0;
} /* end awaiting input */

/* get standard input to VT manager */


c = getchar();

if(c == '\e') {
c = getchar();
if(c == 'q')
vt_abort();
else if(c == 'c') {
if(vt_status[0].running && vt_status[1].running)
active_vt = (active_vt+1) % NUM_VTS;
#ifdef DEBUG
printf("active_vt = %ld\n", active_vt);
#endif
/* Write out active VT screen buffer to update display for
switch */
switch(active_vt) {
case 0:
#ifdef DEBUG
printf("writing out %ld vt lines\n",
vt_status[0].total_lines_out);
#endif
if(vt_status[0].total_lines_out > NUMLINES) {
j = (vt_status[0].line + 1) % NUMLINES;
lastline = NUMLINES - 1;
}
else {
j = 0;
lastline = vt_status[0].total_lines_out;
}
for(i=0;i<=lastline;i++) {
write(STDOUT_FILENO, vt_buffer[0][j],
strlen(vt_buffer[0][j]));
j = (j + 1) % NUMLINES;
}
write(STDOUT_FILENO, vt_input_buffer[0],
vt_status[0].inputchars);
break;
case 1:
#ifdef DEBUG
printf("writing out %ld vt lines\n",
vt_status[1].total_lines_out);
#endif
if(vt_status[1].total_lines_out > NUMLINES) {
j = (vt_status[1].line + 1) % NUMLINES;
lastline = NUMLINES - 1;
}
else {
j = 0;
lastline = vt_status[1].total_lines_out;
}

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
for(i=0;i<=lastline;i++) {
write(STDOUT_FILENO, vt_buffer[1][j],
strlen(vt_buffer[1][j]));
j = (j + 1) % NUMLINES;
}
write(STDOUT_FILENO, vt_input_buffer[1],
vt_status[1].inputchars);
break;
default:
write(STDERR_FILENO, "switch to unknown vt\n", 21);
}
}
} /* end if esc-c vt switch command */

/* VT manager standard input to be directed to active VT */


else {

switch(active_vt) {

case 0:
vt_input_buffer[0][vt_status[0].inputchars] = c;
vt_status[0].inputchars++;
if(c == '\n') {
vt_input_buffer[0][vt_status[0].inputchars] = '\0';
strcat(vt_buffer[0][vt_status[0].line],
vt_input_buffer[0]);
#ifdef DEBUG
printf("vt0 written: %s", vt_input_buffer[0]);
#endif
write(pipe_to_child_1[1], vt_input_buffer[0],
vt_status[0].inputchars);
vt_status[0].inputchars = 0;
#ifdef DEBUG
printf("vt0 buffer line: %s",
vt_buffer[0][vt_status[0].line]);
#endif
vt_status[0].line = (vt_status[0].line+1) % NUMLINES;
vt_status[0].total_lines_out++;
vt_status[0].bufchars = 0;

do {
/* initial blocking read here assumes command results in
some
newline terminated output */
#ifdef DEBUG
printf("calling blocking read from child\n");
#endif
nbytes = read(pipe_from_child_1[0],

&(vt_buffer[0][vt_status[0].line][vt_status[0].bufchars]),
1);
#ifdef DEBUG
printf("nbytes = %ld\n", nbytes);
#endif
if(nbytes > 0) {
#ifdef DEBUG
printf("vt0 read char: %c",

vt_buffer[0][vt_status[0].line][vt_status[0].bufchars]);
#endif
if(vt_buffer[0][vt_status[0].line][vt_status[0].bufchars]
== '\n') {
#ifdef DEBUG

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
printf("vt0 read line: %s",
vt_buffer[0][vt_status[0].line]);
#endif
write(STDOUT_FILENO, vt_buffer[0][vt_status[0].line],
vt_status[0].bufchars+1);

vt_buffer[0][vt_status[0].line][vt_status[0].bufchars+1] = '\0';
vt_status[0].line = (vt_status[0].line+1) % NUMLINES;
vt_status[0].total_lines_out++;
vt_status[0].bufchars = 0;

/* read any additional output non-blocking */


val = fcntl(pipe_from_child_1[0], F_GETFL, 0);
val |= O_NONBLOCK;
fcntl(pipe_from_child_1[0], F_SETFL, val);

}
else {
vt_status[0].bufchars++;
}
} /* end if bytes > 0 */
} while(nbytes > 0);

val = fcntl(pipe_from_child_1[0], F_GETFL, 0);


val &= ~O_NONBLOCK;
fcntl(pipe_from_child_1[0], F_SETFL, val);

if(vt_status[0].bufchars > 0) {
vt_buffer[0][vt_status[0].line][vt_status[0].bufchars] ==
'\0';
write(STDOUT_FILENO, vt_buffer[0][vt_status[0].line],
vt_status[0].bufchars);
vt_status[0].bufchars = 0;
}
else if(promptsynch) /* prompt not retrieved here */
vt_status[0].awaiting_newinput = 1;
}
break;

case 1:
vt_input_buffer[1][vt_status[1].inputchars] = c;
vt_status[1].inputchars++;
if(c == '\n') {
vt_input_buffer[1][vt_status[1].inputchars] = '\0';
strcat(vt_buffer[1][vt_status[1].line],
vt_input_buffer[1]);
#ifdef DEBUG
printf("vt1 written: %s", vt_input_buffer[1]);
#endif
write(pipe_to_child_2[1], vt_input_buffer[1],
vt_status[1].inputchars);
vt_status[1].inputchars = 0;
#ifdef DEBUG
printf("vt1 buffer line: %s",
vt_buffer[1][vt_status[1].line]);
#endif
vt_status[1].line = (vt_status[1].line+1) % NUMLINES;
vt_status[1].total_lines_out++;
vt_status[1].bufchars = 0;

do {
/* initial blocking read here assumes command results

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
in some output */
#ifdef DEBUG
printf("calling blocking read from child\n");
#endif
nbytes = read(pipe_from_child_2[0],

&(vt_buffer[1][vt_status[1].line][vt_status[1].bufchars]),
1);
#ifdef DEBUG
printf("nbytes = %ld\n", nbytes);
#endif
if(nbytes > 0) {
#ifdef DEBUG
printf("vt1 read char: %c",

vt_buffer[1][vt_status[1].line][vt_status[1].bufchars]);
#endif
if(vt_buffer[1][vt_status[1].line][vt_status[1].bufchars]
== '\n') {
#ifdef DEBUG
printf("vt1 read: %s",
vt_buffer[1][vt_status[1].line]);
#endif
write(STDOUT_FILENO, vt_buffer[1][vt_status[1].line],
vt_status[1].bufchars+1);

vt_buffer[1][vt_status[1].line][vt_status[1].bufchars+1] = '\0';
vt_status[1].line = (vt_status[1].line+1) % NUMLINES;
vt_status[1].total_lines_out++;
vt_status[1].bufchars = 0;

val = fcntl(pipe_from_child_2[0], F_GETFL, 0);


val |= O_NONBLOCK;
fcntl(pipe_from_child_2[0], F_SETFL, val);

}
else {
vt_status[1].bufchars++;
}
}
} while(nbytes > 0);

val = fcntl(pipe_from_child_2[0], F_GETFL, 0);


val &= ~O_NONBLOCK;
fcntl(pipe_from_child_2[0], F_SETFL, val);

if(vt_status[1].bufchars > 0) {
vt_buffer[1][vt_status[1].line][vt_status[1].bufchars] ==
'\0';
write(STDOUT_FILENO, vt_buffer[1][vt_status[1].line],
vt_status[1].bufchars);
vt_status[1].bufchars = 0;
}
else if(promptsynch) /* prompt not retrieved here */
vt_status[1].awaiting_newinput = 1;
}
break;

default:
write(STDERR_FILENO, "input to unknown virtual terminal\n",
34);

©2004 «GreetingLine»
Gary Nutt, Operating Systems 3/e
Instructor’s Solutions
} /* end switch active vt */
} /* end else if vt input */
} /* end for loop */

ioctl(STDIN_FILENO, TCSETA, &tiold);

} /* end else parent code after second fork -- the vt manager process
*/
} /* end else parent code after first fork */

©2004 «GreetingLine»
Another random document with
no related content on Scribd:
The Project Gutenberg eBook of Ending the
depression through planned obsolescence
This ebook is for the use of anyone anywhere in the United States
and most other parts of the world at no cost and with almost no
restrictions whatsoever. You may copy it, give it away or re-use it
under the terms of the Project Gutenberg License included with this
ebook or online at www.gutenberg.org. If you are not located in the
United States, you will have to check the laws of the country where
you are located before using this eBook.

Title: Ending the depression through planned obsolescence

Author: Bernard London

Release date: November 1, 2023 [eBook #72003]

Language: English

Original publication: New York: self-published, 1932

Credits: Bob Taylor, Tim Lindell and the Online Distributed


Proofreading Team at https://www.pgdp.net (This book was
produced from images made available by the HathiTrust
Digital Library.)

*** START OF THE PROJECT GUTENBERG EBOOK ENDING THE


DEPRESSION THROUGH PLANNED OBSOLESCENCE ***
Ending the Depression
through
Planned Obsolescence

BY
Bernard London
Ending the Depression
through
Planned Obsolescence
BY
Bernard London

21 EAST FORTIETH STREET


NEW YORK, N. Y.
COPYRIGHT, 1932, BY BERNARD LONDON
Ending the Depression
Through Planned Obsolescence
By Bernard London

F RANK A. VANDERLIP, former President of the National City


Bank, of New York, characterized this as a stupid depression. He
emphasized the fact that millions were suffering amidst glutted
markets and surpluses.
The new paradox of plenty constitutes a challenge to revolutionize
our economic thinking. Classical economics was predicated on the
belief that nature was niggardly and that the human race was
constantly confronted by the spectre of shortages. The economist
Malthus writing in 1798 warned that the race would be impoverished
by an increase in population which he predicted would greatly
exceed gains in the production of foodstuffs.
However, modern technology and the whole adventure of applying
creative science to business have so tremendously increased the
productivity of our factories and our fields that the essential
economic problem has become one of organizing buyers rather than
of stimulating producers. The essential and bitter irony of the present
depression lies in the fact that millions of persons are deprived of a
satisfactory standard of living at a time when the granaries and
warehouses of the world are overstuffed with surplus supplies, which
have so broken the price level as to make new production
unattractive and unprofitable.
Primarily, this country and other countries are suffering from
disturbed human relationships.
Factories, warehouses, and fields are still intact and are ready to
produce in unlimited quantities, but the urge to go ahead has been
paralyzed by a decline in buying power. The existing troubles are
man-made, and the remedies must be man-conceived and man-
executed.
In the present inadequate economic organization of society, far too
much is staked on the unpredictable whims and caprices of the
consumer. Changing habits of consumption have destroyed property
values and opportunities for employment. The welfare of society has
been left to pure chance and accident.
In a word, people generally, in a frightened and hysterical mood,
are using everything that they own longer than was their custom
before the depression. In the earlier period of prosperity, the
American people did not wait until the last possible bit of use had
been extracted from every commodity. They replaced old articles
with new for reasons of fashion and up-to-dateness. They gave up
old homes and old automobiles long before they were worn out,
merely because they were obsolete. All business, transportation, and
labor had adjusted themselves to the prevailing habits of the
American people. Perhaps, prior to the panic, people were too
extravagant; if so, they have now gone to the other extreme and
have become retrenchment-mad.
People everywhere are today disobeying the law of obsolescence.
They are using their old cars, their old tires, their old radios and their
old clothing much longer than statisticians had expected on the basis
of earlier experience.
The question before the American people is whether they want to
risk their future on such continued planless, haphazard, fickle
attitudes of owners of ships and shoes and sealing wax.
What the people can afford is very different at a time when the
majority are gainfully employed than it is in a period when perhaps
ten million are without gainful employment. The job of modern
management is to balance production with consumption—to enable
one large group, like the factory workers in the cities, to exchange
the products of their hours of labor for the output of farmers. The
prevailing defeatist assumption that depression and unemployment
must continue because we have too much of everything, is the
counsel of despair.
Society is suffering untold loss in foregoing the workpower of ten
million human beings. The present deadlock is the inevitable result
of traveling along blind alleys. Chaos must unavoidably flow from an
unplanned economic existence.
In the future, we must not only plan what we shall do, but we
should also apply management and planning to undoing the obsolete
jobs of the past. This thought constitutes the essence of my plan for
ending the depression and for restoring affluence and a better
standard of living to the average man.
My proposal would put the entire country on the road to recovery,
and eventually restore normal employment conditions and sound
prosperity. My suggested remedy would provide a permanent source
of income for the Federal Government and would relieve it for all
time of the difficulties of balancing its budget.
Briefly stated, the essence of my plan for accomplishing these
much-to-be-desired ends is to chart the obsolescence of capital and
consumption goods at the time of their production.
I would have the Government assign a lease of life to shoes and
homes and machines, to all products of manufacture, mining and
agriculture, when they are first created, and they would be sold and
used with the term of their existence definitely known by the
consumer. After the allotted time had expired, these things would be
legally “dead” and would be controlled by the duly appointed
governmental agency and destroyed if there is widespread
unemployment. New products would constantly be pouring forth from
the factories and marketplaces, to take the place of the obsolete,
and the wheels of industry would be kept going and employment
regularized and assured for the masses.
I am not advocating the total destruction of anything, with the
exception of such things as are outworn and useless. To start
business going and employ people in the manufacture of things, it
would be necessary to destroy such things in the beginning—but for
the first time only. After the first sweeping up process necessary to
clean away obsolete products in use today, the system would work
smoothly in the future, without loss or harm to anybody. Wouldn’t it
be profitable to spend a sum of—say—two billion dollars to buy up,
immediately, obsolete and useless buildings, machinery, automobiles
and other outworn junk, and in their place create from twenty to thirty
billion dollars worth of work in the construction field and in the
factory? Such a process would put the entire country on the road to
recovery and eventually would restore normal employment and
business prosperity.
An equally important advantage of a system of planned
obsolescence would be its function in providing a new reservoir from
which to draw income for the operation of the Government. The
actual mechanism involved would be briefly something like this:
The people would turn in their used and obsolete goods to certain
governmental agencies, situated at strategic locations for the
convenience of the public. The individual surrendering, for example,
a set of old dining room furniture, would receive from the Comptroller
or Inspector of such a Station or Bureau, a receipt indicating the
nature of the goods turned in, the date, and the possible value of the
furniture (which is to be paid to him in the future by the Government).
This receipt would be stamped in a receipt book with a number,
which the individual would have received when he first brought in an
obsolete article to be destroyed. Receipts so issued would be
partially equivalent to money in the purchase of new goods by the
individual, in that they would be acceptable to the Government in
payment of the sales tax which would be levied as part of my plan.
For example, a consumer purchasing a $100 radio, on which the
sales tax is 10 per cent or $10, the purchaser would pay cash for the
radio, but could offer $10 worth of receipts for obsolete merchandise
turned in, in payment of the sales tax. The merchant or manufacturer
would have to accept these receipts for this purpose, and would turn
them back to the Government in payment of the sales tax, which
must be borne ultimately by the consumer in any event.
Under this system, the purchaser would feel he had been paid for
the used-up article which he turned in to the Government, yet the
Government would not have had to pay a cent of cash for the goods
so surrendered. As a result of the process, nevertheless, the wheels
of industry would be greased, and factories would be kept busy
supplying new goods, while employment would be maintained at a
higher level.
I maintain that taxes should be levied on the people who are
retarding progress and preventing business from functioning
normally, rather than as at present on those who are cooperating
and promoting progress. Therefore I propose that when a person
continues to possess and use old clothing, automobiles and
buildings, after they have passed their obsolescence date, as
determined at the time they were created, he should be taxed for
such continued use of what is legally “dead.” He could not deny that
he does not possess such goods, as he might hide his income to
avoid paying an income tax, because they are material things, with
their date of manufacture known. Today we penalize by taxation
persons who spend their money to purchase commodities, which are
necessary in order to create business. Would it not be far more
desirable to tax instead the man who is hoarding his money and
keeping old and useless things? We should tax the man who holds
old things for a longer time than originally allotted.
Under the present estate and inheritance tax system, the State
has to wait an indefinite period, and allow the owner of a building or
commodity to keep on earning and adding more to his fortune until
he dies, before it can collect its inheritance tax. With obsolescence of
merchandise computed in advance, the Government will collect
when the article dies, instead of when its owner dies.
Moreover, the present method of collecting revenue under the
income tax is speculative and uncertain, because the profits of
industry and business, upon which the income tax is based, are
subject to vast fluctuations.
If the plan I propose is adopted, there will be a source of
permanent income to the State from goods and merchandise in
existence, and which are bound to continue to exist. Through a
process of checking control of what the manufacturer sells to the
dealer, and through reports by retailers of what they sell to the
consumers, the Government will know by the end of the year just
what income it will be sure of getting, and this amount it will be paid
irrespective of whether people are making big profits or not.
My plan would rectify the fundamental inequalities of our present
economic system, in which we follow a hit-or-miss method, one
getting much more than he needs or can use, and another less or
nothing. We should learn to use our material resources so that all
can partake of them, yet so that none will be any poorer or worse off
than today.
In our present haphazard organization, the product of the worker’s
toil continues to benefit and produce income for its owner long after
the one whose sweat created it has spent and exhausted the
meagre compensation he received for his labor.
The worker’s wages are exhausted in a week or a month in the
purchase of food, clothing and shelter. He has for himself little that is
permanent to show for his hours of toil, whereas the owner of the
building or machine which the worker’s labor helped to construct has
a unit of capital goods which will last for years or even decades. The
man who performed the work received as compensation only
enough to purchase comfort and sustenance for a short time, and he
must continue to labor if he wishes to go on living. The product of the
worker’s hand, however, is a semi-permanent thing and produces
income for its owner for an indefinite period of years. In the end, not
only is the original cost of production repaid and interest yield on the
investment, but far more besides. This very lasting quality of the
product of the worker’s toil results to his disadvantage, for a time
comes such as we are passing through today, when there is an
excess of capital goods and the worker is told: “We have enough
production of wealth; we are going to use up what we have and need
no more for the present. You laborer, go and find work elsewhere.
We do not need you now.”
And so the worker, whose sweat wrought this vast store of
material goods, suffers from poverty and want, while the country is
glutted with everything. My plan would correct this obviously
inequitable situation by arbitrarily limiting the return to capital, to a
stipulated period of years, after which the benefits would revert to the
people.
The situation in which the country now finds itself, in which there is
poverty amidst plenty, is well illustrated by the analogy of a great
giant standing in a pool of fresh water up to his lips, yet crying out
that he is thirsty because he is paralyzed and cannot stoop to drink.
His muscles must be enabled to relax, for him to bend down in order
that he may quench his thirst. So, too, the paralysis which prevents
our economic society from consuming the abundant supplies of raw
materials and manufactured commodities which glut our markets
must be cured before normal conditions can be restored.
Furniture and clothing and other commodities should have a span
of life, just as humans have. When used for their allotted time, they
should be retired, and replaced by fresh merchandise. It should be
the duty of the State as the regulator of business to see that the
system functions smoothly, deciding matters for capital and labor
and seeing that everybody is sufficiently employed. The Government
will have the power to extend the life of articles for a year or two
(upon agreed terms), if they are still useable after their allotted time
has expired and if employment can be maintained at a high peak
without their replacement.
If a machine has been functioning steadily for five years or so, it
can fairly be considered dead—dead to the one who paid his money
for it—because he has had all the use of it during those five years
and it will have paid for its life by its earnings in the five-year period.
Then it should go to the workmen, through the State; its life can be
prolonged if the factories are already busy and there are no
unemployed. But if by its replacement idle workers can be given jobs
and closed factories reopened, then this machine should be
destroyed and new (and probably improved) apparatus produced in
its place.
The original span of life of a commodity would be determined by
competent engineers, economists and mathematicians, specialists in
their fields, on behalf of the Government.
In the course of 30 years under this arrangement, most
construction and production would undergo a fundamental change
for the better, as old, dilapidated and obsolete buildings and
machines disappeared and new ones appeared in their place.
During this period some manufactured commodities would have
been destroyed and replaced 15 times, others 10 times, still others 5
times, etc., depending on the span of life allotted to each, in order for
it to earn sufficient for its purpose before it dies. We must work on
the principle of nature, which creates and destroys, and carries the
process of elimination and replacement through the ages. There
would be no overproduction, were this method adopted, for
production and consumption would be regularized and adjusted to
each other, and it would no longer be necessary to send our surplus
goods to find outlet in foreign markets. We would not then, as we do
today, have to sell these goods on credit and later have to beg for
our money, which in the long run foreign nations do not want to
repay anyway.
In the description of things under the present organization of
society, we continually make use of a system of weights and
measures. Thus, a commodity is evaluated in terms of size—shape,
weight, value, etc. The weights and measures we use are
standardized and regulated by the Government so that they may not
be violated. But, though we may not realize it, this system is
incomplete because in the description of things it omits consideration
of two elements which are equal in importance to those in everyday
use in determining real values. These are life and time, life with
respect to the commodity produced, and time, the period it should
last.
If we add the elements of life and time to our measurement of
what we produce, and say that the life of this automobile shall be not
more than 5 years, or the life of this building shall last not more than
25 years, then, with the addition of our customary measurement of
these commodities, we will have a really complete description of
them right from the beginning. And, when capital purchases the
automobile or the building, it will be doing so only for that limited
period of years, after which the remaining value left in the product
will revert to labor, which produced it in the first place, and which
thus will receive its rightful share in the end, even if it did not do so in
the beginning.
Miracles do not happen. They must be planned in order to occur.
Similarly in this time of economic crisis, we must work out our own
salvation.
If we can afford to sink ships, that cost millions of dollars to
construct, merely for the purpose of giving target practice to the
gunner, then surely we can afford to destroy other obsolete and
useless products in order to give work to millions and pull the country
out of the dire catastrophe in which it is now wallowing.
At the present time our country has plenty of everything, yet
people are in want because of a breakdown in distribution, an
inadequate division of the fruits of labor. Worn-out automobiles,
radios and hundreds of other items, which would long ago have been
discarded and replaced in more normal times, are being made to last
another season or two or three, because the public is afraid or has
not the funds to buy now. The Government should be enabled to
advance a sum of money to certain Trust Agencies to purchase part
of these obsolete buildings and machines and clothing. They should
be thrown into a junk pile, and money lent toward creating new
buildings, machines and commodities.
The State can lend money for the erection of new buildings at an
interest rate of no more than 2½ or 3 per cent. Suppose, though, that
new builders or owners of the buildings pay 5 or 5½ per cent
interest. Two and a half per cent of this would go to the Government
as interest and 2½ or 3 per cent for amortization or to a sinking fund,
out of which to pay back for the construction of the building within 25
or 30 years, computed on a basis of compound interest. At that time,
the building can be destroyed and a new one erected, with resultant
stimulus to employment. The original building in the intervening
years would have served its purpose and fairly repaid its owner.
Capital should be willing to invest its wealth on a 2½ or 3 per cent
interest basis under such circumstances, because the investment
will be safe, steady and permanent. In the present economic chaos,
investments at great interest rates are in jeopardy and, while at
present lenders are getting large returns for their money, their capital
is in constant danger of being wiped out altogether.
The tax-collecting machinery at present used by the Government
could readily be converted into the media for carrying into operation
the system here proposed. It could be used with the same force and
effect, and new laws passed concerning everything produced, just as
our present excise and tariff laws cover in their fixing of rates
thousands of individual items and categories. Such a means of
solving our economic problem could be brought into operation
quickly and in a few months the machinery of administration
perfected so that thousands of people could be put back to work
within a comparatively short time.
If this plan were in operation, speculators would not acquire
fortunes simply by manipulating and creating false values or
synthetic wealth. If it were decreed that the life of wheat were to be
no more than two years, for example, no man would buy the grain
solely for speculation, thus creating an artificial market and holding a
club over the farmer’s head, as today. He would not dare because he
would know that he would have to pay the Government a tax on the
wheat after it had lived its legal life and this would make it
unprofitable or at least highly dangerous to buy speculatively and
hold for the future.
The widespread suffering from unemployment and want in this
country today is a symptom of a fundamental maladjustment—a
sickness, if you like, in our body economic. Almost every sickness
can be cured, provided we get the right doctor to diagnose the case
and prescribe the proper medicine, but the patient must take the
medicine in order to get well. My plan is in essence a prescription for
the relief and cure of the ailments from which our economic
organization is today suffering.
Of course, the inauguration of such a system of planned
obsolescence will be opposed by many merely because it is new, for
it is hard for us to abandon our old notions and adjust ourselves to a
new way of thinking. Unlike most changes for the good of the
masses, however, this scheme need not involve much hardship,
strife or suffering. That is not necessary. With a reasonable amount
of common sense used, the plan ought gradually to work smoothly
without much loss to anybody. In war-time we conscript the flower of
our country’s manhood, and send them to the front to fight and often
be destroyed. If such drastic procedure is deemed wise and
necessary in the crisis of war, would it not be far more logical and
profitable in our present emergency to conscript the dead things—
material, not human—such as obsolete buildings, machinery and
outmoded commodities, and send them to the front to be destroyed
in the war against depression, thus saving the country from
economic chaos by providing work?
It is far cheaper to destroy useless and obsolete goods now, and
perhaps some of our synthetic wealth as well, than to risk destroying
far more priceless assets, such as human life, and undermining the
health and confidence of the people, by continuing to fight the
depression with our old, slow and costly methods.
Even in the present organization of our economic society, we
recognize in many instances the necessity of destroying some of our
wealth in order to increase it. For example, coal is wealth, but it is
burned up and destroyed daily in locomotives, furnaces and other
devices in order to create power to drive machinery and manufacture
goods. Similarly, oil is wealth, but to serve its purpose it must be
used and consumed in the engines of automobiles and the whirring
wheels of factories. Grain is wealth, but we destroy it by feeding it to
cattle, by consuming it ourselves, and by scattering it on the ground
as seed to produce more grain. It is by this process that people live,
function and create material goods.
Wealth may be compared to our language. Although we use our
language every day, it does not get used up. On the contrary, new
words and idioms are constantly being added to the national
vocabulary, and the language increases in usefulness the more it is
spoken, instead of deteriorating.
In olden times, only a few chosen ones, such as kings and priests
and nobles, could read and write. The rest of the people were kept in
ignorance and poverty. Today, with our standardized and simplified
grammar and our mass education, the benefits of literacy are
available to everybody, to rich and poor alike.
Such a condition should exist also with respect to the enjoyment of
wealth. A minimum standard should be created for everyone, and
rich and poor, old and young should participate in its benefits, and
profit from its use and management.
Our economic society has advanced little from Medieval times in
the distribution of our wealth. We still continue on the basis of our old
theories and notions that only the chosen ones should enjoy it.
There is as much wealth in existence as there is time, but people
do not visualize it. Wealth, like food, must be digested for human
beings to be able to live, function and create—in other words, to
produce more wealth. If we want to acquire new wealth, the supply
lines must be drained so that fresh commodities can come in. If there
are stale goods left in the lines, the fresh supply must force them out.
The cause of our present stagnation is that the supply line or
arteries furnishing the needs of the country are clogged with
obsolete, outworn and outmoded machinery, buildings and
commodities of all kinds. These are obstructing the avenues of
commerce and industry and are preventing new products from
coming through. There is little demand for new goods when people
make their old and worn-out things do, by keeping them longer than
they should.
We need to apply better managerial foresight to public affairs. I
contend that any business or corporation, public or private, which
operates and expects to get an income of several billions of dollars a
year from its operations, deserves much attention, and requires
thoughtful planning, in order to perfect the machinery of its
organization. The aim should be to make it function smoothly in
order to satisfy the self-supporting multitudes, by providing them with
regular employment at a living wage which will assure the American
standard of living.
Such a socially responsible system, which is anxious for the well-
being of all of its citizens, is on a vastly sounder and more
permanent basis than one which allows business merely to take out
profits without improving the organization with new methods and
without renewing the equipment.
I maintain that with wealth should go responsibility. Too many
nowadays regard wealth as a license to freedom and immunity from
obligation to the people. Such irresponsible possessors of wealth are
shirkers, who tend to make all of us poorer.
Summarizing the benefits which would accrue to this nation and to
the world at large if my plan were adopted and put into effect, it
would:
1. Bring order out of the chaos now disrupting the whole
economic and social organization.
2. Organize and regularize opportunities for
employment.
3. Obviate the tremendous social waste of making no
use of the workpower of millions of men and women (who
are compelled to stay idle). In this connection, it is
significant to note that “the cost of the present depression
will very probably exceed 50 billions of dollars” (a
staggering amount), according to Malcolm C. Rorty,
business executive and statistician, writing in a recent
issue of the Harvard Business Review.
4. My plan would take Government finances out of their
present speculative status and would put Government
income on a more stable basis, by receiving annually at
least between 25 and 50 per cent of the net income of all
the buildings, machinery and other commodities which
have been declared obsolete after their allotted time, and
nevertheless allowed to function longer in the event there
is ample employment.

You might also like