2. The server receives the request, interprets it, and manipulates its resources in
12.6 Using Threads for Parallelism
12.7.3 Using Existing Library Functions in Threaded Programs
Most Linux functions, including the fànctions•c,lefined in. the standard C library (such as malloc, free, realloc,.printf, and'scf.llf), are threa.d-safe, with only a few exceptions. Figure 12.41 lists some common exceptions. (See [110] for a complete list.) The strtok function is a deprecated function (one whose use is discouraged) for parsing strings. The asctime, ctime, and localtime functions are popular functions for convertingã back and forth between,different time and date formats. Theãgethostbyaddr, gethostbyname, and inet_ntoa functions are obsolete network programming ftmctions that have been repla'ced by the reentrant getaddrinf o,.getnameinfo, and inet_ntop functions, respectively (see Chapter 11 ). With the exceptions of rand and strt9k, they-are of the class 3 variety that i;eturn a pointer to a static variable. If we need to eall 'one nf these'ftihctions ili
a threaded program, the least disruptiv~ ~pproach to the calleris to lock' and copy.
Howe\?er, the lock-ahd-copy approach ~as a number of disad'vantages. Ffrst;the additional synchr6nizatlon'slow~ doWil the piogra'.m. Second, functi'ons that return pointers fo'c'omplh $tructures of structureJrequife a deep copy 'ofthe structures in order to' col<y the entire structure liierarchy. Third, the lbck :and-copy approach
l J p I
will not work for a class 2 thread-unsafe function such as rand' that'relles'on' static state across calls.
Thread-unsafe function
ranq
sti:"tok , ,.
asctime ctime
ge'l;hostbyaddr gethostbyname , inet_ntoa lotaltime 1
Thread-unsafe" class
1, ' I
( '
2 2
?
3 3 3 3 , 3
I ~\. ' '>'
•1 ...
• ,..rr
Linux thread-safe version rand_r
strtok_r .51-sctime_r
c:tipe_r 1, gethostbyqddr_r gethostbyname_r (none)
localtime_r
"
Figure, 12.41 .fq!]lrpo,n thread-unsafe libraryãfunct)qns.
HJ I
Section 12.7 .Qther Concurrency Issues 1025 Therefore, Linux systems prdvide reentrant versions of most thread-unsafe
functicms. The names•of the reentrant versions always end with the _r suffix. For example, the reentrant version of asctime is called g.sctime!,_r. •We recommend using these functions whenever possible.
12.7.4 Races
A race occurs when the correctness of a program depends on one thread reaching point x in its control flow before another thread reaches point y ,,Races usually occur because programmers assume that threads will take some particular trajec- tory thrbugh the execution state space,. forgetting the 'golden rule; that thre"aded programs mustlwork' correctly for any feasible trajectoryi
l\'h'example is the easiest way to understand the nature-of races. Consider the simple program inãFigure 12.42. The 'main-thread create&r.four peer threads and passes a pointer to a 'unique integer ID t6 each one. Each p<"er thread copies the
\, " ã I
2 3 4 5 ã!
6!
7 8 9 10 11
/••WARNING: Thfs code is 'buggy!° •/
#in'a.ude 11csaP~~h"'1
#define N 4
ti' void •thread ~oid' :.vargp) ;
' "
int main()
J JI•
pthread_t tid[N];
int i;
..
..
12 13 14 15 16 17 18 1~
20
for (i = O; i < N; i+T)
Pthread_create(&tid[i], NULL, thread, &i);
21
for (i = O; i < N; i++) Pthread_join(tid(i], exit(O);
"
/* Thread' rout
0
iiie •/
vojd !thre&d(void •vargp) {
NULL);
22 int myid = •((int *)Vargp) i
23 printf ( ~'J!ello from ~hread I %d\n'•! ''myid)ãi
24 retur,n NULL ;i
25 1.1..} ,,
Fig_ure 12.42 A progfam with a race.
.,
lj \!
J
..
..
II r
l
I
I I
~
\ '
I I
t
J 026 Chapter 12 Concurrent Programming
ID passed in its argument to aãlocal variable.(line 22) and then prints a message containing the ID. It looks simple enough, but.when we run this program.en our syste'm;ãwe get the following incorrect result: " ,, ,. ~ ,, linux> ./race
Hello from thread 1 Hello from thread 3 Jlello from thread 2
H9llo from thread 3 ,, 11,..
•1
I
The prp):)lem is caus!'d by,a.race IJet:.v~en ea~h peer thread,.and,the main thread. Can you spot the race.? Here i<t.wi)at happens . .\Yijen the main thread creates a peer thread.i.n line 13, it pas;;es a point~r to the'loca! stackã,wriable i. ãAt this point, .the ,race is on l)~twe!'n the ãn!'xt increment; of i in line. 12 and
\.Qe dereferencing.and assignment.of the argument in line124 .. IJ the peer thread executes line 22 before the maip thread increments i in line 12, then the myid variable gets the correct ID. Otherwise, it will contain the ID of some other thread.
Tu(;. ~cary thing is that whether we get the corr~ct answer depends .on how the kernel schedules the execution of the threads. On our system it fails, but on other systems it might work correctly, leaving the programmer 'blissfully uriaware of a
serious bug. ,,.
:ro eliminate the race, we can dynamical,ly allocate a separate block for eac;h integer ID and pass the thread routine a pointer to.this l;Jl9ck, as.sho}".n,/n Fig- ure 12.43 (lines 12-14). Notice that the thread routine must free the'block in order to avoid a memory leak.
When we run this program on our system, we now get the correct result:
linux> ./norace Hello from thread 0 Hello f rOm thread 1 Hello from thread 2 Hello f tom thread 3
~tiã
!fiii,i:lc~.2.h!filll'lj iiil1::Efi1"':lliib}ila9e-JQJ23;;:_; J'"'ããã,' ~~;'~1Ê:1
In Figure 12.43, we might be tempted to free the allocated memory block immedi- ately after line 14 in the main thread, instead of freeingã it in the peer thread. But
this would be a bad idea. Why? '
l ' ' , ~ '
tfiatti~.tt~1fi'l!i4ifu1Đh'ij~9';~~~ ã::J
A. In Figure 12.43, we eliminated the race by allocating "a separate block for each integer ID. Outline a different approach that does not call the malloc or fre~ functions.
B. What are the advantages and qisadvantages of t"'is approach?
J ... l .
i I