This is the mail archive of the mailing list for the pthreas-win32 project.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Problems still open: for discussion

Hi all,

A few weeks back (May 7) Milan Gardian sent me some
patches, which have been merged into the new snapshot.

I'm fowarding Milan's message because it contains some items
that are still open problems that he suggests others may like
to discuss. It will also be of interest to anyone who wants to
know exactly what HAS been fixed in the latest snapshot
(apart from mutexes [Thomas Pfaff], condition variables
[Alexander Terekhov/Louis Thomas], and read/write
locks [Alexander Terekhov]) - and what hasn't.

For those interested, the two zip attachments referred to
in section 5 and the Conclusion can be downloaded from:

Regards and thanks all for the continued improvements to the



Dear Ross,

Recently I (finally) decided to bring our pthreads library up-to-date
the latest snapshot, 2000-12-29. Unfortunately, I've had some problems
compiling the new stuff and running the test suite correctly (in our
environment). The rest of this mail will provide detailed discussion on
changes I have made together with reasons for those changes. Of course
all of them might be reasonable, in that case I will be happy to hear
you ASAP! Attached please find my version of the pthreads library that
incorporates my fixes of all the problems mentioned below.

My configuration is as follows:
Dual PIII/500 (SMP), 512MB RAM
MS W2k, SP1
MS VC++ 6, SP3
MS Platform SDK Nov/2000

Now, the problems...

(1) Makefile typo

"pthreads/Makefile" (for VC++ nmake) on line 57 says 'if exit'... Should
'if exist'. This is encountered only when using 'realclean' makefile
predicate (i.e. "nmake realclean").

(2) errno warnings

Those warnings emerge when compiling pthreads library with /MD (or /MDd)
compiler switch, instead of /MT (or /MTd) (i.e. when compiling pthreads
using Multithreaded DLL CRT instead of Multithreaded statically linked
You can reproduce this problem easily by replacing the switch '/MT' in
"pthreads/Makefile", line 21 with switch '/MD'. Afterwards, while
both VSE and VCE versions of pthreads, compiler produces the following

warning C4273: '_errno' : inconsistent dll linkage.  dllexport assumed

The problem (as I have identified it) is that pre-processor directive
decides whether the current compiler supports thread-safe version of
or not is not correct/complete. After I replaced the snapshot directive
(additional newlines for better readability),
#if (! defined(NEED_ERRNO)) ||
    (! defined( _REENTRANT ) &&
     (! defined( _MT ) ||
      ! defined( _MD )
with the following directive (again, additional newlines for better
#if (! defined(HAVE_ERRNO)) &&
    (! defined(_REENTRANT)) &&
    (! defined(_MT))

there are no linkage warnings anymore. The new directive evaluates to
if any of the macros 'HAVE_ERRNO', '_REENTRANT' or '_MT' is defined
(DeMorgan's law) - and true means the current compiler already has
thread-safe version of errno and does not need an additional one. The
scenarios (in the case of Visual C++ & Win32 platform) are as follows:

  a) /MT:  defined macro _MT
  b) /MTd: defined macro _MT and _DEBUG
  c) /MD:  defined macro _MT and _DLL
  d) /MDd: defined macro _MT and _DLL and _DEBUG

The bottom line is that all those 4 cases now evaluate (in
pre-processor) to
true (because all define the '_MT' macro) and that means compiler's
of errno will be used (which is correct as VC++ errno is already thread
in both /MT (/MTd) and /MD (/MDd) modes).

The directive was changed at two places:
"pthreads/errno.c", line 27
"pthreads/pthread.h", line 945

(3) Calling convention problem

When I built the debug version of pthreads library and tried to run one
the test cases in the integrated environment as a debug build, I
received a
CRT error message that claimed that "checking of ESP register failed"
that this problem is usually due to incompatible calling conventions
using pointers to functions.

After investigating sources of pthreads library a little, this (i.e.
calling conventions, __stdcall vs. __cdecl) turned out to be a real
The main part of the trouble was the unfortunate combination of
(non-uniform) usage of macro "PT_STDCALL" and explicit

The non-uniform usage of "PT_STDCALL" macro means that SOMETIMES the
pointer-to-cleanup-function was defined like this:

void (PT_STDCALL * ptr1)(void *)

and at other times the same pointer was defined WITHOUT the PT_STDCALL

void (* ptr2)(void *)

Because "PT_STDCALL" is defined as "__stdcall" in VC++, and because
calling convention in C/C++ programs is "__cdecl", the above
pointers-to-cleanup-functions are actually compiled differently:

void (__stdcall * ptr1)(void *)


void (__cdecl * ptr2)(void *)

Of course, compiler WOULD detect the violation of calling convention
using C/C++ function pointers (__cdecl calling convention) where WINAPI
function pointers (PASCAL alias __stdcall calling convention) are
(and vice-versa), if there were no explicit casts in the
"pthread_cleanup_push" macro...

Those explicit casts forced compiler to accept
(see ptr2 above) in constructor of PThreadCleanup internal class even
the constructor expected pointer-to-stdcall-function (see ptr1 above).
caused the PThreadCleanup class to call the given cleanup function
to "__stdcall" invocation rules, no matter what was the actual (true)
calling convention of the function.

Because by default all C/C++ functions use "__cdecl" calling convention
because pthreads VCE expect C++ compiler and uses the PThreadCleanup for
cleanup, anytime pthreads VCE was using cleanup functionality
internally, it
was forcing the cleanup function to be invoked as "__stdcall" function
though it was actually "__cdecl" function... The same is also true for
cleanup test cases.

I think I need not stress the fact that mixing calling conventions might
have unpredictable consequences in production code (especially in C++
we rely heavily on stack unrolling and correct automatic variables
deletion). While in "__cdecl" calling convention the caller is
for cleaning-up the stack, in "__stdcall" calling convention the callee
responsible for cleaning-up the stack - therefore if we have a function
compiled as "__cdecl" (default in C/C++), it expects that stack is
cleaned-up by the one that calls it. Now when we call this function
a pointer from a code that is compiled believing the function uses
"__stdcall", the code supposes the function will clean-up the stack
itself... and that is not good ;).

