// $Id$ #ifndef ACE_FUTURE_CPP #define ACE_FUTURE_CPP #include "ace/Future.h" #if !defined (ACE_LACKS_PRAGMA_ONCE) # pragma once #endif /* ACE_LACKS_PRAGMA_ONCE */ ACE_RCSID (ace, Future, "$Id$") #if defined (ACE_HAS_THREADS) template ACE_Future_Holder::ACE_Future_Holder (void) { } template ACE_Future_Holder::ACE_Future_Holder (const ACE_Future &item) : item_ (item) { } template ACE_Future_Holder::~ACE_Future_Holder (void) { } template ACE_Future_Observer::ACE_Future_Observer (void) { } template ACE_Future_Observer::~ACE_Future_Observer (void) { } // Dump the state of an object. template void ACE_Future_Rep::dump (void) const { ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); ACE_DEBUG ((LM_DEBUG, "ref_count_ = %d\n", (int) this->ref_count_)); ACE_DEBUG ((LM_INFO,"value_: \n")); if (this->value_) ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" (NON-NULL)\n"))); else ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" (NULL)\n"))); ACE_DEBUG ((LM_INFO,"value_ready_: \n")); this->value_ready_.dump (); ACE_DEBUG ((LM_INFO,"value_ready_mutex_: \n")); this->value_ready_mutex_.dump (); ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); } template ACE_Future_Rep * ACE_Future_Rep::create (void) { // Yes set ref count to zero. return new ACE_Future_Rep (); } template ACE_Future_Rep * ACE_Future_Rep::attach (ACE_Future_Rep*& rep) { ACE_ASSERT (rep != 0); // Use value_ready_mutex_ for both condition and ref count management ACE_MT (ACE_Guard r_mon (rep->value_ready_mutex_)); ++rep->ref_count_; return rep; } template void ACE_Future_Rep::detach (ACE_Future_Rep*& rep) { ACE_ASSERT (rep != 0); // Use value_ready_mutex_ for both condition and ref count management ACE_MT (ACE_GUARD (ACE_Thread_Mutex, r_mon, rep->value_ready_mutex_)); if (rep->ref_count_-- == 0) { ACE_MT (r_mon.release ()); // We do not need the lock when deleting the representation. // There should be no side effects from deleting rep and we don // not want to release a deleted mutex. delete rep; } } template void ACE_Future_Rep::assign (ACE_Future_Rep*& rep, ACE_Future_Rep* new_rep) { ACE_ASSERT (rep != 0); ACE_ASSERT (new_rep != 0); // Use value_ready_mutex_ for both condition and ref count management ACE_MT (ACE_GUARD (ACE_Thread_Mutex, r_mon, rep->value_ready_mutex_)); ACE_Future_Rep* old = rep; rep = new_rep; // detached old last for exception safety if (old->ref_count_-- == 0) { ACE_MT (r_mon.release ()); // We do not need the lock when deleting the representation. // There should be no side effects from deleting rep and we don // not want to release a deleted mutex. delete old; } } template ACE_Future_Rep::ACE_Future_Rep (void) : value_ (0), ref_count_ (0), value_ready_ (this->value_ready_mutex_) { } template ACE_Future_Rep::~ACE_Future_Rep (void) { delete this->value_; } template int ACE_Future_Rep::ready (void) { return this->value_ != 0; } template int ACE_Future_Rep::set (const T &r, ACE_Future &caller) { // If the value is already produced, ignore it... if (this->value_ == 0) { ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->value_ready_mutex_, -1)); // Otherwise, create a new result value. Note the use of the // Double-checked locking pattern to avoid multiple allocations. if (this->value_ == 0) // Still no value, so proceed { ACE_NEW_RETURN (this->value_, T (r), -1); // Remove and notify all subscribed observers. ACE_TYPENAME OBSERVER_COLLECTION::iterator iterator = this->observer_collection_.begin (); ACE_TYPENAME OBSERVER_COLLECTION::iterator end = this->observer_collection_.end (); for (; iterator != end; ++iterator) { OBSERVER *observer = *iterator; observer->update (caller); } // Signal all the waiting threads. return this->value_ready_.broadcast (); } // Destructor releases the lock. } return 0; } template int ACE_Future_Rep::get (T &value, ACE_Time_Value *tv) { // If the value is already produced, return it. if (this->value_ == 0) { ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->value_ready_mutex_, -1)); // If the value is not yet defined we must block until the // producer writes to it. while (this->value_ == 0) // Perform a timed wait. if (this->value_ready_.wait (tv) == -1) return -1; // Destructor releases the lock. } value = *this->value_; return 0; } template int ACE_Future_Rep::attach (ACE_Future_Observer *observer, ACE_Future &caller) { ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->value_ready_mutex_, -1)); // Otherwise, create a new result value. Note the use of the // Double-checked locking pattern to avoid corrupting the list. int result = 1; // If the value is already produced, then notify observer if (this->value_ == 0) { result = this->observer_collection_.insert (observer); } else observer->update (caller); return result; } template int ACE_Future_Rep::detach (ACE_Future_Observer *observer) { ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->value_ready_mutex_, -1)); // Remove all occurrences of the specified observer from this // objects hash map. return this->observer_collection_.remove (observer); } template ACE_Future_Rep::operator T () { // If the value is already produced, return it. if (this->value_ == 0) { // Constructor of ace_mon acquires the mutex. ACE_MT (ACE_GUARD_RETURN (ACE_Thread_Mutex, ace_mon, this->value_ready_mutex_, 0)); // If the value is not yet defined we must block until the // producer writes to it. // Wait ``forever.'' while (this->value_ == 0) if (this->value_ready_.wait () == -1) // What to do in this case since we've got to indicate // failure somehow? Exceptions would be nice, but they're // not portable... return 0; // Destructor releases the mutex } return *this->value_; } template ACE_Future::ACE_Future (void) : future_rep_ (FUTURE_REP::create ()) { } template ACE_Future::ACE_Future (const ACE_Future &r) : future_rep_ (FUTURE_REP::attach (((ACE_Future &) r).future_rep_)) { } template ACE_Future::ACE_Future (const T &r) : future_rep_ (FUTURE_REP::create ()) { ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" (%t) funny constructor\n"))); this->future_rep_->set (r, *this); } template ACE_Future::~ACE_Future (void) { FUTURE_REP::detach (future_rep_); } template int ACE_Future::operator== (const ACE_Future &r) const { return r.future_rep_ == this->future_rep_; } template int ACE_Future::operator!= (const ACE_Future &r) const { return r.future_rep_ != this->future_rep_; } template int ACE_Future::cancel (const T &r) { this->cancel (); return this->future_rep_->set (r, *this); } template int ACE_Future::cancel (void) { // If this ACE_Future is already attached to a ACE_Future_Rep, // detach it (maybe delete the ACE_Future_Rep). FUTURE_REP::assign (this->future_rep_, FUTURE_REP::create ()); return 0; } template int ACE_Future::set (const T &r) { // Give the pointer to the result to the ACE_Future_Rep. return this->future_rep_->set (r, *this); } template int ACE_Future::ready (void) { // We're ready if the ACE_Future_rep is ready... return this->future_rep_->ready (); } template int ACE_Future::get (T &value, ACE_Time_Value *tv) { // We return the ACE_Future_rep. return this->future_rep_->get (value, tv); } template int ACE_Future::attach (ACE_Future_Observer *observer) { return this->future_rep_->attach (observer, *this); } template int ACE_Future::detach (ACE_Future_Observer *observer) { return this->future_rep_->detach (observer); } template ACE_Future::operator T () { // note that this will fail (and COREDUMP!) // if future_rep_ == 0 ! // // but... // this is impossible unless somebody is so stupid to // try something like this: // // Future futT; // T t; // t = futT; // perform type conversion on Future_Rep. return *future_rep_; } template void ACE_Future::operator = (const ACE_Future &rhs) { // assignment: // // bind to the same as . // This will work if &r == this, by first increasing the ref count ACE_Future &r = (ACE_Future &) rhs; FUTURE_REP::assign (this->future_rep_, FUTURE_REP::attach (r.future_rep_)); } template void ACE_Future::dump (void) const { ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); if (this->future_rep_) this->future_rep_->dump (); ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); } template ACE_Future_Rep * ACE_Future::get_rep () { return this->future_rep_; } template void * ACE_Future::operator new (size_t) { ACE_throw_bad_alloc; #if defined (__HP_aCC) return 0; #endif /* 0 */ } template void ACE_Future::operator delete (void *) { } template void ACE_Future::operator & () { } #endif /* ACE_HAS_THREADS */ #endif /* ACE_FUTURE_CPP */