Wait is typically called by the shell to wait for all of the processes in a command to complete.
select() is typically used to read from multiple socket connections, or from standard input and multiple socket connections. Although it can be avoided by threads, without threads, it's the only way to block selectively on multiple events.
![]() |
Part B: To fix this, you should simply do what we advocated in class. Write a signal handler for SIGPIPE and have it do nothing but register itself as the handler for future SIGPIPE signals. Then, test fflush() for failure, and if it fails, re-establish the connection. This code is in a while() loop, because the first thing that you want to do when you re-open the connection is to write "GET-QUOTES\n" to the server.
I've put the new code in red.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct {
char *host;
int port;
int qtime;
FILE *fin;
FILE *fout;
int socket;
} Ripoff_Info;
void ignore_sigpipe(int x)
{
signal(SIGPIPE, ignore_sigpipe);
return;
}
void establish(Ripoff_Info *r, int close_em)
{
if (close_em) {
fclose(r->fin);
fclose(r->fout);
close(r->socket);
}
r->socket = request_connection(r->host, r->port);
r->fin = fdopen(r->socket, "r");
r->fout = fdopen(r->socket, "w");
}
void read_quotes(Ripoff_Info *r)
{
signal(SIGPIPE, ignore_sigpipe);
establish(r, 0);
while (1) {
sleep(r->qtime);
while (fprintf(r->fout, "GET-QUOTES\n") < 0 || fflush(r->fout) != 0) establish(r, 1);
if (read_and_display_quotes(r->fin) == 0) establish(r, 1);
}
}
|
![]() |
Part B: Nuts and bolts fork(), execl(), wait():
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main()
{
int pid, status;
while (1) {
if (fork() == 0) {
execl("./ripoff", "ripoff", "servier.ripoff.com", "5555", "1", NULL);
exit(1);
} else {
pid = wait(&status);
}
}
}
|
Does this program scare you a little? It does me -- if the execl() call fails, then we're going to be in an infinite fork()/execl()/wait() loop. It's not as bad as a fork bomb, though, since there are only two processes that exist at any one time, and you can kill the parent and stop the loop.
![]() |
Note the use of the condition variable. I have ripoff_to_server() hold the lock while it calls fputs() and fflush(). That might seem bad, but the only time that server_to_ripoff() tries to lock the mutex is when it has detected that the connection to the server has been lost and it is reconnecting. In that case, the fflush() call will fail and release the mutex, waiting for the connection to be reestablished.
void ignore_sigpipe(int x)
{
signal(SIGPIPE, ignore_sigpipe);
return;
}
void *ripoff_to_server(void *arg)
{
Thread_Info *ti;
char buffer[510];
ti = (Thread_Info *) arg;
signal(SIGPIPE, ignore_sigpipe);
while (1) {
if (fgets(buffer, 510, ti->fin_ripoff) == NULL) exit(0);
pthread_mutex_lock(ti->lock);
while (fputs(buffer, ti->fout_server) == EOF || fflush(ti->fout_server) != 0) {
pthread_cond_wait(ti->cond, ti->lock);
}
pthread_mutex_unlock(ti->lock);
}
}
void *server_to_ripoff(void *arg)
{
Thread_Info *ti;
char buffer[510];
ti = (Thread_Info *) arg;
signal(SIGPIPE, ignore_sigpipe);
while (1) {
if (fgets(buffer, 510, ti->fin_server) == NULL) {
pthread_mutex_lock(ti->lock);
fclose(ti->fin_server);
fclose(ti->fout_server);
close(ti->fd_server);
ti->fd_server = request_connection("server.ripoff.com", 5555);
ti->fin_server = fdopen(ti->fd_server, "r");
ti->fout_server = fdopen(ti->fd_server, "w");
pthread_cond_signal(ti->cond);
pthread_mutex_unlock(ti->lock);
} else {
fputs(buffer, ti->fout_ripoff);
if (fflush(ti->fout_ripoff) != 0) exit(0);
}
}
}
|
Part B: If the main thread exits naturally, the operating system terminates the process. pthread_exit() has the thread exit without terminating the process.
Part C:
You did not have to mimic my structure -- there are other valid ways to handle this problem. Even if you didn't handle everything well, if you explained what you did, that got you credit.
![]() |