I fixed this problem by removing the macro "PT_STDCALL" completely from
code and defined a new type that describes what form should the cleanup
function have:

typedef void (__cdecl *ptw32_cleanup_callback_t)(void *);

I changed the pthreads library to use this pointer-to-cleanup-function
exclusively throughout the code (i.e. uniformly, I hope ;) ). I will not
list all the changes here, instead you may want to compare the snapshot
version with my new version using your favourite diff tool...

(4) Cancellation problems in optimised code

This is a problem that I have NOT fixed - I haven't found a way to solve
Therefore I post it only as a warning / suggestion for discussion on
pthreads mailing list...

The cancellation (actually, cleanup-after-cancel) tests fail when using
(professional) optimisation switches (/O1 or /O2) in pthreads library. I
have not investigated which concrete optimisation technique causes this
problem (/Og, /Oi, /Ot, /Oy, /Ob1, /Gs, /Gf, /Gy, etc.), but here is a
summary of builds and corresponding failures:

  * Original pthreads VSE (optimised tests): OK
  * Original pthreads VCE (optimised tests): Failed "cleanup0" test

  * Original pthreads VSE (DLL CRT, optimised tests): OK
  * Original pthreads VCE (DLL CRT, optimised tests): Failed "cleanup0"

  * My pthreads VSE (optimised tests): OK
  * My pthreads VCE (optimised tests): Failed "cleanup1" test (runtime)

  * My pthreads VSE (DLL in CRT, optimised tests): OK
  * My pthreads VCE (DLL in CRT, optimised tests): Failed "cleanup1"

Please note that while in VSE version of the pthreads library the
optimisation does not really have any impact on the tests (they pass
OK), in
VCE version addition of optimisation (/O2 in this case) causes the tests
fail uniformly - either in "cleanup0" or "cleanup1" test cases.

Please note that all the tests above use default pthreads DLL (no
optimisations, linked with either static or DLL CRT, based on test
Therefore the problem lies not within the pthreads DLL but within the
compiled client code (the application using pthreads -> involvement of

I think the message of this section is that usage of VCE version of
in applications relying on cancellation/cleanup AND using optimisations
creation of production code is highly unreliable for the current version
the pthreads library.

(5) Terminate problem in VC

The exception handling code in pthreads library takes advantage of the
standard C++ termination handler, "terminate()". I would recommend the
following article discussing this topic:

MSDN: Handling Exceptions (Part 12)

One thing of particular interest should be the sentence "unlike an
unexpected handler, a terminate handler must end the program" -> there
be no further exception thrown from a terminate handler, as stated by
C++ standard. But this is exactly what pthreads library relies on - e.g.
"exception3" test (which fails in default pthreads VCE build) uses
"pthread_exit" to exit the current thread in the terminate handler.
Unfortunately, pthread_exit throws an exception internally. This
is caught later on by pthreads code and allows graceful termination of

However, this design causes the fact that usage of "pthread_exit" in
terminate handler ultimately breaks the rule "terminate handler is not
allowed to throw further exceptions". There's still a possibility of
"unexpected" instead of "terminate" (unexpected is, unlike terminate,
allowed to throw further exceptions), but I will leave this decision to
more in-depth discussion between people using pthreads library.

What I did change in the pthreads library is the way caught exceptions
handled. Instead of calling "terminate()" function directly, I only use
"set_terminate()" function to acquire address of terminate handler,
it manually (in file "private.c"):

    terminate_function term_func = set_terminate(0);

    if (term_func != 0) {

This (magically :) ) solves the problem of "exception3" test in VCE
build of
pthreads library compiled using VC++ compiler, but ONLY when using DLL
CRT... (/MD or /MDd switch). When using statically linked CRT, it does

The problem with the static CRT version seems to be that the address of
user-defined terminate handler set by the call to "set_terminate"
in an application is not propagated correctly to the attached pthreads
Let me illustrate my point with the following (fictive) example:

    //application code
    void my_function() { ... };

    void main() {
1.    set_terminate(my_function);
2.    dll_function()
        ---> //dll code
             void dll_function() {
3.             terminate_function fn = set_terminate(0);

On line 1, we set a custom terminate handler using "set_terminate". Then
call a function in an attached DLL, line 2. If we use "set_terminate"
the DLL code to get the address of the termination handler (line 3), we
either receive a totally different address (when using static CRT), or
receive the correct address (when using DLL CRT).

To further illustrate this problem I have created a VC++ project that
implements the above example (see the attached ""). It
a trivial dll exporting one simple function and an application that
this DLL function. It has 4 compilation targets, "Static Debug", "DLL
Debug", "Static Release" and "DLL Release" (static debug/release use
CRT i.e. /MTd and /MT respectively, DLL debug/release use DLL CRT i.e.
and /MD respectively). Like expected, "DLL Debug" and "DLL Release" work
correctly while "Static Debug" and "Static Release" work incorrectly.

I suppose that once we are able to solve the problem of the
with static CRT in the "dll_test" example, we will be able to apply the
solution to the pthreads library. Until then, I have to give up on the
pthreads with static CRT (our production code uses DLL CRT, fortunately


The attached archive "" contains summary of tests I have
performed with the pthreads. Excel table contains two sheets, one
the makefiles used in tests and the other one describing the actual
and their results.

The tests were performed using the "pthreads_test.rb" script (for Ruby
scripting language). Example usage is like this:

ruby pthreads_test.rb c:\temp\pthreads c:\temp\tst01 prj_ab tst_a VSE

This causes the script to copy the sources located in c:\temp\pthreads
to a
new location, c:\temp\tst01. Then it replaces c:\temp\tst01\Makefile
with a
copy of "prj_ab" (renamed to Makefile) and also replaces
c:\temp\tst01\tests\Makefile with a copy of "tst_a" (also renamed to
Makefile). Afterwards it proceeds in making a clean build of pthreads
library and a clean build of the tests.

This automation enabled me to perform all 16 tests using only two base
directories (one for the original pthreads snapshot, the other one for
pthreads version), 6 makefiles (i.e. perform optimisation or not, use
CRT or DLL CRT, etc.) by simply invoking the script with different

I hope this mail is clear enough and that some of the points will
to a better quality of the pthreads library. If you have any
questions/comments/flames :), please contact me. I am looking forward to
hearing from you.

Best regards,

	Milan Gardian, Styrker Leibinger (TatraMed)

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]