Assignment #2 Marking Comments
  1. Errors least commonly checked:
    a) Out of memory

    malloc(), and all related memory allocation functions, can return NULL when there isn't enough memory. Your library should deal with this case appropriately by returning an error code, not by exiting. Please, please, don't use exit when you write a library that is meant to be used by other people. Think about this little scenario. Bill Likegates wants to be build the next big software company. He asks you to write a library that he will use in the first release of Sentence Perfect, the next great word processor. Bill Likegates was very meticulous in his error checking and he made sure that the users would NEVER lose their work. Even in catastrophic/panic situations (OS failures, and things as such), Bill Likegates dumps all the users work into a backup file, so that the user can latter recover. Wow, with this level of stability, I'm sure to overthrow all the competitors! thinks Bill Likegates. So Sentence Perfect 1.0 comes out, but the release is a MEGA flop. People are complaining that the program crashes all the time, and that they are losing their work left right and center. Bill Likegates doesn't understand what happened. How could people be losing data when he was so careful in making his product so solid? Do you see what could have gone wrong? (hint: How will Bill Likegates react if he finds out that your library just simply exits on errors, bypassing all of his sophisticated error handling mechanism...)

    b) Double initialization

    Suppose I write:

            ThreadsInit();
            ThreadsInit();
    
    First of all, your library should not crash. Second, whether you choose to return an error code on the second call to ThreadsInit, or you choose to reinitialize the library, you should make sure that the library is still in a consistent state. One common problem was that on each call to ThreadsInit, the main thread was assigned a different id, which is a violation of the assignment specifications. This would happen if you keep a global variable for assigning ids, but you never reset it in ThreadsInit.

    c) Freeing of the stack

    This is not an error condition, but I included it in error checking. In ThreadCreate, you should store the value returned by malloc() and then free this pointer in ThreadDestroy. Please don't free Active->regs.sp. That's wrong, and will make your program crash.

    d) Removal of last ready thread

    Suppose I write the following:

            ThreadsInit();
            ThreadBlock();
    
    or:
            ThreadsInit();
            ThreadDestroy(0);
    
    What will happen? Most people's code will crash in both of these cases. The idea is that you should check if you get something when you remove an element from the ReadyQueue. Otherwise, when you restore the context, Active will be NULL and your program will segfault. I was very lenient with this error condition, and just mentioning that the user of the library needed to ensure that this never happens was enough.

  2. Testing
    Testing was done pretty well.

    In general, there are several steps to testing. First, you want to do functionality tests. That is, you want check all the possible paths in your code. If you have an if statement, you want to check that the code works properly when the if conditions is true, and when the if condition is false. That means you need to check ALL the possible error conditions (except for those that are difficult to simulate, such as out of memory), and possible extremes (empty queue, one element queue).

    Then, after you've tested that each one of the functions behaves the way it is supposed to, it is usually a good idea to have an integration test, or a comprehensive test. I mean, your engine can work properly, the seats might be well installed, the steering wheel might work properly, but you still want to take the car for a test drive before you're really sure that it all works together. The integration code that I've written is far beyond what you were expected to do, but it's not even close to what you would need in order to actually sell your library.

    Finally, in real life you also need to do stress tests. For example, you write some code that creates, destroys, and switches between threads forever, and then you let the program run overnight. You'd be surprised how many things can go wrong in overnight tests! The first thing that usually comes up in stress tests are memory leaks (memory that you allocate using malloc() but never free using free()). But other weird and obscure bugs can come up!

  3. Report
    Amongst other things, I expected you to explain any choices that you made (how does data flow, how do you find a thread given it's id; do you search through all the queues, or through the global array, etc.), give me limitations on your library, and tell me how you would go about removing these limitations (if possible). For the scheduler, I expected you to talk about the choice of your function to compute internal priorities, and to discuss how well the scheduler performs. I didn't need a detailed account of how you implemented the queues, although the type of queue you used is worth a little discussion (circularly linked, simply linked, or some other implementation).