JMU
Programming Assignment 9


1 Summary

The (fictitious) company zmedia has decided that they can increase the advertising revenues being generated by the free_bookz_player by using a central repository. You have been hired to implement the necessary changes/additions.

2 Specifications and Other Documents

zmedia has provided you with the following documents.

3 An adz Repository

zmedia has provided you with a repository of "generic" adz that don't correspond to the collection of "adventure" bookz that you have worked with in the past.

4 Additional Specifications/Constraints

In addition to conforming to the documents provided by zmedia (and all related course policies), your code must satisfy the following additional specifications/constraints.
  1. To handle multiple requests concurrently, the adz_server must be multi-threaded. Specifically, each request must be handled in its own thread.
  2. Your adz_server must use one exogenous, circular, single-linked data structure to hold the repository in memory. (Note: This structure will be shared by the threads that handle the requests.)
  3. For testing puposes, the free_bookz_player must accept a second command-line argument containing the IP address of the adz_server. If no such argument is provided, it must use 127.0.0.1 as the default. (Note: There is no need to error-check the argument since it will only be provided by qualified testers.)

5 A Recommended Process

Obviously, you won't be able to start working on communications until after we cover that material in lecture (unless you want to read ahead). However, you can and should, design, implement, and test the data structure long before you start working on communications.

6 Help with the Exogenous, Circular, Single-Linked Data Structure

6.1 Understanding the Data Structure

Linked data structures are constructed from nodes that contain at least one pointer to another node and either data or a pointer to data. In the discussion that follows, these two components are visualized as follows.
/bernstdh/web/common/lectures/images/linked_structure_components.gif

When the node contains the data itself it is said to be an endogenous structure and when the node contains a pointer to the data it is said to be an exogenous structure.

When the node contains only one pointer to another node (either the next node in the structure or the previous node in the structure) it is said to be single-linked. On the other hand, when the node contains a pointer to both the next node in the structure and the previous node in the structure it is said to be doubly-linked.

Finally, when the "last" node in the structure points to NULL it is said to be a linear structure and when the "last" node in the structure points to the "first" node in the structure, it is said to be a circular structure.

So, the structure that you must use for this assignment can be visualized as follows.

/bernstdh/web/common/lectures/images/linked_structure_exogenous_single_circular.gif

6.2 Components of the Data Sucture

The data in this case is an adz, which consists of both a delay and text. So, it can be encapsulated as follows.
struct adz {
  int  delay;
  char text[38];
};

The elegant way to encapsulate a node is to use a void * so that it can be used with any kind of data (though a typecast will be required). This might be done as follows.

struct node {
  void *data;
  struct node *next;
};

The less elegant approach is to create a node structure that is appropriate only for use with an adz.

struct adz_node {
  struct adz *data;
  struct adz_node *next;
};

6.3 Reading the Repository and Populating the Structure

Each time you read an adz you will want to:
  1. Allocate memory for the new data (i.e., the new adz).
  2. Allocate memory for the new node.
  3. Point the new node to the new data.
  4. Point the "last" node to the new node.
  5. Point the new node to the "first" node.
  6. Make the new node the "last" node.
  7. .

To that end, when populating the structure, you will want to have a pointer to the "first" node and a pointer to the "last" node.

6.4 Iterating Over the Structure

To iterate over the data structure you will need to maintain a pointer to the "current" node.

Initially, the "current" node must be the "first" node. Then, as you move through the structure the "current" node will become the "current's next" node.

6.5 Thread Safety

Since the data structure will be populated at startup by the main thread, the operations/variables involved in its population need not be thread-safe.

However, since the request-handler threads all must use the data structure, the operations/variables associated with iterating over the structure must be thread-safe.

6.6 Design

You may use either an OOP-inspired design (in which the operations that operate on the structure are encapsulated with the variables that define it) or a traditionally-inspired design. When deciding which design to use, think carefully about thread-safety.

7 Help with adz_lib

adz_lib currently reads adz information from the local file system (probably in a while() loop that makes use of keep_going in its condition). For this version it must instead send a request to the adz_serve.

Fortunately, since read() is already being called in a helper thread, the additional latency won't have any deleterious impacts on the display of the bookz.

(Note: It would have been better if we had included a get_adz() function in the original design that called read(). Then, the only function we would need to change in this version would be get_adz().)

8 Testing Advice

8.1 Testing the Data Structure

You should test your data structure (both populating it and iterating over it) independently of the rest of the system (and, indeed, before you start working on the rest of the system). When testing the data structure, make sure you iterate over it using multiple threads (with judiciously placed calls to sleep()) in an effort to identify any race conditions.

8.2 Testing the Complete System

Unfortunately, you will not be able to test the system until you have completed both the client (i.e., the free_bookz_player and the server (i.e., the adz_server). When you've completed both and begin testing you should:
  1. Test your server using repository.adz and one of the earlier adz files.
  2. Start testing using a single machine (and multiple terminal windows) but, ultimately, be sure to test your code using multiple different machines (i.e., with the server on one machine and the clients on other machines).
  3. Start testing using a single client but, ultimately, test multiple simultaneous clients. (Note: You may need to slow the server down by temporarily adding a call to sleep() to ensure that it is handling multiple connections simultaneously.)
  4. Remember to test the time-out feature. (Note: While you could do this by not starting the server, it is better to modify the server so that the request handler sleeps for a random amount of time. This way some requests will time-out and some won't.)

8.3 Follow-On Testing

Since you are writing both the client and the server, you may have defects in one that counteract defects in the other. Fortunately, unlike the "real world", there are many of you working on the same system. So, after you have tested your client and server, you should:
  1. Test your client with another student's server.
  2. Test your server with another student's client.

9 Known Defects

The adz_server dynamically allocates memory (for the linked structure) and, so, should deallocate that memory at some point. Unfortunately, to keep the server as simple as possible, it does not terminate normally (i.e., it must be interrupted from the keyboard using Ctrl+C).

You might think that you could change the disposition of SIGINT and deallocate memory in the handler. However, the disposition of SIGINT can't be changed (to ensure that processes can always be interrupted).

You might also think that you could register an exit handler using atexit(). However, exit handlers are only invoke when a process terminates normally.

10 Submission

You must submit all of the files (including the makefile) needed to build both the free_bookz_player and the adz_server.

Though you must keep an "old" version of the adz_lib module, you do not need be able to build both v2 and v3 of the free_bookz_player from the same source code. (You should, however, understand why it would be beneficial to be able to do so and how you would do so.)

Copyright 2017