Snail Gate Project
by: burt rosenberg
at: september 2024
Overview
This project is a fun project with three serious goals,
- To introduce thread programming,
- To show how condition-wait works,
- To show the techniques of data sharing between threads in a process.
A draw thread is launched, which loops at a certain rate, incrementing the snail and
drawing a picture of it, and a gate. The gate is either open and closed. If the gate
is open when the snail arrives it passes through and eventually the game is won.
Else the snail smushes against the gate and the game is lost.
The Snail Gate Game
The nag program
Man page
NAME
snail --- the snail gate game
SYNOPSIS
snail [-v]
DESCRIPTION
As the snail moves towards a gate, the user guesses the one digit key to the
door. If the user guesses the key, the door opens (depicted by the ? in the
gate disappearing) and the snail can pass through the gate.
OPTIONS
-v verbose
ARGUMENTS
None
OUTPUT
A graphic with a snail @/, an closed gate [?] and an open gate [ ].
HISTORY
New for csc421-231.
The nag program,
github://csc-courses/csc421/nag.c,
is the basis for this project. The nag routine is a thread that from time to time wakes
up and checks if something new has been said. If not it nags the user to say something.
However if so, it insults the user for what they said.
Note in this program,
- The thread is launched in the main route with pthread_create().
- The main and nag threads communicate through a struct T_arg, passed to the
nag thread on creation.
- Use of data in common between the nag and main thread is synchronized by being placed
inside a block beginning with pthread_mutex_lock() and ending
with pthread_mutex_unlock().
- The nag thread waits by calling pthread_cond_timedwait(),
taking three arguments,
- The mutex to wait on, of type pthread_mutex_t,
- The condition signal to listen for, of type pthread_cond_t,
- A timer value for the time-out, struct timespec.
- The main thread can signal thread nag thread on the pthread_cond_t
by calling pthread_cond_signal().
- The nag thread knows whether the shared buffer data is new or old by comparing a counter
that is set by the main thread with an older value that is saved by the nag thread.
- The exit is asked for by the main by way of a request variable,
req_exit, and the effective acknowledgement is by the
main waiting for the thread's call to pthread_exit
by its own call to pthread_exit.
For more on Pthreads, see LLNL's documentation.
Data structures
The snail gate program operates very similar to the nag program. It is on the technical
level the nag program, but with a different story line.
Snail gate runs with two threads. The main thread and the draw_thread thread.
The main thread initializes the data structures and then creates the draw thread, exactly
as nag created the nag thread.
The two threads communicate with two shared data structures using two different techniques,
- The common reference to (struct Board) board_g, is shared as being a global;
- or (struct T_arg) t_arg which is allocated on the stack as a local variable
of main, is shared by being the argument passed to draw_thread.
struct Board {
int snail_loc ;
char guess ;
int gate_loc ;
int gate_key ;
int gate_state ;
} ;
struct T_arg {
int req_exit ;
pthread_mutex_t * mutex ;
pthread_cond_t * cond ;
} ;
The board information is held in a global struct of type struct Board.
The snail has advanced to integer position snail_loc. The
main thread update guess with what the user has guessed.
The get is located at gate_loc and is opened by the
character gate_key. The gate_key takes
one of two values, GATE_IS_CLOSED or GATE_IS_OPEN.
This field is used when creating the string tha represents the game.
The thread argument is a structure bundling up the shared lock,
(pthread_mutex_t) * mutex
and the shared condition variable
(pthread_cond_t) * cond
as well as a flag req_exit which is set to request the draw thread to exit.
Note that draw_thread formally declares the argument to be a void *.
Review the meaning of a void-star. TLDR; version — you will need to cast it to its
actually type struct T_arg *.
snail.c
The task of the draw thread is to loop, sleeping on either a timeout or a wakeup signal from the main thread.
When the draw thread is awakened,
- The snail advances by one;
- The snail is checked for either getting smushed or game over;
- The latest guess his checked and recorded as the gate opened or closed;
- If the thread is being asked to exit, this is handled.
The main thread is the controller thread, and is waiting for input. If it gets input it wakes the draw
thread to handle the input. There are two types of input: to quit, or a new guess.
snail-util.c
You have been provided with utility functions to draw the game board. In the discipline
of Model-View-Controller, the main thread is the Controller and the draw thread is the Viewer.
The Model is latent in the data structures, which are shared between viewer and controller.
Makefile
- The makefile puts a dependency between snail and snail-util, that will build or
rebuild snail-util.o.
- The product of the cc of snail-util is controlled by the -c (compile only) flag.
The loader is not invoked wrap a main inside a runtime; the result is left unlinked as a .o.
- The cc line in the final build invokes the linker on both the compiled result at
the already compiled snail-util.o, along with the pthreads library.
-
The header file snail.h makes known the types, external variables, and external functions,
between snail.c and snail-util.c.