From: Alexander Terekhov (terekhov_at_[hidden])
Date: 2003-05-26 05:12:15


Alexander Terekhov wrote:
>
> "William E. Kempf" wrote:
> [...]
> > >> How about moving this discussion to c.p.t.?
> > >
> > > Well, just in case... <Forward Quoted>
> >
> > Thanks... I currently can't access c.p.t. in any reasonable manner. I'm
> > working to rectify this,
>
> http://news.cis.dfn.de might help. ;-)
>
> but in the mean time, I appreciate the cross
> > post.
>
> I'll wait a day or two and post a reply addressing some of your points
> to comp.programming.threads.

"just in case..." <Forward Inline>

Alexander Terekhov wrote:
>
> <Forward-Quoted source=Boost>
>
> "William E. Kempf" wrote:
[...]
> > >> The big hurdle for a true C++ binding is that the current state of
> > >> affairs is "good enough" for most people, and the political process of
> > >> developing a full native C++ binding would be painful. (Remember, it's
> > >> not just saying that the thread::create method takes a class member at
> > >> which the thread will start... it means reviewing every method and
> > >> template in the STL to determine which have thread safety
> > >> requirements, and deciding precisely what those requirements are and
> > >> how to meet them. Then there's the matter of cancellation points...
> > >> and so forth.)
> >
> > Most STL libraries are thread-safe today, so the analysis there wouldn't
> > be too difficult.

Well, <http://listarchives.boost.org/MailArchives/boost/msg47701.php>.
 
> > It just needs to be stated explicitly in the standard.
> > Cancellation points are another issue... but I don't think C++ will add
> > too many to the list already provided by POSIX.

The current C++ Std is in conflict with POSIX, to begin with. The C++
standard says that almost the entire C library doesn't throw (in effect,
everything is throw()) except just a few calls for which C++ headers
provide extern "C++" overloading to facilitate C++ callbacks. In POSIX,
a whole bunch of standard C library functions DO "throw" due to thread
cancelation. Isn't that funny?

> >
> > >> When and if the C++ standard adds true thread support, that will be,
> > >> by default and in practice, the thread binding for C++; whether the
> > >> underlying thread environment is POSIX, Win32, or something else. This
> > >> is great, as long as it doesn't do or say anything stupid, but it
> > >> still leaves a few loopholes because inevitably people will continue
> > >> to write applications that mix languages. Mixing C and C++ has never
> > >> been a problem; but if the thread model in C++ is radically different,
> > >> it could become a problem.
> >
> > Absolutely agreed. I've said all along that Boost.Threads has to be very
> > aware of what POSIX says. We can not deviate in any way from POSIX that
> > will result in conflicts between the threading systems.

Bill, Boost.Threads shall end up in standard <thread> header. But there
should also be a <cthread> standard header (it should penetrate ISO C as
well; in the form of either <pthread.h> or <cthread.h> header). Now, in
the past, I was thinking of <thread> as "just" an object layer on top of
<cthread> stuff. That was wrong. <cthread> should be fully implementable
using stuff from <thread>. I mean things along the lines of:

   typedef std::thread * pthread_t;

   extern "C" pthread_t pthread_self() throw() {
     return std::thread_self().raw_ptr();
   }

   typedef thread_specific_ptr<void, __c_TSD_cleanup> * pthread_key_t;

   extern "C" int pthread_key_create(pthread_key_t * key,
                                     void ( * dtor)(void *)) throw() {
     try {
       // can throw "shall fail" stuff only (std::bad_alloc and std::try_again)
       *key = new pthread_key_t(__c_TSD_cleanup(dtor));
       // "may fail" shall be caught in the std::unexpected() handler
     }
     catch(...) {
       return __translate_exception_to_error_code_using_throw_and_catch();
     }
     return 0;
   }

   typedef std::aligned_storage<std::mutex> pthread_mutex_t;
   typedef std::aligned_storage<std::mutexattr_t> pthread_mutexattr_t;

   extern "C" int pthread_mutex_init(pthread_mutex_t * mutex_storage,
                            const pthread_mutexattr_t * attr_storage) throw() {
     try {
       // can throw "shall fail" stuff only
       new (mutex_storage->place()) std::mutex(attr_storage->object());
       // "may fail" shall be caught in the std::unexpected() handler
     }
     catch(...) {
       return __translate_exception_to_error_code_using_throw_and_catch();
     }
     return 0;
   }

   extern "C" int pthread_mutex_destroy(pthread_mutex_t * mutex_storage) throw() {
     // destructor with implicit throw()-nothing ES
     ~mutex_storage->object();
     // "may fail" shall be caught in the std::unexpected() handler
     return 0;
   }

   #define PTHREAD_CANCELED std::thread_canceled()

   extern "C" void pthread_exit(void * ptr) throw(std::thread_termination_request) {
     (ptr == PTHREAD_CANCELED) ? std::thread_cancel() : std::thread_exit(ptr);
   }

using stuff from <thread>:

  struct thread_canceled {
    operator void * () { return &unique; }
    static thread_canceled unique;
  };

  template<typename T>
  void thread_exit(T value) {
    assert(std::thread_self().can_exit_with<T>());
    throw thread_exit_value(value);
  }

  template<>
  void thread_exit(std::thread_canceled) {
    thread_cancel();
  }

  void thread_cancel() {
    throw std::thread_cancel_request();
  }

or something like that.

> >
> > >> Furthermore, there's a missing piece that
> > >> neither POSIX 1003.1-2001 plus ISO C++ 2005 (or whatever), or even
> > >> 1003.1-2001 plus a hypothetical "1003.C++" will necessarily (or even
> > >> likely) supply -- and that's how the two interoperate.
> >
> > Agreed, but that's the case today when mixing languages. There are a lot
> > of areas which are left unspecified, even when the languages being mixed
> > are C and C++.

C and C++ WILL merge, sooner or later. The separation of C and C++
languages is/was the stupidest thing C++ authors ever did (or allowed
to happen). Well, just look what's going on with respect to GCC/G++
(adding exceptions and C-destructors IS kinda "merging", I believe).

> >
> > >> If C++ or 1003.C++ says that thread::cancel raises an exception, and
> > >> 1003.1 says that pthread_cancel() invokes cleanup handlers, does that
> > >> mean that cancelling a thread with pthread_cancel() will trigger
> > >> catch(...), or even destructors? Well, maybe not. This could more
> > >> easily be solved with a 1003.C++, perhaps, since at least the two
> > >> standards are in a family. Since the C++ standard is unlikely to
> > >> mention POSIX any more than now, it's unlikely to provide any
> > >> guarantees.
> >
> > No gaurantees. But with a C++ definition, one can at least hope that
> > implementations will try and deal with these cross-language binding issues
> > in some reasonable manner.

*You* should do it for "C subset of C++" (incompatibilities and
"restrict"-like extensions of ISO-C aside for a moment). There will
be NO problems with cross-C/C++-language bindings if Boost would come
up with some "reference implementation" (see above) of <cthread>
(i.e. also <pthread.h>) using stuff from the REAL <thread> header.

You did not seem to like to "force" windows user of Boost.Threads to
download pthreads-win32 and use Boost.Threads on top of it. YOU'RE
RIGHT! Pthreads-win32 will be shutdown as soon as you'll deliver a
<cthread> and <pthread.h> beasts implemented using stuff from the
"boosted" <thread>. And, perhaps, then even Microsoft will follow...
delivering a much better implementation of BOTH (including fully
supported cancelation for all C and C++ standard calls meant to throw
std:thread_cancel_request exception).

> > >> Perhaps that would provide an opportunity for a smaller POSIX project,
> > >> though; a PROFILE that would chink the holes where the two walls meet.
> > >> In effect, specifying a "POSIX platform" supporting both threads and
> > >> C++ that simply says "C++ cancellation is the same as POSIX
> > >> cancellation", "POSIX cleanup handlers are logically and semantically
> > >> the same as C++ object destructors", and "POSIX cancellation is
> > >> visible as a C++ exception".
> >
> > Reasonable things for POSIX to do, or at least consider, IMO.

Hardcore POSIX folks don't really "think C++", unfortunately. The only
way to get it done is to deliver POSIX threading API implementation "on
top" of Boost.Threads to them, I think. Well, this would also mean a
whole bunch of extensions to the current POSIX threading -- things like
parameterized pthread_once() and TSD destructors (to support shared_ptr
like "custom deleters" without too much overhead), etcetera.

What do you think?

regards,
alexander.