<Forward Inline> -------- Original Message -------- Message-ID: <[EMAIL PROTECTED]> Newsgroups: comp.lang.c++.moderated Subject: Re: OO design: Is "errno" Exception? References: ... <[EMAIL PROTECTED]>
Early Ehlinger wrote: > > "Alexander Terekhov" <[EMAIL PROTECTED]> 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" _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost