There are several threading issues when we are in a multithreading environment. In this section, we will discuss the threading issues with system calls, cancellation of thread, signal handling, thread pool and thread-specific data.
Along with the threading issues, we will also discuss how these issues can be deal or resolve to retain the benefit of the multithreaded programming environment.
Threading Issues in OS
1. fork() and exec() System Calls
The fork() and exec() are the system calls. The fork() call creates a duplicate process of the process that invokes fork(). The new duplicate process is called child process and process invoking the fork() is called the parent process. Both the parent process and the child process continue their execution from the instruction that is just after the fork().
Let us now discuss the issue with the fork() system call. Consider that a thread of the multithreaded program has invoked the fork(). So, the fork() would create a new duplicate process. Here the issue is whether the new duplicate process created by fork() will duplicate all the threads of the parent process or the duplicate process would be single-threaded.
Well, there are two versions of fork() in some of the UNIX systems. Either the fork() can duplicate all the threads of the parent process in the child process or the fork() would only duplicate that thread from parent process that has invoked it.
Which version of fork() must be used totally depends upon the application.
Next system call i.e. exec() system call when invoked replaces the program along with all its threads with the program that is specified in the parameter to exec(). Typically the exec() system call is lined up after the fork() system call.
Here the issue is if the exec() system call is lined up just after the fork() system call then duplicating all the threads of parent process in the child process by fork() is useless. As the exec() system call will replace the entire process with the process provided to exec() in the parameter.
2. Thread cancellation
Termination of the thread in the middle of its execution it is termed as ‘thread cancellation’. Let us understand this with the help of an example. Consider that there is a multithreaded program which has let its multiple threads to search through a database for some information. However, if one of the thread returns with the desired result the remaining threads will be cancelled.
Now a thread which we want to cancel is termed as target thread. Thread cancellation can be performed in two ways:
Asynchronous Cancellation: In asynchronous cancellation, a thread is employed to terminate the target thread instantly.
Deferred Cancellation: In deferred cancellation, the target thread is scheduled to check itself at regular interval whether it can terminate itself or not.
The issue related to the target threads are listed below:
- What if the resources had been allotted to the cancel target thread?
- What if the target thread is terminated when it was updating the data, it was sharing with some other thread.
Here the asynchronous cancellation of the thread where a thread immediately cancels the target thread without checking whether it is holding any resources or not creates troublesome.
However, in deferred cancellation, the thread that indicates the target thread about the cancellation, the target thread crosschecks its flag in order to confirm that it should it be cancelled immediately or not. The thread cancellation takes place where they can be cancelled safely such points are termed as cancellation points by Pthreads.
3. Signal Handling
Signal handling is more convenient in the single-threaded program as the signal would be directly forwarded to the process. But when it comes to multithreaded program, the issue arrives to which thread of the program the signal should be delivered.
Let’s say the signal would be delivered to:
- All the threads of the process.
- To some specific threads in a process.
- To the thread to which it applies
- Or you can assign a thread to receive all the signals.
Well, how the signal would be delivered to the thread would be decided, depending upon the type of generated signal. The generated signal can be classified into two type’s synchronous signal and asynchronous signal.
Synchronous signals are forwarded to the same process that leads to the generation of the signal. Asynchronous signals are generated by the event external to the running process thus the running process receives the signals asynchronously.
So if the signal is synchronous it would be delivered to the specific thread causing the generation of the signal. If the signal is asynchronous it cannot be specified to which thread of the multithreaded program it would be delivered. If the asynchronous signal is notifying to terminate the process the signal would be delivered to all the thread of the process.
The issue of an asynchronous signal is resolved up to some extent in most of the multithreaded UNIX system. Here the thread is allowed to specify which signal it can accept and which it cannot. However, the Window operating system does not support the concept of the signal instead it uses asynchronous procedure call (ACP) which is similar to the asynchronous signal of the UNIX system.
4. Thread Pool
When a user requests for a webpage to the server, the server creates a separate thread to service the request. Although the server also has some potential issues. Consider if we do not have a bound on the number of actives thread in a system and would create a new thread for every new request then it would finally result in exhaustion of system resources.
We are also concerned about the time it will take to create a new thread. It must not be that case that the time require to create a new thread is more than the time required by the thread to service the request and then getting discarded as it would result in wastage of CPU time.
The solution to this issue is the thread pool. The idea is to create a finite amount of threads when the process starts. This collection of threads is referred to as the thread pool. The threads stay in the thread pool and wait till they are assigned any request to be serviced.
Whenever the request arrives at the server, it invokes a thread from the pool and assigns it the request to be serviced. The thread completes its service and return back to the pool and wait for the next request.
If the server receives a request and it does not find any thread in the thread pool it waits for some or the other thread to become free and return to the pool. This much better than creating a new thread each time a request arrives and convenient for the system that cannot handle a large number of concurrent threads.
5. Thread Specific data
We all are aware of the fact that the threads belonging to the same process share the data of that process. Here the issue is what if each particular thread of the process needs its own copy of data. So the specific data associated with the specific thread is referred to as thread-specific data.
Consider a transaction processing system, here we can process each transaction in a different thread. To determine each transaction uniquely we will associate a unique identifier with it. Which will help the system to identify each transaction uniquely.
As we are servicing each transaction in a separate thread. So we can use thread-specific data to associate each thread to a specific transaction and its unique id. Thread libraries such as Win32, Pthreads and Java support to thread-specific data.
So these are threading issues that occur in the multithreaded programming environment. We have also seen how these issues can be resolved.