Wednesday 10 October 2012

Pseudo-Code for thread library

In my previous post, I was explaining about the method of creating the thread library using context switch. In this post, I am gonna give the pseudo-code and then discuss certain issues at last.

I hope this post will give you the clear picture of the thread library :) . I don't say that this is the only way, but its one of the ways of implementing it.

The code:


//create a global queue which stores all the list of created threads...
//and create "Current_thread" a pointer to currently executing thread or context...

void initialize()    //called at the starting of main function...
{
   //Includes all the code like initializing the timer and attaching the signal
// handler function "schedule()" to the signal SIGPROF. 
     
}

void schedule()      //signal handler for SIGPROF
{

 /*swap the context with the thread next to the "Current_thread"
 in the queue.and
point the "Current_thread" to the next thread...

If it was the last thread in the queue, then execute the main function
 once and then again start from the first thread in the queue and continue
 this until all threads have finished their execution
 
*/

/*when there are no more threads in the queue... just stop the 
timer and execute the main function until it completes...
*/

}

void create_thread(thread*, function*, any_argumnets_to_be_passed)
{
   /*Initialize the context using getcontext(), attach a function
 using makecontext() and keep this in the ready_queue*/

//If this was the first thread that is created, then start the timer....

/*important: block all the signals while adding a new thread to the queue...
 and after adding it, just unblock all the signals that were previously 
blocked...
*/
}

void kill_thread()
{

/* when the currently executing thread has finished its execution before
 the context switch, then this function should be executed...
*/

/*the main task is to remove the thread from the ready queue (ofcourse you
 have to block all the signals and then unblock at last..) and at last it
 should call the schedule function. */

}


That's it!! we are done...!!

I was saying in my pseudo-code to block all the signals before adding or removing any thread in the queue. This was the question I posed in my previous post and here is the solution to solve the issue. I said that every process has a signal mask associated with it and it includes all the list of signals that should be blocked. We are gonna change this mask to overcome this issue.

We have to use the following function to perform the operation:

int sigprocmask(int how, const sigset_t *set, sigset_t *oset)

You may not have seen this function before, but for sure you must know the type of 2nd and 3rd arguments as we have discussed in the previous post.

This function is pretty much easy to use. You specify the signal that you want to block in the set variable using the functions that i have discussed previously. Then call this function and it blocks the desired signal. I have to say something about the first argument.

It specifies what the function has to perform exactly. It has three types of values.

SIG_BLOCK makes the function to block the additional signals specified in the set variable along with the previous ones.

SIG_SETMASK will set the mask directly with the fresh values.

SIG_UNBLOCK makes the function to unblock all the signals specified in the set variable.

If the third argument is not null, it returns the previously used mask. In this case the variable old has the previous mask.

It will be clear if i take an example. Here is the sample code to give you a clear idea about the usage of the function.


sigset_t a,b;
sigemptyset(&a);
sigaddset(&a, SIGPROF);   // i want to block SIGPROF...
sigprocmask(SIG_BLOCK, &a, &b); //SIGPROF blocked...
//*******

//perform all the operaitons related to queue here....

//*******
//at last unblock the SIGPROF signal....
sigprocmask(SIG_SETMASK, &b, NULL);
//In the above function call we are simply restoring the previous contents of signal mask... hence SIGPROF gets unblocked...

I hope much of it is clear from the comments beside it. :)


Now, I have said that the function kill_thread() must be executed when a thread gets terminated before its time slot has finished. So, how do you accomplish this. Here comes the uc_pointer to rescue. We just make every threads context's uc_pointer to point to some context which executes the kill_thread in which we remove Current_thread from the queue and then executing the schedule function. This accomplishes the task without any errors.

I hope you will get the desired results without too many segmentation errors. ;).


2 comments:

  1. Hi,

    In the man page, it says
    If the context was obtained by a call to a signal handler, then old standard text says that "program execution continues with the program instruction following the instruction interrupted by the
    signal". However, this sentence was removed in SUSv2, and the present verdict is "the result is unspecified".

    So then if we swap context in the signal handlers, I guess its unreliable ?

    -Tejas

    ReplyDelete
    Replies
    1. I think the equivalent calls for executing a signal-handler are swap_context(..) and then in the signal-handler we call set_context(..). Assuming that this is true, I don't think it would be unreliable to have another swap_context(..) inside the signal-handler.

      Delete