summaryrefslogtreecommitdiff
path: root/cpp/src/IceGrid/Allocatable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/IceGrid/Allocatable.cpp')
-rw-r--r--cpp/src/IceGrid/Allocatable.cpp529
1 files changed, 529 insertions, 0 deletions
diff --git a/cpp/src/IceGrid/Allocatable.cpp b/cpp/src/IceGrid/Allocatable.cpp
new file mode 100644
index 00000000000..5783263aa8a
--- /dev/null
+++ b/cpp/src/IceGrid/Allocatable.cpp
@@ -0,0 +1,529 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include <IceGrid/Allocatable.h>
+#include <IceGrid/SessionI.h>
+
+using namespace std;
+using namespace IceGrid;
+
+AllocationRequest::~AllocationRequest()
+{
+}
+
+bool
+AllocationRequest::pending()
+{
+ Lock sync(*this);
+ assert(_state == Initial);
+
+ if(_timeout == 0)
+ {
+ _state = Canceled;
+ canceled(AllocationTimeoutException());
+ return false;
+ }
+ else if(!_session->addAllocationRequest(this))
+ {
+ _state = Canceled;
+ canceled(AllocationException("session destroyed"));
+ return false;
+ }
+
+ if(_timeout > 0)
+ {
+ try
+ {
+ _session->getTimer()->schedule(this, IceUtil::Time::milliSeconds(_timeout));
+ }
+ catch(const IceUtil::Exception&)
+ {
+ // Ignore, timer is destroyed because of shutdown
+ }
+ }
+ _state = Pending;
+ return true;
+}
+
+bool
+AllocationRequest::allocate(const AllocatablePtr& allocatable, const SessionIPtr& session)
+{
+ Lock sync(*this);
+ switch(_state)
+ {
+ case Initial:
+ break;
+ case Canceled:
+ return false;
+ case Pending:
+ if(_timeout > 0)
+ {
+ _session->getTimer()->cancel(this);
+ }
+ _session->removeAllocationRequest(this);
+ break;
+ case Allocated:
+ assert(false);
+ break;
+ }
+
+ //
+ // Check if the allocatable is already allocated by the session.
+ //
+ if(_session == session)
+ {
+ _state = Canceled;
+ canceled(AllocationException("already allocated by the session"));
+ return false;
+ }
+ else
+ {
+ _state = Allocated;
+ return true;
+ }
+}
+
+void
+AllocationRequest::cancel(const AllocationException& ex)
+{
+ Lock sync(*this);
+ switch(_state)
+ {
+ case Initial:
+ break;
+ case Canceled:
+ case Allocated:
+ return;
+ case Pending:
+ if(_timeout > 0)
+ {
+ _session->getTimer()->cancel(this);
+ }
+ _session->removeAllocationRequest(this);
+ break;
+ }
+
+ _state = Canceled;
+ canceled(ex);
+}
+
+void
+AllocationRequest::runTimerTask() // TimerTask::runTimerTask() method implementation
+{
+ Lock sync(*this);
+ switch(_state)
+ {
+ case Initial:
+ assert(false);
+ case Canceled:
+ case Allocated:
+ return;
+ case Pending:
+ _session->removeAllocationRequest(this);
+ break;
+ }
+
+ _state = Canceled;
+ canceled(AllocationTimeoutException());
+}
+
+bool
+AllocationRequest::isCanceled() const
+{
+ Lock sync(*this);
+ return _state == Canceled;
+}
+
+bool
+AllocationRequest::operator<(const AllocationRequest& r) const
+{
+ return this < &r;
+}
+
+AllocationRequest::AllocationRequest(const SessionIPtr& session) :
+ _session(session),
+ _timeout(_session->getAllocationTimeout()), // The session timeout can be updated so we need to cache it here.
+ _state(Initial)
+{
+}
+
+Allocatable::Allocatable(bool allocatable, const AllocatablePtr& parent) :
+ _allocatable(allocatable || (parent && parent->isAllocatable())),
+ _count(0),
+ _releasing(false)
+{
+ //
+ // COMPILERFIX: the constructor initializaton:
+ //
+ // _parent((parent && parent->isAllocatable()) ? parent : AllocatablePtr())
+ //
+ // doesn't work on HP-UX (aCC: HP ANSI C++ B3910B A.03.56). It
+ // results in a SEGFAULT at runtime.
+ //
+ if(parent && parent->isAllocatable())
+ {
+ const_cast<AllocatablePtr&>(_parent) = parent;
+ }
+ assert(!_parent || _parent->isAllocatable()); // Parent is only set if it's allocatable.
+}
+
+Allocatable::~Allocatable()
+{
+}
+
+void
+Allocatable::checkAllocatable()
+{
+ if(!isAllocatable())
+ {
+ throw AllocationException("not allocatable");
+ }
+}
+
+bool
+Allocatable::allocate(const AllocationRequestPtr& request, bool fromRelease)
+{
+ try
+ {
+ return allocate(request, false, fromRelease);
+ }
+ catch(const SessionDestroyedException&)
+ {
+ return false; // The session was destroyed
+ }
+}
+
+bool
+Allocatable::tryAllocate(const AllocationRequestPtr& request, bool fromRelease)
+{
+ try
+ {
+ return allocate(request, true, fromRelease);
+ }
+ catch(const AllocationException&)
+ {
+ return false; // Not allocatable
+ }
+}
+
+void
+Allocatable::release(const SessionIPtr& session, bool fromRelease)
+{
+ bool isReleased = false;
+ bool hasRequests = false;
+ {
+ Lock sync(*this);
+ if(!fromRelease)
+ {
+ while(_releasing)
+ {
+ wait();
+ }
+ assert(!_releasing);
+ }
+
+ if(!_session || _session != session)
+ {
+ throw AllocationException("can't release object which is not allocated");
+ }
+
+ if(--_count == 0)
+ {
+ _session = 0;
+
+ released(session);
+
+ isReleased = true;
+
+ if(!fromRelease && !_requests.empty())
+ {
+ assert(!_parent);
+ _releasing = true; // Prevent new allocations.
+ hasRequests = true;
+ }
+ }
+ }
+
+ if(isReleased)
+ {
+ releasedNoSync(session);
+ }
+
+ if(_parent)
+ {
+ _parent->release(session, fromRelease);
+ }
+ else if(!fromRelease)
+ {
+ if(hasRequests)
+ {
+ while(true)
+ {
+ AllocationRequestPtr request;
+ AllocatablePtr allocatable;
+ {
+ Lock sync(*this);
+ allocatable = dequeueAllocationAttempt(request);
+ if(!allocatable)
+ {
+ assert(_count == 0 && _requests.empty());
+ _releasing = false;
+ notifyAll();
+ return;
+ }
+ }
+
+ //
+ // Try to allocate the allocatable with the request or if
+ // there's no request, just notify the allocatable that it can
+ // be allocated again.
+ //
+ if((request && allocatable->allocate(request, true)) || (!request && allocatable->canTryAllocate()))
+ {
+ while(true)
+ {
+ {
+ Lock sync(*this);
+ assert(_count);
+
+ allocatable = 0;
+ request = 0;
+
+ //
+ // Check if there's other requests from the session
+ // waiting to allocate this allocatable.
+ //
+ list<pair<AllocatablePtr, AllocationRequestPtr> >::iterator p = _requests.begin();
+ while(p != _requests.end())
+ {
+ if(p->second && p->second->getSession() == _session)
+ {
+ allocatable = p->first;
+ request = p->second;
+ _requests.erase(p);
+ break;
+ }
+ ++p;
+ }
+ if(!allocatable)
+ {
+ _releasing = false;
+ notifyAll();
+ return; // We're done, the allocatable was released (but is allocated again)!
+ }
+ }
+
+ assert(allocatable && request);
+ allocatable->allocate(request, true);
+ }
+ }
+ }
+ }
+ else if(isReleased)
+ {
+ canTryAllocate(); // Notify that this allocatable can be allocated.
+ }
+ }
+}
+
+SessionIPtr
+Allocatable::getSession() const
+{
+ Lock sync(*this);
+ return _session;
+}
+
+bool
+Allocatable::operator<(const Allocatable& r) const
+{
+ return this < &r;
+}
+
+void
+Allocatable::queueAllocationAttempt(const AllocatablePtr& allocatable,
+ const AllocationRequestPtr& request,
+ bool tryAllocate)
+{
+ assert(!_parent);
+ if(!tryAllocate)
+ {
+ if(request->pending())
+ {
+ _requests.push_back(make_pair(allocatable, request));
+ }
+ }
+ else
+ {
+ _requests.push_back(make_pair(allocatable, AllocationRequestPtr()));
+ }
+}
+
+void
+Allocatable::queueAllocationAttemptFromChild(const AllocatablePtr& allocatable)
+{
+ if(_parent)
+ {
+ _parent->queueAllocationAttemptFromChild(allocatable);
+ return;
+ }
+
+ Lock sync(*this);
+ _requests.push_back(make_pair(allocatable, AllocationRequestPtr()));
+}
+
+AllocatablePtr
+Allocatable::dequeueAllocationAttempt(AllocationRequestPtr& request)
+{
+ if(_requests.empty())
+ {
+ return 0;
+ }
+
+ pair<AllocatablePtr, AllocationRequestPtr> alloc = _requests.front();
+ _requests.pop_front();
+ if(alloc.second)
+ {
+ request = alloc.second;
+ }
+ return alloc.first;
+}
+
+bool
+Allocatable::allocate(const AllocationRequestPtr& request, bool tryAllocate, bool fromRelease)
+{
+ if(_parent && !_parent->allocateFromChild(request, this, tryAllocate, fromRelease))
+ {
+ return false;
+ }
+
+ bool queueWithParent = false;
+ int allocationCount = 0;
+ try
+ {
+ Lock sync(*this);
+ checkAllocatable();
+
+ if(!_session && (fromRelease || !_releasing))
+ {
+ if(request->allocate(this, _session))
+ {
+ try
+ {
+ allocated(request->getSession()); // This might throw SessionDestroyedException
+ }
+ catch(const SessionDestroyedException&)
+ {
+ request->canceled(AllocationException("session destroyed"));
+ throw;
+ }
+ assert(_count == 0);
+ _session = request->getSession();
+ request->allocated(this, request->getSession());
+ ++_count;
+ allocationCount = _count;
+ }
+ }
+ else if(_session == request->getSession())
+ {
+ if(!tryAllocate)
+ {
+ if(request->allocate(this, _session))
+ {
+ assert(_count > 0);
+ ++_count;
+ request->allocated(this, _session);
+ allocationCount = _count;
+ }
+ }
+ else
+ {
+ queueWithParent = true;
+ }
+ }
+ else
+ {
+ queueAllocationAttempt(this, request, tryAllocate);
+ }
+ }
+ catch(const SessionDestroyedException& ex)
+ {
+ if(_parent)
+ {
+ _parent->release(request->getSession(), fromRelease);
+ }
+ throw ex;
+ }
+ catch(const AllocationException& ex)
+ {
+ if(_parent)
+ {
+ _parent->release(request->getSession(), fromRelease);
+ }
+ throw ex;
+ }
+
+ if(allocationCount == 1)
+ {
+ allocatedNoSync(request->getSession());
+ }
+ else if(allocationCount == 0 && _parent)
+ {
+ if(queueWithParent)
+ {
+ _parent->queueAllocationAttemptFromChild(this);
+ }
+ _parent->release(request->getSession(), fromRelease);
+ }
+ return allocationCount > 0;
+}
+
+bool
+Allocatable::allocateFromChild(const AllocationRequestPtr& request,
+ const AllocatablePtr& child,
+ bool tryAllocate,
+ bool fromRelease)
+{
+ if(_parent && !_parent->allocateFromChild(request, child, tryAllocate, fromRelease))
+ {
+ return false;
+ }
+
+ int allocationCount = 0;
+ {
+ Lock sync(*this);
+ if((!_session || _session == request->getSession()) && (fromRelease || !_releasing))
+ {
+ if(!_session)
+ {
+ try
+ {
+ allocated(request->getSession());
+ }
+ catch(const SessionDestroyedException&)
+ {
+ // Ignore
+ }
+ }
+ _session = request->getSession();
+ ++_count;
+ allocationCount = _count;
+ }
+ else
+ {
+ queueAllocationAttempt(child, request, tryAllocate);
+ }
+ }
+
+ if(allocationCount == 1)
+ {
+ allocatedNoSync(request->getSession());
+ }
+ return allocationCount > 0;
+}
+