SlideShare a Scribd company logo
1 of 173
Threads
INTRODUCTION
• In the traditional Unix model, when a process
needs something performed by another
entity, it forks a child process and lets the child
perform the processing.
• Most network servers under Unix are written
this way, as we have seen in our concurrent
server examples: The parent accepts the
connection, forks a child, and the child
handles the client.
INTRODUCTION
• While this paradigm has served well for many
years, there are problems with fork:
Fork is expensive.
• Memory is copied from the parent to the child, all
descriptors are duplicated in the child, and so on.
• Current implementations use a technique called
copy-on-write, which avoids a copy of the
parent's data space to the child until the child
needs its own copy. But, regardless of this
optimization, fork is expensive.
INTRODUCTION
• IPC is required to pass information between
the parent and child after the fork.
• Passing information from the parent to the
child before the fork is easy, since the child
starts with a copy of the parent's data space
and with a copy of all the parent's descriptors.
But, returning information from the child to
the parent takes more work.
INTRODUCTION
Threads help with both problems.
• Threads are sometimes called lightweight
processes since a thread is "lighter weight" than a
process.
• That is, thread creation can be 10–100 times
faster than process creation.
• All threads within a process share the same
global memory. This makes the sharing of
information easy between the threads, but along
with this simplicity comes the problem of
synchronization.
INTRODUCTION
• More than just the global variables are shared.
All threads within a process share the
following:
– Process instructions
– Most data
– Open files (e.g., descriptors)
– Signal handlers and signal dispositions
– Current working directory
– User and group IDs
INTRODUCTION
But each thread has its own
– Thread ID
– Set of registers, including program counter and
stack pointer
– Stack (for local variables and return addresses)
– errno
– Signal mask
– Priority
• In this text, we cover POSIX threads, also
called Pthreads.
Basic Thread Functions: Creation and
Termination
• In this section, we will cover five basic thread
functions and then use these in the next two
sections to recode our TCP client/server using
threads instead of fork.
• pthread_create Function
• When a program is started by exec, a single
thread is created, called the initial thread or main
thread.
• Additional threads are created by
pthread_create.
Basic Thread Functions: Creation and
Termination
#include <pthread.h>
int pthread_create(pthread_t *tid, const
pthread_attr_t *attr, void *(*func) (void *), void
*arg);
Returns: 0 if OK, positive Exxx value on error
• Each thread within a process is identified by a
thread ID, whose datatype is pthread_t (often an
unsigned int).
• On successful creation of a new thread, its ID is
returned through the pointer tid.
Basic Thread Functions: Creation and
Termination
• Each thread has numerous attributes: its
priority, its initial stack size, whether it should
be a daemon thread or not, and so on.
• When a thread is created, we can specify
these attributes by initializing a
pthread_attr_t variable that overrides the
default.
• We normally take the default, in which case,
we specify the attr argument as a null pointer.
Basic Thread Functions: Creation and
Termination
• Finally, when we create a thread, we specify a
function for it to execute.
• The thread starts by calling this function and
then terminates either explicitly (by calling
pthread_exit) or implicitly (by letting the
function return).
• The address of the function is specified as the
func argument, and this function is called with
a single pointer argument, arg.
Basic Thread Functions: Creation and
Termination
• If we need multiple arguments to the function,
we must package them into a structure and then
pass the address of this structure as the single
argument to the start function.
• Notice the declarations of func and arg. The
function takes one argument, a generic pointer
(void *), and returns a generic pointer (void *).
This lets us pass one pointer (to anything we
want) to the thread, and lets the thread return
one pointer (again, to anything we want).
Basic Thread Functions: Creation and
Termination
• The return value from the Pthread functions is
normally 0 if successful or nonzero on an
error.
• But unlike the socket functions, and most
system calls, which return –1 on an error and
set errno to a positive value, the Pthread
functions return the positive error indication
as the function's return value.
Basic Thread Functions: Creation and
Termination
• For example, if pthread_create cannot create a
new thread because of exceeding some system
limit on the number of threads, the function
return value is EAGAIN.
• The Pthread functions do not set errno. The
convention of 0 for success or nonzero for an
error is fine since all the Exxx values in
<sys/errno.h> are positive.
• A value of 0 is never assigned to one of the Exxx
names.
Basic Thread Functions: Creation and
Termination
pthread_join Function
• We can wait for a given thread to terminate by
calling pthread_join.
• Comparing threads to Unix processes,
pthread_create is similar to fork, and
pthread_join is similar to waitpid.
#include <pthread.h>
int pthread_join (pthread_t tid, void ** status);
Returns: 0 if OK, positive Exxx value on error
Basic Thread Functions: Creation and
Termination
• We must specify the tid of the thread that we
want to wait for.
• Unfortunately, there is no way to wait for any
of our threads (similar to waitpid with a
process ID argument of –1).
• If the status pointer is non-null, the return
value from the thread (a pointer to some
object) is stored in the location pointed to by
status.
Basic Thread Functions: Creation and
Termination
pthread_self Function
• Each thread has an ID that identifies it within
a given process.
• The thread ID is returned by pthread_create
and we saw it was used by pthread_join.
• A thread fetches this value for itself using
pthread_self.
Basic Thread Functions: Creation and
Termination
#include <pthread.h>
pthread_t pthread_self (void);
Returns: thread ID of calling thread
Comparing threads to Unix processes,
pthread_self is similar to getpid.
Basic Thread Functions: Creation and
Termination
pthread_detach Function
• A thread is either joinable (the default) or
detached.
• When a joinable thread terminates, its thread ID
and exit status are retained until another thread
calls pthread_join.
• But a detached thread is like a daemon process:
When it terminates, all its resources are released
and we cannot wait for it to terminate.
• If one thread needs to know when another
thread terminates, it is best to leave the thread as
joinable.
Basic Thread Functions: Creation and
Termination
• The pthread_detach function changes the
specified thread so that it is detached.
#include <pthread.h>
int pthread_detach (pthread_t tid);
Returns: 0 if OK, positive Exxx value on error
• This function is commonly called by the thread
that wants to detach itself, as in
• pthread_detach (pthread_self());
Basic Thread Functions: Creation and
Termination
pthread_exit Function
• One way for a thread to terminate is to call
pthread_exit.
#include <pthread.h>
void pthread_exit (void *status);
Does not return to caller
• If the thread is not detached, its thread ID and
exit status are retained for a later pthread_join by
some other thread in the calling process.
Basic Thread Functions: Creation and
Termination
• The pointer status must not point to an object
that is local to the calling thread since that
object disappears when the thread
terminates.
Basic Thread Functions: Creation and
Termination
• There are two other ways for a thread to
terminate:
– The function that started the thread (the third
argument to pthread_create) can return. Since
this function must be declared as returning a void
pointer, that return value is the exit status of the
thread.
– If the main function of the process returns or if
any thread calls exit, the process terminates,
including any threads.
str_cli Function Using Threads
• Our first example using threads is to recode
the str_cli function from Figure 16.10, which
uses fork, to use threads.
• Figure 26.1 shows the design of our threads
version.
str_cli Function Using Threads
str_cli Function Using Threads
void *copyto (void *);
static int sockfd; /* global for both threads to access */
static FILE *fp;
void str_cli(FILE *fp_arg, int sockfd_arg)
{
char recvline[MAXLINE];
pthread_t tid;
sockfd = sockfd_arg; /* copy arguments to externals */
fp = fp_arg;
str_cli Function Using Threads
Pthread_create(&tid, NULL, copyto, NULL);
while (Readline(sockfd, recvline, MAXLINE) >
0)
Fputs(recvline, stdout);
}
void * copyto(void *arg)
{
char sendline[MAXLINE];
str_cli Function Using Threads
while (Fgets(sendline, MAXLINE, fp) ! = NULL)
Writen(sockfd, sendline, strlen(sendline));
Shutdown(sockfd, SHUT_WR); /* EOF on
stdin, send FIN */
return (NULL); /* return (i.e., thread
terminates) when EOF on stdin */
}
str_cli Function Using Threads
Save arguments in externals
• 10–11 The thread that we are about to create
needs the values of the two arguments to str_cli:
fp, the standard I/O FILE pointer for the input file,
and sockfd, the TCP socket connected to the
server.
• For simplicity, we store these two values in
external variables.
• An alternative technique is to put the two values
into a structure and then pass a pointer to the
structure as the argument to the thread we are
about to create.
str_cli Function Using Threads
Create new thread
• 12:The thread is created and the new thread
ID is saved in tid.
• The function executed by the new thread is
copyto.
• No arguments are passed to the thread.
str_cli Function Using Threads
Main thread loop: copy socket to standard
output
• 13–14 The main thread calls readline and
fputs, copying from the socket to the standard
output.
Terminate
• 15 When the str_cli function returns, the main
function terminates by calling exit.
str_cli Function Using Threads
• When this happens, all threads in the process
are terminated.
• Normally, the copyto thread will have already
terminated by the time the server's main
function completes.
• But in the case where the server terminates
prematurely, calling exit when the server's
main function completes will terminate the
copyto thread, which is what we want.
str_cli Function Using Threads
copyto thread
• 16–25 This thread just copies from standard
input to the socket. When it reads an EOF on
standard input, a FIN is sent across the socket
by shutdown and the thread returns.
• The return from this function (which started
the thread) terminates the thread.
TCP Echo Server Using Threads
• We now redo our TCP echo server using one
thread per client instead of one child process
per client.
static void *doit(void *); /* each thread executes
this function */
int main(int argc, char **argv)
{
int listenfd, connfd;
TCP Echo Server Using Threads
pthread_t tid;
socklen_t addrlen, len;
struct sockaddr *cliaddr;
if (argc == 2)
listenfd = Tcp_listen(NULL, argv[1], &addrlen); else
if (argc == 3)
listenfd = Tcp_listen(argv[1], argv[2], &addrlen); else
err_quit("usage: tcpserv01 [ <host> ] <service or
port>");
TCP Echo Server Using Threads
cliaddr = Malloc(addrlen);
for (; ; )
{
len = addrlen;
connfd = Accept(listenfd, cliaddr, &len);
Pthread_create(&tid, NULL, &doit, (void *) connfd);
}
}
TCP Echo Server Using Threads
static void * doit(void *arg)
{
Pthread_detach(pthread_self());
str_echo((int) arg); /* same function as before
*/
Close((int) arg); /* done with connected
socket */
return (NULL);
}
TCP Echo Server Using Threads
Create thread
• 17–21 When accept returns, we call
pthread_create instead of fork. The single
argument that we pass to the doit function is the
connected socket descriptor, connfd.
Thread function
• 23–30 doit is the function executed by the
thread.
• The thread detaches itself since there is no
reason for the main thread to wait for each
thread it creates. The function str_echo does not
change from Figure 5.3.
TCP Echo Server Using Threads
• When this function returns, we must close the
connected socket since the thread shares all
descriptors with the main thread.
• With fork, the child did not need to close the
connected socket because when the child
terminated, all open descriptors were closed
on process termination.
TCP Echo Server Using Threads
• Also notice that the main thread does not close
the connected socket, which we always did with a
concurrent server that calls fork.
• This is because all threads within a process share
the descriptors, so if the main thread called close,
it would terminate the connection.
• Creating a new thread does not affect the
reference counts for open descriptors, which is
different from fork.
TCP Echo Server Using Threads
Passing Arguments to New Threads
• We mentioned that in Figure 26.3, we cast the
integer variable connfd to be a void pointer, but
this is not guaranteed to work on all systems.
• To handle this correctly requires additional work.
• First, notice that we cannot just pass the address
of connfd to the new thread.
• That is, the following does not work:
TCP Echo Server Using Threads
int main(int argc, char **argv)
{
int listenfd, connfd;
...
for ( ; ; )
{
len = addrlen;
TCP Echo Server Using Threads
connfd = Accept(listenfd, cliaddr, &len);
Pthread_create(&tid, NULL, &doit, &connfd);
}
}
static void * doit(void *arg)
{
int connfd;
connfd = * ((int *) arg);
TCP Echo Server Using Threads
pthread_detach (pthread_self());
str_echo (connfd); /* same function as before
*/
Close (connfd); /* done with connected socket
*/
return (NULL);
}
TCP Echo Server Using Threads
• From an ANSI C perspective this is acceptable:
We are guaranteed that we can cast the
integer pointer to be a void * and then cast
this pointer back to an integer pointer.
• The problem is what this pointer points to.
• There is one integer variable, connfd in the
main thread, and each call to accept
overwrites this variable with a new value (the
connected descriptor).
TCP Echo Server Using Threads
The following scenario can occur:
• accept returns, connfd is stored into (say the new
descriptor is 5), and the main thread calls
pthread_create. The pointer to connfd (not its
contents) is the final argument to pthread_create.
• A thread is created and the doit function is scheduled
to start executing.
• Another connection is ready and the main thread runs
again (before the newly created thread). accept
returns, connfd is stored into (say the new descriptor is
now 6), and the main thread calls pthread_create.
TCP Echo Server Using Threads
• Even though two threads are created, both
will operate on the final value stored in
connfd, which we assume is 6.
• The problem is that multiple threads are
accessing a shared variable (the integer value
in connfd) with no synchronization.
TCP Echo Server Using Threads
• In Figure 26.3, we solved this problem by
passing the value of connfd to pthread_create
instead of a pointer to the value.
• Figure 26.4 TCP echo server using threads
with more portable argument passing.
TCP Echo Server Using Threads
static void *doit(void *); /* each thread executes
this function */
int main(int argc, char **argv)
{
int listenfd, *iptr;
thread_t tid;
socklen_t addrlen, len;
struct sockaddr *cliaddr;
TCP Echo Server Using Threads
if (argc == 2)
listenfd = Tcp_listen(NULL, argv[1], &addrlen);
else if (argc == 3)
listenfd = Tcp_listen(argv[1], argv[2],
&addrlen);
else
err_quit("usage: tcpserv01 [ <host> ] <service
or port>");
TCP Echo Server Using Threads
cliaddr = Malloc(addrlen);
for ( ; ; )
{
len = addrlen;
iptr = Malloc(sizeof(int));
*iptr = Accept(listenfd, cliaddr, &len);
Pthread_create(&tid, NULL, &doit, iptr);
}
}
TCP Echo Server Using Threads
static void * doit(void *arg)
{
int connfd;
connfd = *((int *) arg);
free(arg);
Pthread_detach(pthread_self());
str_echo(confd); /* same function as before */
Close(confd); /* done with connected socket */
return (NULL);
}
TCP Echo Server Using Threads
• 17–22 Each time we call accept, we first call
malloc and allocate space for an integer
variable, the connected descriptor.
• This gives each thread its own copy of the
connected descriptor.
• 28–29 The thread fetches the value of the
connected descriptor and then calls free to
release the memory.
TCP Echo Server Using Threads
• Historically, the malloc and free functions
have been nonre-entrant.
• That is, calling either function from a signal
handler while the main thread is in the middle
of one of these two functions has been a
recipe for disaster, because of static data
structures that are manipulated by these two
functions.
TCP Echo Server Using Threads
• POSIX requires that these two functions, along
with many others, be thread-safe.
• This is normally done by some form of
synchronization performed within the library
functions that is transparent to us.
TCP Echo Server Using Threads
Thread-Safe Functions
• POSIX.1 requires that all the functions defined
by POSIX.1 and by the ANSI C standard be
thread-safe, with the exceptions listed in
Figure 26.5.
TCP Echo Server Using Threads
TCP Echo Server Using Threads
• Unfortunately, POSIX says nothing about thread
safety with regard to the networking API
functions.
• The last five lines in this table are from Unix 98.
• We see from Figure 26.5 that the common
technique for making a function thread-safe is to
define a new function whose name ends in _r.
• Two of the functions are thread-safe only if the
caller allocates space for the result and passes
that pointer as the argument to the function.
Thread-Specific Data
• When converting existing functions to run in a
threads environment, a common problem
encountered is due to static variables.
• A function that keeps state in a private buffer,
or one that returns a result in the form of a
pointer to a static buffer, is not thread-safe
because multiple threads cannot use the
buffer to hold different things at the same
time.
Thread-Specific Data
• When faced with this problem, there are
various solutions:
• Use thread-specific data. This is nontrivial and
then converts the function into one that works
only on systems with threads support.
• The advantage to this approach is that the
calling sequence does not change and all the
changes go into the library function and not
the applications that call the function.
Thread-Specific Data
• Change the calling sequence so that the caller
packages all the arguments into a structure,
and also store in that structure the static
variables from Figure 3.18.
• Figure 26.6 shows the new structure and new
function prototypes.
• Figure 26.6 Data structure and function
prototype for re-entrant version of readline.
Thread-Specific Data
Thread-Specific Data
• These new functions can be used on threaded
and nonthreaded systems, but all applications
that call readline must change.
• Restructure the interface to avoid any static
variables so that the function is thread-safe.
• For the readline example, this would be the
equivalent of ignoring the speedups introduced in
Figure 3.18 and going back to the older version in
Figure 3.17.
• Since we said the older version was "painfully
slow," taking this option is not always viable.
Thread-Specific Data
• Thread-specific data is a common technique
for making an existing function thread-safe.
• Before describing the Pthread functions that
work with thread-specific data, we describe
the concept and a possible implementation,
because the functions appear more
complicated than they really are.
Thread-Specific Data
• Each system supports a limited number of
thread-specific data items.
• POSIX requires this limit be no less than 128
(per process), and we assume this limit in the
following example.
• The system (probably the threads library)
maintains one array of structures per process,
which we call key structures, as we show in
Figure 26.7.
Thread-Specific Data
Thread-Specific Data
• The flag in the Key structure indicates whether
this array element is currently in use, and all the
flags are initialized to be "not in use."
• When a thread calls pthread_key_create to
create a new thread-specific data item, the
system searches through its array of Key
structures and finds the first one not in use.
• Its index, 0 through 127, is called the key, and this
index is returned to the calling thread.
• We will talk about the "destructor pointer," the
other member of the Key structure, shortly.
Thread-Specific Data
• In addition to the process-wide array of Key
structures, the system maintains numerous
pieces of information about each thread
within a process.
• We call this a Pthread structure and part of
this information is a 128-element array of
pointers, which we call the pkey array. We
show this in Figure 26.8.
Thread-Specific Data
Thread-Specific Data
• All entries in the pkey array are initialized to
null pointers. These 128 pointers are the
"values" associated with each of the possible
128 "keys" in the process.
• When we create a key with
pthread_key_create, the system tells us its key
(index).
• Each thread can then store a value (pointer)
for the key, and each thread normally obtains
the pointer by calling malloc.
Thread-Specific Data
• We now go through an example of how
thread-specific data is used, assuming that our
readline function uses thread-specific data to
maintain the per-thread state across
successive calls to the function.
• Shortly we will show the code for this,
modifying our readline function to follow
these steps:
Thread-Specific Data
1.A process is started and multiple threads are
created
2.One of the threads will be the first to call readline,
and it in turn calls pthread_key_create. The
system finds the first unused Key structure in
Figure 26.7 and returns its index (0–127) to the
caller. We assume an index of 1 in this example.
• We will use the pthread_once function to
guarantee that pthread_key_create is called only
by the first thread to call readline.
Thread-Specific Data
3.readline calls pthread_getspecific to get the
pkey[1] value (the "pointer" in Figure 26.8 for
this key of 1) for this thread, and the returned
value is a null pointer.
Therefore, readline calls malloc to allocate the
memory that it needs to keep the per-thread
information across successive calls to readline
for this thread.
Thread-Specific Data
• readline initializes this memory as needed
and calls pthread_setspecific to set the
thread-specific data pointer (pkey[1]) for this
key to point to the memory it just allocated.
We show this in Figure 26.9, assuming that
the calling thread is thread 0 in the process.
Thread-Specific Data
Thread-Specific Data
• In this figure, we note that the Pthread structure
is maintained by the system (probably the thread
library), but the actual thread-specific data that
we malloc is maintained by our function
(readline, in this case).
• All that pthread_setspecific does is set the
pointer for this key in the Pthread structure to
point to our allocated memory.
• Similarly, all that pthread_getspecific does is
return that pointer to us.
Thread-Specific Data
4. Another thread, say thread n, calls readline,
perhaps while thread 0 is still executing within
readline.
readline calls pthread_once to initialize the key
for this thread-specific data item, but since it
has already been called, it is not called again.
Thread-Specific Data
5.readline calls pthread_getspecific to fetch the
pkey [1] pointer for this thread, and a null
pointer is returned. This thread then calls
malloc, followed by pthread_setspecific, just
like thread 0, initializing its thread-specific
data for this key (1).
We show this in Figure 26.10.
Thread-Specific Data
Thread-Specific Data
6.Thread n continues executing in readline, using
and modifying its own thread-specific data.
• One item we have not addressed is: What
happens when a thread terminates? If the
thread has called our readline function, that
function has allocated a region of memory
that needs to be freed.
• This is where the "destructor pointer" from
Figure 26.7 is used.
Thread-Specific Data
• When the thread that creates the thread-
specific data item calls pthread_key_create,
one argument to this function is a pointer to a
destructor function.
• When a thread terminates, the system goes
through that thread's pkey array, calling the
corresponding destructor function for each
non-null pkey pointer.
Thread-Specific Data
• What we mean by "corresponding destructor"
is the function pointer stored in the Key array
in Figure 26.7.
• This is how the thread-specific data is freed
when a thread terminates.
• The first two functions that are normally
called when dealing with thread-specific data
are pthread_once and pthread_key_create.
Thread-Specific Data
#include <pthread.h>
int pthread_once(pthread_once_t *onceptr,
void (*init) (void));
int pthread_key_create(pthread_key_t *keyptr,
void (*destructor) (void *value));
Both return: 0 if OK, positive Exxx value on error
Thread-Specific Data
• pthread_once is normally called every time a
function that uses thread-specific data is
called, but pthread_once uses the value in the
variable pointed to by onceptr to guarantee
that the init function is called only one time
per process.
Thread-Specific Data
• pthread_key_create must be called only one
time for a given key within a process.
• The key is returned through the keyptr
pointer, and the destructor function, if the
argument is a non-null pointer, will be called
by each thread on termination if that thread
has stored a value for this key.
Thread-Specific Data
• Typical usage of these two functions (ignoring
error returns) is as follows:
pthread_key_t rl_key;
pthread_once_t rl_once = PTHREAD_ONCE_INIT;
void readline_destructor(void *ptr)
{
free(ptr);
}
Thread-Specific Data
void readline_once(void)
{
pthread_key_create(&rl_key, readline_destructor);
}
ssize_t readline( ... )
{
...
pthread_once(&rl_once, readline_once);
Thread-Specific Data
if ( (ptr = pthread_getspecific(rl_key)) == NULL)
{
ptr = Malloc( ... );
pthread_setspecific(rl_key, ptr); /* initialize
memory pointed to by ptr */
}
...
/* use values pointed to by ptr */
}
Thread-Specific Data
• Every time readline is called, it calls
pthread_once.
• This function uses the value pointed to by its
onceptr argument (the contents of the variable
rl_once) to make certain that its init function is
called only one time.
• This initialization function, readline_once, creates
the thread-specific data key that is stored in
rl_key, and which readline then uses in calls to
pthread_getspecific and pthread_setspecific.
Thread-Specific Data
• The pthread_getspecific and
pthread_setspecific functions are used to
fetch and store the value associated with a
key.
• This value is what we called the "pointer" in
Figure 26.8.
• What this pointer points to is up to the
application, but normally, it points to
dynamically allocated memory.
Thread-Specific Data
#include <pthread.h>
void *pthread_getspecific(pthread_key_t key);
Returns: pointer to thread-specific data (possibly
a null pointer)
int pthread_setspecific(pthread_key_t key, const
void *value);
Returns: 0 if OK, positive Exxx value on error
Thread-Specific Data
• Notice that the argument to
pthread_key_create is a pointer to the key
(because this function stores the value
assigned to the key), while the arguments to
the get and set functions are the key itself
(probably a small integer index as discussed
earlier).
Thread-Specific Data
Example: readline Function Using Thread-
Specific Data
• We now show a complete example of thread-
specific data by converting the optimized
version of our readline function from Figure
3.18 to be thread-safe, without changing the
calling sequence.
Thread-Specific Data
• Figure 26.11 shows the first part of the
function: the pthread_key_t variable, the
pthread_once_t variable, the
readline_destructor function, the
readline_once function, and our Rline
structure that contains all the information we
must maintain on a per-thread basis.
Thread-Specific Data
Figure 26.11 First part of thread-safe readline
function.
static pthread_key_t rl_key;
static pthread_once_t rl_once =
PTHREAD_ONCE_INIT;
static void readline_destructor(void *ptr)
{
free(ptr);
}
Thread-Specific Data
static void readline_once(void)
{
Pthread_key_creat(&rl_key,
readline_destructor);
}
typedef struct {
int rl_cnt; /* initialize to 0 */
Thread-Specific Data
char *rl_bufptr; /* initialize to rl_buf */
char rl_buf[MAXLINE];
} Rline;
Thread-Specific Data
Destructor
4–8 Our destructor function just frees the
memory that was allocated for this thread.
One-time function
9–13 We will see that our one-time function is
called one time by pthread_once, and it just
creates the key that is used by readline.
Thread-Specific Data
Rline structure
14–18 Our Rline structure contains the three
variables that caused the problem by being
declared static in Figure 3.18.
One of these structures will be dynamically
allocated per thread and then released by our
destructor function.
• Figure 26.12 shows the actual readline function,
plus the function my_read it calls. This figure is a
modification of Figure 3.18.
Thread-Specific Data
my_read function
19–35 The first argument to the function is now a
pointer to the Rline structure that was allocated
for this thread (the actual thread-specific data).
Allocate thread-specific data
42 We first call pthread_once so that the first
thread that calls readline in this process calls
readline_once to create the thread-specific data
key.
Thread-Specific Data
• Fetch thread-specific data pointer
• 43–46 pthread_getspecific returns the pointer
to the Rline structure for this thread.
• But if this is the first time this thread has
called readline, the return value is a null
pointer.
• In this case, we allocate space for an Rline
structure and the rl_cnt member is initialized
to 0 by calloc.
Thread-Specific Data
• We then store the pointer for this thread by
calling pthread_setspecific.
• The next time this thread calls readline,
pthread_getspecific will return this pointer
that was just stored.
Thread-Specific Data
Figure 26.12 Second part of thread-safe readline
function.
static ssize_t my_read(Rline *tsd, int fd, char *ptr)
{
if (tsd->rl_cnt < = 0)
{
again: if ( (tsd->rl_cnt = read(fd, tsd->rl_buf,
MAXLINE)) < 0)
{
if (error == EINTR)
goto again;
Thread-Specific Data
return (-1);
}
else if (tsd->rl_cnt == 0)
return (0);
tsd->rl_bufptr = tsd->rl_buf;
}
Thread-Specific Data
tsd->rl_cnt--;
*ptr = *tsd->rl_bufptr++;
return (1);
}
ssize_t readline(int fd, void *vptr, size_t maxlen)
{
size_t n, rc;
char c, *ptr;
Rline *tsd;
Thread-Specific Data
Pthread_once(&rl_once, readline_once);
if ( (tsd = pthread_getspecific(rl_key)) ==
NULL)
{
tsd = Calloc(1, sizeof(Rline)); /* init to 0 */
Pthread_setspecific(rl_key, tsd);
}
Thread-Specific Data
ptr = vptr;
for (n = 1; n < maxlen; n++)
{
if ( (rc = my_read(tsd, fd, &c)) == 1)
{
*ptr++ = c;
if (c == 'n‘)
break;
}
Thread-Specific Data
else if (rc == 0)
{
*ptr = 0;
return (n - 1); /* EOF, n - 1 bytes read */
}
else
return (-1); /* error, errno set by read() */
}
*ptr = 0;
return (n);
}
Nonblocking connect: Web Client
Nonblocking connect: Web Client
• A real-world example of nonblocking connects
started with the Netscape Web.
• The client establishes an HTTP connection
with a Web server and fetches a home page.
• Often, that page will have numerous
references to other Web pages. Instead of
fetching these other pages serially, one at a
time, the client can fetch more than one at
the same time using nonblocking connects.
Nonblocking connect: Web Client
• Figure 16.12 shows an example of establishing
multiple connections in parallel.
• The leftmost scenario shows all three
connections performed serially.
• We assume that the first connection takes 10
units of time, the second 15, and the third 4,
for a total of 29 units of time.
Nonblocking connect: Web Client
Nonblocking connect: Web Client
• In the middle scenario, we perform two
connections in parallel.
• At time 0, the first two connections are
started, and when the first of these finishes,
we start the third.
• The total time is almost halved, from 29 to 15,
but realize that this is the ideal case.
Nonblocking connect: Web Client
• If the parallel connections are sharing a
common link, each can compete against each
other for the limited resources and all the
individual connection times might get longer.
• For example, the time of 10 might be 15, the
time of 15 might be 20, and the time of 4
might be 6.
• Nevertheless, the total time would be 21, still
shorter than the serial scenario.
Nonblocking connect: Web Client
• In the third scenario, we perform three
connections in parallel, and we again assume
there is no interference between the three
connections (the ideal case).
• But, the total time is the same (15 units) as
the second scenario given the example times
that we choose.
Nonblocking connect: Web Client
• When dealing with Web clients, the first
connection is done by itself, followed by
multiple connections for the references found
in the data from that first connection.
• We show this in Figure 16.13.
Nonblocking connect: Web Client
Nonblocking connect: Web Client
• To further optimize this sequence, the client can
start parsing the data that is returned for the first
connection before the first connection completes
and initiate additional connections as soon as it
knows that additional connections are needed.
• Program will read up to 20 files from a Web
server.
• We specify as command-line arguments the
maximum number of parallel connections, the
server's hostname, and each of the filenames to
fetch from the server.
Nonblocking connect: Web Client
solaris % web 3 www.foobar.com / image1.gif
image2.gif  image3.gif image4.gif image5.gif 
image6.gif image7.gif
• The command-line arguments specify three
simultaneous connections: the server's
hostname, the filename for the home page (/,
the server's root page), and seven files to then
read (which in this example are all GIF
images).
Nonblocking connect: Web Client
• These seven files would normally be
referenced on the home page, and a Web
client would read the home page and parse
the HTML to obtain these filenames.
Web Client and Simultaneous
Connections (Continued)
• We now revisit the Web client example from
Section 16.5 and recode it using threads
instead of nonblocking connects.
• With threads, we can leave the sockets in their
default blocking mode and create one thread
per connection.
• Each thread can block in its call to connect, as
the kernel will just run some other thread that
is ready.
Web Client and Simultaneous
Connections (Continued)
• Figure 26.13 shows the first part of the
program, the globals, and the start of the
main function.
Globals
• 1–16 #include <thread.h>, in addition to the
normal <pthread.h>, because we need to use
Solaris threads in addition to Pthreads.
Web Client and Simultaneous
Connections (Continued)
• 10 We have added one member to the file
structure: f_tid, the thread ID.
• The remainder of this code is similar to Figure
16.15.
• With this threads version, we do not use
select and therefore do not need any
descriptor sets or the variable maxfd.
• 36 The home_page function that is called is
unchanged from Figure 16.16.
Web Client and Simultaneous
Connections (Continued)
Web Client and Simultaneous
Connections (Continued)
Web Client and Simultaneous
Connections (Continued)
Web Client and Simultaneous
Connections (Continued)
Web Client and Simultaneous
Connections (Continued)
Web Client and Simultaneous
Connections (Continued)
If possible, create another thread
• 40–52 If we are allowed to create another
thread (nconn is less than maxnconn), we do
so. The function that each new thread
executes is do_get_read and the argument is
the pointer to the file structure.
Web Client and Simultaneous
Connections (Continued)
Wait for any thread to terminate
• 53–54 We call the Solaris thread function
thr_join with a first argument of 0 to wait for
any one of our threads to terminate.
Unfortunately, Pthreads does not provide a
way to wait for any one of our threads to
terminate; the pthread_join function makes us
specify exactly which thread we want to wait
for
Web Client and Simultaneous
Connections (Continued)
Wait for any thread to terminate
• We will see in Section 26.9 that the Pthreads
solution for this problem is more complicated,
requiring us to use a condition variable for the
terminating thread to notify the main thread
when it is done.
Web Client and Simultaneous
Connections (Continued)
• Figure 26.15 shows the do_get_read function,
which is executed by each thread. This
function establishes the TCP connection,
sends an HTTP GET command to the server,
and reads the server's reply.
Web Client and Simultaneous
Connections (Continued)
Web Client and Simultaneous
Connections (Continued)
Web Client and Simultaneous
Connections (Continued)
Create TCP socket, establish connection
• 68–71 A TCP socket is created and a
connection is established by our tcp_connect
function. The socket is a normal blocking
socket, so the thread will block in the call to
connect until the connection is established.
Web Client and Simultaneous
Connections (Continued)
Write request to server
• 72 write_get_cmd builds the HTTP GET
command and sends it to the server. We do
not show this function again as the only
difference from Figure 16.18 is that the
threads version does not call FD_SET and does
not use maxfd.
Web Client and Simultaneous
Connections (Continued)
Read server's reply
• 73–82 The server's reply is then read. When
the connection is closed by the server, the
F_DONE flag is set and the function returns,
terminating the thread.
Mutexes: Mutual Exclusion
Figure 26.17 Two threads that increment a
global variable incorrectly.
#include "unpthread.h"
#define NLOOP 5000
int counter; /* incremented by threads */
void *doit(void *);
int main(int argc, char **argv)
{
pthread_t tidA, tidB;
Mutexes: Mutual Exclusion
Pthread_create(&tidA, NULL, &doit, NULL);
Pthread_create(&tidB, NULL, &doit, NULL);
/* wait for both threads to terminate */
Pthread_join(tidA, NULL);
Pthread_join(tidB, NULL);
exit(0);
}
Mutexes: Mutual Exclusion
void * doit(void *vptr)
{
int i, val;
/* Each thread fetches, prints, and increments
the counter NLOOP times. 22 * The value of
the counter should increase monotonically. */
Mutexes: Mutual Exclusion
for (i = 0; i < NLOOP; i++)
{
val = counter;
printf("%d: %dn", pthread_self(), val + 1);
counter = val + 1;
}
return (NULL);
}
Mutexes: Mutual Exclusion
• Notice the error the first time the system
switches from thread 4 to thread 5: The value
518 i
Figure 26.16. Output from program in Figure
26.17.
Mutexes: Mutual Exclusion
Mutexes: Mutual Exclusion
• The problem we just discussed, multiple
threads updating a shared variable, is the
simplest problem.
• The solution is to protect the shared variable
with a mutex (which stands for "mutual
exclusion") and access the variable only when
we hold the mutex.
• In terms of Pthreads, a mutex is a variable of
type pthread_mutex_t.
Mutexes: Mutual Exclusion
• We lock and unlock a mutex using the
following two functions:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *
mptr);
int pthread_mutex_unlock(pthread_mutex_t *
mptr);
Both return: 0 if OK, positive Exxx value on error
Mutexes: Mutual Exclusion
Figure 26.18 Corrected version of Figure 26.17
using a mutex to protect the shared variable.
#include "unpthread.h"
#define NLOOP 5000
int counter; /* incremented by threads */
pthread_mutex_t counter_mutex =
PTHREAD_MUTEX_INITIALIZER;
void *doit(void *);
Mutexes: Mutual Exclusion
int main(int argc, char **argv)
{
pthread_t tidA, tidB;
Pthread_create(&tidA, NULL, &doit, NULL);
Pthread_create(&tidB, NULL, &doit, NULL);
/* wait for both threads to terminate */
Pthread_join(tidA, NULL);
Pthread_join(tidB, NULL);
Mutexes: Mutual Exclusion
exit(0);
}
void * doit(void *vptr)
{
int i, val;
/* Each thread fetches, prints, and increments
the counter NLOOP times. The value of the
counter should increase monotonically. */
Mutexes: Mutual Exclusion
for (i = 0; i < NLOOP; i++)
{
Pthread_mutex_lock(&counter_mutex);
val = counter;
printf("%d: %dn", pthread_self(), val + 1);
counter = val + 1;
Pthread_mutex_unlock(&counter_mutex);
}
return (NULL);
}
Condition Variables
• A mutex is fine to prevent simultaneous access to
a shared variable, but we need something else to
let us go to sleep waiting for some condition to
occur.
• Let's demonstrate this with an example. We
return to our Web client in Section 26.6 and
replace the Solaris thr_join with pthread_join.
• But, we cannot call the Pthread function until we
know that a thread has terminated.
• We first declare a global variable that counts the
number of terminated threads and protect it with
a mutex.
Condition Variables
• int ndone; /* number of terminated threads
*/
• pthread_mutex_t ndone_mutex =
PTHREAD_MUTEX_INITIALIZER;
• We then require that each thread increment
this counter when it terminates, being careful
to use the associated mutex.
Condition Variables
void * do_get_read (void *vptr)
{
...
Pthread_mutex_lock(&ndone_mutex);
ndone++;
Pthread_mutex_unlock(&ndone_mutex);
return(fptr); /* terminate thread */
}
Condition Variables
• This is fine, but how do we code the main
loop? It needs to lock the mutex continually
and check if any threads have terminated.
while (nlefttoread > 0)
{
while (nconn < maxnconn && nlefttoconn > 0)
{ /* find a file to read */ ... } /* See if one of
the threads is done */
Pthread_mutex_lock(&ndone_mutex);
Condition Variables
if (ndone > 0)
{
for (i = 0; i < nfiles; i++)
{
if (file[i].f_flags & F_DONE)
{
Pthread_join(file[i].f_tid, (void **) &fptr); /*
update file[i] for terminated thread */
...
}
Condition Variables
}
}
Pthread_mutex_unlock(&ndone_mutex);
}
Condition Variables
• While this is okay, it means the main loop
never goes to sleep; it just loops, checking
ndone every time around the loop.
• This is called polling and is considered a waste
of CPU time.
• We want a method for the main loop to go to
sleep until one of its threads notifies it that
something is ready.
Condition Variables
• A condition variable, in conjunction with a
mutex, provides this facility.
• The mutex provides mutual exclusion and the
condition variable provides a signaling
mechanism.
• In terms of Pthreads, a condition variable is a
variable of type pthread_cond_t.
Condition Variables
• They are used with the following two
functions:
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cptr,
pthread_mutex_t *mptr);
int pthread_cond_signal(pthread_cond_t *cptr);
Both return: 0 if OK, positive Exxx value on error
Condition Variables
• A thread notifies the main loop that it is
terminating by incrementing the counter
while its mutex lock is held and by signaling
the condition variable.
• Pthread_mutex_lock(&ndone_mutex);
ndone++;
Pthread_cond_signal(&ndone_cond);
Pthread_mutex_unlock(&ndone_mutex);
Condition Variables
• The main loop then blocks in a call to
pthread_cond_wait, waiting to be signaled by
a terminating thread.
Condition Variables
while (nlefttoread > 0)
{
while (nconn < maxnconn && nlefttoconn > 0)
{
/* find file to read */
...
} /* Wait for thread to terminate */
Pthread_mutex_lock(&ndone_mutex);
while (ndone == 0)
Pthread_cond_wait (&ndone_cond,
&ndone_mutex);
Condition Variables
for (i = 0; i < nfiles; i++)
{
if (file[i].f_flags & F_DONE)
{
Pthread_join(file[i].f_tid, (void **) &fptr);
/* update file[i] for terminated thread */
...
}
}
Pthread_mutex_unlock (&ndone_mutex); }
Condition Variables
• Notice that the variable ndone is still checked
only while the mutex is held.
• Then, if there is nothing to do,
pthread_cond_wait is called.
• This puts the calling thread to sleep and
releases the mutex lock it holds.
• Furthermore, when the thread returns from
pthread_cond_wait (after some other thread
has signaled it), the thread again holds the
mutex.
Condition Variables
• There are instances when a thread knows that
multiple threads should be awakened, in which
case, pthread_cond_broadcast will wake up all
threads that are blocked on the condition
variable.
• pthread_cond_timedwait lets a thread place a
limit on how long it will block. abstime is a
timespec structure that specifies the system time
when the function must return, even if the
condition variable has not been signaled yet. If
this timeout occurs, ETIME is returned.
Condition Variables
#include <pthread.h>
int pthread_cond_broadcast (pthread_cond_t *
cptr);
int pthread_cond_timedwait (pthread_cond_t *
cptr, pthread_mutex_t *mptr, const struct
timespec *abstime);
Both return: 0 if OK, positive Exxx value on error
Web Client and Simultaneous
Connections (Continued)
• Figure 26.19 Main processing loop of main
function.
while (nlefttoread > 0)
{
while (nconn < maxnconn && nlefttoconn > 0)
{
/* find a file to read */
Web Client and Simultaneous
Connections (Continued)
for (i = 0; i < nfiles; i++)
if (file[i].f_flags == 0)
break;
if (i == nfiles)
err_quit("nlefttoconn = %d but nothing
found", nlefttoconn);
Web Client and Simultaneous
Connections (Continued)
file[i].f_flags = F_CONNECTING;
Pthread_create(&tid, NULL, &do_get_read,
&file[i]);
file[i].f_tid = tid;
nconn++;
nlefttoconn--;
}
Web Client and Simultaneous
Connections (Continued)
/* Wait for thread to terminate */
Pthread_mutex_lock(&ndone_mutex);
while (ndone == 0)
Pthread_cond_wait(&ndone_cond,
&ndone_mutex);
for (i = 0; i < nfiles; i++)
{
Web Client and Simultaneous
Connections (Continued)
if (file[i].f_flags & F_DONE)
{
Pthread_join(file[i].f_tid, (void **) &fptr);
if (&file[i] != fptr)
err_quit("file[i]!= fptr");
fptr->f_flags = F_JOINED; /* clears F_DONE */
ndone--;
nconn--;
Web Client and Simultaneous
Connections (Continued)
nlefttoread--;
printf("thread %d for %s donen", fptr->f_tid,
fptr->f_name);
}
}
Pthread_mutex_unlock(&ndone_mutex);
}
exit(0);
}
Web Client and Simultaneous
Connections (Continued)
If possible, create another thread
• 44–56 This code has not changed.
Wait for thread to terminate
• 57–60 To wait for one of the threads to
terminate, we wait for ndone to be nonzero.
As discussed in Section 26.8, the test must be
done while the mutex is locked.
• The sleep is performed by
pthread_cond_wait.
Web Client and Simultaneous
Connections (Continued)
Handle terminated thread
• 61–73 When a thread has terminated, we go
through all the file structures to find the
appropriate thread, call pthread_join, and
then set the new F_JOINED flag.

More Related Content

What's hot

Introduction to Go programming language
Introduction to Go programming languageIntroduction to Go programming language
Introduction to Go programming languageSlawomir Dorzak
 
golang_getting_started.pptx
golang_getting_started.pptxgolang_getting_started.pptx
golang_getting_started.pptxGuy Komari
 
Regular Expressions in Java
Regular Expressions in JavaRegular Expressions in Java
Regular Expressions in JavaOblivionWalker
 
C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2Chris Ohk
 
Golang getting started
Golang getting startedGolang getting started
Golang getting startedHarshad Patil
 
Golang and Eco-System Introduction / Overview
Golang and Eco-System Introduction / OverviewGolang and Eco-System Introduction / Overview
Golang and Eco-System Introduction / OverviewMarkus Schneider
 
Goroutines and Channels in practice
Goroutines and Channels in practiceGoroutines and Channels in practice
Goroutines and Channels in practiceGuilherme Garnier
 
Coding with golang
Coding with golangCoding with golang
Coding with golangHannahMoss14
 
Introduce to Rust-A Powerful System Language
Introduce to Rust-A Powerful System LanguageIntroduce to Rust-A Powerful System Language
Introduce to Rust-A Powerful System Language安齊 劉
 
Write microservice in golang
Write microservice in golangWrite microservice in golang
Write microservice in golangBo-Yi Wu
 
Threads 02: Acesso exclusivo e comunicação entre threads
Threads 02: Acesso exclusivo e comunicação entre threadsThreads 02: Acesso exclusivo e comunicação entre threads
Threads 02: Acesso exclusivo e comunicação entre threadsHelder da Rocha
 
JavaScript Event Loop
JavaScript Event LoopJavaScript Event Loop
JavaScript Event LoopDesignveloper
 
Unsafe JAX-RS: Breaking REST API
Unsafe JAX-RS: Breaking REST APIUnsafe JAX-RS: Breaking REST API
Unsafe JAX-RS: Breaking REST APIMikhail Egorov
 
Unit Testing with Python
Unit Testing with PythonUnit Testing with Python
Unit Testing with PythonMicroPyramid .
 
AngularJS $http Interceptors (Explanation and Examples)
AngularJS $http Interceptors (Explanation and Examples)AngularJS $http Interceptors (Explanation and Examples)
AngularJS $http Interceptors (Explanation and Examples)Brian Swartzfager
 
Practical non blocking microservices in java 8
Practical non blocking microservices in java 8Practical non blocking microservices in java 8
Practical non blocking microservices in java 8Michal Balinski
 

What's hot (20)

Introduction to Go programming language
Introduction to Go programming languageIntroduction to Go programming language
Introduction to Go programming language
 
golang_getting_started.pptx
golang_getting_started.pptxgolang_getting_started.pptx
golang_getting_started.pptx
 
Regular Expressions in Java
Regular Expressions in JavaRegular Expressions in Java
Regular Expressions in Java
 
C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2
 
Golang getting started
Golang getting startedGolang getting started
Golang getting started
 
Golang and Eco-System Introduction / Overview
Golang and Eco-System Introduction / OverviewGolang and Eco-System Introduction / Overview
Golang and Eco-System Introduction / Overview
 
Golang workshop
Golang workshopGolang workshop
Golang workshop
 
Goroutines and Channels in practice
Goroutines and Channels in practiceGoroutines and Channels in practice
Goroutines and Channels in practice
 
Coding with golang
Coding with golangCoding with golang
Coding with golang
 
Introduce to Rust-A Powerful System Language
Introduce to Rust-A Powerful System LanguageIntroduce to Rust-A Powerful System Language
Introduce to Rust-A Powerful System Language
 
Rust vs C++
Rust vs C++Rust vs C++
Rust vs C++
 
Write microservice in golang
Write microservice in golangWrite microservice in golang
Write microservice in golang
 
Threads 02: Acesso exclusivo e comunicação entre threads
Threads 02: Acesso exclusivo e comunicação entre threadsThreads 02: Acesso exclusivo e comunicação entre threads
Threads 02: Acesso exclusivo e comunicação entre threads
 
Sockets
SocketsSockets
Sockets
 
JavaScript Event Loop
JavaScript Event LoopJavaScript Event Loop
JavaScript Event Loop
 
Unsafe JAX-RS: Breaking REST API
Unsafe JAX-RS: Breaking REST APIUnsafe JAX-RS: Breaking REST API
Unsafe JAX-RS: Breaking REST API
 
Unit Testing with Python
Unit Testing with PythonUnit Testing with Python
Unit Testing with Python
 
AngularJS $http Interceptors (Explanation and Examples)
AngularJS $http Interceptors (Explanation and Examples)AngularJS $http Interceptors (Explanation and Examples)
AngularJS $http Interceptors (Explanation and Examples)
 
Go Lang Tutorial
Go Lang TutorialGo Lang Tutorial
Go Lang Tutorial
 
Practical non blocking microservices in java 8
Practical non blocking microservices in java 8Practical non blocking microservices in java 8
Practical non blocking microservices in java 8
 

Viewers also liked

Creation of threads
Creation of threadsCreation of threads
Creation of threadsmyrajendra
 
Basic Multithreading using Posix Threads
Basic Multithreading using Posix ThreadsBasic Multithreading using Posix Threads
Basic Multithreading using Posix ThreadsTushar B Kute
 
Basic Thread Knowledge
Basic Thread KnowledgeBasic Thread Knowledge
Basic Thread KnowledgeShipra Roy
 
Learning Java 3 – Threads and Synchronization
Learning Java 3 – Threads and SynchronizationLearning Java 3 – Threads and Synchronization
Learning Java 3 – Threads and Synchronizationcaswenson
 
Posix threads(asha)
Posix threads(asha)Posix threads(asha)
Posix threads(asha)Nagarajan
 
Java Thread Synchronization
Java Thread SynchronizationJava Thread Synchronization
Java Thread SynchronizationBenj Del Mundo
 
Ch5: Threads (Operating System)
Ch5: Threads (Operating System)Ch5: Threads (Operating System)
Ch5: Threads (Operating System)Ahmar Hashmi
 
Operating System-Threads-Galvin
Operating System-Threads-GalvinOperating System-Threads-Galvin
Operating System-Threads-GalvinSonali Chauhan
 

Viewers also liked (15)

Creation of threads
Creation of threadsCreation of threads
Creation of threads
 
P-Threads
P-ThreadsP-Threads
P-Threads
 
Thread dumps
Thread dumpsThread dumps
Thread dumps
 
Basic Multithreading using Posix Threads
Basic Multithreading using Posix ThreadsBasic Multithreading using Posix Threads
Basic Multithreading using Posix Threads
 
Pthread
PthreadPthread
Pthread
 
P threads
P threadsP threads
P threads
 
Basic Thread Knowledge
Basic Thread KnowledgeBasic Thread Knowledge
Basic Thread Knowledge
 
Learning Java 3 – Threads and Synchronization
Learning Java 3 – Threads and SynchronizationLearning Java 3 – Threads and Synchronization
Learning Java 3 – Threads and Synchronization
 
Posix threads(asha)
Posix threads(asha)Posix threads(asha)
Posix threads(asha)
 
Posix Threads
Posix ThreadsPosix Threads
Posix Threads
 
Java Thread Synchronization
Java Thread SynchronizationJava Thread Synchronization
Java Thread Synchronization
 
Ch5: Threads (Operating System)
Ch5: Threads (Operating System)Ch5: Threads (Operating System)
Ch5: Threads (Operating System)
 
Threads
ThreadsThreads
Threads
 
Java Basics
Java BasicsJava Basics
Java Basics
 
Operating System-Threads-Galvin
Operating System-Threads-GalvinOperating System-Threads-Galvin
Operating System-Threads-Galvin
 

Similar to Threads

MULTI-THREADING in python appalication.pptx
MULTI-THREADING in python appalication.pptxMULTI-THREADING in python appalication.pptx
MULTI-THREADING in python appalication.pptxSaiDhanushM
 
Parallel program design
Parallel program designParallel program design
Parallel program designZongYing Lyu
 
Tutorial4 Threads
Tutorial4  ThreadsTutorial4  Threads
Tutorial4 Threadstech2click
 
.NET Multithreading/Multitasking
.NET Multithreading/Multitasking.NET Multithreading/Multitasking
.NET Multithreading/MultitaskingSasha Kravchuk
 
Chap3 multi threaded programming
Chap3 multi threaded programmingChap3 multi threaded programming
Chap3 multi threaded programmingraksharao
 
Multithreaded_Programming_in_Python.pdf
Multithreaded_Programming_in_Python.pdfMultithreaded_Programming_in_Python.pdf
Multithreaded_Programming_in_Python.pdfgiridharsripathi
 
Threads and multi threading
Threads and multi threadingThreads and multi threading
Threads and multi threadingAntonio Cesarano
 
Understanding Threads in operating system
Understanding Threads in operating systemUnderstanding Threads in operating system
Understanding Threads in operating systemHarrytoye2
 
Medical Image Processing Strategies for multi-core CPUs
Medical Image Processing Strategies for multi-core CPUsMedical Image Processing Strategies for multi-core CPUs
Medical Image Processing Strategies for multi-core CPUsDaniel Blezek
 
Operating Systems 1 (7/12) - Threads
Operating Systems 1 (7/12) - ThreadsOperating Systems 1 (7/12) - Threads
Operating Systems 1 (7/12) - ThreadsPeter Tröger
 
Chapter 5 - THREADING & REGULAR exp - MAULIK BORSANIYA
Chapter 5 - THREADING & REGULAR exp - MAULIK BORSANIYAChapter 5 - THREADING & REGULAR exp - MAULIK BORSANIYA
Chapter 5 - THREADING & REGULAR exp - MAULIK BORSANIYAMaulik Borsaniya
 
Intro To .Net Threads
Intro To .Net ThreadsIntro To .Net Threads
Intro To .Net Threadsrchakra
 
Generators & Decorators.pptx
Generators & Decorators.pptxGenerators & Decorators.pptx
Generators & Decorators.pptxIrfanShaik98
 
OpenHPI - Parallel Programming Concepts - Week 3
OpenHPI - Parallel Programming Concepts - Week 3OpenHPI - Parallel Programming Concepts - Week 3
OpenHPI - Parallel Programming Concepts - Week 3Peter Tröger
 

Similar to Threads (20)

MULTI-THREADING in python appalication.pptx
MULTI-THREADING in python appalication.pptxMULTI-THREADING in python appalication.pptx
MULTI-THREADING in python appalication.pptx
 
Parallel program design
Parallel program designParallel program design
Parallel program design
 
Chap7 slides
Chap7 slidesChap7 slides
Chap7 slides
 
Tutorial4 Threads
Tutorial4  ThreadsTutorial4  Threads
Tutorial4 Threads
 
.NET Multithreading/Multitasking
.NET Multithreading/Multitasking.NET Multithreading/Multitasking
.NET Multithreading/Multitasking
 
Pthread
PthreadPthread
Pthread
 
Threading
ThreadingThreading
Threading
 
Chap3 multi threaded programming
Chap3 multi threaded programmingChap3 multi threaded programming
Chap3 multi threaded programming
 
Multithreaded_Programming_in_Python.pdf
Multithreaded_Programming_in_Python.pdfMultithreaded_Programming_in_Python.pdf
Multithreaded_Programming_in_Python.pdf
 
Threads and multi threading
Threads and multi threadingThreads and multi threading
Threads and multi threading
 
Understanding Threads in operating system
Understanding Threads in operating systemUnderstanding Threads in operating system
Understanding Threads in operating system
 
Medical Image Processing Strategies for multi-core CPUs
Medical Image Processing Strategies for multi-core CPUsMedical Image Processing Strategies for multi-core CPUs
Medical Image Processing Strategies for multi-core CPUs
 
Operating System lab
Operating System labOperating System lab
Operating System lab
 
Linux Internals - Part III
Linux Internals - Part IIILinux Internals - Part III
Linux Internals - Part III
 
Operating Systems 1 (7/12) - Threads
Operating Systems 1 (7/12) - ThreadsOperating Systems 1 (7/12) - Threads
Operating Systems 1 (7/12) - Threads
 
Chapter 5 - THREADING & REGULAR exp - MAULIK BORSANIYA
Chapter 5 - THREADING & REGULAR exp - MAULIK BORSANIYAChapter 5 - THREADING & REGULAR exp - MAULIK BORSANIYA
Chapter 5 - THREADING & REGULAR exp - MAULIK BORSANIYA
 
Intake 38 12
Intake 38 12Intake 38 12
Intake 38 12
 
Intro To .Net Threads
Intro To .Net ThreadsIntro To .Net Threads
Intro To .Net Threads
 
Generators & Decorators.pptx
Generators & Decorators.pptxGenerators & Decorators.pptx
Generators & Decorators.pptx
 
OpenHPI - Parallel Programming Concepts - Week 3
OpenHPI - Parallel Programming Concepts - Week 3OpenHPI - Parallel Programming Concepts - Week 3
OpenHPI - Parallel Programming Concepts - Week 3
 

Recently uploaded

%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfonteinmasabamasaba
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrainmasabamasaba
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastPapp Krisztián
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...masabamasaba
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...masabamasaba
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...masabamasaba
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park masabamasaba
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdfPearlKirahMaeRagusta1
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...Shane Coughlan
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionOnePlan Solutions
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...SelfMade bd
 
tonesoftg
tonesoftgtonesoftg
tonesoftglanshi9
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in sowetomasabamasaba
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is insideshinachiaurasa2
 

Recently uploaded (20)

%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?WSO2CON 2024 - Does Open Source Still Matter?
WSO2CON 2024 - Does Open Source Still Matter?
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 

Threads

  • 2. INTRODUCTION • In the traditional Unix model, when a process needs something performed by another entity, it forks a child process and lets the child perform the processing. • Most network servers under Unix are written this way, as we have seen in our concurrent server examples: The parent accepts the connection, forks a child, and the child handles the client.
  • 3. INTRODUCTION • While this paradigm has served well for many years, there are problems with fork: Fork is expensive. • Memory is copied from the parent to the child, all descriptors are duplicated in the child, and so on. • Current implementations use a technique called copy-on-write, which avoids a copy of the parent's data space to the child until the child needs its own copy. But, regardless of this optimization, fork is expensive.
  • 4. INTRODUCTION • IPC is required to pass information between the parent and child after the fork. • Passing information from the parent to the child before the fork is easy, since the child starts with a copy of the parent's data space and with a copy of all the parent's descriptors. But, returning information from the child to the parent takes more work.
  • 5. INTRODUCTION Threads help with both problems. • Threads are sometimes called lightweight processes since a thread is "lighter weight" than a process. • That is, thread creation can be 10–100 times faster than process creation. • All threads within a process share the same global memory. This makes the sharing of information easy between the threads, but along with this simplicity comes the problem of synchronization.
  • 6. INTRODUCTION • More than just the global variables are shared. All threads within a process share the following: – Process instructions – Most data – Open files (e.g., descriptors) – Signal handlers and signal dispositions – Current working directory – User and group IDs
  • 7. INTRODUCTION But each thread has its own – Thread ID – Set of registers, including program counter and stack pointer – Stack (for local variables and return addresses) – errno – Signal mask – Priority • In this text, we cover POSIX threads, also called Pthreads.
  • 8. Basic Thread Functions: Creation and Termination • In this section, we will cover five basic thread functions and then use these in the next two sections to recode our TCP client/server using threads instead of fork. • pthread_create Function • When a program is started by exec, a single thread is created, called the initial thread or main thread. • Additional threads are created by pthread_create.
  • 9. Basic Thread Functions: Creation and Termination #include <pthread.h> int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg); Returns: 0 if OK, positive Exxx value on error • Each thread within a process is identified by a thread ID, whose datatype is pthread_t (often an unsigned int). • On successful creation of a new thread, its ID is returned through the pointer tid.
  • 10. Basic Thread Functions: Creation and Termination • Each thread has numerous attributes: its priority, its initial stack size, whether it should be a daemon thread or not, and so on. • When a thread is created, we can specify these attributes by initializing a pthread_attr_t variable that overrides the default. • We normally take the default, in which case, we specify the attr argument as a null pointer.
  • 11. Basic Thread Functions: Creation and Termination • Finally, when we create a thread, we specify a function for it to execute. • The thread starts by calling this function and then terminates either explicitly (by calling pthread_exit) or implicitly (by letting the function return). • The address of the function is specified as the func argument, and this function is called with a single pointer argument, arg.
  • 12. Basic Thread Functions: Creation and Termination • If we need multiple arguments to the function, we must package them into a structure and then pass the address of this structure as the single argument to the start function. • Notice the declarations of func and arg. The function takes one argument, a generic pointer (void *), and returns a generic pointer (void *). This lets us pass one pointer (to anything we want) to the thread, and lets the thread return one pointer (again, to anything we want).
  • 13. Basic Thread Functions: Creation and Termination • The return value from the Pthread functions is normally 0 if successful or nonzero on an error. • But unlike the socket functions, and most system calls, which return –1 on an error and set errno to a positive value, the Pthread functions return the positive error indication as the function's return value.
  • 14. Basic Thread Functions: Creation and Termination • For example, if pthread_create cannot create a new thread because of exceeding some system limit on the number of threads, the function return value is EAGAIN. • The Pthread functions do not set errno. The convention of 0 for success or nonzero for an error is fine since all the Exxx values in <sys/errno.h> are positive. • A value of 0 is never assigned to one of the Exxx names.
  • 15. Basic Thread Functions: Creation and Termination pthread_join Function • We can wait for a given thread to terminate by calling pthread_join. • Comparing threads to Unix processes, pthread_create is similar to fork, and pthread_join is similar to waitpid. #include <pthread.h> int pthread_join (pthread_t tid, void ** status); Returns: 0 if OK, positive Exxx value on error
  • 16. Basic Thread Functions: Creation and Termination • We must specify the tid of the thread that we want to wait for. • Unfortunately, there is no way to wait for any of our threads (similar to waitpid with a process ID argument of –1). • If the status pointer is non-null, the return value from the thread (a pointer to some object) is stored in the location pointed to by status.
  • 17. Basic Thread Functions: Creation and Termination pthread_self Function • Each thread has an ID that identifies it within a given process. • The thread ID is returned by pthread_create and we saw it was used by pthread_join. • A thread fetches this value for itself using pthread_self.
  • 18. Basic Thread Functions: Creation and Termination #include <pthread.h> pthread_t pthread_self (void); Returns: thread ID of calling thread Comparing threads to Unix processes, pthread_self is similar to getpid.
  • 19. Basic Thread Functions: Creation and Termination pthread_detach Function • A thread is either joinable (the default) or detached. • When a joinable thread terminates, its thread ID and exit status are retained until another thread calls pthread_join. • But a detached thread is like a daemon process: When it terminates, all its resources are released and we cannot wait for it to terminate. • If one thread needs to know when another thread terminates, it is best to leave the thread as joinable.
  • 20. Basic Thread Functions: Creation and Termination • The pthread_detach function changes the specified thread so that it is detached. #include <pthread.h> int pthread_detach (pthread_t tid); Returns: 0 if OK, positive Exxx value on error • This function is commonly called by the thread that wants to detach itself, as in • pthread_detach (pthread_self());
  • 21. Basic Thread Functions: Creation and Termination pthread_exit Function • One way for a thread to terminate is to call pthread_exit. #include <pthread.h> void pthread_exit (void *status); Does not return to caller • If the thread is not detached, its thread ID and exit status are retained for a later pthread_join by some other thread in the calling process.
  • 22. Basic Thread Functions: Creation and Termination • The pointer status must not point to an object that is local to the calling thread since that object disappears when the thread terminates.
  • 23. Basic Thread Functions: Creation and Termination • There are two other ways for a thread to terminate: – The function that started the thread (the third argument to pthread_create) can return. Since this function must be declared as returning a void pointer, that return value is the exit status of the thread. – If the main function of the process returns or if any thread calls exit, the process terminates, including any threads.
  • 24. str_cli Function Using Threads • Our first example using threads is to recode the str_cli function from Figure 16.10, which uses fork, to use threads. • Figure 26.1 shows the design of our threads version.
  • 26. str_cli Function Using Threads void *copyto (void *); static int sockfd; /* global for both threads to access */ static FILE *fp; void str_cli(FILE *fp_arg, int sockfd_arg) { char recvline[MAXLINE]; pthread_t tid; sockfd = sockfd_arg; /* copy arguments to externals */ fp = fp_arg;
  • 27. str_cli Function Using Threads Pthread_create(&tid, NULL, copyto, NULL); while (Readline(sockfd, recvline, MAXLINE) > 0) Fputs(recvline, stdout); } void * copyto(void *arg) { char sendline[MAXLINE];
  • 28. str_cli Function Using Threads while (Fgets(sendline, MAXLINE, fp) ! = NULL) Writen(sockfd, sendline, strlen(sendline)); Shutdown(sockfd, SHUT_WR); /* EOF on stdin, send FIN */ return (NULL); /* return (i.e., thread terminates) when EOF on stdin */ }
  • 29. str_cli Function Using Threads Save arguments in externals • 10–11 The thread that we are about to create needs the values of the two arguments to str_cli: fp, the standard I/O FILE pointer for the input file, and sockfd, the TCP socket connected to the server. • For simplicity, we store these two values in external variables. • An alternative technique is to put the two values into a structure and then pass a pointer to the structure as the argument to the thread we are about to create.
  • 30. str_cli Function Using Threads Create new thread • 12:The thread is created and the new thread ID is saved in tid. • The function executed by the new thread is copyto. • No arguments are passed to the thread.
  • 31. str_cli Function Using Threads Main thread loop: copy socket to standard output • 13–14 The main thread calls readline and fputs, copying from the socket to the standard output. Terminate • 15 When the str_cli function returns, the main function terminates by calling exit.
  • 32. str_cli Function Using Threads • When this happens, all threads in the process are terminated. • Normally, the copyto thread will have already terminated by the time the server's main function completes. • But in the case where the server terminates prematurely, calling exit when the server's main function completes will terminate the copyto thread, which is what we want.
  • 33. str_cli Function Using Threads copyto thread • 16–25 This thread just copies from standard input to the socket. When it reads an EOF on standard input, a FIN is sent across the socket by shutdown and the thread returns. • The return from this function (which started the thread) terminates the thread.
  • 34. TCP Echo Server Using Threads • We now redo our TCP echo server using one thread per client instead of one child process per client. static void *doit(void *); /* each thread executes this function */ int main(int argc, char **argv) { int listenfd, connfd;
  • 35. TCP Echo Server Using Threads pthread_t tid; socklen_t addrlen, len; struct sockaddr *cliaddr; if (argc == 2) listenfd = Tcp_listen(NULL, argv[1], &addrlen); else if (argc == 3) listenfd = Tcp_listen(argv[1], argv[2], &addrlen); else err_quit("usage: tcpserv01 [ <host> ] <service or port>");
  • 36. TCP Echo Server Using Threads cliaddr = Malloc(addrlen); for (; ; ) { len = addrlen; connfd = Accept(listenfd, cliaddr, &len); Pthread_create(&tid, NULL, &doit, (void *) connfd); } }
  • 37. TCP Echo Server Using Threads static void * doit(void *arg) { Pthread_detach(pthread_self()); str_echo((int) arg); /* same function as before */ Close((int) arg); /* done with connected socket */ return (NULL); }
  • 38. TCP Echo Server Using Threads Create thread • 17–21 When accept returns, we call pthread_create instead of fork. The single argument that we pass to the doit function is the connected socket descriptor, connfd. Thread function • 23–30 doit is the function executed by the thread. • The thread detaches itself since there is no reason for the main thread to wait for each thread it creates. The function str_echo does not change from Figure 5.3.
  • 39. TCP Echo Server Using Threads • When this function returns, we must close the connected socket since the thread shares all descriptors with the main thread. • With fork, the child did not need to close the connected socket because when the child terminated, all open descriptors were closed on process termination.
  • 40. TCP Echo Server Using Threads • Also notice that the main thread does not close the connected socket, which we always did with a concurrent server that calls fork. • This is because all threads within a process share the descriptors, so if the main thread called close, it would terminate the connection. • Creating a new thread does not affect the reference counts for open descriptors, which is different from fork.
  • 41. TCP Echo Server Using Threads Passing Arguments to New Threads • We mentioned that in Figure 26.3, we cast the integer variable connfd to be a void pointer, but this is not guaranteed to work on all systems. • To handle this correctly requires additional work. • First, notice that we cannot just pass the address of connfd to the new thread. • That is, the following does not work:
  • 42. TCP Echo Server Using Threads int main(int argc, char **argv) { int listenfd, connfd; ... for ( ; ; ) { len = addrlen;
  • 43. TCP Echo Server Using Threads connfd = Accept(listenfd, cliaddr, &len); Pthread_create(&tid, NULL, &doit, &connfd); } } static void * doit(void *arg) { int connfd; connfd = * ((int *) arg);
  • 44. TCP Echo Server Using Threads pthread_detach (pthread_self()); str_echo (connfd); /* same function as before */ Close (connfd); /* done with connected socket */ return (NULL); }
  • 45. TCP Echo Server Using Threads • From an ANSI C perspective this is acceptable: We are guaranteed that we can cast the integer pointer to be a void * and then cast this pointer back to an integer pointer. • The problem is what this pointer points to. • There is one integer variable, connfd in the main thread, and each call to accept overwrites this variable with a new value (the connected descriptor).
  • 46. TCP Echo Server Using Threads The following scenario can occur: • accept returns, connfd is stored into (say the new descriptor is 5), and the main thread calls pthread_create. The pointer to connfd (not its contents) is the final argument to pthread_create. • A thread is created and the doit function is scheduled to start executing. • Another connection is ready and the main thread runs again (before the newly created thread). accept returns, connfd is stored into (say the new descriptor is now 6), and the main thread calls pthread_create.
  • 47. TCP Echo Server Using Threads • Even though two threads are created, both will operate on the final value stored in connfd, which we assume is 6. • The problem is that multiple threads are accessing a shared variable (the integer value in connfd) with no synchronization.
  • 48. TCP Echo Server Using Threads • In Figure 26.3, we solved this problem by passing the value of connfd to pthread_create instead of a pointer to the value. • Figure 26.4 TCP echo server using threads with more portable argument passing.
  • 49. TCP Echo Server Using Threads static void *doit(void *); /* each thread executes this function */ int main(int argc, char **argv) { int listenfd, *iptr; thread_t tid; socklen_t addrlen, len; struct sockaddr *cliaddr;
  • 50. TCP Echo Server Using Threads if (argc == 2) listenfd = Tcp_listen(NULL, argv[1], &addrlen); else if (argc == 3) listenfd = Tcp_listen(argv[1], argv[2], &addrlen); else err_quit("usage: tcpserv01 [ <host> ] <service or port>");
  • 51. TCP Echo Server Using Threads cliaddr = Malloc(addrlen); for ( ; ; ) { len = addrlen; iptr = Malloc(sizeof(int)); *iptr = Accept(listenfd, cliaddr, &len); Pthread_create(&tid, NULL, &doit, iptr); } }
  • 52. TCP Echo Server Using Threads static void * doit(void *arg) { int connfd; connfd = *((int *) arg); free(arg); Pthread_detach(pthread_self()); str_echo(confd); /* same function as before */ Close(confd); /* done with connected socket */ return (NULL); }
  • 53. TCP Echo Server Using Threads • 17–22 Each time we call accept, we first call malloc and allocate space for an integer variable, the connected descriptor. • This gives each thread its own copy of the connected descriptor. • 28–29 The thread fetches the value of the connected descriptor and then calls free to release the memory.
  • 54. TCP Echo Server Using Threads • Historically, the malloc and free functions have been nonre-entrant. • That is, calling either function from a signal handler while the main thread is in the middle of one of these two functions has been a recipe for disaster, because of static data structures that are manipulated by these two functions.
  • 55. TCP Echo Server Using Threads • POSIX requires that these two functions, along with many others, be thread-safe. • This is normally done by some form of synchronization performed within the library functions that is transparent to us.
  • 56. TCP Echo Server Using Threads Thread-Safe Functions • POSIX.1 requires that all the functions defined by POSIX.1 and by the ANSI C standard be thread-safe, with the exceptions listed in Figure 26.5.
  • 57. TCP Echo Server Using Threads
  • 58. TCP Echo Server Using Threads • Unfortunately, POSIX says nothing about thread safety with regard to the networking API functions. • The last five lines in this table are from Unix 98. • We see from Figure 26.5 that the common technique for making a function thread-safe is to define a new function whose name ends in _r. • Two of the functions are thread-safe only if the caller allocates space for the result and passes that pointer as the argument to the function.
  • 59. Thread-Specific Data • When converting existing functions to run in a threads environment, a common problem encountered is due to static variables. • A function that keeps state in a private buffer, or one that returns a result in the form of a pointer to a static buffer, is not thread-safe because multiple threads cannot use the buffer to hold different things at the same time.
  • 60. Thread-Specific Data • When faced with this problem, there are various solutions: • Use thread-specific data. This is nontrivial and then converts the function into one that works only on systems with threads support. • The advantage to this approach is that the calling sequence does not change and all the changes go into the library function and not the applications that call the function.
  • 61. Thread-Specific Data • Change the calling sequence so that the caller packages all the arguments into a structure, and also store in that structure the static variables from Figure 3.18. • Figure 26.6 shows the new structure and new function prototypes. • Figure 26.6 Data structure and function prototype for re-entrant version of readline.
  • 63. Thread-Specific Data • These new functions can be used on threaded and nonthreaded systems, but all applications that call readline must change. • Restructure the interface to avoid any static variables so that the function is thread-safe. • For the readline example, this would be the equivalent of ignoring the speedups introduced in Figure 3.18 and going back to the older version in Figure 3.17. • Since we said the older version was "painfully slow," taking this option is not always viable.
  • 64. Thread-Specific Data • Thread-specific data is a common technique for making an existing function thread-safe. • Before describing the Pthread functions that work with thread-specific data, we describe the concept and a possible implementation, because the functions appear more complicated than they really are.
  • 65. Thread-Specific Data • Each system supports a limited number of thread-specific data items. • POSIX requires this limit be no less than 128 (per process), and we assume this limit in the following example. • The system (probably the threads library) maintains one array of structures per process, which we call key structures, as we show in Figure 26.7.
  • 67. Thread-Specific Data • The flag in the Key structure indicates whether this array element is currently in use, and all the flags are initialized to be "not in use." • When a thread calls pthread_key_create to create a new thread-specific data item, the system searches through its array of Key structures and finds the first one not in use. • Its index, 0 through 127, is called the key, and this index is returned to the calling thread. • We will talk about the "destructor pointer," the other member of the Key structure, shortly.
  • 68. Thread-Specific Data • In addition to the process-wide array of Key structures, the system maintains numerous pieces of information about each thread within a process. • We call this a Pthread structure and part of this information is a 128-element array of pointers, which we call the pkey array. We show this in Figure 26.8.
  • 70. Thread-Specific Data • All entries in the pkey array are initialized to null pointers. These 128 pointers are the "values" associated with each of the possible 128 "keys" in the process. • When we create a key with pthread_key_create, the system tells us its key (index). • Each thread can then store a value (pointer) for the key, and each thread normally obtains the pointer by calling malloc.
  • 71. Thread-Specific Data • We now go through an example of how thread-specific data is used, assuming that our readline function uses thread-specific data to maintain the per-thread state across successive calls to the function. • Shortly we will show the code for this, modifying our readline function to follow these steps:
  • 72. Thread-Specific Data 1.A process is started and multiple threads are created 2.One of the threads will be the first to call readline, and it in turn calls pthread_key_create. The system finds the first unused Key structure in Figure 26.7 and returns its index (0–127) to the caller. We assume an index of 1 in this example. • We will use the pthread_once function to guarantee that pthread_key_create is called only by the first thread to call readline.
  • 73. Thread-Specific Data 3.readline calls pthread_getspecific to get the pkey[1] value (the "pointer" in Figure 26.8 for this key of 1) for this thread, and the returned value is a null pointer. Therefore, readline calls malloc to allocate the memory that it needs to keep the per-thread information across successive calls to readline for this thread.
  • 74. Thread-Specific Data • readline initializes this memory as needed and calls pthread_setspecific to set the thread-specific data pointer (pkey[1]) for this key to point to the memory it just allocated. We show this in Figure 26.9, assuming that the calling thread is thread 0 in the process.
  • 76. Thread-Specific Data • In this figure, we note that the Pthread structure is maintained by the system (probably the thread library), but the actual thread-specific data that we malloc is maintained by our function (readline, in this case). • All that pthread_setspecific does is set the pointer for this key in the Pthread structure to point to our allocated memory. • Similarly, all that pthread_getspecific does is return that pointer to us.
  • 77. Thread-Specific Data 4. Another thread, say thread n, calls readline, perhaps while thread 0 is still executing within readline. readline calls pthread_once to initialize the key for this thread-specific data item, but since it has already been called, it is not called again.
  • 78. Thread-Specific Data 5.readline calls pthread_getspecific to fetch the pkey [1] pointer for this thread, and a null pointer is returned. This thread then calls malloc, followed by pthread_setspecific, just like thread 0, initializing its thread-specific data for this key (1). We show this in Figure 26.10.
  • 80. Thread-Specific Data 6.Thread n continues executing in readline, using and modifying its own thread-specific data. • One item we have not addressed is: What happens when a thread terminates? If the thread has called our readline function, that function has allocated a region of memory that needs to be freed. • This is where the "destructor pointer" from Figure 26.7 is used.
  • 81. Thread-Specific Data • When the thread that creates the thread- specific data item calls pthread_key_create, one argument to this function is a pointer to a destructor function. • When a thread terminates, the system goes through that thread's pkey array, calling the corresponding destructor function for each non-null pkey pointer.
  • 82. Thread-Specific Data • What we mean by "corresponding destructor" is the function pointer stored in the Key array in Figure 26.7. • This is how the thread-specific data is freed when a thread terminates. • The first two functions that are normally called when dealing with thread-specific data are pthread_once and pthread_key_create.
  • 83. Thread-Specific Data #include <pthread.h> int pthread_once(pthread_once_t *onceptr, void (*init) (void)); int pthread_key_create(pthread_key_t *keyptr, void (*destructor) (void *value)); Both return: 0 if OK, positive Exxx value on error
  • 84. Thread-Specific Data • pthread_once is normally called every time a function that uses thread-specific data is called, but pthread_once uses the value in the variable pointed to by onceptr to guarantee that the init function is called only one time per process.
  • 85. Thread-Specific Data • pthread_key_create must be called only one time for a given key within a process. • The key is returned through the keyptr pointer, and the destructor function, if the argument is a non-null pointer, will be called by each thread on termination if that thread has stored a value for this key.
  • 86. Thread-Specific Data • Typical usage of these two functions (ignoring error returns) is as follows: pthread_key_t rl_key; pthread_once_t rl_once = PTHREAD_ONCE_INIT; void readline_destructor(void *ptr) { free(ptr); }
  • 87. Thread-Specific Data void readline_once(void) { pthread_key_create(&rl_key, readline_destructor); } ssize_t readline( ... ) { ... pthread_once(&rl_once, readline_once);
  • 88. Thread-Specific Data if ( (ptr = pthread_getspecific(rl_key)) == NULL) { ptr = Malloc( ... ); pthread_setspecific(rl_key, ptr); /* initialize memory pointed to by ptr */ } ... /* use values pointed to by ptr */ }
  • 89. Thread-Specific Data • Every time readline is called, it calls pthread_once. • This function uses the value pointed to by its onceptr argument (the contents of the variable rl_once) to make certain that its init function is called only one time. • This initialization function, readline_once, creates the thread-specific data key that is stored in rl_key, and which readline then uses in calls to pthread_getspecific and pthread_setspecific.
  • 90. Thread-Specific Data • The pthread_getspecific and pthread_setspecific functions are used to fetch and store the value associated with a key. • This value is what we called the "pointer" in Figure 26.8. • What this pointer points to is up to the application, but normally, it points to dynamically allocated memory.
  • 91. Thread-Specific Data #include <pthread.h> void *pthread_getspecific(pthread_key_t key); Returns: pointer to thread-specific data (possibly a null pointer) int pthread_setspecific(pthread_key_t key, const void *value); Returns: 0 if OK, positive Exxx value on error
  • 92. Thread-Specific Data • Notice that the argument to pthread_key_create is a pointer to the key (because this function stores the value assigned to the key), while the arguments to the get and set functions are the key itself (probably a small integer index as discussed earlier).
  • 93. Thread-Specific Data Example: readline Function Using Thread- Specific Data • We now show a complete example of thread- specific data by converting the optimized version of our readline function from Figure 3.18 to be thread-safe, without changing the calling sequence.
  • 94. Thread-Specific Data • Figure 26.11 shows the first part of the function: the pthread_key_t variable, the pthread_once_t variable, the readline_destructor function, the readline_once function, and our Rline structure that contains all the information we must maintain on a per-thread basis.
  • 95. Thread-Specific Data Figure 26.11 First part of thread-safe readline function. static pthread_key_t rl_key; static pthread_once_t rl_once = PTHREAD_ONCE_INIT; static void readline_destructor(void *ptr) { free(ptr); }
  • 96. Thread-Specific Data static void readline_once(void) { Pthread_key_creat(&rl_key, readline_destructor); } typedef struct { int rl_cnt; /* initialize to 0 */
  • 97. Thread-Specific Data char *rl_bufptr; /* initialize to rl_buf */ char rl_buf[MAXLINE]; } Rline;
  • 98. Thread-Specific Data Destructor 4–8 Our destructor function just frees the memory that was allocated for this thread. One-time function 9–13 We will see that our one-time function is called one time by pthread_once, and it just creates the key that is used by readline.
  • 99. Thread-Specific Data Rline structure 14–18 Our Rline structure contains the three variables that caused the problem by being declared static in Figure 3.18. One of these structures will be dynamically allocated per thread and then released by our destructor function. • Figure 26.12 shows the actual readline function, plus the function my_read it calls. This figure is a modification of Figure 3.18.
  • 100. Thread-Specific Data my_read function 19–35 The first argument to the function is now a pointer to the Rline structure that was allocated for this thread (the actual thread-specific data). Allocate thread-specific data 42 We first call pthread_once so that the first thread that calls readline in this process calls readline_once to create the thread-specific data key.
  • 101. Thread-Specific Data • Fetch thread-specific data pointer • 43–46 pthread_getspecific returns the pointer to the Rline structure for this thread. • But if this is the first time this thread has called readline, the return value is a null pointer. • In this case, we allocate space for an Rline structure and the rl_cnt member is initialized to 0 by calloc.
  • 102. Thread-Specific Data • We then store the pointer for this thread by calling pthread_setspecific. • The next time this thread calls readline, pthread_getspecific will return this pointer that was just stored.
  • 103. Thread-Specific Data Figure 26.12 Second part of thread-safe readline function. static ssize_t my_read(Rline *tsd, int fd, char *ptr) { if (tsd->rl_cnt < = 0) { again: if ( (tsd->rl_cnt = read(fd, tsd->rl_buf, MAXLINE)) < 0) { if (error == EINTR) goto again;
  • 104. Thread-Specific Data return (-1); } else if (tsd->rl_cnt == 0) return (0); tsd->rl_bufptr = tsd->rl_buf; }
  • 105. Thread-Specific Data tsd->rl_cnt--; *ptr = *tsd->rl_bufptr++; return (1); } ssize_t readline(int fd, void *vptr, size_t maxlen) { size_t n, rc; char c, *ptr; Rline *tsd;
  • 106. Thread-Specific Data Pthread_once(&rl_once, readline_once); if ( (tsd = pthread_getspecific(rl_key)) == NULL) { tsd = Calloc(1, sizeof(Rline)); /* init to 0 */ Pthread_setspecific(rl_key, tsd); }
  • 107. Thread-Specific Data ptr = vptr; for (n = 1; n < maxlen; n++) { if ( (rc = my_read(tsd, fd, &c)) == 1) { *ptr++ = c; if (c == 'n‘) break; }
  • 108. Thread-Specific Data else if (rc == 0) { *ptr = 0; return (n - 1); /* EOF, n - 1 bytes read */ } else return (-1); /* error, errno set by read() */ } *ptr = 0; return (n); }
  • 110. Nonblocking connect: Web Client • A real-world example of nonblocking connects started with the Netscape Web. • The client establishes an HTTP connection with a Web server and fetches a home page. • Often, that page will have numerous references to other Web pages. Instead of fetching these other pages serially, one at a time, the client can fetch more than one at the same time using nonblocking connects.
  • 111. Nonblocking connect: Web Client • Figure 16.12 shows an example of establishing multiple connections in parallel. • The leftmost scenario shows all three connections performed serially. • We assume that the first connection takes 10 units of time, the second 15, and the third 4, for a total of 29 units of time.
  • 113. Nonblocking connect: Web Client • In the middle scenario, we perform two connections in parallel. • At time 0, the first two connections are started, and when the first of these finishes, we start the third. • The total time is almost halved, from 29 to 15, but realize that this is the ideal case.
  • 114. Nonblocking connect: Web Client • If the parallel connections are sharing a common link, each can compete against each other for the limited resources and all the individual connection times might get longer. • For example, the time of 10 might be 15, the time of 15 might be 20, and the time of 4 might be 6. • Nevertheless, the total time would be 21, still shorter than the serial scenario.
  • 115. Nonblocking connect: Web Client • In the third scenario, we perform three connections in parallel, and we again assume there is no interference between the three connections (the ideal case). • But, the total time is the same (15 units) as the second scenario given the example times that we choose.
  • 116. Nonblocking connect: Web Client • When dealing with Web clients, the first connection is done by itself, followed by multiple connections for the references found in the data from that first connection. • We show this in Figure 16.13.
  • 118. Nonblocking connect: Web Client • To further optimize this sequence, the client can start parsing the data that is returned for the first connection before the first connection completes and initiate additional connections as soon as it knows that additional connections are needed. • Program will read up to 20 files from a Web server. • We specify as command-line arguments the maximum number of parallel connections, the server's hostname, and each of the filenames to fetch from the server.
  • 119. Nonblocking connect: Web Client solaris % web 3 www.foobar.com / image1.gif image2.gif image3.gif image4.gif image5.gif image6.gif image7.gif • The command-line arguments specify three simultaneous connections: the server's hostname, the filename for the home page (/, the server's root page), and seven files to then read (which in this example are all GIF images).
  • 120. Nonblocking connect: Web Client • These seven files would normally be referenced on the home page, and a Web client would read the home page and parse the HTML to obtain these filenames.
  • 121. Web Client and Simultaneous Connections (Continued) • We now revisit the Web client example from Section 16.5 and recode it using threads instead of nonblocking connects. • With threads, we can leave the sockets in their default blocking mode and create one thread per connection. • Each thread can block in its call to connect, as the kernel will just run some other thread that is ready.
  • 122. Web Client and Simultaneous Connections (Continued) • Figure 26.13 shows the first part of the program, the globals, and the start of the main function. Globals • 1–16 #include <thread.h>, in addition to the normal <pthread.h>, because we need to use Solaris threads in addition to Pthreads.
  • 123. Web Client and Simultaneous Connections (Continued) • 10 We have added one member to the file structure: f_tid, the thread ID. • The remainder of this code is similar to Figure 16.15. • With this threads version, we do not use select and therefore do not need any descriptor sets or the variable maxfd. • 36 The home_page function that is called is unchanged from Figure 16.16.
  • 124. Web Client and Simultaneous Connections (Continued)
  • 125. Web Client and Simultaneous Connections (Continued)
  • 126. Web Client and Simultaneous Connections (Continued)
  • 127. Web Client and Simultaneous Connections (Continued)
  • 128. Web Client and Simultaneous Connections (Continued)
  • 129. Web Client and Simultaneous Connections (Continued) If possible, create another thread • 40–52 If we are allowed to create another thread (nconn is less than maxnconn), we do so. The function that each new thread executes is do_get_read and the argument is the pointer to the file structure.
  • 130. Web Client and Simultaneous Connections (Continued) Wait for any thread to terminate • 53–54 We call the Solaris thread function thr_join with a first argument of 0 to wait for any one of our threads to terminate. Unfortunately, Pthreads does not provide a way to wait for any one of our threads to terminate; the pthread_join function makes us specify exactly which thread we want to wait for
  • 131. Web Client and Simultaneous Connections (Continued) Wait for any thread to terminate • We will see in Section 26.9 that the Pthreads solution for this problem is more complicated, requiring us to use a condition variable for the terminating thread to notify the main thread when it is done.
  • 132. Web Client and Simultaneous Connections (Continued) • Figure 26.15 shows the do_get_read function, which is executed by each thread. This function establishes the TCP connection, sends an HTTP GET command to the server, and reads the server's reply.
  • 133. Web Client and Simultaneous Connections (Continued)
  • 134. Web Client and Simultaneous Connections (Continued)
  • 135. Web Client and Simultaneous Connections (Continued) Create TCP socket, establish connection • 68–71 A TCP socket is created and a connection is established by our tcp_connect function. The socket is a normal blocking socket, so the thread will block in the call to connect until the connection is established.
  • 136. Web Client and Simultaneous Connections (Continued) Write request to server • 72 write_get_cmd builds the HTTP GET command and sends it to the server. We do not show this function again as the only difference from Figure 16.18 is that the threads version does not call FD_SET and does not use maxfd.
  • 137. Web Client and Simultaneous Connections (Continued) Read server's reply • 73–82 The server's reply is then read. When the connection is closed by the server, the F_DONE flag is set and the function returns, terminating the thread.
  • 138. Mutexes: Mutual Exclusion Figure 26.17 Two threads that increment a global variable incorrectly. #include "unpthread.h" #define NLOOP 5000 int counter; /* incremented by threads */ void *doit(void *); int main(int argc, char **argv) { pthread_t tidA, tidB;
  • 139. Mutexes: Mutual Exclusion Pthread_create(&tidA, NULL, &doit, NULL); Pthread_create(&tidB, NULL, &doit, NULL); /* wait for both threads to terminate */ Pthread_join(tidA, NULL); Pthread_join(tidB, NULL); exit(0); }
  • 140. Mutexes: Mutual Exclusion void * doit(void *vptr) { int i, val; /* Each thread fetches, prints, and increments the counter NLOOP times. 22 * The value of the counter should increase monotonically. */
  • 141. Mutexes: Mutual Exclusion for (i = 0; i < NLOOP; i++) { val = counter; printf("%d: %dn", pthread_self(), val + 1); counter = val + 1; } return (NULL); }
  • 142. Mutexes: Mutual Exclusion • Notice the error the first time the system switches from thread 4 to thread 5: The value 518 i Figure 26.16. Output from program in Figure 26.17.
  • 144. Mutexes: Mutual Exclusion • The problem we just discussed, multiple threads updating a shared variable, is the simplest problem. • The solution is to protect the shared variable with a mutex (which stands for "mutual exclusion") and access the variable only when we hold the mutex. • In terms of Pthreads, a mutex is a variable of type pthread_mutex_t.
  • 145. Mutexes: Mutual Exclusion • We lock and unlock a mutex using the following two functions: #include <pthread.h> int pthread_mutex_lock(pthread_mutex_t * mptr); int pthread_mutex_unlock(pthread_mutex_t * mptr); Both return: 0 if OK, positive Exxx value on error
  • 146. Mutexes: Mutual Exclusion Figure 26.18 Corrected version of Figure 26.17 using a mutex to protect the shared variable. #include "unpthread.h" #define NLOOP 5000 int counter; /* incremented by threads */ pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER; void *doit(void *);
  • 147. Mutexes: Mutual Exclusion int main(int argc, char **argv) { pthread_t tidA, tidB; Pthread_create(&tidA, NULL, &doit, NULL); Pthread_create(&tidB, NULL, &doit, NULL); /* wait for both threads to terminate */ Pthread_join(tidA, NULL); Pthread_join(tidB, NULL);
  • 148. Mutexes: Mutual Exclusion exit(0); } void * doit(void *vptr) { int i, val; /* Each thread fetches, prints, and increments the counter NLOOP times. The value of the counter should increase monotonically. */
  • 149. Mutexes: Mutual Exclusion for (i = 0; i < NLOOP; i++) { Pthread_mutex_lock(&counter_mutex); val = counter; printf("%d: %dn", pthread_self(), val + 1); counter = val + 1; Pthread_mutex_unlock(&counter_mutex); } return (NULL); }
  • 150. Condition Variables • A mutex is fine to prevent simultaneous access to a shared variable, but we need something else to let us go to sleep waiting for some condition to occur. • Let's demonstrate this with an example. We return to our Web client in Section 26.6 and replace the Solaris thr_join with pthread_join. • But, we cannot call the Pthread function until we know that a thread has terminated. • We first declare a global variable that counts the number of terminated threads and protect it with a mutex.
  • 151. Condition Variables • int ndone; /* number of terminated threads */ • pthread_mutex_t ndone_mutex = PTHREAD_MUTEX_INITIALIZER; • We then require that each thread increment this counter when it terminates, being careful to use the associated mutex.
  • 152. Condition Variables void * do_get_read (void *vptr) { ... Pthread_mutex_lock(&ndone_mutex); ndone++; Pthread_mutex_unlock(&ndone_mutex); return(fptr); /* terminate thread */ }
  • 153. Condition Variables • This is fine, but how do we code the main loop? It needs to lock the mutex continually and check if any threads have terminated. while (nlefttoread > 0) { while (nconn < maxnconn && nlefttoconn > 0) { /* find a file to read */ ... } /* See if one of the threads is done */ Pthread_mutex_lock(&ndone_mutex);
  • 154. Condition Variables if (ndone > 0) { for (i = 0; i < nfiles; i++) { if (file[i].f_flags & F_DONE) { Pthread_join(file[i].f_tid, (void **) &fptr); /* update file[i] for terminated thread */ ... }
  • 156. Condition Variables • While this is okay, it means the main loop never goes to sleep; it just loops, checking ndone every time around the loop. • This is called polling and is considered a waste of CPU time. • We want a method for the main loop to go to sleep until one of its threads notifies it that something is ready.
  • 157. Condition Variables • A condition variable, in conjunction with a mutex, provides this facility. • The mutex provides mutual exclusion and the condition variable provides a signaling mechanism. • In terms of Pthreads, a condition variable is a variable of type pthread_cond_t.
  • 158. Condition Variables • They are used with the following two functions: #include <pthread.h> int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr); int pthread_cond_signal(pthread_cond_t *cptr); Both return: 0 if OK, positive Exxx value on error
  • 159. Condition Variables • A thread notifies the main loop that it is terminating by incrementing the counter while its mutex lock is held and by signaling the condition variable. • Pthread_mutex_lock(&ndone_mutex); ndone++; Pthread_cond_signal(&ndone_cond); Pthread_mutex_unlock(&ndone_mutex);
  • 160. Condition Variables • The main loop then blocks in a call to pthread_cond_wait, waiting to be signaled by a terminating thread.
  • 161. Condition Variables while (nlefttoread > 0) { while (nconn < maxnconn && nlefttoconn > 0) { /* find file to read */ ... } /* Wait for thread to terminate */ Pthread_mutex_lock(&ndone_mutex); while (ndone == 0) Pthread_cond_wait (&ndone_cond, &ndone_mutex);
  • 162. Condition Variables for (i = 0; i < nfiles; i++) { if (file[i].f_flags & F_DONE) { Pthread_join(file[i].f_tid, (void **) &fptr); /* update file[i] for terminated thread */ ... } } Pthread_mutex_unlock (&ndone_mutex); }
  • 163. Condition Variables • Notice that the variable ndone is still checked only while the mutex is held. • Then, if there is nothing to do, pthread_cond_wait is called. • This puts the calling thread to sleep and releases the mutex lock it holds. • Furthermore, when the thread returns from pthread_cond_wait (after some other thread has signaled it), the thread again holds the mutex.
  • 164. Condition Variables • There are instances when a thread knows that multiple threads should be awakened, in which case, pthread_cond_broadcast will wake up all threads that are blocked on the condition variable. • pthread_cond_timedwait lets a thread place a limit on how long it will block. abstime is a timespec structure that specifies the system time when the function must return, even if the condition variable has not been signaled yet. If this timeout occurs, ETIME is returned.
  • 165. Condition Variables #include <pthread.h> int pthread_cond_broadcast (pthread_cond_t * cptr); int pthread_cond_timedwait (pthread_cond_t * cptr, pthread_mutex_t *mptr, const struct timespec *abstime); Both return: 0 if OK, positive Exxx value on error
  • 166. Web Client and Simultaneous Connections (Continued) • Figure 26.19 Main processing loop of main function. while (nlefttoread > 0) { while (nconn < maxnconn && nlefttoconn > 0) { /* find a file to read */
  • 167. Web Client and Simultaneous Connections (Continued) for (i = 0; i < nfiles; i++) if (file[i].f_flags == 0) break; if (i == nfiles) err_quit("nlefttoconn = %d but nothing found", nlefttoconn);
  • 168. Web Client and Simultaneous Connections (Continued) file[i].f_flags = F_CONNECTING; Pthread_create(&tid, NULL, &do_get_read, &file[i]); file[i].f_tid = tid; nconn++; nlefttoconn--; }
  • 169. Web Client and Simultaneous Connections (Continued) /* Wait for thread to terminate */ Pthread_mutex_lock(&ndone_mutex); while (ndone == 0) Pthread_cond_wait(&ndone_cond, &ndone_mutex); for (i = 0; i < nfiles; i++) {
  • 170. Web Client and Simultaneous Connections (Continued) if (file[i].f_flags & F_DONE) { Pthread_join(file[i].f_tid, (void **) &fptr); if (&file[i] != fptr) err_quit("file[i]!= fptr"); fptr->f_flags = F_JOINED; /* clears F_DONE */ ndone--; nconn--;
  • 171. Web Client and Simultaneous Connections (Continued) nlefttoread--; printf("thread %d for %s donen", fptr->f_tid, fptr->f_name); } } Pthread_mutex_unlock(&ndone_mutex); } exit(0); }
  • 172. Web Client and Simultaneous Connections (Continued) If possible, create another thread • 44–56 This code has not changed. Wait for thread to terminate • 57–60 To wait for one of the threads to terminate, we wait for ndone to be nonzero. As discussed in Section 26.8, the test must be done while the mutex is locked. • The sleep is performed by pthread_cond_wait.
  • 173. Web Client and Simultaneous Connections (Continued) Handle terminated thread • 61–73 When a thread has terminated, we go through all the file structures to find the appropriate thread, call pthread_join, and then set the new F_JOINED flag.