-
Notifications
You must be signed in to change notification settings - Fork 2
/
threads.tex
1604 lines (1449 loc) · 66.2 KB
/
threads.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
include(`macros.m4')
\pagebreak
\pdfbookmark[0]{threads and their synchronization}{threads}
\begin{slide}
\sltitle{Contents}
\slidecontents{8}
\end{slide}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{slide}
\sltitle{Threads}
\begin{itemize}
\item \emph{thread} = \emph{thread of execution}, a basic software ``thing''
that can do work on a computer
\item classic Unix model: single threaded processes
\item with introduction of threads, a process becomes just a container for
threads
\item advantages of multithreaded applications
\begin{itemize}
\item speed-up -- a typical objective is having threads on multiple CPUs
running in parallel
\item more modular programming
\end{itemize}
\item disadvantages
\begin{itemize}
\item more complex code
\item debugging may become more difficult
\end{itemize}
\end{itemize}
\end{slide}
\begin{itemize}
\item \emsl{While one has to put resources into sharing data when working with
processes, one has to put resources into managing
inherent data sharing if working with threads.} Note that all threads of the
same process have equal access to the process virtual address space.
\item Not all applications are fit for multithreading as some tasks are not
inherently concurrent in nature.
\item Even that debuggers typically support threads, debugging changes timing so
the problem may not reproduce when using the debugger. That is usually not an
issue in a single threaded application.
\item There is an excellent book on programming with POSIX threads by Butenhof,
see page \pageref{REF_PROGRAMMING}. You can also use an online book
\emph{Multithreaded Programming Guide} available on
\url{http://docs.oracle.com}.
\item \hlabel{PRIVILEGE_SEPARATION} An example situation when you do not want to
use threads is if you want to change a real and effective UID of processes. Take
OpenSSH -- every connection is served by two processes. One, with maximum
privileges, usually runs as root, and provides services (allocating a pseudo
terminal is one of them) to a second, unprivileged process. The idea is that
most of the OpenSSH code does not need any special privilege so if a bug is
found in code that is run under an unprivileged user, the damage is much smaller
than if such code was run with maximum privileges. This technique is called a
\emph{privilege separation} and you could not do the same thing with threads.
\end{itemize}
\begin{slide}
\sltitle{Implementation of threads}
\setlength{\baselineskip}{0.8\baselineskip}
\begin{description}
\item[library-thread model (1:N)]~\\\vspace{-2.5ex}
\begin{itemize}
\item threads are implemented in a library. Kernel has no knowledge of
such threads.
\item run-time library schedules threads on processes and kernel schedules
processes on CPUs
\item[$\oplus$] less overhead
\item[$\ominus$] more threads of the same process cannot run in parallel
\end{itemize}
\item [kernel-thread model (1:1)]~\\\vspace{-2.5ex}
\begin{itemize}
\item threads are a first class kernel citizen
\item[$\oplus$] more threads of the same process can run in parallel on
multiple CPUs
\end{itemize}
\item[hybrid models (M:N)]~\\\vspace{-2.5ex}
\begin{itemize}
\item N library threads scheduled on M kernel threads, N $>=$ M
\item[$\ominus$] too complex to implement, not really used today
\end{itemize}
\end{description}
\end{slide}
\begin{itemize}
\item Original Unix systems used library models (sometimes called
\emph{lightweight threads} or \emph{green threads}). Today in general
most of the systems stick to the 1:1 model. There was some evolution in
the past, e.g. Solaris 9 was using the M:N model and switched to 1:1 in
Solaris 10.
\item The drawback of the 1:1 model is that there is always some non-trivial
overhead associated with thread creation as the library has to call into the
kernel as well in order to create the associated kernel thread.
\item Threads implemented in a library may be either preemptive or
non-pre\-emp\-tive. To achieve preemption, you can use timers and signals.
However, if the objective is more in better modular programming than real
parallelism, usually non-preemptive threads do fine. Switching threads will be
done when a process would normally block in system calls.
\item \hlabel{SETJMP} If a system call blocks in a library implemented thread
model, the whole process will block as the kernel has no knowledge there are
more threads in the process. So the threading library is written the way that
non-blocking calls are used, the thread context is saved after that and the
library switches to another thread via \funnm{setjmp}() and \funnm{longjmp}()
system calls. Example: \example{pthreads/setjmp.c}. Another way is to use a
non-standard API for user context manipulation, see \texttt{ucontext.h}, if the
system provides it. Both Linux and macOS support it.
\item To implement a 1:N library, there are several things for consideration:
\begin{itemize}
\item how to deal with threads trying to install \texttt{SIGALRM} handler
if it is already used for firing signal to trigger the dispatcher/scheduler
periodically
\item how to implement separate stacks for each thread (especially when
\texttt{setjmp} was chosen as a building block).
\item what should be the thread states
\item how to avoid all threads to be blocked when one threads blocks e.g. on I/O
\end{itemize}
\end{itemize}
\begin{slide}
\sltitle{POSIX threads (pthreads)}
\begin{itemize}
\item first came with IEEE Std 1003.1c-1995
\item POSIX thread API uses a prefix \texttt{pthread\_}
\item these functions return 0 (= OK) or an error number (values as for
\texttt{errno})
\begin{itemize}
\item \dots{} functions do \emsl{not} set \texttt{errno}
\item so you cannot use functions \funnm{perror}() or \funnm{err}()
\end{itemize}
\item the standard also defines other functions, for example those that could
not be possible to adjust for the use with threads without changing its API (e.g.
\texttt{readdir\_r}, \texttt{strtok\_r}, etc.)
\begin{itemize}
\item \texttt{\_r} means \emph{reentrant}, i.e. the function can be called by
multiple threads without any side effects
\end{itemize}
\end{itemize}
\end{slide}
\hlabel{POSIXTHREADS}
\begin{itemize}
\item General information on POSIX is on page \pageref{POSIX}.
\item There are more threading APIs, the POSIX thread API is just one of them.
For example, there is a system call \texttt{sproc()} on IRIX, then
ifdef([[[NOSPELLCHECK]]], [[[Cthreads]]]),
Solaris threads, GNU ifdef([[[NOSPELLCHECK]]], [[[Ptr threads]]]) (= portable),
\dots
\item The POSIX thread API is available in different libraries on different
systems. For example, on Linux you usually need \texttt{-lpthread} but on
Solaris the API is part of standard \texttt{libc}. With \texttt{gcc}, instead
of \texttt{-lpthread}, you can use \texttt{-pthread} and the compiler will do
what is needed for the specific system (which does not have to be Linux).
\item Each POSIX thread API implementation is usually built on top of the native
threading library. For example, on Solaris, it's the \texttt{thr\_} API
functions.
\item We will talk more on reentrant functions in connection with threads on
page \pageref{THREADSAFE}.
\item As already mentioned, given that the POSIX thread API uses \texttt{errno}
codes directly as return values, the following piece of code is not correct:
\begin{verbatim}
if (pthread_create(&thr, NULL, thrfn, NULL) != 0)
err(1, "pthread_create");
\end{verbatim}
as \texttt{err()} will print possibly something like the following on error
(unless \texttt{errno} was set by previous code which would make it even more
confusing):
\begin{itemize}
\item ``\texttt{a.out: pthread\_create: Success}'' on Linux distributions
\item ``\texttt{a.out: pthread\_create: Error 0}'' on Solaris
\item ``\texttt{a.out: pthread\_create: Unknown error: 0}'' on FreeBSD
\item or something else based on the system and the concrete message it uses for
\texttt{errno} equal to 0, unless \texttt{errno} is already set otherwise.
\end{itemize}
The Linux approach might confuse the programmer as leaving \texttt{errno} zero
does not have to mean the function did not fail, as we just showed. FreeBSD
makes it obvious that something is not entirely right. Example that shows such
a situation: \example{pthreads/wrong-err-use.c}. The correct code could look
like this:
\begin{verbatim}
int e;
if ((e = pthread_create(&thr, NULL, thrfn, NULL)) != 0)
errx(1, "pthread_create: %s", strerror(e));
\end{verbatim}
\item \hlabel{ERRNO_IN_THREADS} Other functions that use \texttt{errno} work the
same with POSIX threads as each thread has its own \texttt{errno}. In that
case, it is redefined using a function (which can either return the value or an
address which is dereferenced). Check \texttt{/usr/include/errno.h} on Linux if
interested.
\end{itemize}
%%%%%
\begin{slide}
\sltitle{Example: thread creation}
{\catcode95=12\catcode38=12
\begin{center}
\input{img/tex/threads.pstex_t}
\end{center}}
\end{slide}
\begin{itemize}
\item This is a trivial example. The process (main thread) creates two more
threads and waits for them to finish. This process thus has 3 threads in
total.
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{pthread\_create}{thrcreate}
]]])
\begin{slide}
\hlabel{PTHREAD_T}
\sltitle{Thread creation}
ifdef([[[NOSPELLCHECK]]], [[[
\funml{int \funnm{pthread\_create}(\=pthread\_t *\emph{thread},
\\\>const pthread\_attr\_t *\emph{attr},
\\\>void *(*\emph{start\_fn})(void*), void *\emph{arg});}
]]])
\begin{itemize}
\item creates a new thread, puts its ID to \emph{thread}
\item with attributes from \texttt{attr}, e.g. its stack size,
\texttt{NULL} means default attributes
\item function \emph{\texttt{start\_fn}}() will be started in the thread using
argument \emph{\texttt{arg}}. After the function returns, the thread ceases to
exist.
\item with the \texttt{pthread\_attr\_t} objects can be manipulated using
\funnm{pthread\_attr\_init}(), \funnm{pthread\_attr\_destroy}(),
\funnm{pthread\_attr\_setstackaddr}(), etc\dots{}
\end{itemize}
\end{slide}
\begin{itemize}
\item Once a thread is created, it is up to the scheduler when the thread
will really start running. The thread can voluntarily give up the CPU by using
\texttt{sched\_yield} (this is POSIX function, unlike \texttt{pthread\_yield}).
\item If the main thread does not wait for the thread to finish, the whole
program will terminate even though there are still some threads running,
see \example{pthreads/pthread\_create.c}
\item Be careful not to use this:
\begin{alltt}
for (i = 0; i < N; i++)
pthread\_create(&tid, attr, start\_routine, &i);
\end{alltt}
It looks like we pass each thread its index. However, before the started thread
gets scheduled to run, a next iteration might happen, modifying \texttt{i}.
\item \hlabel{WRONG_USE_OF_ARG} Examples: \example{pthreads/wrong-use-of-arg.c},
\example{pthreads/correct-use-of-arg.c}.
\item If you need to pass only one value, you could use the following
(\textbf{note that it is implementation specific in the C standard so it is not
portable});
\begin{alltt}
assert(sizeof (void *) >= sizeof (int));
for (i = 0; i < N; i++)
pthread\_create(&tid, attr, start\_routine, (void *)(intptr\_t)i);
\end{alltt}
\dots and in function \texttt{void *start\_routine(void *arg)} cast the pointer
back to integer.
\begin{alltt}
printf("thread \%d started\bs{}n", (int)arg);
\end{alltt}
\hlabel{INT_AS_ARG} Example: \example{pthreads/int-as-arg.c}
\item If we need to pass more bytes than the size of the pointer, you must
pass a pointer to memory where the passed data is stored, or use global
variables. Accessing global variables must be synchronized, of course. More on
that on page \pageref{THREADSYNCHRONIZATION}.
\item \hlabel{PTHREAD_CREATE_CYCLE} \texttt{pthread\_t} is a transparent type and
its implementation is not of your concern. Usually it is an integer though used
to map to the native threads provided by the system. If you create several
threads, you need to pass a different address for a \texttt{pthread\_t} variable
otherwise you will not be able to further manipulate with the threads from the
main thread, e.g. waiting for them to finish.
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{pthread\_self, pthread\_key\_create}{pthreadkey}
\hlabel{THREAD_ATTRS}
]]])
\begin{slide}
\sltitle{Thread private attributes}
\begin{itemize}
\item instruction pointer
\item stack (automatic variables)
\item thread ID, available through
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{pthread\_t \funnm{pthread\_self}(void);}
]]])
\item scheduling priority and policy
\item value of \texttt{errno}
\item thread specific data -- a pair of
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{(pthread\_key\_t \emph{key}, void *\emph{ptr})}
]]])
\item signal mask
\end{itemize}
\end{slide}
\begin{itemize}
\item A key created by \funnm{pthread\_key\_create}() is visible from all
threads. However, in every thread the key may be associated with a different
value via \funnm{pthread\_setspecific}().
\item Each thread has a fixed size stack which \emsl{does not automatically
increase.} It is usually anywhere from 64 kilobytes to a few megabytes. If you
cross that limit, the program will quite probably crash. If you want a stack of
a greater size than what is the system default, you have to use
set the stack size via an attribute when creating a thread.
The attribute is set using the \funnm{pthread\_attr\_setstacksize}() function.
\\
Example: \example{pthreads/pthread-stack-overflow.c}
\item You can read more about thread specific data on page
\pageref{THREAD_SPECIFIC_DATA}.
\item More on per thread signal mask is on page \pageref{PTHREADSIGMASK}.
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{pthread\_exit, pthread\_join, pthread\_detach}{pthreadexit}
]]])
\begin{slide}
\sltitle{Terminating the calling thread}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{void \funnm{pthread\_exit}(void *\emph{val\_ptr});}
]]])
\begin{itemize}
\item terminates the calling thread, it is similar to \funnm{exit}() for
processes
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{int \funnm{pthread\_join}(pthread\_t \emph{thr},
void **\emph{val\_ptr});}
]]])
\begin{itemize}
\item waits for thread \emph{\texttt{thr}} to finish, the value passed to
\funnm{pthread\_exit}() or the return value is stored in the location referenced
by \emph{\texttt{val\_ptr}}
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{int \funnm{pthread\_detach}(pthread\_t \emph{thr});}
]]])
\begin{itemize}
\item indicate that storage for the \emph{\texttt{thr}} can be reclaimed when
the thread terminates. \funnm{pthread\_join}() can no longer be used.
\end{itemize}
\end{slide}
\begin{itemize}
\item If \funnm{pthread\_exit}() is not used, it is implicitly called when the
thread terminates with the value from the function \texttt{return}.
\item The contents of the exiting thread's stack are undefined so you should
not use pointers to the function local variables from memory pointed to by
\emph{\texttt{val\_ptr}}.
\item If you do not intend to call \funnm{pthread\_join}(), you need to call
\funnm{pthread\_det\-ach}() or use the attributes (see below). If you do not, a
memory needed to carry information for a subsequent \funnm{pthread\_join}() will
not be freed. It is a similar situation as with accumulated zombies. You can
just call it like this in the thread function:
\begin{alltt}
pthread\_detach(pthread\_self());
\end{alltt}
\item You can also set the thread attributes when creating the thread, using
\funnm{p\-thr\-ead\_attr\_setdetachstate}() with
\texttt{PTHREAD\_CREATE\_DETACHED} on the attribute variable and then use that
in \funnm{pthread\_create}(). Example on setting the attributes:
\example{pthreads/set-detachstate.c}
\item You can use \texttt{NULL} as \emph{\texttt{val\_ptr}} in
\funnm{pthread\_join}(), telling the system you are not interested in the return
value.
\item Any thread can wait for another thread, not just the one that created it.
\item We recommend to always check the return value of \funnm{pthread\_join}()
to make sure you wait for the right thread. If you use an incorrect thread ID,
the function returns immediately with an error.
\item In contrast to waiting for processes to finish, \emsl{one cannot wait for
any thread to finish}. The rationale is that since there is not parent--child
relation, it was not deemed necessary. However, some system provide that
functionality, e.g. on Solaris you can use \texttt{0} for a thread ID in
\funnm{thr\_join}(). If you needed this functionality with POSIX thread API, it
is easy to set threads as \emph{detached} and use a condition variable together
with a global variable. More on that on page \pageref{CONDITION_VARIABLES}.
\item \hlabel{PTHREAD_JOIN} Examples: \example{pthreads/pthread-join.c},
\example{pthreads/pthread-detach-join.c}
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{pthread\_once}{pthreadonce}
]]])
\begin{slide}
\sltitle{Initialization}
ifdef([[[NOSPELLCHECK]]], [[[
\funml{int \funnm{pthread\_once}(\=pthread\_once\_t *\emph{once\_control},
\\\>void (*\emph{init\_routine})(void));}
]]])
\begin{itemize}
\item in \emph{once\_control} you pass a pointer to statically initialized
variable
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{pthread\_once\_t \emph{once\_control} = PTHREAD\_ONCE\_INIT;}
]]])
\item first thread that calls \funnm{pthread\_once}() calls
\emph{init\_routine()}. Other threads will not call the function, and if it has
not finished yet, will block waiting for it to finish.
\item you can use it for dynamic initialization of global data in libraries
where multiple threads may be using the library API at the same time
\end{itemize}
\end{slide}
\begin{itemize}
\item This function is primarily meant to be used in libraries and rarely
needed in a program. In the latter case, the initialization function can be
called before the first thread is created.
\item The behavior is undefined if \emph{once\_control} is a local variable or
does not have an expected value.
\item This is handy for lazy initialization, i.e. only after one of the threads
call into APIs of the library (as opposed to when the library is being loaded),
the library becomes initialized and the semantics of \texttt{pthread\_once}
will make sure this will happen only once.
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{pthread\_cancel, pthread\_setcancelstate, pthread\_setcanceltype}{pthreadcancel}
]]])
\begin{slide}
\sltitle{Cancel execution of a thread}
\setlength{\baselineskip}{0.9\baselineskip}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{int \funnm{pthread\_cancel}(pthread\_t \emph{thread});}
]]])
\begin{itemize}
\item cancel \emph{thread}. Depends on:
\end{itemize}
\texttt{int \funnm{pthread\_setcancelstate}(int \emph{state},
int *\emph{old});}
\begin{itemize}
\item sets new state and returns the old value:
\begin{itemize}
\item \texttt{PTHREAD\_CANCEL\_ENABLE} \dots{} cancellation allowed
\item \texttt{PTHREAD\_CANCEL\_DISABLE} \dots{} cancellation requests
against the target thread are held pending
\end{itemize}
\end{itemize}
\texttt{int \funnm{pthread\_setcanceltype}(int \emph{type}, int *\emph{old});}
\begin{itemize}
\item \texttt{PTHREAD\_CANCEL\_ASYNCHRONOUS} \dots{} immediate cancellation
\item \texttt{PTHREAD\_CANCEL\_DEFERRED} \dots{} cancellation requests are held
pending until a cancellation point is reached.
\end{itemize}
\end{slide}
\begin{itemize}
\item Cancellation points will occur when a thread is executing functions
specified in the standard, like \funnm{open}(), \funnm{read}(),
\funnm{accept}(), etc. The full list is usually in the
\texttt{pthread\_setcancelstate} man page.
\item The \funnm{pthread\_testcancel}() function creates a cancellation point in
the calling thread. The \funnm{pthread\_testcancel}() function has no effect if
the ability to cancel is disabled.
\item Be very careful with the use of \texttt{PTHREAD\_CANCEL\_ASYNCHRONOUS} as
it may lead to data inconsistency as the cancellation may happen any time, even
in your critical sections.
\item Cleanup functions are called on cancellation, see page
\pageref{PTHREAD_CLEANUP}. For example, if cancelling a thread holding a mutex,
you could use the cleanup function to unlock it.
\item Functions \funnm{pthread\_setcancelstate}() and
\funnm{pthread\_setcanceltype}() provide similar functionality to threads as is
manipulating a signal mask to processes.
\item \hlabel{PTHREAD_CANCEL} Example: \example{pthreads/pthread-setcanceltype.c}
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{pthread\_key\_create, pthread\_key\_delete,
pthread\_setspecific, pthread\_getspecific}{pthreadglobals}
]]])
\begin{slide}
\sltitle{Global variables per thread}
ifdef([[[NOSPELLCHECK]]], [[[
\funml{int \funnm{pthread\_key\_create}(\=pthread\_key\_t *\emph{key},
\\\>void (*\emph{destructor})(void *));}
]]])
\begin{itemize}
\item creates a key that can be associated with a value of
\texttt{(void *)} type. The \emph{destructor()} function is called for all keys
whose value is not \texttt{NULL} on thread termination.
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{int \funnm{pthread\_key\_delete}(pthread\_key\_t \emph{key});}
]]])
\begin{itemize}
\item deletes the key, does not change the associated data
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\funml{int \funnm{pthread\_setspecific}(\=pthread\_key\_t \emph{key},
\\\>const void *\emph{value});}
]]])
\begin{itemize}
\item binds pointer \emph{value} to previously created \emph{key}
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{void *\funnm{pthread\_getspecific}(pthread\_key\_t \emph{key});}
]]])
\begin{itemize}
\item returns the value of \emph{key}
\end{itemize}
\end{slide}
\begin{itemize}
\item \hlabel{THREAD_SPECIFIC_DATA} Global variables and dynamically
allocated data are common to all threads. Thread specific data provides a way to
create a global variable per thread. Note the difference between that and a
local variable in the thread function -- as you know, in C, the local variable
is not visible in other functions called from the thread function. Thread
specific data is a very useful feature. Imagine you have existing
multithreading code using a global storage place which suffers from heavy
contention. You can easily create a thread specific data to create a storage
place per thread with minimal changes to the original code.
\item When you create a key, the \texttt{NULL} value is associated with it.
\item If you do not need the destructor function, use \texttt{NULL}.
\item Destructors are called at thread exit in unspecified order on all keys
with a value different from \texttt{NULL}. Its value is set to \texttt{NULL}
and the original value is used as a parameter to the destructor. If, after
all the destructors have been called for all non-\texttt{NULL} values with
associated destructors, there are still some non-\texttt{NULL} values with
associated destructors, then the process is repeated. If, after at least
\texttt{PTHREAD\_DESTRUCTOR\_ITERATIONS} iterations of destructor calls for
outstanding non-NULL values, there are still some non-\texttt{NULL} values with
associated destructors, the implementation may (it usually does otherwise you
could end up with an infinite loop) stop calling destructors.
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{pthread\_cleanup\_push, pthread\_cleanup\_pop}{pthreadcleanup}
]]])
\begin{slide}
\sltitle{Cleanup functions}
\begin{itemize}
\item each thread has a stack of cleanup routines called when functions
\funnm{pthread\_exit}() or \funnm{pthread\_cancel}() are called (but not when
\texttt{return} is used). Routines are run from the top of the stack down.
\item after cleanup functions are called, thread specific data destructors are
called in unspecified order
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\funml{void \funnm{pthread\_cleanup\_push}(\=void (*\emph{routine})(void *),
\\\>void *\emph{arg});}
]]])
\begin{itemize}
\item add \emph{routine} to the top of the stack
\end{itemize}
\texttt{void \funnm{pthread\_cleanup\_pop}(int \emph{execute});}
\begin{itemize}
\item remove a routine from the top of the stack. Will call the routine if
\emph{execute} is non-zero
\end{itemize}
\end{slide}
\hlabel{PTHREAD_CLEANUP}
\begin{itemize}
\item The cleanup function is called as \texttt{routine(arg)}.
\item The \funnm{pthread\_cleanup\_push} and \funnm{pthread\_cleanup\_pop}
functions provide sort of a bracketing around code block that might need a
cleanup. In fact, more often than not these functions are implemented as macros
that open and close a code block. This enforces this usage pattern
(and causes trouble with \texttt{goto} statements).
\item Run a C preprocessor on \example{pthreads/pthread-cleanup.c} and see how
this is done.
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{pthread\_atfork}{pthreadatfork}
]]])
\begin{slide}
\sltitle{\funnm{fork}() and POSIX threads}
\prgchars
\begin{itemize}
\item it is necessary to define semantics of \funnm{fork}() in multithreaded
applications. The standard says:
\begin{itemize}
\item the calling process contains the exact copy of the calling thread,
including all the mutex states
\item other threads are not propagated to the new process
\item if such other threads contain sole references to allocated memory, the
memory will remain allocated but lost (leaked)
\item mutexes locked in other threads will remain locked for ever
\end{itemize}
\item creating a process from a multithreaded application makes sense for
subsequent \funnm{exec}(), for example, including \funnm{popen}() or
\funnm{system}()
\end{itemize}
\end{slide}
\begin{itemize}
\item No cleanup routines or thread specific data destructors are called for
threads not propagated to the new process.
\item \hlabel{FORKALL} Note that the way how \funnm{fork}() works also depends on
the system used. For example, in Solaris before version 10 (i.e. before 2005),
\funnm{fork}() in the \texttt{libthread} library (different from
\texttt{libpthread}) was the same as \funnm{forkall}().
\item Examples: \example{pthreads/fork.c},
\example{pthreads/fork-not-in-main.c}, and also \example{pthreads/forkall.c}
\item \hlabel{ATFORK} You can use \funnm{pthread\_atfork} to set handlers that
are executed before \funnm{fork} is called in the parent process, and then after
\funnm{fork} is called both in the parent and its child. The handlers are
executed in the context of the thread that calls the \funnm{fork}. Such
handlers are very useful when \funnm{fork} is used not only as a wrapper
around \funnm{exec}. After \funnm{fork}, all variables in the child are in
the state as in the parent, so if a thread not present in the child held a mutex
in the parent (see page \pageref{MUTEXES}), the mutex stays locked in the child,
and trying to lock it in the child will lead to a deadlock. However, if the
parent locks all the mutexes in the \emph{\texttt{pre-fork}} handler and then
unlocks them in the \emph{\texttt{post-fork}} handler (both for the parent and
the child), you will avoid such deadlocks. That is because when locking mutexes
in the \emph{\texttt{pre-fork}} handler, other threads are still running so the
mutexes held by them should be released eventually (usually each thread exits a
critical section in a short time in well written code).
\item This scheme will only work if the \emph{\texttt{pre-fork}} handler
maintains the same locking protocol/ordering as is used in the
application/library. Sometimes that is just not possible due to multiple complex
orderings in place. Very often \funnm{fork} is called just as a means for
later \funnm{exec}. In such case the programmer might be better off using
\funnm{posix\_spawn}.
\item Example: \example{pthreads/atfork.c}
\item For more on this topic, see [Butenhof].
\item See page \pageref{MUTEXES} on why mutexes locked in other threads on
\funnm{fork}() stay locked forever.
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{pthread\_sigmask}{pthreadsigmask}
]]])
\begin{slide}
\sltitle{Signals and threads}
\prgchars
\begin{itemize}
\item signals can be generated for a process (the \texttt{kill} syscall) or
for a thread (error conditions, the \texttt{pthread\_kill} call).
\item signal handling is the same for all threads in the process,
the mask of blocked signals is specific for each thread, can be set with
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\funml{int \funnm{pthread\_sigmask}(\=int \emph{how},
const sigset\_t *\emph{set},\\\> sigset\_t *\emph{oset});}
]]])
\begin{itemize}
\item a signal for a process is handled by one of its threads, one of those
that do not have such signal blocked.
\item one thread can be dedicated for signal handling using the
\texttt{sigwait} call. The other threads have the signals blocked.
\end{itemize}
\end{slide}
\hlabel{PTHREADSIGMASK}
\begin{itemize}
\item If the action for a signal is set to process exit, the whole process
will exit, not just one thread.
\item New thread will inherit signal mask from the creator thread.
\item Similarly to the use of \texttt{sigwait} with processes (page
\pageref{SIGWAIT}) -- just block given signals in all threads, including the
thread processing the signals using \texttt{sigwait}.
\emsl{This way of signal handling in threaded environment is usually the only
recommended.} And it is easy to implement as well. From a previous note, it is
sufficient to mask the signals only once, in the main thread (before creating
any new threads), because the mask will be inherited with each
\texttt{pthread\_create} call.
\item Do not use \texttt{sigprocmask} (page \pageref{SIGPROCMASK}) in threaded
environment, because the behavior of this call is not specified by the standard
in such environment. It may work, or not.
\item \hlabel{THREADS_SIGWAIT} Example: \example{pthreads/sigwait.c}.
\item \emsl{Note} that this way of signal handling should not be used for
synchronous signals such as \texttt{SIGSEGV}, \texttt{SIGILL}, etc. These
signals are generated directly for a thread, so if blocked the dedicated signal
handling thread may not ``see'' them (if it did not cause them by itself).
Also, the standard does not specify if blocking these signals should actually
work as mentioned on page \pageref{SPECIALSIGNALS}. Some systems
normally deliver these signals, making the process exit. The standard says:
\begin{quote}
\emph{If any of the \texttt{SIGFPE, SIGILL, SIGSEGV}, or \texttt{SIGBUS} signals
are generated while they are blocked, the result is undefined, unless the signal
was generated by the \texttt{kill} function, the \texttt{sigqueue} function, or
the \texttt{raise} function.}
\end{quote}
Example: \example{pthreads/sigwait-with-sync-signals.c}. This example shows that
Solaris 10 and 11, FreeBSD 7.2 and a Linux distribution, that reports
itself as ``Gentoo Base System release 1.12.13'', is the \texttt{SIGSEGV} signal
delivered and the process killed even though it is masked.
There used to be a system that did not deliver the signal when masked
-- FreeBSD 6.0. It should be possible to handle synchronous signals
if you terminate the process in the handler itself (e.g. after printing a user
friendly error message), see page \pageref{SPECIALSIGNALS}, which also contains
an example.
\end{itemize}
%%%%%
\begin{slide}
\sltitle{Thread synchronization in general}
\begin{itemize}
\item most of the programs employing threads needs to share data between them
\item or needs to execute given actions in certain order
\item \dots{}all of this needs to \emsl{synchronize} running threads activity
\item for processes it is necessary to make some effort to actually share data,
for threads on the other hand it is necessary to maintain natural data sharing.
\item will describe:
\begin{itemize}
\item mutexes
\item condition variables
\item read-write locks
\end{itemize}
\end{itemize}
\end{slide}
\hlabel{THREADSYNCHRONIZATION}
\begin{itemize}
\item Process synchronization is described on pages
\pageref{SYNCHRONIZATION} to \pageref{SYNCHRONIZATIONEND}.
\item By using mutexes and condition variables it is possible to construct any
other synchronization model.
\item The exact behavior of synchronization primitives is largely determined by
the scheduler. It decides which of the threads waiting for releasing a lock
will be woken up after the lock is actually released. This leads to classical
problems such as a \emph{thundering horde} (lots of threads waiting for unlock)
or a \emph{priority inversion} (thread holding a lock has lower priority than
the thread waiting for the lock).
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{pthread\_mutex\_init, pthread\_mutex\_destroy}{pthreadinit}
]]])
\begin{slide}
\sltitle{Thread synchronization: mutexes (1)}
\begin{itemize}
\item the simplest way how to ensure synchronized access to shared data between
threads
\item initialization of statically defined mutex:
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{pthread\_mutex\_t mutex = PTHREAD\_MUTEX\_INITIALIZER;}
]]])
\begin{itemize}
\item initialization of dynamically allocated mutex \texttt{mx} with attributes
\texttt{attr} (these are set using \texttt{pthread\_mutexattr\_...};
if \texttt{attr} is \texttt{NULL}, default attributes will be used)
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\funml{int \funnm{pthread\_mutex\_init}(\=pthread\_mutex\_t *\emph{mx},
\\\>const pthread\_mutexattr\_t *\emph{attr});}
]]])
\begin{itemize}
\item after done using the mutex it is possible to destroy it:
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{int \funnm{pthread\_mutex\_destroy}(pthread\_mutex\_t *\emph{mx});}
]]])
\end{slide}
\hlabel{MUTEXES}
\begin{itemize}
\item Mutex = \emph{mutual exclusion}
\item Special form of Dijkstra semaphores -- the difference between mutexes and
binary semaphores is that \emsl{mutex has an owner and locked mutex must be
unlocked only by the thread that acquired it.} This is not the case with
semaphores. In order to check whether given mutex was locked by different thread
when acquiring it, it is necessary to test the return value of
\texttt{p\-thread\_mutex\_lock}, and also have the lock checking set, see below.
\item Mutexes are meant to be held for short time only. They are used for
critical section (see the definition on page \pageref{CRITICALSECTION})
implementation, similarly to lock-files or semaphores (if used like locks).
\item Lock checking is governed by a mutex type. By default the mutex type is
set to \texttt{PTHREAD\_MUTEX\_DEF\-AULT}. This type by itself does not define
the result of (a) locking a locked mutex, (b) unlocking a mutex locked by a
different thread, or (c) unlocking an unlocked mutex. Unix/Linux systems will
map that macro to either \texttt{PTHREAD\_MUTEX\_NORMAL} or
\texttt{PTHREAD\_\-MUT\-EX\_ERRORCHECK} (ignoring the recursive type, see
below). Thus, depending on a specific system, locking an already locked mutex
will result in a deadlock (\texttt{NORMAL}) or not (\texttt{ERRORCHECK}). In
the latter case, a return value will contain information about the error and if
not tested, the program will wrongly assume the mutex is locked. For the
\texttt{NORMAL} mutex type the result of (b) and (c) is not
defined, for \texttt{ERRORCHECK} an error will be returned.
In general you should avoid any undefined behavior unless specifically
documented by the system at hand. More information can be found in the POSIX
standard or the \texttt{pth\-read\_mutex\-attr\_set\-ty\-pe} man page. Checking
return values of mutex functions can make the code slightly less readable
however it can be wrapped in a macro. Alternatively, the checks can be used
during development only. Solaris and Linux use \texttt{NORMAL} type by default,
FreeBSD uses \texttt{ERRORCHECK}. \hlabel{NOTMYLOCK}
Example: \example{mutexes/not-my-lock.c}.
\item Another type is \texttt{PTHREAD\_MUTEX\_RECURSIVE} that holds a count of
lock actions done by given thread. The remaining threads will be granted access
only if the count reaches 0. This mutex cannot be shared between processes.
\item What are recursive mutexes good for? Let's assume there are two
libraries, \texttt{A} and \texttt{B}. There is a library
function \texttt{A:foo()} acquires a mutex and calls \texttt{B:bar()},
and in turn calls \texttt{A:bar()} which tries to acquire the same
mutex. Without recursive locks a deadlock will ensue. With recursive mutexes
that's fine if these two calls are done by the same thread (another thread will
get blocked). That is, assuming \texttt{A:foo()} and \texttt{A:bar()} are aware
that the same thread can be already in the critical section.
\item \hlabel{MUTEXTAB} The behavior according to mutex types:\\
\\
\raisetab{
\begin{tabular}[t]{r|c|c|c|}
% \cline{2-4}
&\texttt{NORMAL}&\texttt{ERRORCHECK}&\texttt{RECURSIVE}\\
% \cline{2-4}
detects deadlock&N&Y&N/A\\
multi locking&deadlock&error&success\\
unlock by different thread&undefined&error&error\\
unlock unlocked&undefined&error&error\\
can be shared between processes&Y&Y&N
% \cline{2-4}
\end{tabular}}
\item Static mutex initialization using before mentioned macro will set default
attributes. It is possible to use initializer function also for statically
allocated mutex. If a mutex is dynamically allocated, it is always necessary to
use \texttt{pthread\_mutex\_init}, even if the default attributes are desired or
not.
\item Dynamic mutexes are needed e.g. when a data structure containing a mutex
protecting it is dynamically allocated. In such a case, before calling
\texttt{free} with the data structure, it is first necessary to properly destroy
the mutex (that can also have some memory allocated). Destroying a locked mutex
is not defined by the standard.
\item Copying mutexes is also not defined by the standard -- the result of such
an operation depends on the implementation. It is possible to copy a pointer to
a mutex and work with that.
\item A mutex destroy means its deinitialization.
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{pthread\_mutex\_lock, pthread\_mutex\_unlock,%
pthread\_mutex\_trylock}{pthreadmutexfncs}
]]])
\begin{slide}
\sltitle{Mutexes (2)}
\begin{itemize}
\item to lock and unlock mutex:
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{int \funnm{pthread\_mutex\_lock}(pthread\_mutex\_t *\emph{mx});}
]]])
and
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{int \funnm{pthread\_mutex\_unlock}(pthread\_mutex\_t *\emph{mx});}
]]])
\begin{itemize}
\item If a mutex is already locked, the attempt to acquire it will result
in the thread being locked (depending on mutex type).
It is possible to use:
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{int \funnm{pthread\_mutex\_trylock}(pthread\_mutex\_t *\emph{mx});}
]]])
\begin{itemize}
\item[\dots] that will attempt to acquire the mutex and if that fails it will
return error
\end{itemize}
\end{slide}
\hlabel{MUTEXES2}
\begin{itemize}
\item If you need to unlock a mutex locked by a different thread, use binary
semaphores instead.
\item When creating a program where efficiency is paramount, it is necessary to
think about how many mutexes will be needed and how exactly they will be used.
Even a library that was not written with threads in mind can be converted to be
thread-safe (see page \pageref{THREADSAFE}) by acquiring a per-library lock on
any library function entry and releasing the lock before the function exits.
Such a lock may be called a ``giant'' mutex, and it may lead to lock contention
for every consumer of such a library as at any given moment only one thread may
execute the library code. On the other hand, if using a large number of mutexes
to synchronize access to many small sections, significant amount of time might
be spent in the overhead of calling functions implementing the locking. It is
therefore desired to search for a compromise. (Or use an algorithm that does
not require locks at all - such algorithms/techniques are called \emph{lock
free} or \emph{lockless}).
\item \hlabel{MUTEX_RACE} Examples: \example{mutexes/race.c},
\example{mutexes/race-fixed.c}
\item Mutexes can be shared between processes so that their threads will
synchronize on them. This is done by using shared memory that will be set as an
attribute of such mutexes. See the
\texttt{pthread\_mutexattr\_setpshared} man page.
\end{itemize}
%%%%%
\begin{slide}
\sltitle{Condition variables (1)}
\begin{itemize}
\item mutexes provide synchronization for shared data
\item condition variables pass information about the shared data --
for example that the value has changed
\item \dots{}and allows to put threads to sleep and wake them up
\item therefore \emsl{each condition variable is always associated
with exactly one mutex}
\item one mutex can be associated with multiple condition variables
\item using mutexes and condition variables it is possible to construct
other synchronization primitives -- semaphores, barriers, \dots
\end{itemize}
\end{slide}
\hlabel{CONDITION_VARIABLES}
\begin{itemize}
\item In other words -- condition variables are handy in a situation when a
thread needs to test the state of \emsl{shared} data (e.g. number of elements in
a queue) and voluntarily put itself to sleep if the state is not as desired.
The sleeping thread may be woken up by another thread after the latter
changed the state of the data in a way that the situation which the first thread
was waiting on actually happened (e.g. by inserting an item into a queue). The
second thread wakes the first one by calling a designated function. If no
thread is sleeping at the moment, that function would have no effect -- nothing
will be saved anywhere, it is as if it never happened.
\item A condition variable, which is an opaque type for the programmer, is not
associated with a concrete condition like ``\emph{\texttt{n} is greater than
7}''. A condition variable may in fact be compared to a flag of a certain
color; if it is lifted up, it means that the threads waiting for the flag to be
raised are informed (= woken up) and may use this information to its own
judgment. Some threads may wait for \texttt{n} to be bigger than 7, some other
may be waiting solely for \texttt{n} to change, and another then for \texttt{n}
to become 99. It is only up to the programmer whether a separate condition
variable will be used for all states of \texttt{n} (i.e. we would use multiple
flags of different colors) or whether a single condition variable will be used
(the same flag color for all situations). For the latter, the threads waiting
on \texttt{n > 7} and \texttt{n == 99} must always test \texttt{n} as they know
that they are woken up whenever the variable changed. If the variable is not
equal to 7, the thread must voluntarily put itself to sleep again. As it is
explained further, the \emsl{test is necessary to perform after an every
wake-up} even if a dedicated condition variable is used for every possible
state -- it may happen that the system can wake up a sleeping thread (because
of various implementation reasons) without any other thread causing this; it is
called a \emph{spurious wake-up}.
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{pthread\_cond\_init, pthread\_cond\_destroy,%
pthread\_cond\_wait}{pthreadcondvarfncs}
]]])
\begin{slide}