#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 */ #if defined (ACE_HAS_THREADS) # include "ace/Guard_T.h" # include "ace/Recursive_Thread_Mutex.h" ACE_BEGIN_VERSIONED_NAMESPACE_DECL ACE_ALLOC_HOOK_DEFINE_Tc(ACE_Future_Holder) ACE_ALLOC_HOOK_DEFINE_Tc(ACE_Future_Observer) ACE_ALLOC_HOOK_DEFINE_Tc(ACE_Future_Rep) ACE_ALLOC_HOOK_DEFINE_Tc(ACE_Future) 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 () const { #if defined (ACE_HAS_DUMP) ACELIB_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); ACELIB_DEBUG ((LM_DEBUG, "ref_count_ = %d\n", (int) this->ref_count_)); ACELIB_DEBUG ((LM_INFO,"value_:\n")); if (this->value_) ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT (" (NON-NULL)\n"))); else //FUZZ: disable check_for_NULL ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT (" (NULL)\n"))); //FUZZ: enable check_for_NULL ACELIB_DEBUG ((LM_INFO,"value_ready_:\n")); this->value_ready_.dump (); ACELIB_DEBUG ((LM_INFO,"value_ready_mutex_:\n")); this->value_ready_mutex_.dump (); ACELIB_DEBUG ((LM_DEBUG, ACE_END_DUMP)); #endif /* ACE_HAS_DUMP */ } template ACE_Future_Rep * ACE_Future_Rep::internal_create (void) { ACE_Future_Rep *temp = 0; ACE_NEW_RETURN (temp, ACE_Future_Rep (), 0); return temp; } template ACE_Future_Rep * ACE_Future_Rep::create (void) { // Yes set ref count to zero. ACE_Future_Rep *temp = internal_create (); if (!temp) throw std::bad_alloc (); return temp; } 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_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, r_mon, rep->value_ready_mutex_, 0); ++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_GUARD (ACE_SYNCH_RECURSIVE_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_GUARD (ACE_SYNCH_RECURSIVE_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_ (value_ready_mutex_) { } template ACE_Future_Rep::~ACE_Future_Rep (void) { delete this->value_; } template int ACE_Future_Rep::ready () const { 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_GUARD_RETURN (ACE_SYNCH_RECURSIVE_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. typename OBSERVER_COLLECTION::iterator iterator = this->observer_collection_.begin (); typename OBSERVER_COLLECTION::iterator end = this->observer_collection_.end (); while (iterator != end) { OBSERVER *observer = *iterator++; if (observer) { 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) const { // If the value is already produced, return it. if (this->value_ == 0) { ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_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_GUARD_RETURN (ACE_SYNCH_RECURSIVE_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_GUARD_RETURN (ACE_SYNCH_RECURSIVE_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_GUARD_RETURN (ACE_SYNCH_RECURSIVE_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 ()) { this->future_rep_->set (r, *this); } template ACE_Future::~ACE_Future (void) { FUTURE_REP::detach (future_rep_); } template bool ACE_Future::operator== (const ACE_Future &r) const { return r.future_rep_ == this->future_rep_; } template bool 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 () const { // 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) const { // 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 () const { #if defined (ACE_HAS_DUMP) ACELIB_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); if (this->future_rep_) this->future_rep_->dump (); ACELIB_DEBUG ((LM_DEBUG, ACE_END_DUMP)); #endif /* ACE_HAS_DUMP */ } template ACE_Future_Rep * ACE_Future::get_rep () { return this->future_rep_; } ACE_END_VERSIONED_NAMESPACE_DECL #endif /* ACE_HAS_THREADS */ #endif /* ACE_FUTURE_CPP */