Homework 7 Solutions

22C:116, Spring 2002

Douglas W. Jones
  1. Part A: Which of the functions in the example thread package should perform the shmget(), shmat(), and fork() kernel calls? The thread_manager_init() and thread_manager_start() functions, but which kernel calls go in which function, and why?

    The shared heap must be initialized and available at the time the first threads are created, since the data structures for these threads go in the shared heap. Thereforem, shmget() and shmat() must be called from thread_manager_init().

    Executin of the threads does not begin until thread_manager_start() is called, so there is no reason to call fork() to create parallel processes until then.

    Part B: Can the built-in heap manager of the C programming environment, malloc(), be used to manage the shared heap, or must the thread package include a new heap manager for this purpose?

    Malloc operates only on the standard private heap of the process. Therefore, we need a new heap manager to manage the heap in the shared segment that was added to the address space by shmat().

    Part C: Several key static variables are used in the implementation of the thread manager, among them, current and readylist. Must one or both of these be shared by all n of the processes used to run threads? Must one or both of these be private? If some of these must be shared, what changes to the thread package must be made in order to share it. (The solution you adopt for shared variables is likely to be equally applicable to shared static variables of the application.)

    The ready list must be entirely in the shared heap, including the head and tail pointers for this list. In contrast, each process used in the group of processes that run the threads will have its own current pointer, so this remains private to each process, that is, a static variable in the process static segment.

    To share the ready list, we will have to first use shmat() to attach the shared heap segment, and then allocate the head of the ready list in that segment. Having done this, we must retain a pointer to the ready list data structure, so where the original code has a private data structure in the static segment, we must replace it with a pointer to a dynamically allocated shared data structure in the shared heap.

    Curiously, this simplifies the code a tiny bit. Where we used to pass &readylist we now pass simply readylist.

    Part D: How would you change the thread package to make it work in this environment. For this part, focus on thread_relinquish(), the functions it calls, and the data structures they manipulate, in order to find where new spin-locks must be installed and where the lock and unlock operations on these should be put. (If you can give the correct short answer to this, I trust that you would be able to finish the project).

    		void thread_relinquish()
    		/* call this to allow scheduling of a new thread */
    		{  
    			if (_setjmp( current->state ) == 0) {
    				spinlock( readylist_lock );
    				thread_enqueue( current, readylist );
    				current = thread_dequeue( readylist );
    				spinunlock( readylist_lock );;
    				_longjmp( current->state, 1 );
    			}
    		}
    

    Part E: In the original thread package, there was no need for idle threads. There is a need for idle threads in this new package! How many need to be launched and what code should they run?

    The thread package is still alive if there is even one thread running on any of the cooperating processes. If the number of threads that are ready or running is less than the number of processes, we need idle threads for the other processes, so if we have n processes, the ready list should contain n-1 idle threads. We don't need n idle threads, because if all n processes are idle, either the application has terminated or reached a deadlock.

    The idle threads could just cyclically relinquish, but they should not merely relinquish the thread, but also relinquish the CPU to the system scheduler. This might be done as follows:

    		void idle_thread()
    		{
    			for (;;) {
    				thread_relinquish();
    				usleep( 10000 ); /* wait .01 seconds */
    			}
    		}