• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

manual/H03-May-2022-7567

tests/H03-May-2022-22,57510,152

.clang-formatH A D04-Oct-202154 43

ANNOUNCEH A D04-Oct-202113.5 KiB484381

BUGSH A D04-Oct-20216.2 KiB142109

BmakefileH A D04-Oct-20216.1 KiB269232

CONTRIBUTORSH A D04-Oct-20215.4 KiB141137

COPYINGH A D04-Oct-20215.7 KiB151108

COPYING.LIBH A D04-Oct-202125.8 KiB

ChangeLogH A D04-Oct-2021191.1 KiB5,2123,869

FAQH A D04-Oct-202117.2 KiB452340

GNUmakefileH A D04-Oct-202116.8 KiB594462

MAINTAINERSH A D04-Oct-202195 53

MakefileH A D04-Oct-202114.7 KiB515436

NEWSH A D04-Oct-202141.2 KiB1,242921

NmakefileH A D04-Oct-2021773 2519

Nmakefile.testsH A D04-Oct-20215.7 KiB261256

PROGRESSH A D04-Oct-2021138 53

READMEH A D04-Oct-202121.8 KiB602439

README.BorlandH A D04-Oct-20212.5 KiB5846

README.CVH A D04-Oct-202184.7 KiB3,0372,408

README.NONPORTABLEH A D04-Oct-202130.4 KiB784607

README.WatcomH A D04-Oct-20212.2 KiB6346

README.WinCEH A D04-Oct-2021214 75

TODOH A D04-Oct-2021210 84

WinCE-PORTH A D04-Oct-20217.6 KiB223171

attr.cH A D04-Oct-20212 KiB5412

autostatic.cH A D04-Oct-20212.4 KiB7024

barrier.cH A D04-Oct-20211.8 KiB489

builddmc.batH A D04-Oct-2021431 107

cancel.cH A D04-Oct-20211.6 KiB456

cleanup.cH A D04-Oct-20214.9 KiB14926

condvar.cH A D04-Oct-20211.9 KiB5111

config.hH A D04-Oct-20214.5 KiB15452

context.hH A D04-Oct-20212.3 KiB7528

create.cH A D04-Oct-20217.9 KiB309136

dll.cH A D04-Oct-20212.5 KiB9332

errno.cH A D04-Oct-20212.7 KiB9520

exit.cH A D04-Oct-20211.6 KiB456

fork.cH A D04-Oct-20211.5 KiB402

global.cH A D04-Oct-20213.3 KiB10822

implement.hH A D04-Oct-202134.5 KiB944551

misc.cH A D04-Oct-20211.8 KiB5112

mutex.cH A D04-Oct-20212.2 KiB6324

need_errno.hH A D04-Oct-20213 KiB14693

nonportable.cH A D04-Oct-20211.8 KiB4810

private.cH A D04-Oct-20212 KiB5516

pthread.cH A D04-Oct-20212.1 KiB6725

pthread.dspH A D04-Oct-20214.4 KiB143117

pthread.dswH A D04-Oct-2021508 3019

pthread.hH A D04-Oct-202141.6 KiB1,373592

pthread.lastbuildstateH A D04-Oct-202171 32

pthread.slnH A D04-Oct-20212.1 KiB3937

pthread.vcxprojH A D04-Oct-202126.4 KiB514514

pthread.vcxproj.filtersH A D04-Oct-20211.3 KiB3636

pthread_attr_destroy.cH A D04-Oct-20212.4 KiB8014

pthread_attr_getdetachstate.cH A D04-Oct-20213 KiB8712

pthread_attr_getinheritsched.cH A D04-Oct-20211.8 KiB5213

pthread_attr_getschedparam.cH A D04-Oct-20211.8 KiB5314

pthread_attr_getschedpolicy.cH A D04-Oct-20211.9 KiB6217

pthread_attr_getscope.cH A D04-Oct-20211.9 KiB5515

pthread_attr_getstackaddr.cH A D04-Oct-20213.1 KiB9819

pthread_attr_getstacksize.cH A D04-Oct-20213.2 KiB10119

pthread_attr_init.cH A D04-Oct-20213.5 KiB11832

pthread_attr_setdetachstate.cH A D04-Oct-20213 KiB9217

pthread_attr_setinheritsched.cH A D04-Oct-20211.9 KiB5818

pthread_attr_setschedparam.cH A D04-Oct-20212 KiB6421

pthread_attr_setschedpolicy.cH A D04-Oct-20211.8 KiB5616

pthread_attr_setscope.cH A D04-Oct-20212 KiB6323

pthread_attr_setstackaddr.cH A D04-Oct-20213.1 KiB9816

pthread_attr_setstacksize.cH A D04-Oct-20213.4 KiB11122

pthread_barrier_destroy.cH A D04-Oct-20213.2 KiB10442

pthread_barrier_init.cH A D04-Oct-20212.2 KiB7026

pthread_barrier_wait.cH A D04-Oct-20213.4 KiB10537

pthread_barrierattr_destroy.cH A D04-Oct-20212.6 KiB8418

pthread_barrierattr_getpshared.cH A D04-Oct-20213.2 KiB9618

pthread_barrierattr_init.cH A D04-Oct-20212.6 KiB8619

pthread_barrierattr_setpshared.cH A D04-Oct-20213.7 KiB12031

pthread_cancel.cH A D04-Oct-20215.1 KiB19086

pthread_cond_destroy.cH A D04-Oct-20217.5 KiB25492

pthread_cond_init.cH A D04-Oct-20214.1 KiB16870

pthread_cond_signal.cH A D04-Oct-20216.6 KiB23286

pthread_cond_wait.cH A D04-Oct-202116.5 KiB568124

pthread_condattr_destroy.cH A D04-Oct-20212.6 KiB8718

pthread_condattr_getpshared.cH A D04-Oct-20213.2 KiB9817

pthread_condattr_init.cH A D04-Oct-20212.9 KiB8815

pthread_condattr_setpshared.cH A D04-Oct-20213.7 KiB11831

pthread_delay_np.cH A D04-Oct-20214.8 KiB17367

pthread_detach.cH A D04-Oct-20214.1 KiB13748

pthread_equal.cH A D04-Oct-20212.5 KiB779

pthread_exit.cH A D04-Oct-20213.2 KiB10724

pthread_getconcurrency.cH A D04-Oct-20211.6 KiB467

pthread_getschedparam.cH A D04-Oct-20212.4 KiB7621

pthread_getspecific.cH A D04-Oct-20212.7 KiB8824

pthread_getunique_np.cH A D04-Oct-20211.7 KiB487

pthread_getw32threadhandle_np.cH A D04-Oct-20212.1 KiB6612

pthread_join.cH A D04-Oct-20214.4 KiB15857

pthread_key_create.cH A D04-Oct-20213.7 KiB10928

pthread_key_delete.cH A D04-Oct-20214 KiB12640

pthread_kill.cH A D04-Oct-20213.4 KiB10626

pthread_mutex_consistent.cH A D04-Oct-20214.5 KiB15893

pthread_mutex_destroy.cH A D04-Oct-20213.9 KiB14961

pthread_mutex_init.cH A D04-Oct-20213.7 KiB13163

pthread_mutex_lock.cH A D04-Oct-20219.2 KiB270190

pthread_mutex_timedlock.cH A D04-Oct-202110.1 KiB325213

pthread_mutex_trylock.cH A D04-Oct-20214.4 KiB15593

pthread_mutex_unlock.cH A D04-Oct-20215.3 KiB176104

pthread_mutexattr_destroy.cH A D04-Oct-20212.6 KiB8418

pthread_mutexattr_getkind_np.cH A D04-Oct-20211.7 KiB457

pthread_mutexattr_getpshared.cH A D04-Oct-20213.2 KiB9617

pthread_mutexattr_getrobust.cH A D04-Oct-20214.5 KiB11413

pthread_mutexattr_gettype.cH A D04-Oct-20211.8 KiB5716

pthread_mutexattr_init.cH A D04-Oct-20212.6 KiB8720

pthread_mutexattr_setkind_np.cH A D04-Oct-20211.7 KiB457

pthread_mutexattr_setpshared.cH A D04-Oct-20213.7 KiB12031

pthread_mutexattr_setrobust.cH A D04-Oct-20214.6 KiB12019

pthread_mutexattr_settype.cH A D04-Oct-20215.3 KiB14426

pthread_num_processors_np.cH A D04-Oct-20211.8 KiB5712

pthread_once.cH A D04-Oct-20212.5 KiB8032

pthread_rwlock_destroy.cH A D04-Oct-20214 KiB14473

pthread_rwlock_init.cH A D04-Oct-20212.7 KiB11056

pthread_rwlock_rdlock.cH A D04-Oct-20212.9 KiB10348

pthread_rwlock_timedrdlock.cH A D04-Oct-20213 KiB11055

pthread_rwlock_timedwrlock.cH A D04-Oct-20213.6 KiB14076

pthread_rwlock_tryrdlock.cH A D04-Oct-20212.9 KiB10348

pthread_rwlock_trywrlock.cH A D04-Oct-20213.2 KiB12367

pthread_rwlock_unlock.cH A D04-Oct-20212.6 KiB9442

pthread_rwlock_wrlock.cH A D04-Oct-20213.5 KiB13470

pthread_rwlockattr_destroy.cH A D04-Oct-20212.6 KiB8519

pthread_rwlockattr_getpshared.cH A D04-Oct-20213.3 KiB9819

pthread_rwlockattr_init.cH A D04-Oct-20212.6 KiB8420

pthread_rwlockattr_setpshared.cH A D04-Oct-20213.7 KiB12132

pthread_self.cH A D04-Oct-20214 KiB14245

pthread_setcancelstate.cH A D04-Oct-20214 KiB12633

pthread_setcanceltype.cH A D04-Oct-20214 KiB12734

pthread_setconcurrency.cH A D04-Oct-20211.7 KiB5415

pthread_setschedparam.cH A D04-Oct-20213.2 KiB12460

pthread_setspecific.cH A D04-Oct-20214.4 KiB16868

pthread_spin_destroy.cH A D04-Oct-20213.1 KiB11246

pthread_spin_init.cH A D04-Oct-20212.8 KiB12463

pthread_spin_lock.cH A D04-Oct-20212.3 KiB8135

pthread_spin_trylock.cH A D04-Oct-20212.3 KiB7833

pthread_spin_unlock.cH A D04-Oct-20212.2 KiB7228

pthread_testcancel.cH A D04-Oct-20213.3 KiB10427

pthread_timechange_handler_np.cH A D04-Oct-20213.8 KiB10918

pthread_win32_attach_detach_np.cH A D04-Oct-20216.9 KiB259150

ptw32_MCS_lock.cH A D04-Oct-20219.1 KiB279115

ptw32_OLL_lock.cH A D04-Oct-202120.8 KiB735504

ptw32_callUserDestroyRoutines.cH A D04-Oct-20216.7 KiB23390

ptw32_calloc.cH A D04-Oct-20211.7 KiB5715

ptw32_cond_check_need_init.cH A D04-Oct-20212.5 KiB7919

ptw32_getprocessors.cH A D04-Oct-20212.5 KiB9232

ptw32_is_attr.cH A D04-Oct-20211.7 KiB488

ptw32_mutex_check_need_init.cH A D04-Oct-20213.2 KiB9335

ptw32_new.cH A D04-Oct-20212.7 KiB9542

ptw32_processInitialize.cH A D04-Oct-20213 KiB9317

ptw32_processTerminate.cH A D04-Oct-20213 KiB10631

ptw32_relmillisecs.cH A D04-Oct-20214 KiB13363

ptw32_reuse.cH A D04-Oct-20214.5 KiB15251

ptw32_rwlock_cancelwrwait.cH A D04-Oct-20211.9 KiB5111

ptw32_rwlock_check_need_init.cH A D04-Oct-20212.6 KiB7819

ptw32_semwait.cH A D04-Oct-20213.8 KiB13663

ptw32_spinlock_check_need_init.cH A D04-Oct-20212.6 KiB7919

ptw32_threadDestroy.cH A D04-Oct-20212.5 KiB8023

ptw32_threadStart.cH A D04-Oct-20218.3 KiB358211

ptw32_throw.cH A D04-Oct-20214.5 KiB19091

ptw32_timespec.cH A D04-Oct-20213.1 KiB8421

ptw32_tkAssocCreate.cH A D04-Oct-20213.9 KiB11929

ptw32_tkAssocDestroy.cH A D04-Oct-20213.3 KiB11543

rwlock.cH A D04-Oct-20212 KiB5215

sched.cH A D04-Oct-20212 KiB5416

sched.hH A D04-Oct-20214.9 KiB18486

sched_get_priority_max.cH A D04-Oct-20216.2 KiB13517

sched_get_priority_min.cH A D04-Oct-20216.2 KiB13617

sched_getscheduler.cH A D04-Oct-20212.1 KiB7226

sched_setscheduler.cH A D04-Oct-20212.4 KiB8431

sched_yield.cH A D04-Oct-20212.5 KiB729

sem_close.cH A D04-Oct-20212 KiB5912

sem_destroy.cH A D04-Oct-20214.6 KiB14551

sem_getvalue.cH A D04-Oct-20213.2 KiB11131

sem_init.cH A D04-Oct-20214.4 KiB17072

sem_open.cH A D04-Oct-20212 KiB5912

sem_post.cH A D04-Oct-20213.5 KiB12951

sem_post_multiple.cH A D04-Oct-20213.9 KiB14362

sem_timedwait.cH A D04-Oct-20216.3 KiB239111

sem_trywait.cH A D04-Oct-20213.5 KiB11837

sem_unlink.cH A D04-Oct-20212 KiB5912

sem_wait.cH A D04-Oct-20215.1 KiB18884

semaphore.cH A D04-Oct-20212.3 KiB7018

semaphore.hH A D04-Oct-20214.4 KiB17083

signal.cH A D04-Oct-20215.1 KiB18072

spin.cH A D04-Oct-20211.7 KiB478

sync.cH A D04-Oct-20211.6 KiB444

tsd.cH A D04-Oct-20211.7 KiB456

version.rcH A D04-Oct-202115.3 KiB395348

w32-pthreadsConfig.cmake.inH A D04-Oct-2021443 1611

w32-pthreadsConfigVersion.cmake.inH A D04-Oct-20210

w32_CancelableWait.cH A D04-Oct-20214.4 KiB16274

README

1PTHREADS-WIN32
2==============
3
4Pthreads-win32 is free software, distributed under the GNU Lesser
5General Public License (LGPL). See the file 'COPYING.LIB' for terms
6and conditions. Also see the file 'COPYING' for information
7specific to pthreads-win32, copyrights and the LGPL.
8
9
10What is it?
11-----------
12
13Pthreads-win32 is an Open Source Software implementation of the
14Threads component of the POSIX 1003.1c 1995 Standard (or later)
15for Microsoft's Win32 environment. Some functions from POSIX
161003.1b are also supported including semaphores. Other related
17functions include the set of read-write lock functions. The
18library also supports some of the functionality of the Open
19Group's Single Unix specification, version 2, namely mutex types,
20plus some common and pthreads-win32 specific non-portable
21routines (see README.NONPORTABLE).
22
23See the file "ANNOUNCE" for more information including standards
24conformance details and the list of supported and unsupported
25routines.
26
27
28Prerequisites
29-------------
30MSVC or GNU C (MinGW32 MSys development kit)
31	To build from source.
32
33QueueUserAPCEx by Panagiotis E. Hadjidoukas
34	To support any thread cancelation in C++ library builds or
35	to support cancelation of blocked threads in any build.
36	This library is not required otherwise.
37
38	For true async cancelation of threads (including blocked threads).
39	This is a DLL and Windows driver that provides pre-emptive APC
40	by forcing threads into an alertable state when the APC is queued.
41	Both the DLL and driver are provided with the pthreads-win32.exe
42	self-unpacking ZIP, and on the pthreads-win32 FTP site  (in source
43	and pre-built forms). Currently this is a separate LGPL package to
44	pthreads-win32. See the README in the QueueUserAPCEx folder for
45	installation instructions.
46
47	Pthreads-win32 will automatically detect if the QueueUserAPCEx DLL
48	QuserEx.DLL is available and whether the driver AlertDrv.sys is
49	loaded. If it is not available, pthreads-win32 will simulate async
50	cancelation, which means that it can async cancel only threads that
51	are runnable. The simulated async cancellation cannot cancel blocked
52	threads.
53
54        [FOR SECURITY] To be found Quserex.dll MUST be installed in the
55	Windows System Folder. This is not an unreasonable constraint given a
56	driver must also be installed and loaded at system startup.
57
58
59Library naming
60--------------
61
62Because the library is being built using various exception
63handling schemes and compilers - and because the library
64may not work reliably if these are mixed in an application,
65each different version of the library has it's own name.
66
67Note 1: the incompatibility is really between EH implementations
68of the different compilers. It should be possible to use the
69standard C version from either compiler with C++ applications
70built with a different compiler. If you use an EH version of
71the library, then you must use the same compiler for the
72application. This is another complication and dependency that
73can be avoided by using only the standard C library version.
74
75Note 2: if you use a standard C pthread*.dll with a C++
76application, then any functions that you define that are
77intended to be called via pthread_cleanup_push() must be
78__cdecl.
79
80Note 3: the intention was to also name either the VC or GC
81version (it should be arbitrary) as pthread.dll, including
82pthread.lib and libpthread.a as appropriate. This is no longer
83likely to happen.
84
85Note 4: the compatibility number was added so that applications
86can differentiate between binary incompatible versions of the
87libs and dlls.
88
89In general:
90	pthread[VG]{SE,CE,C}[c].dll
91	pthread[VG]{SE,CE,C}[c].lib
92
93where:
94	[VG] indicates the compiler
95	V	- MS VC, or
96	G	- GNU C
97
98	{SE,CE,C} indicates the exception handling scheme
99	SE	- Structured EH, or
100	CE	- C++ EH, or
101	C	- no exceptions - uses setjmp/longjmp
102
103	c	- DLL compatibility number indicating ABI and API
104		  compatibility with applications built against
105		  a snapshot with the same compatibility number.
106		  See 'Version numbering' below.
107
108The name may also be suffixed by a 'd' to indicate a debugging version
109of the library. E.g. pthreadVC2d.lib. Debugging versions contain
110additional information for debugging (symbols etc) and are often not
111optimised in any way (compiled with optimisation turned off).
112
113Examples:
114	pthreadVSE.dll	(MSVC/SEH)
115	pthreadGCE.dll	(GNUC/C++ EH)
116	pthreadGC.dll	(GNUC/not dependent on exceptions)
117	pthreadVC1.dll	(MSVC/not dependent on exceptions - not binary
118			compatible with pthreadVC.dll)
119	pthreadVC2.dll	(MSVC/not dependent on exceptions - not binary
120			compatible with pthreadVC1.dll or pthreadVC.dll)
121
122The GNU library archive file names have correspondingly changed to:
123
124	libpthreadGCEc.a
125	libpthreadGCc.a
126
127
128Versioning numbering
129--------------------
130
131Version numbering is separate from the snapshot dating system, and
132is the canonical version identification system embedded within the
133DLL using the Microsoft version resource system. The versioning
134system chosen follows the GNU Libtool system. See
135http://www.gnu.org/software/libtool/manual.html section 6.2.
136
137See the resource file 'version.rc'.
138
139Microsoft version numbers use 4 integers:
140
141	0.0.0.0
142
143Pthreads-win32 uses the first 3 following the Libtool convention.
144The fourth is commonly used for the build number, but will be reserved
145for future use.
146
147	current.revision.age.0
148
149The numbers are changed as follows:
150
1511. If the library source code has changed at all since the last update,
152   then increment revision (`c:r:a' becomes `c:r+1:a').
1532. If any interfaces have been added, removed, or changed since the last
154   update, increment current, and set revision to 0.
1553. If any interfaces have been added since the last public release, then
156   increment age.
1574. If any interfaces have been removed or changed since the last public
158   release, then set age to 0.
159
160
161DLL compatibility numbering is an attempt to ensure that applications
162always load a compatible pthreads-win32 DLL by using a DLL naming system
163that is consistent with the version numbering system. It also allows
164older and newer DLLs to coexist in the same filesystem so that older
165applications can continue to be used. For pre .NET Windows systems,
166this inevitably requires incompatible versions of the same DLLs to have
167different names.
168
169Pthreads-win32 has adopted the Cygwin convention of appending a single
170integer number to the DLL name. The number used is based on the library
171version number and is computed as 'current' - 'age'.
172
173(See http://home.att.net/~perlspinr/libversioning.html for a nicely
174detailed explanation.)
175
176Using this method, DLL name/s will only change when the DLL's
177backwards compatibility changes. Note that the addition of new
178'interfaces' will not of itself change the DLL's compatibility for older
179applications.
180
181
182Which of the several dll versions to use?
183-----------------------------------------
184or,
185---
186What are all these pthread*.dll and pthread*.lib files?
187-------------------------------------------------------
188
189Simple, use either pthreadGCv.* if you use GCC, or pthreadVCv.* if you
190use MSVC - where 'v' is the DLL versioning (compatibility) number.
191
192Otherwise, you need to choose carefully and know WHY.
193
194The most important choice you need to make is whether to use a
195version that uses exceptions internally, or not. There are versions
196of the library that use exceptions as part of the thread
197cancelation and exit implementation. The default version uses
198setjmp/longjmp.
199
200There is some contension amongst POSIX threads experts as
201to how POSIX threads cancelation and exit should work
202with languages that use exceptions, e.g. C++ and even C
203(Microsoft's Structured Exceptions).
204
205The issue is: should cancelation of a thread in, say,
206a C++ application cause object destructors and C++ exception
207handlers to be invoked as the stack unwinds during thread
208exit, or not?
209
210There seems to be more opinion in favour of using the
211standard C version of the library (no EH) with C++ applications
212for the reason that this appears to be the assumption commercial
213pthreads implementations make. Therefore, if you use an EH version
214of pthreads-win32 then you may be under the illusion that
215your application will be portable, when in fact it is likely to
216behave differently when linked with other pthreads libraries.
217
218Now you may be asking: then why have you kept the EH versions of
219the library?
220
221There are a couple of reasons:
222- there is division amongst the experts and so the code may
223  be needed in the future. Yes, it's in the repository and we
224  can get it out anytime in the future, but it would be difficult
225  to find.
226- pthreads-win32 is one of the few implementations, and possibly
227  the only freely available one, that has EH versions. It may be
228  useful to people who want to play with or study application
229  behaviour under these conditions.
230
231Notes:
232
233[If you use either pthreadVCE or pthreadGCE]
234
2351. [See also the discussion in the FAQ file - Q2, Q4, and Q5]
236
237If your application contains catch(...) blocks in your POSIX
238threads then you will need to replace the "catch(...)" with the macro
239"PtW32Catch", eg.
240
241	#ifdef PtW32Catch
242		PtW32Catch {
243			...
244		}
245	#else
246		catch(...) {
247			...
248		}
249	#endif
250
251Otherwise neither pthreads cancelation nor pthread_exit() will work
252reliably when using versions of the library that use C++ exceptions
253for cancelation and thread exit.
254
255This is due to what is believed to be a C++ compliance error in VC++
256whereby you may not have multiple handlers for the same exception in
257the same try/catch block. GNU G++ doesn't have this restriction.
258
259
260Other name changes
261------------------
262
263All snapshots prior to and including snapshot 2000-08-13
264used "_pthread_" as the prefix to library internal
265functions, and "_PTHREAD_" to many library internal
266macros. These have now been changed to "ptw32_" and "PTW32_"
267respectively so as to not conflict with the ANSI standard's
268reservation of identifiers beginning with "_" and "__" for
269use by compiler implementations only.
270
271If you have written any applications and you are linking
272statically with the pthreads-win32 library then you may have
273included a call to _pthread_processInitialize. You will
274now have to change that to ptw32_processInitialize.
275
276
277Cleanup code default style
278--------------------------
279
280Previously, if not defined, the cleanup style was determined automatically
281from the compiler used, and one of the following was defined accordingly:
282
283	__CLEANUP_SEH	MSVC only
284	__CLEANUP_CXX	C++, including MSVC++, GNU G++
285	__CLEANUP_C	C, including GNU GCC, not MSVC
286
287These defines determine the style of cleanup (see pthread.h) and,
288most importantly, the way that cancelation and thread exit (via
289pthread_exit) is performed (see the routine ptw32_throw()).
290
291In short, the exceptions versions of the library throw an exception
292when a thread is canceled, or exits via pthread_exit(). This exception is
293caught by a handler in the thread startup routine, so that the
294the correct stack unwinding occurs regardless of where the thread
295is when it's canceled or exits via pthread_exit().
296
297In this snapshot, unless the build explicitly defines (e.g. via a
298compiler option) __CLEANUP_SEH, __CLEANUP_CXX, or __CLEANUP_C, then
299the build NOW always defaults to __CLEANUP_C style cleanup. This style
300uses setjmp/longjmp in the cancelation and pthread_exit implementations,
301and therefore won't do stack unwinding even when linked to applications
302that have it (e.g. C++ apps). This is for consistency with most/all
303commercial Unix POSIX threads implementations.
304
305Although it was not clearly documented before, it is still necessary to
306build your application using the same __CLEANUP_* define as was
307used for the version of the library that you link with, so that the
308correct parts of pthread.h are included. That is, the possible
309defines require the following library versions:
310
311	__CLEANUP_SEH	pthreadVSE.dll
312	__CLEANUP_CXX	pthreadVCE.dll or pthreadGCE.dll
313	__CLEANUP_C	pthreadVC.dll or pthreadGC.dll
314
315It is recommended that you let pthread.h use it's default __CLEANUP_C
316for both library and application builds. That is, don't define any of
317the above, and then link with pthreadVC.lib (MSVC or MSVC++) and
318libpthreadGC.a (MinGW GCC or G++). The reason is explained below, but
319another reason is that the prebuilt pthreadVCE.dll is currently broken.
320Versions built with MSVC++ later than version 6 may not be broken, but I
321can't verify this yet.
322
323WHY ARE WE MAKING THE DEFAULT STYLE LESS EXCEPTION-FRIENDLY?
324Because no commercial Unix POSIX threads implementation allows you to
325choose to have stack unwinding. Therefore, providing it in pthread-win32
326as a default is dangerous. We still provide the choice but unless
327you consciously choose to do otherwise, your pthreads applications will
328now run or crash in similar ways irrespective of the pthreads platform
329you use. Or at least this is the hope.
330
331
332Building under VC++ using C++ EH, Structured EH, or just C
333----------------------------------------------------------
334
335From the source directory run nmake without any arguments to list
336help information. E.g.
337
338$ nmake
339
340Microsoft (R) Program Maintenance Utility   Version 6.00.8168.0
341Copyright (C) Microsoft Corp 1988-1998. All rights reserved.
342
343Run one of the following command lines:
344nmake clean VCE (to build the MSVC dll with C++ exception handling)
345nmake clean VSE (to build the MSVC dll with structured exception handling)
346nmake clean VC (to build the MSVC dll with C cleanup code)
347nmake clean VCE-inlined (to build the MSVC inlined dll with C++ exception handling)
348nmake clean VSE-inlined (to build the MSVC inlined dll with structured exception handling)
349nmake clean VC-inlined (to build the MSVC inlined dll with C cleanup code)
350nmake clean VC-static (to build the MSVC static lib with C cleanup code)
351nmake clean VCE-debug (to build the debug MSVC dll with C++ exception handling)
352nmake clean VSE-debug (to build the debug MSVC dll with structured exception handling)
353nmake clean VC-debug (to build the debug MSVC dll with C cleanup code)
354nmake clean VCE-inlined-debug (to build the debug MSVC inlined dll with C++ exception handling)
355nmake clean VSE-inlined-debug (to build the debug MSVC inlined dll with structured exception handling)
356nmake clean VC-inlined-debug (to build the debug MSVC inlined dll with C cleanup code)
357nmake clean VC-static-debug (to build the debug MSVC static lib with C cleanup code)
358
359
360The pre-built dlls are normally built using the *-inlined targets.
361
362You can run the testsuite by changing to the "tests" directory and
363running nmake. E.g.:
364
365$ cd tests
366$ nmake
367
368Microsoft (R) Program Maintenance Utility   Version 6.00.8168.0
369Copyright (C) Microsoft Corp 1988-1998. All rights reserved.
370
371Run one of the following command lines:
372nmake clean VC (to test using VC dll with VC (no EH) applications)
373nmake clean VCX (to test using VC dll with VC++ (EH) applications)
374nmake clean VCE (to test using the VCE dll with VC++ EH applications)
375nmake clean VSE (to test using VSE dll with VC (SEH) applications)
376nmake clean VC-bench (to benchtest using VC dll with C bench app)
377nmake clean VCX-bench (to benchtest using VC dll with C++ bench app)
378nmake clean VCE-bench (to benchtest using VCE dll with C++ bench app)
379nmake clean VSE-bench (to benchtest using VSE dll with SEH bench app)
380nmake clean VC-static (to test using VC static lib with VC (no EH) applications)
381
382
383Building under Mingw32
384----------------------
385
386The dll can be built easily with recent versions of Mingw32.
387(The distributed versions are built using Mingw32 and MsysDTK
388from www.mingw32.org.)
389
390From the source directory, run make for help information. E.g.:
391
392$ make
393Run one of the following command lines:
394make clean GC            (to build the GNU C dll with C cleanup code)
395make clean GCE           (to build the GNU C dll with C++ exception handling)
396make clean GC-inlined    (to build the GNU C inlined dll with C cleanup code)
397make clean GCE-inlined   (to build the GNU C inlined dll with C++ exception handling)
398make clean GC-static     (to build the GNU C inlined static lib with C cleanup code)
399make clean GC-debug      (to build the GNU C debug dll with C cleanup code)
400make clean GCE-debug     (to build the GNU C debug dll with C++ exception handling)
401make clean GC-inlined-debug    (to build the GNU C inlined debug dll with C cleanup code)
402make clean GCE-inlined-debug   (to build the GNU C inlined debug dll with C++ exception handling)
403make clean GC-static-debug     (to build the GNU C inlined static debug lib with C cleanup code)
404
405
406The pre-built dlls are normally built using the *-inlined targets.
407
408You can run the testsuite by changing to the "tests" directory and
409running make for help information. E.g.:
410
411$ cd tests
412$ make
413Run one of the following command lines:
414make clean GC    (to test using GC dll with C (no EH) applications)
415make clean GCX   (to test using GC dll with C++ (EH) applications)
416make clean GCE   (to test using GCE dll with C++ (EH) applications)
417make clean GC-bench       (to benchtest using GNU C dll with C cleanup code)
418make clean GCE-bench   (to benchtest using GNU C dll with C++ exception handling)
419make clean GC-static   (to test using GC static lib with C (no EH) applications)
420
421
422Building under Linux using the Mingw32 cross development tools
423--------------------------------------------------------------
424
425You can build the library without leaving Linux by using the Mingw32 cross
426development toolchain. See http://www.libsdl.org/extras/win32/cross/ for
427tools and info. The GNUmakefile contains some support for this, for example:
428
429make CROSS=i386-mingw32msvc- clean GC-inlined
430
431will build pthreadGCn.dll and libpthreadGCn.a (n=version#), provided your
432cross-tools/bin directory is in your PATH (or use the cross-make.sh script
433at the URL above).
434
435
436Building the library as a statically linkable library
437-----------------------------------------------------
438
439General: PTW32_STATIC_LIB must be defined for both the library build and the
440application build. The makefiles supplied and used by the following 'make'
441command lines will define this for you.
442
443MSVC (creates pthreadVCn.lib as a static link lib):
444
445nmake clean VC-static
446
447
448MinGW32 (creates libpthreadGCn.a as a static link lib):
449
450make clean GC-static
451
452
453Define PTW32_STATIC_LIB when building your application. Also, your
454application must call a two non-portable routines to initialise the
455some state on startup and cleanup before exit. One other routine needs
456to be called to cleanup after any Win32 threads have called POSIX API
457routines. See README.NONPORTABLE or the html reference manual pages for
458details on these routines:
459
460BOOL pthread_win32_process_attach_np (void);
461BOOL pthread_win32_process_detach_np (void);
462BOOL pthread_win32_thread_attach_np (void); // Currently a no-op
463BOOL pthread_win32_thread_detach_np (void);
464
465
466The tests makefiles have the same targets but only check that the
467static library is statically linkable. They don't run the full
468testsuite. To run the full testsuite, build the dlls and run the
469dll test targets.
470
471
472Building the library under Cygwin
473---------------------------------
474
475Cygwin is implementing it's own POSIX threads routines and these
476will be the ones to use if you develop using Cygwin.
477
478
479Ready to run binaries
480---------------------
481
482For convenience, the following ready-to-run files can be downloaded
483from the FTP site (see under "Availability" below):
484
485	pthread.h
486	semaphore.h
487	sched.h
488	pthreadVC.dll	- built with MSVC compiler using C setjmp/longjmp
489	pthreadVC.lib
490	pthreadVCE.dll	- built with MSVC++ compiler using C++ EH
491	pthreadVCE.lib
492	pthreadVSE.dll	- built with MSVC compiler using SEH
493	pthreadVSE.lib
494	pthreadGC.dll	- built with Mingw32 GCC
495	libpthreadGC.a	- derived from pthreadGC.dll
496	pthreadGCE.dll	- built with Mingw32 G++
497	libpthreadGCE.a	- derived from pthreadGCE.dll
498
499As of August 2003 pthreads-win32 pthreadG* versions are built and tested
500using the MinGW + MsysDTK environment current as of that date or later.
501The following file MAY be needed for older MinGW environments.
502
503	gcc.dll 	- needed to build and run applications that use
504			  pthreadGCE.dll.
505
506
507Building applications with GNU compilers
508----------------------------------------
509
510If you're using pthreadGC.dll:
511
512With the three header files, pthreadGC.dll and libpthreadGC.a in the
513same directory as your application myapp.c, you could compile, link
514and run myapp.c under Mingw32 as follows:
515
516	gcc -o myapp.exe myapp.c -I. -L. -lpthreadGC
517	myapp
518
519Or put pthreadGC.dll in an appropriate directory in your PATH,
520put libpthreadGC.a in your system lib directory, and
521put the three header files in your system include directory,
522then use:
523
524	gcc -o myapp.exe myapp.c -lpthreadGC
525	myapp
526
527
528If you're using pthreadGCE.dll:
529
530With the three header files, pthreadGCE.dll, gcc.dll and libpthreadGCE.a
531in the same directory as your application myapp.c, you could compile,
532link and run myapp.c under Mingw32 as follows:
533
534	gcc -x c++ -o myapp.exe myapp.c -I. -L. -lpthreadGCE
535	myapp
536
537Or put pthreadGCE.dll and gcc.dll in an appropriate directory in
538your PATH, put libpthreadGCE.a in your system lib directory, and
539put the three header files in your system include directory,
540then use:
541
542	gcc -x c++ -o myapp.exe myapp.c -lpthreadGCE
543	myapp
544
545
546Availability
547------------
548
549The complete source code in either unbundled, self-extracting
550Zip file, or tar/gzipped format can be found at:
551
552	ftp://sources.redhat.com/pub/pthreads-win32
553
554The pre-built DLL, export libraries and matching pthread.h can
555be found at:
556
557	ftp://sources.redhat.com/pub/pthreads-win32/dll-latest
558
559Home page:
560
561	http://sources.redhat.com/pthreads-win32/
562
563
564Mailing list
565------------
566
567There is a mailing list for discussing pthreads on Win32.
568To join, send email to:
569
570	pthreads-win32-subscribe@sources.redhat.com
571
572Unsubscribe by sending mail to:
573
574	pthreads-win32-unsubscribe@sources.redhat.com
575
576
577Acknowledgements
578----------------
579
580See the ANNOUNCE file for acknowledgements.
581See the 'CONTRIBUTORS' file for the list of contributors.
582
583As much as possible, the ChangeLog file attributes
584contributions and patches that have been incorporated
585in the library to the individuals responsible.
586
587Finally, thanks to all those who work on and contribute to the
588POSIX and Single Unix Specification standards. The maturity of an
589industry can be measured by it's open standards.
590
591----
592Ross Johnson
593<rpj@callisto.canberra.edu.au>
594
595
596
597
598
599
600
601
602

README.Borland

1In ptw32_InterlockedCompareExchange.c, I've added a section for
2Borland's compiler; it's identical to that for the MS compiler except
3that it uses /* ... */ comments instead of ; comments.
4
5[RPJ: need to define HAVE_TASM32 in config.h to use the above.]
6
7
8The other file is a makefile suitable for use with Borland's compiler
9(run "make -fBmakefile" in the directory).  It builds a single version
10of the library, pthreadBC.dll and the corresponding pthreadBC.lib
11import library, which is comparable to the pthreadVC version; I can't
12personally see any demand for the versions that include structured or
13C++ exception cancellation handling so I haven't attempted to build
14those versions of the library.  (I imagine a static version might be
15of use to some, but we can't legally use that on my commercial
16projects so I can't try that out, unfortunately.)
17
18[RPJ: Added tests\Bmakefile as well.]
19
20Borland C++ doesn't define the ENOSYS constant used by pthreads-win32;
21rather than make more extensive patches to the pthreads-win32 source I
22have a mostly-arbitrary constant for it in the makefile.  However this
23doesn't make it visible to the application using the library, so if
24anyone actually wants to use this constant in their apps (why?)
25someone might like to make a seperate NEED_BCC_something define to add
26this stuff.
27
28The makefile also #defines EDEADLK as EDEADLOCK, _timeb as timeb, and
29_ftime as ftime, to deal with the minor differences between the two
30RTLs' naming conventions, and sets the compiler flags as required to
31get a normal compile of the library.
32
33[RPJ: Moved errno values and _timeb etc to pthread.h, so apps will also
34use them.]
35
36(While I'm on the subject, the reason Borland users should recompile
37the library, rather than using the impdef/implib technique suggested
38previously on the mailing list, is that a) the errno constants are
39different, so the results returned by the pthread_* functions can be
40meaningless, and b) the errno variable/pseudo-variable itself is
41different in the MS & BCC runtimes, so you can't access the
42pthreadVC's errno from a Borland C++-compiled host application
43correctly - I imagine there are other potential problems from the RTL
44mismatch too.)
45
46[RPJ: Make sure you use the same RTL in both dll and application builds.
47The dll and tests Bmakefiles use cw32mti.lib. Having some trouble with
48memory read exceptions running the test suite using BCC55.]
49
50Best regards,
51Will
52
53--
54Will Bryant
55Systems Architect, eCOSM Limited
56Cell +64 21 655 443, office +64 3 365 4176
57http://www.ecosm.com/
58

README.CV

1README.CV -- Condition Variables
2--------------------------------
3
4The original implementation of condition variables in
5pthreads-win32 was based on a discussion paper:
6
7"Strategies for Implementing POSIX Condition Variables
8on Win32": http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
9
10The changes suggested below were made on Feb 6 2001. This
11file is included in the package for the benefit of anyone
12interested in understanding the pthreads-win32 implementation
13of condition variables and the (sometimes subtle) issues that
14it attempts to resolve.
15
16Thanks go to the individuals whose names appear throughout
17the following text.
18
19Ross Johnson
20
21--------------------------------------------------------------------
22
23fyi.. (more detailed problem description/demos + possible fix/patch)
24
25regards,
26alexander.
27
28
29Alexander Terekhov
3031.01.2001 17:43
31
32To:   ace-bugs@cs.wustl.edu
33cc:
34From: Alexander Terekhov/Germany/IBM@IBMDE
35Subject:  Implementation of POSIX CVs: spur.wakeups/lost
36      signals/deadlocks/unfairness
37
38
39
40    ACE VERSION:
41
42        5.1.12 (pthread-win32 snapshot 2000-12-29)
43
44    HOST MACHINE and OPERATING SYSTEM:
45
46        IBM IntelliStation Z Pro, 2 x XEON 1GHz, Win2K
47
48    TARGET MACHINE and OPERATING SYSTEM, if different from HOST:
49    COMPILER NAME AND VERSION (AND PATCHLEVEL):
50
51        Microsoft Visual C++ 6.0
52
53    AREA/CLASS/EXAMPLE AFFECTED:
54
55        Implementation of POSIX condition variables - OS.cpp/.h
56
57    DOES THE PROBLEM AFFECT:
58
59        EXECUTION? YES!
60
61    SYNOPSIS:
62
63        a) spurious wakeups (minor problem)
64        b) lost signals
65        c) broadcast deadlock
66        d) unfairness (minor problem)
67
68    DESCRIPTION:
69
70        Please see attached copy of discussion thread
71        from comp.programming.threads for more details on
72        some reported problems. (i've also posted a "fyi"
73        message to ace-users a week or two ago but
74        unfortunately did not get any response so far).
75
76        It seems that current implementation suffers from
77        two essential problems:
78
79        1) cond.waiters_count does not accurately reflect
80           number of waiters blocked on semaphore - w/o
81           proper synchronisation that could result (in the
82           time window when counter is not accurate)
83           in spurious wakeups organised by subsequent
84           _signals  and _broadcasts.
85
86        2) Always having (with no e.g. copy_and_clear/..)
87           the same queue in use (semaphore+counter)
88           neither signal nor broadcast provide 'atomic'
89           behaviour with respect to other threads/subsequent
90           calls to signal/broadcast/wait.
91
92        Each problem and combination of both could produce
93        various nasty things:
94
95        a) spurious wakeups (minor problem)
96
97             it is possible that waiter(s) which was already
98             unblocked even so is still counted as blocked
99             waiter. signal and broadcast will release
100             semaphore which will produce a spurious wakeup
101             for a 'real' waiter coming later.
102
103        b) lost signals
104
105             signalling thread ends up consuming its own
106             signal. please see demo/discussion below.
107
108        c) broadcast deadlock
109
110             last_waiter processing code does not correctly
111             handle the case with multiple threads
112             waiting for the end of broadcast.
113             please see demo/discussion below.
114
115        d) unfairness (minor problem)
116
117             without SignalObjectAndWait some waiter(s)
118             may end up consuming broadcasted signals
119             multiple times (spurious wakeups) because waiter
120             thread(s) can be preempted before they call
121             semaphore wait (but after count++ and mtx.unlock).
122
123    REPEAT BY:
124
125        See below... run problem demos programs (tennis.cpp and
126        tennisb.cpp) number of times concurrently (on multiprocessor)
127        and in multiple sessions or just add a couple of "Sleep"s
128        as described in the attached copy of discussion thread
129        from comp.programming.threads
130
131    SAMPLE FIX/WORKAROUND:
132
133        See attached patch to pthread-win32.. well, I can not
134        claim that it is completely bug free but at least my
135        test and tests provided by pthreads-win32 seem to work.
136        Perhaps that will help.
137
138        regards,
139        alexander.
140
141
142>> Forum: comp.programming.threads
143>> Thread: pthread_cond_* implementation questions
144.
145.
146.
147David Schwartz <davids@webmaster.com> wrote:
148
149> terekhov@my-deja.com wrote:
150>
151>> BTW, could you please also share your view on other perceived
152>> "problems" such as nested broadcast deadlock, spurious wakeups
153>> and (the latest one) lost signals??
154>
155>I'm not sure what you mean. The standard allows an implementation
156>to do almost whatever it likes. In fact, you could implement
157>pthread_cond_wait by releasing the mutex, sleeping a random
158>amount of time, and then reacquiring the mutex. Of course,
159>this would be a pretty poor implementation, but any code that
160>didn't work under that implementation wouldn't be strictly
161>compliant.
162
163The implementation you suggested is indeed correct
164one (yes, now I see it :). However it requires from
165signal/broadcast nothing more than to "{ return 0; }"
166That is not the case for pthread-win32 and ACE
167implementations. I do think that these implementations
168(basically the same implementation) have some serious
169problems with wait/signal/broadcast calls. I am looking
170for help to clarify whether these problems are real
171or not. I think that I can demonstrate what I mean
172using one or two small sample programs.
173.
174.
175.
176==========
177tennis.cpp
178==========
179
180#include "ace/Synch.h"
181#include "ace/Thread.h"
182
183enum GAME_STATE {
184
185  START_GAME,
186  PLAYER_A,     // Player A playes the ball
187  PLAYER_B,     // Player B playes the ball
188  GAME_OVER,
189  ONE_PLAYER_GONE,
190  BOTH_PLAYERS_GONE
191
192};
193
194enum GAME_STATE             eGameState;
195ACE_Mutex*                  pmtxGameStateLock;
196ACE_Condition< ACE_Mutex >* pcndGameStateChange;
197
198void*
199  playerA(
200    void* pParm
201  )
202{
203
204  // For access to game state variable
205  pmtxGameStateLock->acquire();
206
207  // Play loop
208  while ( eGameState < GAME_OVER ) {
209
210    // Play the ball
211    cout << endl << "PLAYER-A" << endl;
212
213    // Now its PLAYER-B's turn
214    eGameState = PLAYER_B;
215
216    // Signal to PLAYER-B that now it is his turn
217    pcndGameStateChange->signal();
218
219    // Wait until PLAYER-B finishes playing the ball
220    do {
221
222      pcndGameStateChange->wait();
223
224      if ( PLAYER_B == eGameState )
225        cout << endl << "----PLAYER-A: SPURIOUS WAKEUP!!!" << endl;
226
227    } while ( PLAYER_B == eGameState );
228
229  }
230
231  // PLAYER-A gone
232  eGameState = (GAME_STATE)(eGameState+1);
233  cout << endl << "PLAYER-A GONE" << endl;
234
235  // No more access to state variable needed
236  pmtxGameStateLock->release();
237
238  // Signal PLAYER-A gone event
239  pcndGameStateChange->broadcast();
240
241  return 0;
242
243}
244
245void*
246  playerB(
247    void* pParm
248  )
249{
250
251  // For access to game state variable
252  pmtxGameStateLock->acquire();
253
254  // Play loop
255  while ( eGameState < GAME_OVER ) {
256
257    // Play the ball
258    cout << endl << "PLAYER-B" << endl;
259
260    // Now its PLAYER-A's turn
261    eGameState = PLAYER_A;
262
263    // Signal to PLAYER-A that now it is his turn
264    pcndGameStateChange->signal();
265
266    // Wait until PLAYER-A finishes playing the ball
267    do {
268
269      pcndGameStateChange->wait();
270
271      if ( PLAYER_A == eGameState )
272        cout << endl << "----PLAYER-B: SPURIOUS WAKEUP!!!" << endl;
273
274    } while ( PLAYER_A == eGameState );
275
276  }
277
278  // PLAYER-B gone
279  eGameState = (GAME_STATE)(eGameState+1);
280  cout << endl << "PLAYER-B GONE" << endl;
281
282  // No more access to state variable needed
283  pmtxGameStateLock->release();
284
285  // Signal PLAYER-B gone event
286  pcndGameStateChange->broadcast();
287
288  return 0;
289
290}
291
292
293int
294main (int, ACE_TCHAR *[])
295{
296
297  pmtxGameStateLock   = new ACE_Mutex();
298  pcndGameStateChange = new ACE_Condition< ACE_Mutex >( *pmtxGameStateLock
299);
300
301  // Set initial state
302  eGameState = START_GAME;
303
304  // Create players
305  ACE_Thread::spawn( playerA );
306  ACE_Thread::spawn( playerB );
307
308  // Give them 5 sec. to play
309  Sleep( 5000 );//sleep( 5 );
310
311  // Set game over state
312  pmtxGameStateLock->acquire();
313  eGameState = GAME_OVER;
314
315  // Let them know
316  pcndGameStateChange->broadcast();
317
318  // Wait for players to stop
319  do {
320
321    pcndGameStateChange->wait();
322
323  } while ( eGameState < BOTH_PLAYERS_GONE );
324
325  // Cleanup
326  cout << endl << "GAME OVER" << endl;
327  pmtxGameStateLock->release();
328  delete pcndGameStateChange;
329  delete pmtxGameStateLock;
330
331  return 0;
332
333}
334
335===========
336tennisb.cpp
337===========
338#include "ace/Synch.h"
339#include "ace/Thread.h"
340
341enum GAME_STATE {
342
343  START_GAME,
344  PLAYER_A,     // Player A playes the ball
345  PLAYER_B,     // Player B playes the ball
346  GAME_OVER,
347  ONE_PLAYER_GONE,
348  BOTH_PLAYERS_GONE
349
350};
351
352enum GAME_STATE             eGameState;
353ACE_Mutex*                  pmtxGameStateLock;
354ACE_Condition< ACE_Mutex >* pcndGameStateChange;
355
356void*
357  playerA(
358    void* pParm
359  )
360{
361
362  // For access to game state variable
363  pmtxGameStateLock->acquire();
364
365  // Play loop
366  while ( eGameState < GAME_OVER ) {
367
368    // Play the ball
369    cout << endl << "PLAYER-A" << endl;
370
371    // Now its PLAYER-B's turn
372    eGameState = PLAYER_B;
373
374    // Signal to PLAYER-B that now it is his turn
375    pcndGameStateChange->broadcast();
376
377    // Wait until PLAYER-B finishes playing the ball
378    do {
379
380      pcndGameStateChange->wait();
381
382      if ( PLAYER_B == eGameState )
383        cout << endl << "----PLAYER-A: SPURIOUS WAKEUP!!!" << endl;
384
385    } while ( PLAYER_B == eGameState );
386
387  }
388
389  // PLAYER-A gone
390  eGameState = (GAME_STATE)(eGameState+1);
391  cout << endl << "PLAYER-A GONE" << endl;
392
393  // No more access to state variable needed
394  pmtxGameStateLock->release();
395
396  // Signal PLAYER-A gone event
397  pcndGameStateChange->broadcast();
398
399  return 0;
400
401}
402
403void*
404  playerB(
405    void* pParm
406  )
407{
408
409  // For access to game state variable
410  pmtxGameStateLock->acquire();
411
412  // Play loop
413  while ( eGameState < GAME_OVER ) {
414
415    // Play the ball
416    cout << endl << "PLAYER-B" << endl;
417
418    // Now its PLAYER-A's turn
419    eGameState = PLAYER_A;
420
421    // Signal to PLAYER-A that now it is his turn
422    pcndGameStateChange->broadcast();
423
424    // Wait until PLAYER-A finishes playing the ball
425    do {
426
427      pcndGameStateChange->wait();
428
429      if ( PLAYER_A == eGameState )
430        cout << endl << "----PLAYER-B: SPURIOUS WAKEUP!!!" << endl;
431
432    } while ( PLAYER_A == eGameState );
433
434  }
435
436  // PLAYER-B gone
437  eGameState = (GAME_STATE)(eGameState+1);
438  cout << endl << "PLAYER-B GONE" << endl;
439
440  // No more access to state variable needed
441  pmtxGameStateLock->release();
442
443  // Signal PLAYER-B gone event
444  pcndGameStateChange->broadcast();
445
446  return 0;
447
448}
449
450
451int
452main (int, ACE_TCHAR *[])
453{
454
455  pmtxGameStateLock   = new ACE_Mutex();
456  pcndGameStateChange = new ACE_Condition< ACE_Mutex >( *pmtxGameStateLock
457);
458
459  // Set initial state
460  eGameState = START_GAME;
461
462  // Create players
463  ACE_Thread::spawn( playerA );
464  ACE_Thread::spawn( playerB );
465
466  // Give them 5 sec. to play
467  Sleep( 5000 );//sleep( 5 );
468
469  // Make some noise
470  pmtxGameStateLock->acquire();
471  cout << endl << "---Noise ON..." << endl;
472  pmtxGameStateLock->release();
473  for ( int i = 0; i < 100000; i++ )
474    pcndGameStateChange->broadcast();
475  cout << endl << "---Noise OFF" << endl;
476
477  // Set game over state
478  pmtxGameStateLock->acquire();
479  eGameState = GAME_OVER;
480  cout << endl << "---Stopping the game..." << endl;
481
482  // Let them know
483  pcndGameStateChange->broadcast();
484
485  // Wait for players to stop
486  do {
487
488    pcndGameStateChange->wait();
489
490  } while ( eGameState < BOTH_PLAYERS_GONE );
491
492  // Cleanup
493  cout << endl << "GAME OVER" << endl;
494  pmtxGameStateLock->release();
495  delete pcndGameStateChange;
496  delete pmtxGameStateLock;
497
498  return 0;
499
500}
501.
502.
503.
504David Schwartz <davids@webmaster.com> wrote:
505>> > It's compliant
506>>
507>> That is really good.
508>
509>> Tomorrow (I have to go urgently now) I will try to
510>> demonstrate the lost-signal "problem" of current
511>> pthread-win32 and ACE-(variant w/o SingleObjectAndWait)
512>> implementations: players start suddenly drop their balls :-)
513>> (with no change in source code).
514>
515>Signals aren't lost, they're going to the main thread,
516>which isn't coded correctly to handle them. Try this:
517>
518>  // Wait for players to stop
519>  do {
520>
521>    pthread_cond_wait( &cndGameStateChange,&mtxGameStateLock );
522>printf("Main thread stole a signal\n");
523>
524>  } while ( eGameState < BOTH_PLAYERS_GONE );
525>
526>I bet everytime you thing a signal is lost, you'll see that printf.
527>The signal isn't lost, it was stolen by another thread.
528
529well, you can probably loose your bet.. it was indeed stolen
530by "another" thread but not the one you seem to think of.
531
532I think that what actually happens is the following:
533
534H:\SA\UXX\pt\PTHREADS\TESTS>tennis3.exe
535
536PLAYER-A
537
538PLAYER-B
539
540----PLAYER-B: SPURIOUS WAKEUP!!!
541
542PLAYER-A GONE
543
544PLAYER-B GONE
545
546GAME OVER
547
548H:\SA\UXX\pt\PTHREADS\TESTS>
549
550here you can see that PLAYER-B after playing his first
551ball (which came via signal from PLAYER-A) just dropped
552it down. What happened is that his signal to player A
553was consumed as spurious wakeup by himself (player B).
554
555The implementation has a problem:
556
557================
558waiting threads:
559================
560
561{ /** Critical Section
562
563  inc cond.waiters_count
564
565}
566
567  /*
568  /* Atomic only if using Win32 SignalObjectAndWait
569  /*
570  cond.mtx.release
571
572  /*** ^^-- A THREAD WHICH DID SIGNAL MAY ACQUIRE THE MUTEX,
573  /***      GO INTO WAIT ON THE SAME CONDITION AND OVERTAKE
574  /***      ORIGINAL WAITER(S) CONSUMING ITS OWN SIGNAL!
575
576  cond.sem.wait
577
578Player-A after playing game's initial ball went into
579wait (called _wait) but was pre-empted before reaching
580wait semaphore. He was counted as waiter but was not
581actually waiting/blocked yet.
582
583===============
584signal threads:
585===============
586
587{ /** Critical Section
588
589  waiters_count = cond.waiters_count
590
591}
592
593  if ( waiters_count != 0 )
594
595    sem.post 1
596
597  endif
598
599Player-B after he received signal/ball from Player A
600called _signal. The _signal did see that there was
601one waiter blocked on the condition (Player-A) and
602released the semaphore.. (but it did not unblock
603Player-A because he was not actually blocked).
604Player-B thread continued its execution, called _wait,
605was counted as second waiter BUT was allowed to slip
606through opened semaphore gate (which was opened for
607Player-B) and received his own signal. Player B remained
608blocked followed by Player A. Deadlock happened which
609lasted until main thread came in and said game over.
610
611It seems to me that the implementation fails to
612correctly implement the following statement
613from specification:
614
615http://www.opengroup.org/
616onlinepubs/007908799/xsh/pthread_cond_wait.html
617
618"These functions atomically release mutex and cause
619the calling thread to block on the condition variable
620cond; atomically here means "atomically with respect
621to access by another thread to the mutex and then the
622condition variable". That is, if another thread is
623able to acquire the mutex after the about-to-block
624thread has released it, then a subsequent call to
625pthread_cond_signal() or pthread_cond_broadcast()
626in that thread behaves as if it were issued after
627the about-to-block thread has blocked."
628
629Question: Am I right?
630
631(I produced the program output above by simply
632adding ?Sleep( 1 )?:
633
634================
635waiting threads:
636================
637
638{ /** Critical Section
639
640  inc cond.waiters_count
641
642}
643
644  /*
645  /* Atomic only if using Win32 SignalObjectAndWait
646  /*
647  cond.mtx.release
648
649Sleep( 1 ); // Win32
650
651  /*** ^^-- A THREAD WHICH DID SIGNAL MAY ACQUIRE THE MUTEX,
652  /***      GO INTO WAIT ON THE SAME CONDITION AND OVERTAKE
653  /***      ORIGINAL WAITER(S) CONSUMING ITS OWN SIGNAL!
654
655  cond.sem.wait
656
657to the source code of pthread-win32 implementation:
658
659http://sources.redhat.com/cgi-bin/cvsweb.cgi/pthreads/
660condvar.c?rev=1.36&content-type=text/
661x-cvsweb-markup&cvsroot=pthreads-win32
662
663
664  /*
665  * We keep the lock held just long enough to increment the count of
666  * waiters by one (above).
667  * Note that we can't keep it held across the
668  * call to sem_wait since that will deadlock other calls
669  * to pthread_cond_signal
670  */
671  cleanup_args.mutexPtr = mutex;
672  cleanup_args.cv = cv;
673  cleanup_args.resultPtr = &result;
674
675  pthread_cleanup_push (ptw32_cond_wait_cleanup, (void *)
676&cleanup_args);
677
678  if ((result = pthread_mutex_unlock (mutex)) == 0)
679    {((result
680Sleep( 1 ); // @AT
681
682      /*
683      * Wait to be awakened by
684      *              pthread_cond_signal, or
685      *              pthread_cond_broadcast, or
686      *              a timeout
687      *
688      * Note:
689      *      ptw32_sem_timedwait is a cancelation point,
690      *      hence providing the
691      *      mechanism for making pthread_cond_wait a cancelation
692      *      point. We use the cleanup mechanism to ensure we
693      *      re-lock the mutex and decrement the waiters count
694      *      if we are canceled.
695      */
696      if (ptw32_sem_timedwait (&(cv->sema), abstime) == -1)         {
697          result = errno;
698        }
699    }
700
701  pthread_cleanup_pop (1);  /* Always cleanup */
702
703
704BTW, on my system (2 CPUs) I can manage to get
705signals lost even without any source code modification
706if I run the tennis program many times in different
707shell sessions.
708.
709.
710.
711David Schwartz <davids@webmaster.com> wrote:
712>terekhov@my-deja.com wrote:
713>
714>> well, it might be that the program is in fact buggy.
715>> but you did not show me any bug.
716>
717>You're right. I was close but not dead on. I was correct, however,
718>that the code is buggy because it uses 'pthread_cond_signal' even
719>though not any thread waiting on the condition variable can do the
720>job. I was wrong in which thread could be waiting on the cv but
721>unable to do the job.
722
723Okay, lets change 'pthread_cond_signal' to 'pthread_cond_broadcast'
724but also add some noise from main() right before declaring the game
725to be over (I need it in order to demonstrate another problem of
726pthread-win32/ACE implementations - broadcast deadlock)...
727.
728.
729.
730It is my understanding of POSIX conditions,
731that on correct implementation added noise
732in form of unnecessary broadcasts from main,
733should not break the tennis program. The
734only 'side effect' of added noise on correct
735implementation would be 'spurious wakeups' of
736players (in fact they are not spurious,
737players just see them as spurious) unblocked,
738not by another player but by main before
739another player had a chance to acquire the
740mutex and change the game state variable:
741.
742.
743.
744
745PLAYER-B
746
747PLAYER-A
748
749---Noise ON...
750
751PLAYER-B
752
753PLAYER-A
754
755.
756.
757.
758
759PLAYER-B
760
761PLAYER-A
762
763----PLAYER-A: SPURIOUS WAKEUP!!!
764
765PLAYER-B
766
767PLAYER-A
768
769---Noise OFF
770
771PLAYER-B
772
773---Stopping the game...
774
775PLAYER-A GONE
776
777PLAYER-B GONE
778
779GAME OVER
780
781H:\SA\UXX\pt\PTHREADS\TESTS>
782
783On pthread-win32/ACE implementations the
784program could stall:
785
786.
787.
788.
789
790PLAYER-A
791
792PLAYER-B
793
794PLAYER-A
795
796PLAYER-B
797
798PLAYER-A
799
800PLAYER-B
801
802PLAYER-A
803
804PLAYER-B
805
806---Noise ON...
807
808PLAYER-A
809
810---Noise OFF
811^C
812H:\SA\UXX\pt\PTHREADS\TESTS>
813
814
815The implementation has problems:
816
817================
818waiting threads:
819================
820
821{ /** Critical Section
822
823  inc cond.waiters_count
824
825}
826
827  /*
828  /* Atomic only if using Win32 SignalObjectAndWait
829  /*
830  cond.mtx.release
831  cond.sem.wait
832
833  /*** ^^-- WAITER CAN BE PREEMPTED AFTER BEING UNBLOCKED...
834
835{ /** Critical Section
836
837  dec cond.waiters_count
838
839  /*** ^^- ...AND BEFORE DECREMENTING THE COUNT (1)
840
841  last_waiter = ( cond.was_broadcast &&
842                    cond.waiters_count == 0 )
843
844  if ( last_waiter )
845
846    cond.was_broadcast = FALSE
847
848  endif
849
850}
851
852  if ( last_waiter )
853
854    /*
855    /* Atomic only if using Win32 SignalObjectAndWait
856    /*
857    cond.auto_reset_event_or_sem.post /* Event for Win32
858    cond.mtx.acquire
859
860  /*** ^^-- ...AND BEFORE CALL TO mtx.acquire (2)
861
862  /*** ^^-- NESTED BROADCASTS RESULT IN A DEADLOCK
863
864
865  else
866
867    cond.mtx.acquire
868
869  /*** ^^-- ...AND BEFORE CALL TO mtx.acquire (3)
870
871  endif
872
873
874==================
875broadcast threads:
876==================
877
878{ /** Critical Section
879
880  waiters_count = cond.waiters_count
881
882  if ( waiters_count != 0 )
883
884    cond.was_broadcast = TRUE
885
886  endif
887
888}
889
890if ( waiters_count != 0 )
891
892  cond.sem.post waiters_count
893
894  /*** ^^^^^--- SPURIOUS WAKEUPS DUE TO (1)
895
896  cond.auto_reset_event_or_sem.wait /* Event for Win32
897
898  /*** ^^^^^--- DEADLOCK FOR FURTHER BROADCASTS IF THEY
899                HAPPEN TO GO INTO WAIT WHILE PREVIOUS
900                BROADCAST IS STILL IN PROGRESS/WAITING
901
902endif
903
904a) cond.waiters_count does not accurately reflect
905number of waiters blocked on semaphore - that could
906result (in the time window when counter is not accurate)
907in spurios wakeups organised by subsequent _signals
908and _broadcasts. From standard compliance point of view
909that is OK but that could be a real problem from
910performance/efficiency point of view.
911
912b) If subsequent broadcast happen to go into wait on
913cond.auto_reset_event_or_sem before previous
914broadcast was unblocked from cond.auto_reset_event_or_sem
915by its last waiter, one of two blocked threads will
916remain blocked because last_waiter processing code
917fails to unblock both threads.
918
919In the situation with tennisb.c the Player-B was put
920in a deadlock by noise (broadcast) coming from main
921thread. And since Player-B holds the game state
922mutex when it calls broadcast, the whole program
923stalled: Player-A was deadlocked on mutex and
924main thread after finishing with producing the noise
925was deadlocked on mutex too (needed to declare the
926game over)
927
928(I produced the program output above by simply
929adding ?Sleep( 1 )?:
930
931==================
932broadcast threads:
933==================
934
935{ /** Critical Section
936
937  waiters_count = cond.waiters_count
938
939  if ( waiters_count != 0 )
940
941    cond.was_broadcast = TRUE
942
943  endif
944
945}
946
947if ( waiters_count != 0 )
948
949Sleep( 1 ); //Win32
950
951  cond.sem.post waiters_count
952
953  /*** ^^^^^--- SPURIOUS WAKEUPS DUE TO (1)
954
955  cond.auto_reset_event_or_sem.wait /* Event for Win32
956
957  /*** ^^^^^--- DEADLOCK FOR FURTHER BROADCASTS IF THEY
958                HAPPEN TO GO INTO WAIT WHILE PREVIOUS
959                BROADCAST IS STILL IN PROGRESS/WAITING
960
961endif
962
963to the source code of pthread-win32 implementation:
964
965http://sources.redhat.com/cgi-bin/cvsweb.cgi/pthreads/
966condvar.c?rev=1.36&content-type=text/
967x-cvsweb-markup&cvsroot=pthreads-win32
968
969  if (wereWaiters)
970    {(wereWaiters)sroot=pthreads-win32eb.cgi/pthreads/Yem...m
971      /*
972      * Wake up all waiters
973      */
974
975Sleep( 1 ); //@AT
976
977#ifdef NEED_SEM
978
979      result = (ptw32_increase_semaphore( &cv->sema, cv->waiters )
980                 ? 0
981                : EINVAL);
982
983#else /* NEED_SEM */
984
985      result = (ReleaseSemaphore( cv->sema, cv->waiters, NULL )
986                 ? 0
987                : EINVAL);
988
989#endif /* NEED_SEM */
990
991    }
992
993  (void) pthread_mutex_unlock(&(cv->waitersLock));
994
995  if (wereWaiters && result == 0)
996    {(wereWaiters
997      /*
998       * Wait for all the awakened threads to acquire their part of
999       * the counting semaphore
1000       */
1001
1002      if (WaitForSingleObject (cv->waitersDone, INFINITE)
1003          == WAIT_OBJECT_0)
1004        {
1005          result = 0;
1006        }
1007      else
1008        {
1009          result = EINVAL;
1010        }
1011
1012    }
1013
1014  return (result);
1015
1016}
1017
1018BTW, on my system (2 CPUs) I can manage to get
1019the program stalled even without any source code
1020modification if I run the tennisb program many
1021times in different shell sessions.
1022
1023===================
1024pthread-win32 patch
1025===================
1026struct pthread_cond_t_ {
1027  long            nWaitersBlocked;   /* Number of threads blocked
1028*/
1029  long            nWaitersUnblocked; /* Number of threads unblocked
1030*/
1031  long            nWaitersToUnblock; /* Number of threads to unblock
1032*/
1033  sem_t           semBlockQueue;     /* Queue up threads waiting for the
1034*/
1035                                     /*   condition to become signalled
1036*/
1037  sem_t           semBlockLock;      /* Semaphore that guards access to
1038*/
1039                                     /* | waiters blocked count/block queue
1040*/
1041                                     /* +-> Mandatory Sync.LEVEL-1
1042*/
1043  pthread_mutex_t mtxUnblockLock;    /* Mutex that guards access to
1044*/
1045                                     /* | waiters (to)unblock(ed) counts
1046*/
1047                                     /* +-> Optional* Sync.LEVEL-2
1048*/
1049};                                   /* Opt*) for _timedwait and
1050cancellation*/
1051
1052int
1053pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)
1054  int result = EAGAIN;
1055  pthread_cond_t cv = NULL;
1056
1057  if (cond == NULL)
1058    {(cond
1059      return EINVAL;
1060    }
1061
1062  if ((attr != NULL && *attr != NULL) &&
1063      ((*attr)->pshared == PTHREAD_PROCESS_SHARED))
1064    {
1065      /*
1066       * Creating condition variable that can be shared between
1067       * processes.
1068       */
1069      result = ENOSYS;
1070
1071      goto FAIL0;
1072    }
1073
1074  cv = (pthread_cond_t) calloc (1, sizeof (*cv));
1075
1076  if (cv == NULL)
1077    {(cv
1078      result = ENOMEM;
1079      goto FAIL0;
1080    }
1081
1082  cv->nWaitersBlocked   = 0;
1083  cv->nWaitersUnblocked = 0;
1084  cv->nWaitersToUnblock = 0;
1085
1086  if (sem_init (&(cv->semBlockLock), 0, 1) != 0)
1087    {(sem_init
1088      goto FAIL0;
1089    }
1090
1091  if (sem_init (&(cv->semBlockQueue), 0, 0) != 0)
1092    {(sem_init
1093      goto FAIL1;
1094    }
1095
1096  if (pthread_mutex_init (&(cv->mtxUnblockLock), 0) != 0)
1097    {(pthread_mutex_init
1098      goto FAIL2;
1099    }
1100
1101
1102  result = 0;
1103
1104  goto DONE;
1105
1106  /*
1107   * -------------
1108   * Failed...
1109   * -------------
1110   */
1111FAIL2:
1112  (void) sem_destroy (&(cv->semBlockQueue));
1113
1114FAIL1:
1115  (void) sem_destroy (&(cv->semBlockLock));
1116
1117FAIL0:
1118DONE:
1119  *cond = cv;
1120
1121  return (result);
1122
1123}                               /* pthread_cond_init */
1124
1125int
1126pthread_cond_destroy (pthread_cond_t * cond)
1127{
1128  int result = 0;
1129  pthread_cond_t cv;
1130
1131  /*
1132   * Assuming any race condition here is harmless.
1133   */
1134  if (cond == NULL
1135      || *cond == NULL)
1136    {
1137      return EINVAL;
1138    }
1139
1140  if (*cond != (pthread_cond_t) PTW32_OBJECT_AUTO_INIT)
1141    {(*cond
1142      cv = *cond;
1143
1144      /*
1145       * Synchronize access to waiters blocked count (LEVEL-1)
1146       */
1147      if (sem_wait(&(cv->semBlockLock)) != 0)
1148        {(sem_wait(&(cv->semBlockLock))
1149          return errno;
1150        }
1151
1152      /*
1153       * Synchronize access to waiters (to)unblock(ed) counts (LEVEL-2)
1154       */
1155      if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0)
1156        {((result
1157          (void) sem_post(&(cv->semBlockLock));
1158          return result;
1159        }
1160
1161      /*
1162       * Check whether cv is still busy (still has waiters blocked)
1163       */
1164      if (cv->nWaitersBlocked - cv->nWaitersUnblocked > 0)
1165        {(cv->nWaitersBlocked
1166          (void) sem_post(&(cv->semBlockLock));
1167          (void) pthread_mutex_unlock(&(cv->mtxUnblockLock));
1168          return EBUSY;
1169        }
1170
1171      /*
1172       * Now it is safe to destroy
1173       */
1174      (void) sem_destroy (&(cv->semBlockLock));
1175      (void) sem_destroy (&(cv->semBlockQueue));
1176      (void) pthread_mutex_unlock (&(cv->mtxUnblockLock));
1177      (void) pthread_mutex_destroy (&(cv->mtxUnblockLock));
1178
1179      free(cv);
1180      *cond = NULL;
1181    }
1182  else
1183    {
1184      /*
1185       * See notes in ptw32_cond_check_need_init() above also.
1186       */
1187      EnterCriticalSection(&ptw32_cond_test_init_lock);
1188
1189      /*
1190       * Check again.
1191       */
1192      if (*cond == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT)
1193        {(*cond
1194          /*
1195           * This is all we need to do to destroy a statically
1196           * initialised cond that has not yet been used (initialised).
1197           * If we get to here, another thread
1198           * waiting to initialise this cond will get an EINVAL.
1199           */
1200          *cond = NULL;
1201        }
1202      else
1203        {
1204          /*
1205           * The cv has been initialised while we were waiting
1206           * so assume it's in use.
1207           */
1208          result = EBUSY;
1209        }
1210
1211      LeaveCriticalSection(&ptw32_cond_test_init_lock);
1212    }
1213
1214  return (result);
1215}
1216
1217/*
1218 * Arguments for cond_wait_cleanup, since we can only pass a
1219 * single void * to it.
1220 */
1221typedef struct {
1222  pthread_mutex_t * mutexPtr;
1223  pthread_cond_t cv;
1224  int * resultPtr;
1225} ptw32_cond_wait_cleanup_args_t;
1226
1227static void
1228ptw32_cond_wait_cleanup(void * args)
1229{
1230  ptw32_cond_wait_cleanup_args_t * cleanup_args =
1231(ptw32_cond_wait_cleanup_args_t *) args;
1232  pthread_cond_t cv = cleanup_args->cv;
1233  int * resultPtr = cleanup_args->resultPtr;
1234  int eLastSignal; /* enum: 1=yes 0=no -1=cancelled/timedout w/o signal(s)
1235*/
1236  int result;
1237
1238  /*
1239   * Whether we got here as a result of signal/broadcast or because of
1240   * timeout on wait or thread cancellation we indicate that we are no
1241   * longer waiting. The waiter is responsible for adjusting waiters
1242   * (to)unblock(ed) counts (protected by unblock lock).
1243   * Unblock lock/Sync.LEVEL-2 supports _timedwait and cancellation.
1244   */
1245  if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0)
1246    {((result
1247      *resultPtr = result;
1248      return;
1249    }
1250
1251  cv->nWaitersUnblocked++;
1252
1253  eLastSignal = (cv->nWaitersToUnblock == 0) ?
1254                   -1 : (--cv->nWaitersToUnblock == 0);
1255
1256  /*
1257   * No more LEVEL-2 access to waiters (to)unblock(ed) counts needed
1258   */
1259  if ((result = pthread_mutex_unlock(&(cv->mtxUnblockLock))) != 0)
1260    {((result
1261      *resultPtr = result;
1262      return;
1263    }
1264
1265  /*
1266   * If last signal...
1267   */
1268  if (eLastSignal == 1)
1269    {(eLastSignal
1270     /*
1271      * ...it means that we have end of 'atomic' signal/broadcast
1272      */
1273      if (sem_post(&(cv->semBlockLock)) != 0)
1274        {(sem_post(&(cv->semBlockLock))
1275          *resultPtr = errno;
1276          return;
1277        }
1278    }
1279  /*
1280   * If not last signal and not timed out/cancelled wait w/o signal...
1281   */
1282  else if (eLastSignal == 0)
1283    {
1284     /*
1285      * ...it means that next waiter can go through semaphore
1286      */
1287      if (sem_post(&(cv->semBlockQueue)) != 0)
1288        {(sem_post(&(cv->semBlockQueue))
1289          *resultPtr = errno;
1290          return;
1291        }
1292    }
1293
1294  /*
1295   * XSH: Upon successful return, the mutex has been locked and is owned
1296   * by the calling thread
1297   */
1298  if ((result = pthread_mutex_lock(cleanup_args->mutexPtr)) != 0)
1299    {((result
1300      *resultPtr = result;
1301    }
1302
1303}                               /* ptw32_cond_wait_cleanup */
1304
1305static int
1306ptw32_cond_timedwait (pthread_cond_t * cond,
1307                      pthread_mutex_t * mutex,
1308                      const struct timespec *abstime)
1309{
1310  int result = 0;
1311  pthread_cond_t cv;
1312  ptw32_cond_wait_cleanup_args_t cleanup_args;
1313
1314  if (cond == NULL || *cond == NULL)
1315    {(cond
1316      return EINVAL;
1317    }
1318
1319  /*
1320   * We do a quick check to see if we need to do more work
1321   * to initialise a static condition variable. We check
1322   * again inside the guarded section of ptw32_cond_check_need_init()
1323   * to avoid race conditions.
1324   */
1325  if (*cond == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT)
1326    {(*cond
1327      result = ptw32_cond_check_need_init(cond);
1328    }
1329
1330  if (result != 0 && result != EBUSY)
1331    {(result
1332      return result;
1333    }
1334
1335  cv = *cond;
1336
1337  /*
1338   * Synchronize access to waiters blocked count (LEVEL-1)
1339   */
1340  if (sem_wait(&(cv->semBlockLock)) != 0)
1341    {(sem_wait(&(cv->semBlockLock))
1342      return errno;
1343    }
1344
1345  cv->nWaitersBlocked++;
1346
1347  /*
1348   * Thats it. Counted means waiting, no more access needed
1349   */
1350  if (sem_post(&(cv->semBlockLock)) != 0)
1351    {(sem_post(&(cv->semBlockLock))
1352      return errno;
1353    }
1354
1355  /*
1356   * Setup this waiter cleanup handler
1357   */
1358  cleanup_args.mutexPtr = mutex;
1359  cleanup_args.cv = cv;
1360  cleanup_args.resultPtr = &result;
1361
1362  pthread_cleanup_push (ptw32_cond_wait_cleanup, (void *) &cleanup_args);
1363
1364  /*
1365   * Now we can release 'mutex' and...
1366   */
1367  if ((result = pthread_mutex_unlock (mutex)) == 0)
1368    {((result
1369
1370      /*
1371       * ...wait to be awakened by
1372       *              pthread_cond_signal, or
1373       *              pthread_cond_broadcast, or
1374       *              timeout, or
1375       *              thread cancellation
1376       *
1377       * Note:
1378       *
1379       *      ptw32_sem_timedwait is a cancellation point,
1380       *      hence providing the mechanism for making
1381       *      pthread_cond_wait a cancellation point.
1382       *      We use the cleanup mechanism to ensure we
1383       *      re-lock the mutex and adjust (to)unblock(ed) waiters
1384       *      counts if we are cancelled, timed out or signalled.
1385       */
1386      if (ptw32_sem_timedwait (&(cv->semBlockQueue), abstime) != 0)
1387        {(ptw32_sem_timedwait
1388          result = errno;
1389        }
1390    }
1391
1392  /*
1393   * Always cleanup
1394   */
1395  pthread_cleanup_pop (1);
1396
1397
1398  /*
1399   * "result" can be modified by the cleanup handler.
1400   */
1401  return (result);
1402
1403}                               /* ptw32_cond_timedwait */
1404
1405
1406static int
1407ptw32_cond_unblock (pthread_cond_t * cond,
1408                    int unblockAll)
1409{
1410  int result;
1411  pthread_cond_t cv;
1412
1413  if (cond == NULL || *cond == NULL)
1414    {(cond
1415      return EINVAL;
1416    }
1417
1418  cv = *cond;
1419
1420  /*
1421   * No-op if the CV is static and hasn't been initialised yet.
1422   * Assuming that any race condition is harmless.
1423   */
1424  if (cv == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT)
1425    {(cv
1426      return 0;
1427    }
1428
1429  /*
1430   * Synchronize access to waiters blocked count (LEVEL-1)
1431   */
1432  if (sem_wait(&(cv->semBlockLock)) != 0)
1433    {(sem_wait(&(cv->semBlockLock))
1434      return errno;
1435    }
1436
1437  /*
1438   * Synchronize access to waiters (to)unblock(ed) counts (LEVEL-2)
1439   * This sync.level supports _timedwait and cancellation
1440   */
1441  if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0)
1442    {((result
1443      return result;
1444    }
1445
1446  /*
1447   * Adjust waiters blocked and unblocked counts (collect garbage)
1448   */
1449  if (cv->nWaitersUnblocked != 0)
1450    {(cv->nWaitersUnblocked
1451      cv->nWaitersBlocked  -= cv->nWaitersUnblocked;
1452      cv->nWaitersUnblocked = 0;
1453    }
1454
1455  /*
1456   * If (after adjustment) there are still some waiters blocked counted...
1457   */
1458  if ( cv->nWaitersBlocked > 0)
1459    {(
1460      /*
1461       * We will unblock first waiter and leave semBlockLock/LEVEL-1 locked
1462       * LEVEL-1 access is left disabled until last signal/unblock
1463completes
1464       */
1465      cv->nWaitersToUnblock = (unblockAll) ? cv->nWaitersBlocked : 1;
1466
1467      /*
1468       * No more LEVEL-2 access to waiters (to)unblock(ed) counts needed
1469       * This sync.level supports _timedwait and cancellation
1470       */
1471      if ((result = pthread_mutex_unlock(&(cv->mtxUnblockLock))) != 0)
1472        {((result
1473          return result;
1474        }
1475
1476
1477      /*
1478       * Now, with LEVEL-2 lock released let first waiter go through
1479semaphore
1480       */
1481      if (sem_post(&(cv->semBlockQueue)) != 0)
1482        {(sem_post(&(cv->semBlockQueue))
1483          return errno;
1484        }
1485    }
1486  /*
1487   * No waiter blocked - no more LEVEL-1 access to blocked count needed...
1488   */
1489  else if (sem_post(&(cv->semBlockLock)) != 0)
1490    {
1491      return errno;
1492    }
1493  /*
1494   * ...and no more LEVEL-2 access to waiters (to)unblock(ed) counts needed
1495too
1496   * This sync.level supports _timedwait and cancellation
1497   */
1498  else
1499    {
1500      result = pthread_mutex_unlock(&(cv->mtxUnblockLock));
1501    }
1502
1503  return(result);
1504
1505}                               /* ptw32_cond_unblock */
1506
1507int
1508pthread_cond_wait (pthread_cond_t * cond,
1509                   pthread_mutex_t * mutex)
1510{
1511  /* The NULL abstime arg means INFINITE waiting. */
1512  return(ptw32_cond_timedwait(cond, mutex, NULL));
1513}                               /* pthread_cond_wait */
1514
1515
1516int
1517pthread_cond_timedwait (pthread_cond_t * cond,
1518                        pthread_mutex_t * mutex,
1519                        const struct timespec *abstime)
1520{
1521  if (abstime == NULL)
1522    {(abstime
1523      return EINVAL;
1524    }
1525
1526  return(ptw32_cond_timedwait(cond, mutex, abstime));
1527}                               /* pthread_cond_timedwait */
1528
1529
1530int
1531pthread_cond_signal (pthread_cond_t * cond)
1532{
1533  /* The '0'(FALSE) unblockAll arg means unblock ONE waiter. */
1534  return(ptw32_cond_unblock(cond, 0));
1535}                               /* pthread_cond_signal */
1536
1537int
1538pthread_cond_broadcast (pthread_cond_t * cond)
1539{
1540  /* The '1'(TRUE) unblockAll arg means unblock ALL waiters. */
1541  return(ptw32_cond_unblock(cond, 1));
1542}                               /* pthread_cond_broadcast */
1543
1544
1545
1546
1547TEREKHOV@de.ibm.com on 17.01.2001 01:00:57
1548
1549Please respond to TEREKHOV@de.ibm.com
1550
1551To:   pthreads-win32@sourceware.cygnus.com
1552cc:   schmidt@uci.edu
1553Subject:  win32 conditions: sem+counter+event = broadcast_deadlock +
1554      spur.wakeup/unfairness/incorrectness ??
1555
1556
1557
1558
1559
1560
1561
1562Hi,
1563
1564Problem 1: broadcast_deadlock
1565
1566It seems that current implementation does not provide "atomic"
1567broadcasts. That may lead to "nested" broadcasts... and it seems
1568that nested case is not handled correctly -> producing a broadcast
1569DEADLOCK as a result.
1570
1571Scenario:
1572
1573N (>1) waiting threads W1..N are blocked (in _wait) on condition's
1574semaphore.
1575
1576Thread B1 calls pthread_cond_broadcast, which results in "releasing" N
1577W threads via incrementing semaphore counter by N (stored in
1578cv->waiters) BUT cv->waiters counter does not change!! The caller
1579thread B1 remains blocked on cv->waitersDone event (auto-reset!!) BUT
1580condition is not protected from starting another broadcast (when called
1581on another thread) while still waiting for the "old" broadcast to
1582complete on thread B1.
1583
1584M (>=0, <N) W threads are fast enough to go thru their _wait call and
1585decrement cv->waiters counter.
1586
1587L (N-M) "late" waiter W threads are a) still blocked/not returned from
1588their semaphore wait call or b) were preempted after sem_wait but before
1589lock( &cv->waitersLock ) or c) are blocked on cv->waitersLock.
1590
1591cv->waiters is still > 0 (= L).
1592
1593Another thread B2 (or some W thread from M group) calls
1594pthread_cond_broadcast and gains access to counter... neither a) nor b)
1595prevent thread B2 in pthread_cond_broadcast from gaining access to
1596counter and starting another broadcast ( for c) - it depends on
1597cv->waitersLock scheduling rules: FIFO=OK, PRTY=PROBLEM,... )
1598
1599That call to pthread_cond_broadcast (on thread B2) will result in
1600incrementing semaphore by cv->waiters (=L) which is INCORRECT (all
1601W1..N were in fact already released by thread B1) and waiting on
1602_auto-reset_ event cv->waitersDone which is DEADLY WRONG (produces a
1603deadlock)...
1604
1605All late W1..L threads now have a chance to complete their _wait call.
1606Last W_L thread sets an auto-reselt event cv->waitersDone which will
1607release either B1 or B2 leaving one of B threads in a deadlock.
1608
1609Problem 2: spur.wakeup/unfairness/incorrectness
1610
1611It seems that:
1612
1613a) because of the same problem with counter which does not reflect the
1614actual number of NOT RELEASED waiters, the signal call may increment
1615a semaphore counter w/o having a waiter blocked on it. That will result
1616in (best case) spurious wake ups - performance degradation due to
1617unnecessary context switches and predicate re-checks and (in worth case)
1618unfairness/incorrectness problem - see b)
1619
1620b) neither signal nor broadcast prevent other threads - "new waiters"
1621(and in the case of signal, the caller thread as well) from going into
1622_wait and overtaking "old" waiters (already released but still not returned
1623from sem_wait on condition's semaphore). Win semaphore just [API DOC]:
1624"Maintains a count between zero and some maximum value, limiting the number
1625of threads that are simultaneously accessing a shared resource." Calling
1626ReleaseSemaphore does not imply (at least not documented) that on return
1627from ReleaseSemaphore all waiters will in fact become released (returned
1628from their Wait... call) and/or that new waiters calling Wait... afterwards
1629will become less importance. It is NOT documented to be an atomic release
1630of
1631waiters... And even if it would be there is still a problem with a thread
1632being preempted after Wait on semaphore and before Wait on cv->waitersLock
1633and scheduling rules for cv->waitersLock itself
1634(??WaitForMultipleObjects??)
1635That may result in unfairness/incorrectness problem as described
1636for SetEvent impl. in "Strategies for Implementing POSIX Condition
1637Variables
1638on Win32": http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
1639
1640Unfairness -- The semantics of the POSIX pthread_cond_broadcast function is
1641to wake up all threads currently blocked in wait calls on the condition
1642variable. The awakened threads then compete for the external_mutex. To
1643ensure
1644fairness, all of these threads should be released from their
1645pthread_cond_wait calls and allowed to recheck their condition expressions
1646before other threads can successfully complete a wait on the condition
1647variable.
1648
1649Unfortunately, the SetEvent implementation above does not guarantee that
1650all
1651threads sleeping on the condition variable when cond_broadcast is called
1652will
1653acquire the external_mutex and check their condition expressions. Although
1654the Pthreads specification does not mandate this degree of fairness, the
1655lack of fairness can cause starvation.
1656
1657To illustrate the unfairness problem, imagine there are 2 threads, C1 and
1658C2,
1659that are blocked in pthread_cond_wait on condition variable not_empty_ that
1660is guarding a thread-safe message queue. Another thread, P1 then places two
1661messages onto the queue and calls pthread_cond_broadcast. If C1 returns
1662from
1663pthread_cond_wait, dequeues and processes the message, and immediately
1664waits
1665again then it and only it may end up acquiring both messages. Thus, C2 will
1666never get a chance to dequeue a message and run.
1667
1668The following illustrates the sequence of events:
1669
16701.   Thread C1 attempts to dequeue and waits on CV non_empty_
16712.   Thread C2 attempts to dequeue and waits on CV non_empty_
16723.   Thread P1 enqueues 2 messages and broadcasts to CV not_empty_
16734.   Thread P1 exits
16745.   Thread C1 wakes up from CV not_empty_, dequeues a message and runs
16756.   Thread C1 waits again on CV not_empty_, immediately dequeues the 2nd
1676        message and runs
16777.   Thread C1 exits
16788.   Thread C2 is the only thread left and blocks forever since
1679        not_empty_ will never be signaled
1680
1681Depending on the algorithm being implemented, this lack of fairness may
1682yield
1683concurrent programs that have subtle bugs. Of course, application
1684developers
1685should not rely on the fairness semantics of pthread_cond_broadcast.
1686However,
1687there are many cases where fair implementations of condition variables can
1688simplify application code.
1689
1690Incorrectness -- A variation on the unfairness problem described above
1691occurs
1692when a third consumer thread, C3, is allowed to slip through even though it
1693was not waiting on condition variable not_empty_ when a broadcast occurred.
1694
1695To illustrate this, we will use the same scenario as above: 2 threads, C1
1696and
1697C2, are blocked dequeuing messages from the message queue. Another thread,
1698P1
1699then places two messages onto the queue and calls pthread_cond_broadcast.
1700C1
1701returns from pthread_cond_wait, dequeues and processes the message. At this
1702time, C3 acquires the external_mutex, calls pthread_cond_wait and waits on
1703the events in WaitForMultipleObjects. Since C2 has not had a chance to run
1704yet, the BROADCAST event is still signaled. C3 then returns from
1705WaitForMultipleObjects, and dequeues and processes the message in the
1706queue.
1707Thus, C2 will never get a chance to dequeue a message and run.
1708
1709The following illustrates the sequence of events:
1710
17111.   Thread C1 attempts to dequeue and waits on CV non_empty_
17122.   Thread C2 attempts to dequeue and waits on CV non_empty_
17133.   Thread P1 enqueues 2 messages and broadcasts to CV not_empty_
17144.   Thread P1 exits
17155.   Thread C1 wakes up from CV not_empty_, dequeues a message and runs
17166.   Thread C1 exits
17177.   Thread C3 waits on CV not_empty_, immediately dequeues the 2nd
1718        message and runs
17198.   Thread C3 exits
17209.   Thread C2 is the only thread left and blocks forever since
1721        not_empty_ will never be signaled
1722
1723In the above case, a thread that was not waiting on the condition variable
1724when a broadcast occurred was allowed to proceed. This leads to incorrect
1725semantics for a condition variable.
1726
1727
1728COMMENTS???
1729
1730regards,
1731alexander.
1732
1733-----------------------------------------------------------------------------
1734
1735Subject: RE: FYI/comp.programming.threads/Re: pthread_cond_*
1736     implementation questions
1737Date: Wed, 21 Feb 2001 11:54:47 +0100
1738From: TEREKHOV@de.ibm.com
1739To: lthomas@arbitrade.com
1740CC: rpj@ise.canberra.edu.au, Thomas Pfaff <tpfaff@gmx.net>,
1741     Nanbor Wang <nanbor@cs.wustl.edu>
1742
1743Hi Louis,
1744
1745generation number 8..
1746
1747had some time to revisit timeouts/spurious wakeup problem..
1748found some bugs (in 7.b/c/d) and something to improve
1749(7a - using IPC semaphores but it should speedup Win32
1750version as well).
1751
1752regards,
1753alexander.
1754
1755---------- Algorithm 8a / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL ------
1756given:
1757semBlockLock - bin.semaphore
1758semBlockQueue - semaphore
1759mtxExternal - mutex or CS
1760mtxUnblockLock - mutex or CS
1761nWaitersGone - int
1762nWaitersBlocked - int
1763nWaitersToUnblock - int
1764
1765wait( timeout ) {
1766
1767  [auto: register int result          ]     // error checking omitted
1768  [auto: register int nSignalsWasLeft ]
1769  [auto: register int nWaitersWasGone ]
1770
1771  sem_wait( semBlockLock );
1772  nWaitersBlocked++;
1773  sem_post( semBlockLock );
1774
1775  unlock( mtxExternal );
1776  bTimedOut = sem_wait( semBlockQueue,timeout );
1777
1778  lock( mtxUnblockLock );
1779  if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
1780    if ( bTimeout ) {                       // timeout (or canceled)
1781      if ( 0 != nWaitersBlocked ) {
1782        nWaitersBlocked--;
1783      }
1784      else {
1785        nWaitersGone++;                     // count spurious wakeups
1786      }
1787    }
1788    if ( 0 == --nWaitersToUnblock ) {
1789      if ( 0 != nWaitersBlocked ) {
1790        sem_post( semBlockLock );           // open the gate
1791        nSignalsWasLeft = 0;                // do not open the gate below
1792again
1793      }
1794      else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {
1795        nWaitersGone = 0;
1796      }
1797    }
1798  }
1799  else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious
1800semaphore :-)
1801    sem_wait( semBlockLock );
1802    nWaitersBlocked -= nWaitersGone;        // something is going on here -
1803test of timeouts? :-)
1804    sem_post( semBlockLock );
1805    nWaitersGone = 0;
1806  }
1807  unlock( mtxUnblockLock );
1808
1809  if ( 1 == nSignalsWasLeft ) {
1810    if ( 0 != nWaitersWasGone ) {
1811      // sem_adjust( -nWaitersWasGone );
1812      while ( nWaitersWasGone-- ) {
1813        sem_wait( semBlockLock );          // better now than spurious
1814later
1815      }
1816    }
1817    sem_post( semBlockLock );              // open the gate
1818  }
1819
1820  lock( mtxExternal );
1821
1822  return ( bTimedOut ) ? ETIMEOUT : 0;
1823}
1824
1825signal(bAll) {
1826
1827  [auto: register int result         ]
1828  [auto: register int nSignalsToIssue]
1829
1830  lock( mtxUnblockLock );
1831
1832  if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
1833    if ( 0 == nWaitersBlocked ) { // NO-OP
1834      return unlock( mtxUnblockLock );
1835    }
1836    if (bAll) {
1837      nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;
1838      nWaitersBlocked = 0;
1839    }
1840    else {
1841      nSignalsToIssue = 1;
1842      nWaitersToUnblock++;
1843      nWaitersBlocked--;
1844    }
1845  }
1846  else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
1847    sem_wait( semBlockLock ); // close the gate
1848    if ( 0 != nWaitersGone ) {
1849      nWaitersBlocked -= nWaitersGone;
1850      nWaitersGone = 0;
1851    }
1852    if (bAll) {
1853      nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;
1854      nWaitersBlocked = 0;
1855    }
1856    else {
1857      nSignalsToIssue = nWaitersToUnblock = 1;
1858      nWaitersBlocked--;
1859    }
1860  }
1861  else { // NO-OP
1862    return unlock( mtxUnblockLock );
1863  }
1864
1865  unlock( mtxUnblockLock );
1866  sem_post( semBlockQueue,nSignalsToIssue );
1867  return result;
1868}
1869
1870---------- Algorithm 8b / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ONEBYONE
1871------
1872given:
1873semBlockLock - bin.semaphore
1874semBlockQueue - bin.semaphore
1875mtxExternal - mutex or CS
1876mtxUnblockLock - mutex or CS
1877nWaitersGone - int
1878nWaitersBlocked - int
1879nWaitersToUnblock - int
1880
1881wait( timeout ) {
1882
1883  [auto: register int result          ]     // error checking omitted
1884  [auto: register int nWaitersWasGone ]
1885  [auto: register int nSignalsWasLeft ]
1886
1887  sem_wait( semBlockLock );
1888  nWaitersBlocked++;
1889  sem_post( semBlockLock );
1890
1891  unlock( mtxExternal );
1892  bTimedOut = sem_wait( semBlockQueue,timeout );
1893
1894  lock( mtxUnblockLock );
1895  if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
1896    if ( bTimeout ) {                       // timeout (or canceled)
1897      if ( 0 != nWaitersBlocked ) {
1898        nWaitersBlocked--;
1899        nSignalsWasLeft = 0;                // do not unblock next waiter
1900below (already unblocked)
1901      }
1902      else {
1903        nWaitersGone = 1;                   // spurious wakeup pending!!
1904      }
1905    }
1906    if ( 0 == --nWaitersToUnblock &&
1907      if ( 0 != nWaitersBlocked ) {
1908        sem_post( semBlockLock );           // open the gate
1909        nSignalsWasLeft = 0;                // do not open the gate below
1910again
1911      }
1912      else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {
1913        nWaitersGone = 0;
1914      }
1915    }
1916  }
1917  else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious
1918semaphore :-)
1919    sem_wait( semBlockLock );
1920    nWaitersBlocked -= nWaitersGone;        // something is going on here -
1921test of timeouts? :-)
1922    sem_post( semBlockLock );
1923    nWaitersGone = 0;
1924  }
1925  unlock( mtxUnblockLock );
1926
1927  if ( 1 == nSignalsWasLeft ) {
1928    if ( 0 != nWaitersWasGone ) {
1929      // sem_adjust( -1 );
1930      sem_wait( semBlockQueue );           // better now than spurious
1931later
1932    }
1933    sem_post( semBlockLock );              // open the gate
1934  }
1935  else if ( 0 != nSignalsWasLeft ) {
1936    sem_post( semBlockQueue );             // unblock next waiter
1937  }
1938
1939  lock( mtxExternal );
1940
1941  return ( bTimedOut ) ? ETIMEOUT : 0;
1942}
1943
1944signal(bAll) {
1945
1946  [auto: register int result ]
1947
1948  lock( mtxUnblockLock );
1949
1950  if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
1951    if ( 0 == nWaitersBlocked ) { // NO-OP
1952      return unlock( mtxUnblockLock );
1953    }
1954    if (bAll) {
1955      nWaitersToUnblock += nWaitersBlocked;
1956      nWaitersBlocked = 0;
1957    }
1958    else {
1959      nWaitersToUnblock++;
1960      nWaitersBlocked--;
1961    }
1962    unlock( mtxUnblockLock );
1963  }
1964  else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
1965    sem_wait( semBlockLock ); // close the gate
1966    if ( 0 != nWaitersGone ) {
1967      nWaitersBlocked -= nWaitersGone;
1968      nWaitersGone = 0;
1969    }
1970    if (bAll) {
1971      nWaitersToUnblock = nWaitersBlocked;
1972      nWaitersBlocked = 0;
1973    }
1974    else {
1975      nWaitersToUnblock = 1;
1976      nWaitersBlocked--;
1977    }
1978    unlock( mtxUnblockLock );
1979    sem_post( semBlockQueue );
1980  }
1981  else { // NO-OP
1982    unlock( mtxUnblockLock );
1983  }
1984
1985  return result;
1986}
1987
1988---------- Algorithm 8c / IMPL_EVENT,UNBLOCK_STRATEGY == UNBLOCK_ONEBYONE
1989---------
1990given:
1991hevBlockLock - auto-reset event
1992hevBlockQueue - auto-reset event
1993mtxExternal - mutex or CS
1994mtxUnblockLock - mutex or CS
1995nWaitersGone - int
1996nWaitersBlocked - int
1997nWaitersToUnblock - int
1998
1999wait( timeout ) {
2000
2001  [auto: register int result          ]     // error checking omitted
2002  [auto: register int nSignalsWasLeft ]
2003  [auto: register int nWaitersWasGone ]
2004
2005  wait( hevBlockLock,INFINITE );
2006  nWaitersBlocked++;
2007  set_event( hevBlockLock );
2008
2009  unlock( mtxExternal );
2010  bTimedOut = wait( hevBlockQueue,timeout );
2011
2012  lock( mtxUnblockLock );
2013  if ( 0 != (SignalsWasLeft = nWaitersToUnblock) ) {
2014    if ( bTimeout ) {                       // timeout (or canceled)
2015      if ( 0 != nWaitersBlocked ) {
2016        nWaitersBlocked--;
2017        nSignalsWasLeft = 0;                // do not unblock next waiter
2018below (already unblocked)
2019      }
2020      else {
2021        nWaitersGone = 1;                   // spurious wakeup pending!!
2022      }
2023    }
2024    if ( 0 == --nWaitersToUnblock )
2025      if ( 0 != nWaitersBlocked ) {
2026        set_event( hevBlockLock );          // open the gate
2027        nSignalsWasLeft = 0;                // do not open the gate below
2028again
2029      }
2030      else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {
2031        nWaitersGone = 0;
2032      }
2033    }
2034  }
2035  else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious
2036event :-)
2037    wait( hevBlockLock,INFINITE );
2038    nWaitersBlocked -= nWaitersGone;        // something is going on here -
2039test of timeouts? :-)
2040    set_event( hevBlockLock );
2041    nWaitersGone = 0;
2042  }
2043  unlock( mtxUnblockLock );
2044
2045  if ( 1 == nSignalsWasLeft ) {
2046    if ( 0 != nWaitersWasGone ) {
2047      reset_event( hevBlockQueue );         // better now than spurious
2048later
2049    }
2050    set_event( hevBlockLock );              // open the gate
2051  }
2052  else if ( 0 != nSignalsWasLeft ) {
2053    set_event( hevBlockQueue );             // unblock next waiter
2054  }
2055
2056  lock( mtxExternal );
2057
2058  return ( bTimedOut ) ? ETIMEOUT : 0;
2059}
2060
2061signal(bAll) {
2062
2063  [auto: register int result ]
2064
2065  lock( mtxUnblockLock );
2066
2067  if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
2068    if ( 0 == nWaitersBlocked ) { // NO-OP
2069      return unlock( mtxUnblockLock );
2070    }
2071    if (bAll) {
2072      nWaitersToUnblock += nWaitersBlocked;
2073      nWaitersBlocked = 0;
2074    }
2075    else {
2076      nWaitersToUnblock++;
2077      nWaitersBlocked--;
2078    }
2079    unlock( mtxUnblockLock );
2080  }
2081  else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
2082    wait( hevBlockLock,INFINITE ); // close the gate
2083    if ( 0 != nWaitersGone ) {
2084      nWaitersBlocked -= nWaitersGone;
2085      nWaitersGone = 0;
2086    }
2087    if (bAll) {
2088      nWaitersToUnblock = nWaitersBlocked;
2089      nWaitersBlocked = 0;
2090    }
2091    else {
2092      nWaitersToUnblock = 1;
2093      nWaitersBlocked--;
2094    }
2095    unlock( mtxUnblockLock );
2096    set_event( hevBlockQueue );
2097  }
2098  else { // NO-OP
2099    unlock( mtxUnblockLock );
2100  }
2101
2102  return result;
2103}
2104
2105---------- Algorithm 8d / IMPL_EVENT,UNBLOCK_STRATEGY == UNBLOCK_ALL ------
2106given:
2107hevBlockLock - auto-reset event
2108hevBlockQueueS - auto-reset event  // for signals
2109hevBlockQueueB - manual-reset even // for broadcasts
2110mtxExternal - mutex or CS
2111mtxUnblockLock - mutex or CS
2112eBroadcast - int                   // 0: no broadcast, 1: broadcast, 2:
2113broadcast after signal(s)
2114nWaitersGone - int
2115nWaitersBlocked - int
2116nWaitersToUnblock - int
2117
2118wait( timeout ) {
2119
2120  [auto: register int result          ]     // error checking omitted
2121  [auto: register int eWasBroadcast   ]
2122  [auto: register int nSignalsWasLeft ]
2123  [auto: register int nWaitersWasGone ]
2124
2125  wait( hevBlockLock,INFINITE );
2126  nWaitersBlocked++;
2127  set_event( hevBlockLock );
2128
2129  unlock( mtxExternal );
2130  bTimedOut = waitformultiple( hevBlockQueueS,hevBlockQueueB,timeout,ONE );
2131
2132  lock( mtxUnblockLock );
2133  if ( 0 != (SignalsWasLeft = nWaitersToUnblock) ) {
2134    if ( bTimeout ) {                       // timeout (or canceled)
2135      if ( 0 != nWaitersBlocked ) {
2136        nWaitersBlocked--;
2137        nSignalsWasLeft = 0;                // do not unblock next waiter
2138below (already unblocked)
2139      }
2140      else if ( 1 != eBroadcast ) {
2141        nWaitersGone = 1;
2142      }
2143    }
2144    if ( 0 == --nWaitersToUnblock ) {
2145      if ( 0 != nWaitersBlocked ) {
2146        set_event( hevBlockLock );           // open the gate
2147        nSignalsWasLeft = 0;                 // do not open the gate below
2148again
2149      }
2150      else {
2151        if ( 0 != (eWasBroadcast = eBroadcast) ) {
2152          eBroadcast = 0;
2153        }
2154        if ( 0 != (nWaitersWasGone = nWaitersGone ) {
2155          nWaitersGone = 0;
2156        }
2157      }
2158    }
2159    else if ( 0 != eBroadcast ) {
2160      nSignalsWasLeft = 0;                  // do not unblock next waiter
2161below (already unblocked)
2162    }
2163  }
2164  else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious
2165event :-)
2166    wait( hevBlockLock,INFINITE );
2167    nWaitersBlocked -= nWaitersGone;        // something is going on here -
2168test of timeouts? :-)
2169    set_event( hevBlockLock );
2170    nWaitersGone = 0;
2171  }
2172  unlock( mtxUnblockLock );
2173
2174  if ( 1 == nSignalsWasLeft ) {
2175    if ( 0 != eWasBroadcast ) {
2176      reset_event( hevBlockQueueB );
2177    }
2178    if ( 0 != nWaitersWasGone ) {
2179      reset_event( hevBlockQueueS );        // better now than spurious
2180later
2181    }
2182    set_event( hevBlockLock );              // open the gate
2183  }
2184  else if ( 0 != nSignalsWasLeft ) {
2185    set_event( hevBlockQueueS );            // unblock next waiter
2186  }
2187
2188  lock( mtxExternal );
2189
2190  return ( bTimedOut ) ? ETIMEOUT : 0;
2191}
2192
2193signal(bAll) {
2194
2195  [auto: register int    result        ]
2196  [auto: register HANDLE hevBlockQueue ]
2197
2198  lock( mtxUnblockLock );
2199
2200  if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
2201    if ( 0 == nWaitersBlocked ) { // NO-OP
2202      return unlock( mtxUnblockLock );
2203    }
2204    if (bAll) {
2205      nWaitersToUnblock += nWaitersBlocked;
2206      nWaitersBlocked = 0;
2207      eBroadcast = 2;
2208      hevBlockQueue = hevBlockQueueB;
2209    }
2210    else {
2211      nWaitersToUnblock++;
2212      nWaitersBlocked--;
2213      return unlock( mtxUnblockLock );
2214    }
2215  }
2216  else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
2217    wait( hevBlockLock,INFINITE ); // close the gate
2218    if ( 0 != nWaitersGone ) {
2219      nWaitersBlocked -= nWaitersGone;
2220      nWaitersGone = 0;
2221    }
2222    if (bAll) {
2223      nWaitersToUnblock = nWaitersBlocked;
2224      nWaitersBlocked = 0;
2225      eBroadcast = 1;
2226      hevBlockQueue = hevBlockQueueB;
2227    }
2228    else {
2229      nWaitersToUnblock = 1;
2230      nWaitersBlocked--;
2231      hevBlockQueue = hevBlockQueueS;
2232    }
2233  }
2234  else { // NO-OP
2235    return unlock( mtxUnblockLock );
2236  }
2237
2238  unlock( mtxUnblockLock );
2239  set_event( hevBlockQueue );
2240  return result;
2241}
2242---------------------- Forwarded by Alexander Terekhov/Germany/IBM on
224302/21/2001 09:13 AM ---------------------------
2244
2245Alexander Terekhov
224602/20/2001 04:33 PM
2247
2248To:   Louis Thomas <lthomas@arbitrade.com>
2249cc:
2250
2251From: Alexander Terekhov/Germany/IBM@IBMDE
2252Subject:  RE: FYI/comp.programming.threads/Re: pthread_cond_* implementatio
2253      n questions
2254Importance:    Normal
2255
2256>Sorry, gotta take a break and work on something else for a while.
2257>Real work
2258>calls, unfortunately. I'll get back to you in two or three days.
2259
2260ok. no problem. here is some more stuff for pauses you might have
2261in between :)
2262
2263---------- Algorithm 7d / IMPL_EVENT,UNBLOCK_STRATEGY == UNBLOCK_ALL ------
2264given:
2265hevBlockLock - auto-reset event
2266hevBlockQueueS - auto-reset event  // for signals
2267hevBlockQueueB - manual-reset even // for broadcasts
2268mtxExternal - mutex or CS
2269mtxUnblockLock - mutex or CS
2270bBroadcast - int
2271nWaitersGone - int
2272nWaitersBlocked - int
2273nWaitersToUnblock - int
2274
2275wait( timeout ) {
2276
2277  [auto: register int result          ]     // error checking omitted
2278  [auto: register int bWasBroadcast   ]
2279  [auto: register int nSignalsWasLeft ]
2280
2281  wait( hevBlockLock,INFINITE );
2282  nWaitersBlocked++;
2283  set_event( hevBlockLock );
2284
2285  unlock( mtxExternal );
2286  bTimedOut = waitformultiple( hevBlockQueueS,hevBlockQueueB,timeout,ONE );
2287
2288  lock( mtxUnblockLock );
2289  if ( 0 != (SignalsWasLeft = nWaitersToUnblock) ) {
2290    if ( bTimeout ) {                       // timeout (or canceled)
2291      if ( 0 != nWaitersBlocked ) {
2292        nWaitersBlocked--;
2293        nSignalsWasLeft = 0;                // do not unblock next waiter
2294below (already unblocked)
2295      }
2296      else if ( !bBroadcast ) {
2297        wait( hevBlockQueueS,INFINITE );    // better now than spurious
2298later
2299      }
2300    }
2301    if ( 0 == --nWaitersToUnblock ) {
2302      if ( 0 != nWaitersBlocked ) {
2303        if ( bBroadcast ) {
2304          reset_event( hevBlockQueueB );
2305          bBroadcast = false;
2306        }
2307        set_event( hevBlockLock );           // open the gate
2308        nSignalsWasLeft = 0;                 // do not open the gate below
2309again
2310      }
2311      else if ( false != (bWasBroadcast = bBroadcast) ) {
2312        bBroadcast = false;
2313      }
2314    }
2315    else {
2316      bWasBroadcast = bBroadcast;
2317    }
2318  }
2319  else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious
2320event :-)
2321    wait( hevBlockLock,INFINITE );
2322    nWaitersBlocked -= nWaitersGone;        // something is going on here -
2323test of timeouts? :-)
2324    set_event( hevBlockLock );
2325    nWaitersGone = 0;
2326  }
2327  unlock( mtxUnblockLock );
2328
2329  if ( 1 == nSignalsWasLeft ) {
2330    if ( bWasBroadcast ) {
2331      reset_event( hevBlockQueueB );
2332    }
2333    set_event( hevBlockLock );              // open the gate
2334  }
2335  else if ( 0 != nSignalsWasLeft && !bWasBroadcast ) {
2336    set_event( hevBlockQueueS );            // unblock next waiter
2337  }
2338
2339  lock( mtxExternal );
2340
2341  return ( bTimedOut ) ? ETIMEOUT : 0;
2342}
2343
2344signal(bAll) {
2345
2346  [auto: register int    result        ]
2347  [auto: register HANDLE hevBlockQueue ]
2348
2349  lock( mtxUnblockLock );
2350
2351  if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
2352    if ( 0 == nWaitersBlocked ) { // NO-OP
2353      return unlock( mtxUnblockLock );
2354    }
2355    if (bAll) {
2356      nWaitersToUnblock += nWaitersBlocked;
2357      nWaitersBlocked = 0;
2358      bBroadcast = true;
2359      hevBlockQueue = hevBlockQueueB;
2360    }
2361    else {
2362      nWaitersToUnblock++;
2363      nWaitersBlocked--;
2364      return unlock( mtxUnblockLock );
2365    }
2366  }
2367  else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
2368    wait( hevBlockLock,INFINITE ); // close the gate
2369    if ( 0 != nWaitersGone ) {
2370      nWaitersBlocked -= nWaitersGone;
2371      nWaitersGone = 0;
2372    }
2373    if (bAll) {
2374      nWaitersToUnblock = nWaitersBlocked;
2375      nWaitersBlocked = 0;
2376      bBroadcast = true;
2377      hevBlockQueue = hevBlockQueueB;
2378    }
2379    else {
2380      nWaitersToUnblock = 1;
2381      nWaitersBlocked--;
2382      hevBlockQueue = hevBlockQueueS;
2383    }
2384  }
2385  else { // NO-OP
2386    return unlock( mtxUnblockLock );
2387  }
2388
2389  unlock( mtxUnblockLock );
2390  set_event( hevBlockQueue );
2391  return result;
2392}
2393
2394
2395----------------------------------------------------------------------------
2396
2397Subject: RE: FYI/comp.programming.threads/Re: pthread_cond_* implementatio
2398     n questions
2399Date: Mon, 26 Feb 2001 22:20:12 -0600
2400From: Louis Thomas <lthomas@arbitrade.com>
2401To: "'TEREKHOV@de.ibm.com'" <TEREKHOV@de.ibm.com>
2402CC: rpj@ise.canberra.edu.au, Thomas Pfaff <tpfaff@gmx.net>,
2403     Nanbor Wang
2404     <nanbor@cs.wustl.edu>
2405
2406Sorry all. Busy week.
2407
2408> this insures the fairness
2409> which POSIX does not (e.g. two subsequent broadcasts - the gate does
2410insure
2411> that first wave waiters will start the race for the mutex before waiters
2412> from the second wave - Linux pthreads process/unblock both waves
2413> concurrently...)
2414
2415I'm not sure how we are any more fair about this than Linux. We certainly
2416don't guarantee that the threads released by the first broadcast will get
2417the external mutex before the threads of the second wave. In fact, it is
2418possible that those threads will never get the external mutex if there is
2419enough contention for it.
2420
2421> e.g. i was thinking about implementation with a pool of
2422> N semaphores/counters [...]
2423
2424I considered that too. The problem is as you mentioned in a). You really
2425need to assign threads to semaphores once you know how you want to wake them
2426up, not when they first begin waiting which is the only time you can assign
2427them.
2428
2429> well, i am not quite sure that i've fully understood your scenario,
2430
2431Hmm. Well, it think it's an important example, so I'll try again. First, we
2432have thread A which we KNOW is waiting on a condition. As soon as it becomes
2433unblocked for any reason, we will know because it will set a flag. Since the
2434flag is not set, we are 100% confident that thread A is waiting on the
2435condition. We have another thread, thread B, which has acquired the mutex
2436and is about to wait on the condition. Thus it is pretty clear that at any
2437point, either just A is waiting, or A and B are waiting. Now thread C comes
2438along. C is about to do a broadcast on the condition. A broadcast is
2439guaranteed to unblock all threads currently waiting on a condition, right?
2440Again, we said that either just A is waiting, or A and B are both waiting.
2441So, when C does its broadcast, depending upon whether B has started waiting
2442or not, thread C will unblock A or unblock A and B. Either way, C must
2443unblock A, right?
2444
2445Now, you said anything that happens is correct so long as a) "a signal is
2446not lost between unlocking the mutex and waiting on the condition" and b) "a
2447thread must not steal a signal it sent", correct? Requirement b) is easy to
2448satisfy: in this scenario, thread C will never wait on the condition, so it
2449won't steal any signals.  Requirement a) is not hard either. The only way we
2450could fail to meet requirement a) in this scenario is if thread B was
2451started waiting but didn't wake up because a signal was lost. This will not
2452happen.
2453
2454Now, here is what happens. Assume thread C beats thread B. Thread C looks to
2455see how many threads are waiting on the condition. Thread C sees just one
2456thread, thread A, waiting. It does a broadcast waking up just one thread
2457because just one thread is waiting. Next, before A can become unblocked,
2458thread B begins waiting. Now there are two threads waiting, but only one
2459will be unblocked. Suppose B wins. B will become unblocked. A will not
2460become unblocked, because C only unblocked one thread (sema_post cond, 1).
2461So at the end, B finishes and A remains blocked.
2462
2463We have met both of your requirements, so by your rules, this is an
2464acceptable outcome. However, I think that the spec says this is an
2465unacceptable outcome! We know for certain that A was waiting and that C did
2466a broadcast, but A did not become unblocked! Yet, the spec says that a
2467broadcast wakes up all waiting threads. This did not happen. Do you agree
2468that this shows your rules are not strict enough?
2469
2470> and what about N2? :) this one does allow almost everything.
2471
2472Don't get me started about rule #2. I'll NEVER advocate an algorithm that
2473uses rule 2 as an excuse to suck!
2474
2475> but it is done (decrement)under mutex protection - this is not a subject
2476> of a race condition.
2477
2478You are correct. My mistake.
2479
2480> i would remove "_bTimedOut=false".. after all, it was a real timeout..
2481
2482I disagree. A thread that can't successfully retract its waiter status can't
2483really have timed out. If a thread can't return without executing extra code
2484to deal with the fact that someone tried to unblock it, I think it is a poor
2485idea to pretend we
2486didn't realize someone was trying to signal us. After all, a signal is more
2487important than a time out.
2488
2489> when nSignaled != 0, it is possible to update nWaiters (--) and do not
2490> touch nGone
2491
2492I realize this, but I was thinking that writing it the other ways saves
2493another if statement.
2494
2495> adjust only if nGone != 0 and save one cache memory write - probably much
2496slower than 'if'
2497
2498Hmm. You are probably right.
2499
2500> well, in a strange (e.g. timeout test) program you may (theoretically)
2501> have an overflow of nWaiters/nGone counters (with waiters repeatedly
2502timing
2503> out and no signals at all).
2504
2505Also true. Not only that, but you also have the possibility that one could
2506overflow the number of waiters as well! However, considering the limit you
2507have chosen for nWaitersGone, I suppose it is unlikely that anyone would be
2508able to get INT_MAX/2 threads waiting on a single condition. :)
2509
2510Analysis of 8a:
2511
2512It looks correct to me.
2513
2514What are IPC semaphores?
2515
2516In the line where you state, "else if ( nWaitersBlocked > nWaitersGone ) {
2517// HARMLESS RACE CONDITION!" there is no race condition for nWaitersGone
2518because nWaitersGone is never modified without holding mtxUnblockLock. You
2519are correct that there is a harmless race on nWaitersBlocked, which can
2520increase and make the condition become true just after we check it. If this
2521happens, we interpret it as the wait starting after the signal.
2522
2523I like your optimization of this. You could improve Alg. 6 as follows:
2524---------- Algorithm 6b ----------
2525signal(bAll) {
2526  _nSig=0
2527  lock counters
2528  // this is safe because nWaiting can only be decremented by a thread that
2529  // owns counters and nGone can only be changed by a thread that owns
2530counters.
2531  if (nWaiting>nGone) {
2532    if (0==nSignaled) {
2533      sema_wait gate // close gate if not already closed
2534    }
2535    if (nGone>0) {
2536      nWaiting-=nGone
2537      nGone=0
2538    }
2539    _nSig=bAll?nWaiting:1
2540    nSignaled+=_nSig
2541    nWaiting-=_nSig
2542  }
2543  unlock counters
2544  if (0!=_nSig) {
2545    sema_post queue, _nSig
2546  }
2547}
2548---------- ---------- ----------
2549I guess this wouldn't apply to Alg 8a because nWaitersGone changes meanings
2550depending upon whether the gate is open or closed.
2551
2552In the loop "while ( nWaitersWasGone-- ) {" you do a sema_wait on
2553semBlockLock. Perhaps waiting on semBlockQueue would be a better idea.
2554
2555What have you gained by making the last thread to be signaled do the waits
2556for all the timed out threads, besides added complexity? It took me a long
2557time to figure out what your objective was with this, to realize you were
2558using nWaitersGone to mean two different things, and to verify that you
2559hadn't introduced any bug by doing this. Even now I'm not 100% sure.
2560
2561What has all this playing about with nWaitersGone really gained us besides a
2562lot of complexity (it is much harder to verify that this solution is
2563correct), execution overhead (we now have a lot more if statements to
2564evaluate), and space overhead (more space for the extra code, and another
2565integer in our data)? We did manage to save a lock/unlock pair in an
2566uncommon case (when a time out occurs) at the above mentioned expenses in
2567the common cases.
2568
2569As for 8b, c, and d, they look ok though I haven't studied them thoroughly.
2570What would you use them for?
2571
2572    Later,
2573        -Louis! :)
2574
2575-----------------------------------------------------------------------------
2576
2577Subject: RE: FYI/comp.programming.threads/Re: pthread_cond_* implementatio
2578     n questions
2579Date: Tue, 27 Feb 2001 15:51:28 +0100
2580From: TEREKHOV@de.ibm.com
2581To: Louis Thomas <lthomas@arbitrade.com>
2582CC: rpj@ise.canberra.edu.au, Thomas Pfaff <tpfaff@gmx.net>,
2583     Nanbor Wang <nanbor@cs.wustl.edu>
2584
2585Hi Louis,
2586
2587>> that first wave waiters will start the race for the mutex before waiters
2588>> from the second wave - Linux pthreads process/unblock both waves
2589>> concurrently...)
2590>
2591>I'm not sure how we are any more fair about this than Linux. We certainly
2592>don't guarantee that the threads released by the first broadcast will get
2593>the external mutex before the threads of the second wave. In fact, it is
2594>possible that those threads will never get the external mutex if there is
2595>enough contention for it.
2596
2597correct. but gate is nevertheless more fair than Linux because of the
2598barrier it establishes between two races (1st and 2nd wave waiters) for
2599the mutex which under 'normal' circumstances (e.g. all threads of equal
2600priorities,..) will 'probably' result in fair behaviour with respect to
2601mutex ownership.
2602
2603>> well, i am not quite sure that i've fully understood your scenario,
2604>
2605>Hmm. Well, it think it's an important example, so I'll try again. ...
2606
2607ok. now i seem to understand this example. well, now it seems to me
2608that the only meaningful rule is just:
2609
2610a) "a signal is not lost between unlocking the mutex and waiting on the
2611condition"
2612
2613and that the rule
2614
2615b) "a thread must not steal a signal it sent"
2616
2617is not needed at all because a thread which violates b) also violates a).
2618
2619i'll try to explain..
2620
2621i think that the most important thing is how POSIX defines waiter's
2622visibility:
2623
2624"if another thread is able to acquire the mutex after the about-to-block
2625thread
2626has released it, then a subsequent call to pthread_cond_signal() or
2627pthread_cond_broadcast() in that thread behaves as if it were issued after
2628the about-to-block thread has blocked. "
2629
2630my understanding is the following:
2631
26321) there is no guarantees whatsoever with respect to whether
2633signal/broadcast
2634will actually unblock any 'waiter' if it is done w/o acquiring the mutex
2635first
2636(note that a thread may release it before signal/broadcast - it does not
2637matter).
2638
26392) it is guaranteed that waiters become 'visible' - eligible for unblock as
2640soon
2641as signalling thread acquires the mutex (but not before!!)
2642
2643so..
2644
2645>So, when C does its broadcast, depending upon whether B has started
2646waiting
2647>or not, thread C will unblock A or unblock A and B. Either way, C must
2648>unblock A, right?
2649
2650right. but only if C did acquire the mutex prior to broadcast (it may
2651release it before broadcast as well).
2652
2653implementation will violate waiters visibility rule (signal will become
2654lost)
2655if C will not unblock A.
2656
2657>Now, here is what happens. Assume thread C beats thread B. Thread C looks
2658to
2659>see how many threads are waiting on the condition. Thread C sees just one
2660>thread, thread A, waiting. It does a broadcast waking up just one thread
2661>because just one thread is waiting. Next, before A can become unblocked,
2662>thread B begins waiting. Now there are two threads waiting, but only one
2663>will be unblocked. Suppose B wins. B will become unblocked. A will not
2664>become unblocked, because C only unblocked one thread (sema_post cond, 1).
2665>So at the end, B finishes and A remains blocked.
2666
2667thread C did acquire the mutex ("Thread C sees just one thread, thread A,
2668waiting"). beginning from that moment it is guaranteed that subsequent
2669broadcast will unblock A. Otherwise we will have a lost signal with respect
2670to A. I do think that it does not matter whether the signal was physically
2671(completely) lost or was just stolen by another thread (B) - in both cases
2672it was simply lost with respect to A.
2673
2674>..Do you agree that this shows your rules are not strict enough?
2675
2676probably the opposite.. :-) i think that it shows that the only meaningful
2677rule is
2678
2679a) "a signal is not lost between unlocking the mutex and waiting on the
2680condition"
2681
2682with clarification of waiters visibility as defined by POSIX above.
2683
2684>> i would remove "_bTimedOut=false".. after all, it was a real timeout..
2685>
2686>I disagree. A thread that can't successfully retract its waiter status
2687can't
2688>really have timed out. If a thread can't return without executing extra
2689code
2690>to deal with the fact that someone tried to unblock it, I think it is a
2691poor
2692>idea to pretend we
2693>didn't realize someone was trying to signal us. After all, a signal is
2694more
2695>important than a time out.
2696
2697a) POSIX does allow timed out thread to consume a signal (cancelled is
2698not).
2699b) ETIMEDOUT status just says that: "The time specified by abstime to
2700pthread_cond_timedwait() has passed."
2701c) it seem to me that hiding timeouts would violate "The
2702pthread_cond_timedwait()
2703function is the same as pthread_cond_wait() except that an error is
2704returned if
2705the absolute time specified by abstime passes (that is, system time equals
2706or
2707exceeds abstime) before the condition cond is signaled or broadcasted"
2708because
2709the abs. time did really pass before cond was signaled (waiter was
2710released via semaphore). however, if it really matters, i could imaging
2711that we
2712can save an abs. time of signal/broadcast and compare it with timeout after
2713unblock to find out whether it was a 'real' timeout or not. absent this
2714check
2715i do think that hiding timeouts would result in technical violation of
2716specification.. but i think that this check is not important and we can
2717simply
2718trust timeout error code provided by wait since we are not trying to make
2719'hard' realtime implementation.
2720
2721>What are IPC semaphores?
2722
2723<sys/sem.h>
2724int   semctl(int, int, int, ...);
2725int   semget(key_t, int, int);
2726int   semop(int, struct sembuf *, size_t);
2727
2728they support adjustment of semaphore counter (semvalue)
2729in one single call - imaging Win32 ReleaseSemaphore( hsem,-N )
2730
2731>In the line where you state, "else if ( nWaitersBlocked > nWaitersGone ) {
2732>// HARMLESS RACE CONDITION!" there is no race condition for nWaitersGone
2733>because nWaitersGone is never modified without holding mtxUnblockLock. You
2734>are correct that there is a harmless race on nWaitersBlocked, which can
2735>increase and make the condition become true just after we check it. If
2736this
2737>happens, we interpret it as the wait starting after the signal.
2738
2739well, the reason why i've asked on comp.programming.threads whether this
2740race
2741condition is harmless or not is that in order to be harmless it should not
2742violate the waiters visibility rule (see above). Fortunately, we increment
2743the counter under protection of external mutex.. so that any (signalling)
2744thread which will acquire the mutex next, should see the updated counter
2745(in signal) according to POSIX memory visibility rules and mutexes
2746(memory barriers). But i am not so sure how it actually works on
2747Win32/INTEL
2748which does not explicitly define any memory visibility rules :(
2749
2750>I like your optimization of this. You could improve Alg. 6 as follows:
2751>---------- Algorithm 6b ----------
2752>signal(bAll) {
2753>  _nSig=0
2754>  lock counters
2755>  // this is safe because nWaiting can only be decremented by a thread
2756that
2757>  // owns counters and nGone can only be changed by a thread that owns
2758>counters.
2759>  if (nWaiting>nGone) {
2760>    if (0==nSignaled) {
2761>      sema_wait gate // close gate if not already closed
2762>    }
2763>    if (nGone>0) {
2764>      nWaiting-=nGone
2765>      nGone=0
2766>    }
2767>    _nSig=bAll?nWaiting:1
2768>    nSignaled+=_nSig
2769>    nWaiting-=_nSig
2770>  }
2771>  unlock counters
2772>  if (0!=_nSig) {
2773>    sema_post queue, _nSig
2774>  }
2775>}
2776>---------- ---------- ----------
2777>I guess this wouldn't apply to Alg 8a because nWaitersGone changes
2778meanings
2779>depending upon whether the gate is open or closed.
2780
2781agree.
2782
2783>In the loop "while ( nWaitersWasGone-- ) {" you do a sema_wait on
2784>semBlockLock. Perhaps waiting on semBlockQueue would be a better idea.
2785
2786you are correct. my mistake.
2787
2788>What have you gained by making the last thread to be signaled do the waits
2789>for all the timed out threads, besides added complexity? It took me a long
2790>time to figure out what your objective was with this, to realize you were
2791>using nWaitersGone to mean two different things, and to verify that you
2792>hadn't introduced any bug by doing this. Even now I'm not 100% sure.
2793>
2794>What has all this playing about with nWaitersGone really gained us besides
2795a
2796>lot of complexity (it is much harder to verify that this solution is
2797>correct), execution overhead (we now have a lot more if statements to
2798>evaluate), and space overhead (more space for the extra code, and another
2799>integer in our data)? We did manage to save a lock/unlock pair in an
2800>uncommon case (when a time out occurs) at the above mentioned expenses in
2801>the common cases.
2802
2803well, please consider the following:
2804
28051) with multiple waiters unblocked (but some timed out) the trick with
2806counter
2807seem to ensure potentially higher level of concurrency by not delaying
2808most of unblocked waiters for semaphore cleanup - only the last one
2809will be delayed but all others would already contend/acquire/release
2810the external mutex - the critical section protected by mtxUnblockLock is
2811made smaller (increment + couple of IFs is faster than system/kernel call)
2812which i think is good in general. however, you are right, this is done
2813at expense of 'normal' waiters..
2814
28152) some semaphore APIs (e.g. POSIX IPC sems) do allow to adjust the
2816semaphore counter in one call => less system/kernel calls.. imagine:
2817
2818if ( 1 == nSignalsWasLeft ) {
2819    if ( 0 != nWaitersWasGone ) {
2820      ReleaseSemaphore( semBlockQueue,-nWaitersWasGone );  // better now
2821than spurious later
2822    }
2823    sem_post( semBlockLock );              // open the gate
2824  }
2825
28263) even on win32 a single thread doing multiple cleanup calls (to wait)
2827will probably result in faster execution (because of processor caching)
2828than multiple threads each doing a single call to wait.
2829
2830>As for 8b, c, and d, they look ok though I haven't studied them
2831thoroughly.
2832>What would you use them for?
2833
28348b) for semaphores which do not allow to unblock multiple waiters
2835in a single call to post/release (e.g. POSIX realtime semaphores -
2836<semaphore.h>)
2837
28388c/8d) for WinCE prior to 3.0 (WinCE 3.0 does have semaphores)
2839
2840ok. so, which one is the 'final' algorithm(s) which we should use in
2841pthreads-win32??
2842
2843regards,
2844alexander.
2845
2846----------------------------------------------------------------------------
2847
2848Louis Thomas <lthomas@arbitrade.com> on 02/27/2001 05:20:12 AM
2849
2850Please respond to Louis Thomas <lthomas@arbitrade.com>
2851
2852To:   Alexander Terekhov/Germany/IBM@IBMDE
2853cc:   rpj@ise.canberra.edu.au, Thomas Pfaff <tpfaff@gmx.net>, Nanbor Wang
2854      <nanbor@cs.wustl.edu>
2855Subject:  RE: FYI/comp.programming.threads/Re: pthread_cond_* implementatio
2856      n questions
2857
2858Sorry all. Busy week.
2859
2860> this insures the fairness
2861> which POSIX does not (e.g. two subsequent broadcasts - the gate does
2862insure
2863> that first wave waiters will start the race for the mutex before waiters
2864> from the second wave - Linux pthreads process/unblock both waves
2865> concurrently...)
2866
2867I'm not sure how we are any more fair about this than Linux. We certainly
2868don't guarantee that the threads released by the first broadcast will get
2869the external mutex before the threads of the second wave. In fact, it is
2870possible that those threads will never get the external mutex if there is
2871enough contention for it.
2872
2873> e.g. i was thinking about implementation with a pool of
2874> N semaphores/counters [...]
2875
2876I considered that too. The problem is as you mentioned in a). You really
2877need to assign threads to semaphores once you know how you want to wake
2878them
2879up, not when they first begin waiting which is the only time you can assign
2880them.
2881
2882> well, i am not quite sure that i've fully understood your scenario,
2883
2884Hmm. Well, it think it's an important example, so I'll try again. First, we
2885have thread A which we KNOW is waiting on a condition. As soon as it
2886becomes
2887unblocked for any reason, we will know because it will set a flag. Since
2888the
2889flag is not set, we are 100% confident that thread A is waiting on the
2890condition. We have another thread, thread B, which has acquired the mutex
2891and is about to wait on the condition. Thus it is pretty clear that at any
2892point, either just A is waiting, or A and B are waiting. Now thread C comes
2893along. C is about to do a broadcast on the condition. A broadcast is
2894guaranteed to unblock all threads currently waiting on a condition, right?
2895Again, we said that either just A is waiting, or A and B are both waiting.
2896So, when C does its broadcast, depending upon whether B has started waiting
2897or not, thread C will unblock A or unblock A and B. Either way, C must
2898unblock A, right?
2899
2900Now, you said anything that happens is correct so long as a) "a signal is
2901not lost between unlocking the mutex and waiting on the condition" and b)
2902"a
2903thread must not steal a signal it sent", correct? Requirement b) is easy to
2904satisfy: in this scenario, thread C will never wait on the condition, so it
2905won't steal any signals.  Requirement a) is not hard either. The only way
2906we
2907could fail to meet requirement a) in this scenario is if thread B was
2908started waiting but didn't wake up because a signal was lost. This will not
2909happen.
2910
2911Now, here is what happens. Assume thread C beats thread B. Thread C looks
2912to
2913see how many threads are waiting on the condition. Thread C sees just one
2914thread, thread A, waiting. It does a broadcast waking up just one thread
2915because just one thread is waiting. Next, before A can become unblocked,
2916thread B begins waiting. Now there are two threads waiting, but only one
2917will be unblocked. Suppose B wins. B will become unblocked. A will not
2918become unblocked, because C only unblocked one thread (sema_post cond, 1).
2919So at the end, B finishes and A remains blocked.
2920
2921We have met both of your requirements, so by your rules, this is an
2922acceptable outcome. However, I think that the spec says this is an
2923unacceptable outcome! We know for certain that A was waiting and that C did
2924a broadcast, but A did not become unblocked! Yet, the spec says that a
2925broadcast wakes up all waiting threads. This did not happen. Do you agree
2926that this shows your rules are not strict enough?
2927
2928> and what about N2? :) this one does allow almost everything.
2929
2930Don't get me started about rule #2. I'll NEVER advocate an algorithm that
2931uses rule 2 as an excuse to suck!
2932
2933> but it is done (decrement)under mutex protection - this is not a subject
2934> of a race condition.
2935
2936You are correct. My mistake.
2937
2938> i would remove "_bTimedOut=false".. after all, it was a real timeout..
2939
2940I disagree. A thread that can't successfully retract its waiter status
2941can't
2942really have timed out. If a thread can't return without executing extra
2943code
2944to deal with the fact that someone tried to unblock it, I think it is a
2945poor
2946idea to pretend we
2947didn't realize someone was trying to signal us. After all, a signal is more
2948important than a time out.
2949
2950> when nSignaled != 0, it is possible to update nWaiters (--) and do not
2951> touch nGone
2952
2953I realize this, but I was thinking that writing it the other ways saves
2954another if statement.
2955
2956> adjust only if nGone != 0 and save one cache memory write - probably much
2957slower than 'if'
2958
2959Hmm. You are probably right.
2960
2961> well, in a strange (e.g. timeout test) program you may (theoretically)
2962> have an overflow of nWaiters/nGone counters (with waiters repeatedly
2963timing
2964> out and no signals at all).
2965
2966Also true. Not only that, but you also have the possibility that one could
2967overflow the number of waiters as well! However, considering the limit you
2968have chosen for nWaitersGone, I suppose it is unlikely that anyone would be
2969able to get INT_MAX/2 threads waiting on a single condition. :)
2970
2971Analysis of 8a:
2972
2973It looks correct to me.
2974
2975What are IPC semaphores?
2976
2977In the line where you state, "else if ( nWaitersBlocked > nWaitersGone ) {
2978// HARMLESS RACE CONDITION!" there is no race condition for nWaitersGone
2979because nWaitersGone is never modified without holding mtxUnblockLock. You
2980are correct that there is a harmless race on nWaitersBlocked, which can
2981increase and make the condition become true just after we check it. If this
2982happens, we interpret it as the wait starting after the signal.
2983
2984I like your optimization of this. You could improve Alg. 6 as follows:
2985---------- Algorithm 6b ----------
2986signal(bAll) {
2987  _nSig=0
2988  lock counters
2989  // this is safe because nWaiting can only be decremented by a thread that
2990  // owns counters and nGone can only be changed by a thread that owns
2991counters.
2992  if (nWaiting>nGone) {
2993    if (0==nSignaled) {
2994      sema_wait gate // close gate if not already closed
2995    }
2996    if (nGone>0) {
2997      nWaiting-=nGone
2998      nGone=0
2999    }
3000    _nSig=bAll?nWaiting:1
3001    nSignaled+=_nSig
3002    nWaiting-=_nSig
3003  }
3004  unlock counters
3005  if (0!=_nSig) {
3006    sema_post queue, _nSig
3007  }
3008}
3009---------- ---------- ----------
3010I guess this wouldn't apply to Alg 8a because nWaitersGone changes meanings
3011depending upon whether the gate is open or closed.
3012
3013In the loop "while ( nWaitersWasGone-- ) {" you do a sema_wait on
3014semBlockLock. Perhaps waiting on semBlockQueue would be a better idea.
3015
3016What have you gained by making the last thread to be signaled do the waits
3017for all the timed out threads, besides added complexity? It took me a long
3018time to figure out what your objective was with this, to realize you were
3019using nWaitersGone to mean two different things, and to verify that you
3020hadn't introduced any bug by doing this. Even now I'm not 100% sure.
3021
3022What has all this playing about with nWaitersGone really gained us besides
3023a
3024lot of complexity (it is much harder to verify that this solution is
3025correct), execution overhead (we now have a lot more if statements to
3026evaluate), and space overhead (more space for the extra code, and another
3027integer in our data)? We did manage to save a lock/unlock pair in an
3028uncommon case (when a time out occurs) at the above mentioned expenses in
3029the common cases.
3030
3031As for 8b, c, and d, they look ok though I haven't studied them thoroughly.
3032What would you use them for?
3033
3034    Later,
3035        -Louis! :)
3036
3037

README.NONPORTABLE

1This file documents non-portable functions and other issues.
2
3Non-portable functions included in pthreads-win32
4-------------------------------------------------
5
6BOOL
7pthread_win32_test_features_np(int mask)
8
9	This routine allows an application to check which
10	run-time auto-detected features are available within
11	the library.
12
13	The possible features are:
14
15		PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE
16			Return TRUE if the native version of
17			InterlockedCompareExchange() is being used.
18			This feature is not meaningful in recent
19			library versions as MSVC builds only support
20			system implemented ICE. Note that all Mingw
21			builds use inlined asm versions of all the
22			Interlocked routines.
23		PTW32_ALERTABLE_ASYNC_CANCEL
24			Return TRUE is the QueueUserAPCEx package
25			QUSEREX.DLL is available and the AlertDrv.sys
26			driver is loaded into Windows, providing
27			alertable (pre-emptive) asyncronous threads
28			cancelation. If this feature returns FALSE
29			then the default async cancel scheme is in
30			use, which cannot cancel blocked threads.
31
32	Features may be Or'ed into the mask parameter, in which case
33	the routine returns TRUE if any of the Or'ed features would
34	return TRUE. At this stage it doesn't make sense to Or features
35	but it may some day.
36
37
38void *
39pthread_timechange_handler_np(void *)
40
41        To improve tolerance against operator or time service
42        initiated system clock changes.
43
44        This routine can be called by an application when it
45        receives a WM_TIMECHANGE message from the system. At
46        present it broadcasts all condition variables so that
47        waiting threads can wake up and re-evaluate their
48        conditions and restart their timed waits if required.
49
50        It has the same return type and argument type as a
51        thread routine so that it may be called directly
52        through pthread_create(), i.e. as a separate thread.
53
54        Parameters
55
56        Although a parameter must be supplied, it is ignored.
57        The value NULL can be used.
58
59        Return values
60
61        It can return an error EAGAIN to indicate that not
62        all condition variables were broadcast for some reason.
63        Otherwise, 0 is returned.
64
65        If run as a thread, the return value is returned
66        through pthread_join().
67
68        The return value should be cast to an integer.
69
70
71HANDLE
72pthread_getw32threadhandle_np(pthread_t thread);
73
74	Returns the win32 thread handle that the POSIX
75	thread "thread" is running as.
76
77	Applications can use the win32 handle to set
78	win32 specific attributes of the thread.
79
80DWORD
81pthread_getw32threadid_np (pthread_t thread)
82
83	Returns the Windows native thread ID that the POSIX
84	thread "thread" is running as.
85
86        Only valid when the library is built where
87        ! (defined(__MINGW64__) || defined(__MINGW32__)) || defined (__MSVCRT__) || defined (__DMC__)
88        and otherwise returns 0.
89
90
91int
92pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr, int kind)
93
94int
95pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr, int *kind)
96
97        These two routines are included for Linux compatibility
98        and are direct equivalents to the standard routines
99                pthread_mutexattr_settype
100                pthread_mutexattr_gettype
101
102        pthread_mutexattr_setkind_np accepts the following
103        mutex kinds:
104                PTHREAD_MUTEX_FAST_NP
105                PTHREAD_MUTEX_ERRORCHECK_NP
106                PTHREAD_MUTEX_RECURSIVE_NP
107
108        These are really just equivalent to (respectively):
109                PTHREAD_MUTEX_NORMAL
110                PTHREAD_MUTEX_ERRORCHECK
111                PTHREAD_MUTEX_RECURSIVE
112
113int
114pthread_delay_np (const struct timespec *interval);
115
116        This routine causes a thread to delay execution for a specific period of time.
117        This period ends at the current time plus the specified interval. The routine
118        will not return before the end of the period is reached, but may return an
119        arbitrary amount of time after the period has gone by. This can be due to
120        system load, thread priorities, and system timer granularity.
121
122        Specifying an interval of zero (0) seconds and zero (0) nanoseconds is
123        allowed and can be used to force the thread to give up the processor or to
124        deliver a pending cancelation request.
125
126        This routine is a cancelation point.
127
128        The timespec structure contains the following two fields:
129
130                tv_sec is an integer number of seconds.
131                tv_nsec is an integer number of nanoseconds.
132
133        Return Values
134
135        If an error condition occurs, this routine returns an integer value
136        indicating the type of error. Possible return values are as follows:
137
138        0          Successful completion.
139        [EINVAL]   The value specified by interval is invalid.
140
141int
142pthread_num_processors_np (void)
143
144        This routine (found on HPUX systems) returns the number of processors
145        in the system. This implementation actually returns the number of
146        processors available to the process, which can be a lower number
147        than the system's number, depending on the process's affinity mask.
148
149BOOL
150pthread_win32_process_attach_np (void);
151
152BOOL
153pthread_win32_process_detach_np (void);
154
155BOOL
156pthread_win32_thread_attach_np (void);
157
158BOOL
159pthread_win32_thread_detach_np (void);
160
161	These functions contain the code normally run via dllMain
162	when the library is used as a dll but which need to be
163	called explicitly by an application when the library
164	is statically linked. As of version 2.9.0 of the library, static
165	builds using either MSC or GCC will call pthread_win32_process_*
166	automatically at application startup and exit respectively.
167
168	Otherwise, you will need to call pthread_win32_process_attach_np()
169	before you can call any pthread routines when statically linking.
170	You should call pthread_win32_process_detach_np() before
171	exiting your application to clean up.
172
173	pthread_win32_thread_attach_np() is currently a no-op, but
174	pthread_win32_thread_detach_np() is needed to clean up
175	the implicit pthread handle that is allocated to a Win32 thread if
176	it calls any pthreads routines. Call this routine when the
177	Win32 thread exits.
178
179	Threads created through pthread_create() do not	need to call
180	pthread_win32_thread_detach_np().
181
182	These functions invariably return TRUE except for
183	pthread_win32_process_attach_np() which will return FALSE
184	if pthreads-win32 initialisation fails.
185
186int
187pthreadCancelableWait (HANDLE waitHandle);
188
189int
190pthreadCancelableTimedWait (HANDLE waitHandle, DWORD timeout);
191
192	These two functions provide hooks into the pthread_cancel
193	mechanism that will allow you to wait on a Windows handle
194	and make it a cancellation point. Both functions block
195	until either the given w32 handle is signaled, or
196	pthread_cancel has been called. It is implemented using
197	WaitForMultipleObjects on 'waitHandle' and a manually
198	reset w32 event used to implement pthread_cancel.
199
200
201Non-portable issues
202-------------------
203
204Thread priority
205
206	POSIX defines a single contiguous range of numbers that determine a
207	thread's priority. Win32 defines priority classes and priority
208	levels relative to these classes. Classes are simply priority base
209	levels that the defined priority levels are relative to such that,
210	changing a process's priority class will change the priority of all
211	of it's threads, while the threads retain the same relativity to each
212	other.
213
214	A Win32 system defines a single contiguous monotonic range of values
215	that define system priority levels, just like POSIX. However, Win32
216	restricts individual threads to a subset of this range on a
217	per-process basis.
218
219	The following table shows the base priority levels for combinations
220	of priority class and priority value in Win32.
221
222	 Process Priority Class               Thread Priority Level
223	 -----------------------------------------------------------------
224	 1 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_IDLE
225	 1 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_IDLE
226	 1 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_IDLE
227	 1 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_IDLE
228	 1 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_IDLE
229	 2 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_LOWEST
230	 3 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_BELOW_NORMAL
231	 4 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_NORMAL
232	 4 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_LOWEST
233	 5 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_ABOVE_NORMAL
234	 5 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_BELOW_NORMAL
235	 5 Background NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_LOWEST
236	 6 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_HIGHEST
237	 6 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_NORMAL
238	 6 Background NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_BELOW_NORMAL
239	 7 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_ABOVE_NORMAL
240	 7 Background NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_NORMAL
241	 7 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_LOWEST
242 	 8 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_HIGHEST
243	 8 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_ABOVE_NORMAL
244	 8 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_BELOW_NORMAL
245	 8 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_LOWEST
246	 9 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_HIGHEST
247	 9 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_NORMAL
248	 9 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_BELOW_NORMAL
249	10 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_ABOVE_NORMAL
250	10 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_NORMAL
251	11 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_HIGHEST
252	11 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_ABOVE_NORMAL
253	11 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_LOWEST
254	12 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_HIGHEST
255	12 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_BELOW_NORMAL
256	13 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_NORMAL
257	14 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_ABOVE_NORMAL
258	15 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_HIGHEST
259	15 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_TIME_CRITICAL
260	15 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_TIME_CRITICAL
261	15 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_TIME_CRITICAL
262	15 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_TIME_CRITICAL
263	15 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_TIME_CRITICAL
264	16 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_IDLE
265	17 REALTIME_PRIORITY_CLASS            -7
266	18 REALTIME_PRIORITY_CLASS            -6
267	19 REALTIME_PRIORITY_CLASS            -5
268	20 REALTIME_PRIORITY_CLASS            -4
269	21 REALTIME_PRIORITY_CLASS            -3
270	22 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_LOWEST
271	23 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_BELOW_NORMAL
272	24 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_NORMAL
273	25 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_ABOVE_NORMAL
274	26 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_HIGHEST
275	27 REALTIME_PRIORITY_CLASS             3
276	28 REALTIME_PRIORITY_CLASS             4
277	29 REALTIME_PRIORITY_CLASS             5
278	30 REALTIME_PRIORITY_CLASS             6
279	31 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_TIME_CRITICAL
280
281	Windows NT:  Values -7, -6, -5, -4, -3, 3, 4, 5, and 6 are not supported.
282
283
284	As you can see, the real priority levels available to any individual
285	Win32 thread are non-contiguous.
286
287	An application using pthreads-win32 should not make assumptions about
288	the numbers used to represent thread priority levels, except that they
289	are monotonic between the values returned by sched_get_priority_min()
290	and sched_get_priority_max(). E.g. Windows 95, 98, NT, 2000, XP make
291	available a non-contiguous range of numbers between -15 and 15, while
292	at least one version of WinCE (3.0) defines the minimum priority
293	(THREAD_PRIORITY_LOWEST) as 5, and the maximum priority
294	(THREAD_PRIORITY_HIGHEST) as 1.
295
296	Internally, pthreads-win32 maps any priority levels between
297	THREAD_PRIORITY_IDLE and THREAD_PRIORITY_LOWEST to THREAD_PRIORITY_LOWEST,
298	or between THREAD_PRIORITY_TIME_CRITICAL and THREAD_PRIORITY_HIGHEST to
299	THREAD_PRIORITY_HIGHEST. Currently, this also applies to
300	REALTIME_PRIORITY_CLASSi even if levels -7, -6, -5, -4, -3, 3, 4, 5, and 6
301	are supported.
302
303	If it wishes, a Win32 application using pthreads-win32 can use the Win32
304	defined priority macros THREAD_PRIORITY_IDLE through
305	THREAD_PRIORITY_TIME_CRITICAL.
306
307
308The opacity of the pthread_t datatype
309-------------------------------------
310and possible solutions for portable null/compare/hash, etc
311----------------------------------------------------------
312
313Because pthread_t is an opague datatype an implementation is permitted to define
314pthread_t in any way it wishes. That includes defining some bits, if it is
315scalar, or members, if it is an aggregate, to store information that may be
316extra to the unique identifying value of the ID. As a result, pthread_t values
317may not be directly comparable.
318
319If you want your code to be portable you must adhere to the following contraints:
320
3211) Don't assume it is a scalar data type, e.g. an integer or pointer value. There
322are several other implementations where pthread_t is also a struct. See our FAQ
323Question 11 for our reasons for defining pthread_t as a struct.
324
3252) You must not compare them using relational or equality operators. You must use
326the API function pthread_equal() to test for equality.
327
3283) Never attempt to reference individual members.
329
330
331The problem
332
333Certain applications would like to be able to access only the 'pure' pthread_t
334id values, primarily to use as keys into data structures to manage threads or
335thread-related data, but this is not possible in a maximally portable and
336standards compliant way for current POSIX threads implementations.
337
338For implementations that define pthread_t as a scalar, programmers often employ
339direct relational and equality operators on pthread_t. This code will break when
340ported to an implementation that defines pthread_t as an aggregate type.
341
342For implementations that define pthread_t as an aggregate, e.g. a struct,
343programmers can use memcmp etc., but then face the prospect that the struct may
344include alignment padding bytes or bits as well as extra implementation-specific
345members that are not part of the unique identifying value.
346
347[While this is not currently the case for pthreads-win32, opacity also
348means that an implementation is free to change the definition, which should
349generally only require that applications be recompiled and relinked, not
350rewritten.]
351
352
353Doesn't the compiler take care of padding?
354
355The C89 and later standards only effectively guarrantee element-by-element
356equivalence following an assignment or pass by value of a struct or union,
357therefore undefined areas of any two otherwise equivalent pthread_t instances
358can still compare differently, e.g. attempting to compare two such pthread_t
359variables byte-by-byte, e.g. memcmp(&t1, &t2, sizeof(pthread_t) may give an
360incorrect result. In practice I'm reasonably confident that compilers routinely
361also copy the padding bytes, mainly because assignment of unions would be far
362too complicated otherwise. But it just isn't guarranteed by the standard.
363
364Illustration:
365
366We have two thread IDs t1 and t2
367
368pthread_t t1, t2;
369
370In an application we create the threads and intend to store the thread IDs in an
371ordered data structure (linked list, tree, etc) so we need to be able to compare
372them in order to insert them initially and also to traverse.
373
374Suppose pthread_t contains undefined padding bits and our compiler copies our
375pthread_t [struct] element-by-element, then for the assignment:
376
377pthread_t temp = t1;
378
379temp and t1 will be equivalent and correct but a byte-for-byte comparison such as
380memcmp(&temp, &t1, sizeof(pthread_t)) == 0 may not return true as we expect because
381the undefined bits may not have the same values in the two variable instances.
382
383Similarly if passing by value under the same conditions.
384
385If, on the other hand, the undefined bits are at least constant through every
386assignment and pass-by-value then the byte-for-byte comparison
387memcmp(&temp, &t1, sizeof(pthread_t)) == 0 will always return the expected result.
388How can we force the behaviour we need?
389
390
391Solutions
392
393Adding new functions to the standard API or as non-portable extentions is
394the only reliable and portable way to provide the necessary operations.
395Remember also that POSIX is not tied to the C language. The most common
396functions that have been suggested are:
397
398pthread_null()
399pthread_compare()
400pthread_hash()
401
402A single more general purpose function could also be defined as a
403basis for at least the last two of the above functions.
404
405First we need to list the freedoms and constraints with restpect
406to pthread_t so that we can be sure our solution is compatible with the
407standard.
408
409What is known or may be deduced from the standard:
4101) pthread_t must be able to be passed by value, so it must be a single object.
4112) from (1) it must be copyable so cannot embed thread-state information, locks
412or other volatile objects required to manage the thread it associates with.
4133) pthread_t may carry additional information, e.g. for debugging or to manage
414itself.
4154) there is an implicit requirement that the size of pthread_t is determinable
416at compile-time and size-invariant, because it must be able to copy the object
417(i.e. through assignment and pass-by-value). Such copies must be genuine
418duplicates, not merely a copy of a pointer to a common instance such as
419would be the case if pthread_t were defined as an array.
420
421
422Suppose we define the following function:
423
424/* This function shall return it's argument */
425pthread_t* pthread_normalize(pthread_t* thread);
426
427For scalar or aggregate pthread_t types this function would simply zero any bits
428within the pthread_t that don't uniquely identify the thread, including padding,
429such that client code can return consistent results from operations done on the
430result. If the additional bits are a pointer to an associate structure then
431this function would ensure that the memory used to store that associate
432structure does not leak. After normalization the following compare would be
433valid and repeatable:
434
435memcmp(pthread_normalize(&t1),pthread_normalize(&t2),sizeof(pthread_t))
436
437Note 1: such comparisons are intended merely to order and sort pthread_t values
438and allow them to index various data structures. They are not intended to reveal
439anything about the relationships between threads, like startup order.
440
441Note 2: the normalized pthread_t is also a valid pthread_t that uniquely
442identifies the same thread.
443
444Advantages:
4451) In most existing implementations this function would reduce to a no-op that
446emits no additional instructions, i.e after in-lining or optimisation, or if
447defined as a macro:
448#define pthread_normalise(tptr) (tptr)
449
4502) This single function allows an application to portably derive
451application-level versions of any of the other required functions.
452
4533) It is a generic function that could enable unanticipated uses.
454
455Disadvantages:
4561) Less efficient than dedicated compare or hash functions for implementations
457that include significant extra non-id elements in pthread_t.
458
4592) Still need to be concerned about padding if copying normalized pthread_t.
460See the later section on defining pthread_t to neutralise padding issues.
461
462Generally a pthread_t may need to be normalized every time it is used,
463which could have a significant impact. However, this is a design decision
464for the implementor in a competitive environment. An implementation is free
465to define a pthread_t in a way that minimises or eliminates padding or
466renders this function a no-op.
467
468Hazards:
4691) Pass-by-reference directly modifies 'thread' so the application must
470synchronise access or ensure that the pointer refers to a copy. The alternative
471of pass-by-value/return-by-value was considered but then this requires two copy
472operations, disadvantaging implementations where this function is not a no-op
473in terms of speed of execution. This function is intended to be used in high
474frequency situations and needs to be efficient, or at least not unnecessarily
475inefficient. The alternative also sits awkwardly with functions like memcmp.
476
4772) [Non-compliant] code that uses relational and equality operators on
478arithmetic or pointer style pthread_t types would need to be rewritten, but it
479should be rewritten anyway.
480
481
482C implementation of null/compare/hash functions using pthread_normalize():
483
484/* In pthread.h */
485pthread_t* pthread_normalize(pthread_t* thread);
486
487/* In user code */
488/* User-level bitclear function - clear bits in loc corresponding to mask */
489void* bitclear (void* loc, void* mask, size_t count);
490
491typedef unsigned int hash_t;
492
493/* User-level hash function */
494hash_t hash(void* ptr, size_t count);
495
496/*
497 * User-level pthr_null function - modifies the origin thread handle.
498 * The concept of a null pthread_t is highly implementation dependent
499 * and this design may be far from the mark. For example, in an
500 * implementation "null" may mean setting a special value inside one
501 * element of pthread_t to mean "INVALID". However, if that value was zero and
502 * formed part of the id component then we may get away with this design.
503 */
504pthread_t* pthr_null(pthread_t* tp)
505{
506  /*
507   * This should have the same effect as memset(tp, 0, sizeof(pthread_t))
508   * We're just showing that we can do it.
509   */
510  void* p = (void*) pthread_normalize(tp);
511  return (pthread_t*) bitclear(p, p, sizeof(pthread_t));
512}
513
514/*
515 * Safe user-level pthr_compare function - modifies temporary thread handle copies
516 */
517int pthr_compare_safe(pthread_t thread1, pthread_t thread2)
518{
519  return memcmp(pthread_normalize(&thread1), pthread_normalize(&thread2), sizeof(pthread_t));
520}
521
522/*
523 * Fast user-level pthr_compare function - modifies origin thread handles
524 */
525int pthr_compare_fast(pthread_t* thread1, pthread_t* thread2)
526{
527  return memcmp(pthread_normalize(&thread1), pthread_normalize(&thread2), sizeof(pthread_t));
528}
529
530/*
531 * Safe user-level pthr_hash function - modifies temporary thread handle copy
532 */
533hash_t pthr_hash_safe(pthread_t thread)
534{
535  return hash((void *) pthread_normalize(&thread), sizeof(pthread_t));
536}
537
538/*
539 * Fast user-level pthr_hash function - modifies origin thread handle
540 */
541hash_t pthr_hash_fast(pthread_t thread)
542{
543  return hash((void *) pthread_normalize(&thread), sizeof(pthread_t));
544}
545
546/* User-level bitclear function - modifies the origin array */
547void* bitclear(void* loc, void* mask, size_t count)
548{
549  int i;
550  for (i=0; i < count; i++) {
551    (unsigned char) *loc++ &= ~((unsigned char) *mask++);
552  }
553}
554
555/* Donald Knuth hash */
556hash_t hash(void* str, size_t count)
557{
558   hash_t hash = (hash_t) count;
559   unsigned int i = 0;
560
561   for(i = 0; i < len; str++, i++)
562   {
563      hash = ((hash << 5) ^ (hash >> 27)) ^ (*str);
564   }
565   return hash;
566}
567
568/* Example of advantage point (3) - split a thread handle into its id and non-id values */
569pthread_t id = thread, non-id = thread;
570bitclear((void*) &non-id, (void*) pthread_normalize(&id), sizeof(pthread_t));
571
572
573A pthread_t type change proposal to neutralise the effects of padding
574
575Even if pthread_nornalize() is available, padding is still a problem because
576the standard only garrantees element-by-element equivalence through
577copy operations (assignment and pass-by-value). So padding bit values can
578still change randomly after calls to pthread_normalize().
579
580[I suspect that most compilers take the easy path and always byte-copy anyway,
581partly because it becomes too complex to do (e.g. unions that contain sub-aggregates)
582but also because programmers can easily design their aggregates to minimise and
583often eliminate padding].
584
585How can we eliminate the problem of padding bytes in structs? Could
586defining pthread_t as a union rather than a struct provide a solution?
587
588In fact, the Linux pthread.h defines most of it's pthread_*_t objects (but not
589pthread_t itself) as unions, possibly for this and/or other reasons. We'll
590borrow some element naming from there but the ideas themselves are well known
591- the __align element used to force alignment of the union comes from K&R's
592storage allocator example.
593
594/* Essentially our current pthread_t renamed */
595typedef struct {
596  struct thread_state_t * __p;
597  long __x; /* sequence counter */
598} thread_id_t;
599
600Ensuring that the last element in the above struct is a long ensures that the
601overall struct size is a multiple of sizeof(long), so there should be no trailing
602padding in this struct or the union we define below.
603(Later we'll see that we can handle internal but not trailing padding.)
604
605/* New pthread_t */
606typedef union {
607  char __size[sizeof(thread_id_t)]; /* array as the first element */
608  thread_id_t __tid;
609  long __align;  /* Ensure that the union starts on long boundary */
610} pthread_t;
611
612This guarrantees that, during an assignment or pass-by-value, the compiler copies
613every byte in our thread_id_t because the compiler guarrantees that the __size
614array, which we have ensured is the equal-largest element in the union, retains
615equivalence.
616
617This means that pthread_t values stored, assigned and passed by value will at least
618carry the value of any undefined padding bytes along and therefore ensure that
619those values remain consistent. Our comparisons will return consistent results and
620our hashes of [zero initialised] pthread_t values will also return consistent
621results.
622
623We have also removed the need for a pthread_null() function; we can initialise
624at declaration time or easily create our own const pthread_t to use in assignments
625later:
626
627const pthread_t null_tid = {0}; /* braces are required */
628
629pthread_t t;
630...
631t = null_tid;
632
633
634Note that we don't have to explicitly make use of the __size array at all. It's
635there just to force the compiler behaviour we want.
636
637
638Partial solutions without a pthread_normalize function
639
640
641An application-level pthread_null and pthread_compare proposal
642(and pthread_hash proposal by extention)
643
644In order to deal with the problem of scalar/aggregate pthread_t type disparity in
645portable code I suggest using an old-fashioned union, e.g.:
646
647Contraints:
648- there is no padding, or padding values are preserved through assignment and
649  pass-by-value (see above);
650- there are no extra non-id values in the pthread_t.
651
652
653Example 1: A null initialiser for pthread_t variables...
654
655typedef union {
656    unsigned char b[sizeof(pthread_t)];
657    pthread_t t;
658} init_t;
659
660const init_t initial = {0};
661
662pthread_t tid = initial.t; /* init tid to all zeroes */
663
664
665Example 2: A comparison function for pthread_t values
666
667typedef union {
668   unsigned char b[sizeof(pthread_t)];
669   pthread_t t;
670} pthcmp_t;
671
672int pthcmp(pthread_t left, pthread_t right)
673{
674  /*
675  * Compare two pthread handles in a way that imposes a repeatable but arbitrary
676  * ordering on them.
677  * I.e. given the same set of pthread_t handles the ordering should be the same
678  * each time but the order has no particular meaning other than that. E.g.
679  * the ordering does not imply the thread start sequence, or any other
680  * relationship between threads.
681  *
682  * Return values are:
683  * 1 : left is greater than right
684  * 0 : left is equal to right
685  * -1 : left is less than right
686  */
687  int i;
688  pthcmp_t L, R;
689  L.t = left;
690  R.t = right;
691  for (i = 0; i < sizeof(pthread_t); i++)
692  {
693    if (L.b[i] > R.b[i])
694      return 1;
695    else if (L.b[i] < R.b[i])
696      return -1;
697  }
698  return 0;
699}
700
701It has been pointed out that the C99 standard allows for the possibility that
702integer types also may include padding bits, which could invalidate the above
703method. This addition to C99 was specifically included after it was pointed
704out that there was one, presumably not particularly well known, architecture
705that included a padding bit in it's 32 bit integer type. See section 6.2.6.2
706of both the standard and the rationale, specifically the paragraph starting at
707line 16 on page 43 of the rationale.
708
709
710An aside
711
712Certain compilers, e.g. gcc and one of the IBM compilers, include a feature
713extention: provided the union contains a member of the same type as the
714object then the object may be cast to the union itself.
715
716We could use this feature to speed up the pthrcmp() function from example 2
717above by casting rather than assigning the pthread_t arguments to the union, e.g.:
718
719int pthcmp(pthread_t left, pthread_t right)
720{
721  /*
722  * Compare two pthread handles in a way that imposes a repeatable but arbitrary
723  * ordering on them.
724  * I.e. given the same set of pthread_t handles the ordering should be the same
725  * each time but the order has no particular meaning other than that. E.g.
726  * the ordering does not imply the thread start sequence, or any other
727  * relationship between threads.
728  *
729  * Return values are:
730  * 1 : left is greater than right
731  * 0 : left is equal to right
732  * -1 : left is less than right
733  */
734  int i;
735  for (i = 0; i < sizeof(pthread_t); i++)
736  {
737    if (((pthcmp_t)left).b[i] > ((pthcmp_t)right).b[i])
738      return 1;
739    else if (((pthcmp_t)left).b[i] < ((pthcmp_t)right).b[i])
740      return -1;
741  }
742  return 0;
743}
744
745
746Result thus far
747
748We can't remove undefined bits if they are there in pthread_t already, but we have
749attempted to render them inert for comparison and hashing functions by making them
750consistent through assignment, copy and pass-by-value.
751
752Note: Hashing pthread_t values requires that all pthread_t variables be initialised
753to the same value (usually all zeros) before being assigned a proper thread ID, i.e.
754to ensure that any padding bits are zero, or at least the same value for all
755pthread_t. Since all pthread_t values are generated by the library in the first
756instance this need not be an application-level operation.
757
758
759Conclusion
760
761I've attempted to resolve the multiple issues of type opacity and the possible
762presence of undefined bits and bytes in pthread_t values, which prevent
763applications from comparing or hashing pthread handles.
764
765Two complimentary partial solutions have been proposed, one an application-level
766scheme to handle both scalar and aggregate pthread_t types equally, plus a
767definition of pthread_t itself that neutralises padding bits and bytes by
768coercing semantics out of the compiler to eliminate variations in the values of
769padding bits.
770
771I have not provided any solution to the problem of handling extra values embedded
772in pthread_t, e.g. debugging or trap information that an implementation is entitled
773to include. Therefore none of this replaces the portability and flexibility of API
774functions but what functions are needed? The threads standard is unlikely to
775include that can be implemented by a combination of existing features and more
776generic functions (several references in the threads rationale suggest this.
777Therefore I propose that the following function could replace the several functions
778that have been suggested in conversations:
779
780pthread_t * pthread_normalize(pthread_t * handle);
781
782For most existing pthreads implementations this function, or macro, would reduce to
783a no-op with zero call overhead.
784

README.Watcom

1Watcom compiler notes
2=====================
3
4Status
5------
6Not yet usable. Although the library builds under Watcom it
7substantially fails the test suite.
8
9There is a working Wmakefile for wmake for the library build.
10
11invoke as any of:
12wmake -f Wmakefile clean WC
13wmake -f Wmakefile clean WC-inlined
14wmake -f Wmakefile clean WCE
15wmake -f Wmakefile clean WCE-inlined
16
17These build pthreadWC.dll and pthreadWCE.dll.
18
19There is a working Wmakefile for wmake for the test suite.
20
21invoke as any of:
22wmake -f Wmakefile clean WC
23wmake -f Wmakefile clean WCX
24wmake -f Wmakefile clean WCE
25wmake -f Wmakefile clean WC-bench
26wmake -f Wmakefile clean WCX-bench
27wmake -f Wmakefile clean WCE-bench
28
29
30Current known problems
31----------------------
32
33Library build:
34The Watcom compiler uses a different default call convention to MS C or GNU C and so
35applications are not compatible with pthreadVC.dll etc using pre 2003-10-14 versions
36of pthread.h, sched.h, or semaphore.h. The cdecl attribute can be used on exposed
37function prototypes to force compatibility with MS C built DLLs.
38
39However, there appear to be other incompatibilities. Errno.h, for example, defines
40different values for the standard C and POSIX errors to those defined by the MS C
41errno.h. It may be that references to Watcom's threads compatible 'errno' do set
42and return translated numbers consistently, but I have not verified this.
43
44Watcom defines errno as a dereferenced pointer returned by the function
45_get_errno_ptr(). This is similar to both the MS and GNU C environments for
46multithreaded use. However, the Watcom version appears to have a number of problems:
47
48- different threads return the same pointer value. Compare with the MS and GNU C
49versions which correctly return different values (since each thread must maintain
50a thread specific errno value).
51
52- an errno value set within the DLL appears as zero in the application even though
53both share the same thread.
54
55Therefore applications built using the Watcom compiler may need to use
56a Watcom built version of the library (pthreadWC.dll). If this is the case, then
57the cdecl function attribute should not be required.
58
59Application builds:
60The test suite fails with the Watcom compiler.
61
62Test semaphore1.c fails for pthreadWC.dll because errno returns 0 instead of EAGAIN.
63

README.WinCE

1WinCE port
2----------
3(See the file WinCE-PORT for a detailed explanation.)
4
5Make sure you define "WINCE" amongst your compiler flags (eg. -DWINCE).
6The config.h file will define all the necessary defines for you.
7