|
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