Boost logo

Boost :

From: Alexander Terekhov (terekhov_at_[hidden])
Date: 2003-05-28 08:35:41


<Forward Inline>

-------- Original Message --------
Message-ID: <3ED4B964.89F3483_at_[hidden]>
Newsgroups: comp.lang.c++.moderated
Subject: Re: OO design: Is "errno" Exception?
References: ... <vd79juafslpga3_at_[hidden]>

Early Ehlinger wrote:
>
> "Alexander Terekhov" <terekhov_at_[hidden]> wrote:
> > Early Ehlinger wrote:
> > [...]
> > > An error is an error is an error.
> >
> > No. "shall fail" POSIX errors are meant to be thrown as exceptions
> > but "may fail" POSIX errors are just *your* (or implementors) BUGS.
>
> Who exactly meant that "shall fail" POSIX errors are to be thrown as
> exceptions? POSIX? I doubt it; unlessed I missed something, POSIX has
> no concept of exceptions.

POSIX (SUS) systems have a concept of thread cancelation and exit.
That's surely an exception in C++ or C-with-exceptions; do a search
on "pthread_cancel_e". ;-)

>
> It seems to me that you haven't really made a distinction. If a "may
> fail" error is indeed a bug, then it should be thrown as an exception
> so that the program containing the bug will fail gloriously :) Why
> would "may fail" be any different than "shall fail" in terms of
>
> > Here's an illustration (BTW, "more on this" can be found in my sig).
>
> I'm not sure how this code illustrates your point... Would you mind
> being more specific?

No problem. Imagine a system interface for TSD access that doesn't
throw anything and just returns "status" info:

  typedef void * __TSD_key_t; // whatever

   int __tsd_key_create(
       __TSD_key_t *
     , void (* dtor)(void *, void *), void *
   ) throw();

  int __tsd_key_delete(
      __TSD_key_t
  ) throw();

  int __tsd_getspecific(
      __TSD_key_t
    , void * *
    ) throw();

  int __tsd_setspecific(
      __TSD_key_t
    , void *
    ) throw();

Now let's try to develop a <thread>'s thread_specific_ptr<> beast:

namespace std {

  struct no_cleanup {
    void operator()(void *) {
      // NOOP
    }
  };

  template<typename cleanup>
  bool no_TSD_cleanup(const cleanup &) throw() {
    return false;
  }

  template<>
  bool no_TSD_cleanup(const no_cleanup &) throw() {
    return true;
  }

  template<typename T, typename cleanup>
  class thread_specific_ptr : cleanup /* noncopyable */ {

    __TSD_key_t __key;

    static void dtor(void * data, void * THIS) {
      static_cast<thread_specific_ptr*>(THIS)->
        operator()(static_cast<T*>(data));
    }

  public:

    thread_specific_ptr()
        throw(std::bad_alloc, std::try_again);

    thread_specific_ptr(const cleanup&)
        throw(std::bad_alloc, std::try_again);

   ~thread_specific_ptr() throw();

    T * get() throw();

    void set(T *) throw(std::bad_alloc);

    T * operator->() throw();

    T * release() throw();

    void dispose() throw();

    void reset(T *) throw(std::bad_alloc);

  };

}

Now, here's its possible implementation:

namespace std {

  void throw_errno_exception(int error) { /* ... */ }

  template<typename T, typename cleanup>
  thread_specific_ptr<T, cleanup>::thread_specific_ptr()
        throw(std::bad_alloc, std::try_again) {
    int status = __tsd_key_create(&__key, no_TSD_cleanup(
       *static_cast<cleanup*> (this)) ? 0 : &dtor, this);
    if (status) throw_errno_exception(status);
  }

  template<typename T, typename cleanup>
  thread_specific_ptr<T, cleanup>::thread_specific_ptr(
        const cleanup& __cleanup) throw(std::bad_alloc,
        std::try_again) : cleanup(__cleanup) {
    int status = __tsd_key_create(&__key, no_TSD_cleanup(
       __cleanup) ? 0 : &dtor, this);
    if (status) throw_errno_exception(status);
  }

  template<typename T, typename cleanup>
  thread_specific_ptr<T, cleanup>::~thread_specific_ptr()
        throw() {
    int status = __tsd_key_delete(__key);
    if (status) throw_errno_exception(status);
  }

  template<typename T, typename cleanup>
  T * thread_specific_ptr<T, cleanup>::get() throw() {
    void * p;
    int status = __tsd_getspecific(__key, &p);
    if (status) throw_errno_exception(status);
    return p;
  }

  template<typename T, typename cleanup>
  void thread_specific_ptr<T, cleanup>::set(T* p)
        throw(std::bad_alloc) {
    int status = __tsd_setspecific(__key, p);
    if (status) throw_errno_exception(status);
  }

  template<typename T, typename cleanup>
  T * thread_specific_ptr<T, cleanup>::release() throw() {
    void * p = get();
    set(0);
    return p;
  }

  template<typename T, typename cleanup>
  void thread_specific_ptr<T, cleanup>::dispose() throw() {
    this->cleanup::operator()(release());
  }

  template<typename T, typename cleanup>
  void thread_specific_ptr<T, cleanup>::reset(T* p)
        throw(std::bad_alloc) {
    void * old_p = get();
    if (old_p != p) {
      set(p);
      this->cleanup::operator()(old_p);
    }
  }

}

Now, the only thing that's still missing is a <cthread> layer...

Here we go:

// Possible implementation for <cthread>
#include <thread>

namespace std {

  extern "C" typedef void (* __c_TSD_dtor_t)(void *);
  extern "C++" typedef void (* __cpp_TSD_dtor_t)(void *);

  struct __cthread_TSD_cleanup {

    __cthread_TSD_cleanup(__c_TSD_dtor_t __c_TSD_dtor_) :
        __func(__c_TSD_dtor_ ? c : null),
        __c_TSD_dtor(__c_TSD_dtor_) {
    }

    __cthread_TSD_cleanup(__cpp_TSD_dtor_t __cpp_TSD_dtor_) :
        __func(__cpp_TSD_dtor_ ? cpp : null),
        __cpp_TSD_dtor(__cpp_TSD_dtor_) {
    }

    void operator()(void * __data) {
      if (__data) switch(__func) {
        case c: __c_TSD_dtor(__data); break;
        case cpp: __cpp_TSD_dtor(__data); break;
      }
    }

    enum { null, c, cpp } __func;

    union {
      __c_TSD_dtor_t __c_TSD_dtor;
      __cpp_TSD_dtor_t __cpp_TSD_dtor;
    };

  };

  template<>
  bool no_TSD_cleanup(const __cthread_TSD_cleanup & __cleanup)
        throw() {
    return __cleanup.__func == __cthread_TSD_cleanup::null;
  }

  typedef std::thread_specific_ptr<void, __cthread_TSD_cleanup> *
        pthread_key_t;

  // try { throw; } catch... "idiom"
  int __translate_exception_to_error_code() throw();

  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 std::thread_specific_ptr<void,
        __cthread_TSD_cleanup>(__cthread_TSD_cleanup(dtor));
      // "may fail" shall be caught in the std::unexpected() handler
      // for details... please click here: <http://tinyurl.com/cu9k>
    }
    catch(...) {
      return __translate_exception_to_error_code();
    }
    return 0;
  }

  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 std::thread_specific_ptr<void,
        __cthread_TSD_cleanup>(__cthread_TSD_cleanup(dtor));
      // "may fail" shall be caught in the std::unexpected() handler
      // for details... please click here: <http://tinyurl.com/cu9k>
    }
    catch(...) {
      return __translate_exception_to_error_code();
    }
    return 0;
  }

  extern "C" int pthread_key_delete(pthread_key_t key) throw() {
    // "may fail" shall be caught in the std::unexpected() handler
    // for details... please click here: <http://tinyurl.com/cu9k>
    delete key;
    return 0;
  }

  extern "C" void * pthread_getspecific(pthread_key_t key) throw() {
    // "may fail" shall be caught in the std::unexpected() handler
    // for details... please click here: <http://tinyurl.com/cu9k>
    return key->get();
  }

  extern "C" int pthread_setspecific(pthread_key_t key,
                                     const void * p) throw(std::bad_alloc) {
    try {
      // can throw "shall fail" stuff only (std::bad_alloc)
      key->set(const_cast<void *>(p));
      // "may fail" shall be caught in the std::unexpected() handler
      // for details... please click here: <http://tinyurl.com/cu9k>
    }
    catch(...) {
      return __translate_exception_to_error_code();
    }
    return 0;
  }

  extern "C" int pthread_resetspecific(pthread_key_t key,
                                       const void * p) throw(std::bad_alloc) {
    try {
      // can throw "shall fail" stuff only (std::bad_alloc)
      key->reset(const_cast<void *>(p));
      // "may fail" shall be caught in the std::unexpected() handler
      // for details... please click here: <http://tinyurl.com/cu9k>
    }
    catch(...) {
      return __translate_exception_to_error_code();
    }
    return 0;
  }

  extern "C" void * pthread_releasespecific(pthread_key_t key) throw() {
    // "may fail" shall be caught in the std::unexpected() handler
    // for details... please click here: <http://tinyurl.com/cu9k>
    return key->release();
  }

  extern "C" void pthread_disposespecific(pthread_key_t key) throw() {
    // "may fail" shall be caught in the std::unexpected() handler
    // for details... please click here: <http://tinyurl.com/cu9k>
    return key->dispose();
  }

}

("or something like that")

Do you understand it now? ;-)

>
> > T * release() throw() {
> > T * p = get();
> > if (p) set(0); // only an idiot will throw std::bad_alloc here
> > return p;
> > }
>
> At the risk of being an idiot, I'm going to beg to differ.
>
> set() uses pthread_setspecific, which only fails with [ENOMEM] when
> insufficient memory exists to associate the value with the key (at
> least according to opengroup.org).
>
> So you're saying that if set(0) fails, then it's ok to just ignore
> the error? If release() is meant to release ownership (in the same
> sense that auto_ptr<>::release() releases ownership) then this is a
> terrible thing to do. What you will be left with is two entities,
> "this" and "the caller", that both think they exclusively own "T*"!!!
> This is absolutely horrid and the epitomy of pure evil.

Please follow the links:

http://lists.boost.org/MailArchives/boost/msg47702.php
(Subject: [boost] Re: thread lib: thread specific storage problem)

Thank you.

regards,
alexander.

--
"// Possible implementation for <pthread.h> and <cthread>
 #include <thread>
 // for <cthread>, please remove the using-directive
 using namespace std;"
                                                   -- "alt"

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk