Files
SingularityViewer/indra/aistatemachine/aicondition.cpp
Aleric Inglewood 09708b6318 Added AICondition.
AICondition is like AIThreadSafeSimpleDC (it is derived from it),
and wraps some variable, protecting it with a builtin mutex.

While in a statemachine, one can call wait(condition) to make
the state machine go idle, and call condition.signal() to
wake it (or another) up again.

While normally a condition variable is used as follows:

condition.lock();
while (!whatwerewaitingfor)
{
  condition.wait();
}
// Here the condition is guaranteed to be true and we're
// still in the critical area of the mutex.
condition.unlock();

where the thread blocks in wait(), the statemachine actually
returns CPU to the thread (the AIEngine), so there is no
while loop involved, and our wait() doesn't even unlock
the mutex: that happens because the state machine returns:

condition_wat condition_w(condition);		// Lock condition.
if (!condition_w->whatwerewaitingfor())		// The variable(s) involved can only be accessed when condition is locked.
{
  wait(condition);				// Causes the state machine to go idle. This does not block.
  break;					// Leave scope (unlock condition) and return to the mainloop - but as idle state machine.
}
// Here the condition is guaranteed to be true and we're
// still in the critical area of the condition variable.

In this case, when condition.signal() is called, the thread
doesn't return from wait() with a locked mutex - but the
statemachine executes the same state again, and enters from
the top: locking the condition and doing the test again,
just like it would be when this was a while loop.
2013-10-15 22:30:53 +02:00

89 lines
2.7 KiB
C++

/**
* @file aicondition.cpp
* @brief Implementation of AICondition
*
* Copyright (c) 2013, Aleric Inglewood.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 14/10/2013
* Initial version, written by Aleric Inglewood @ SL
*/
#include "aicondition.h"
#include "aistatemachine.h"
void AIConditionBase::wait(AIStateMachine* state_machine)
{
// The condition must be locked before calling AIStateMachine::wait().
llassert(mutex().isSelfLocked());
// Add the new state machine at the end.
mWaitingStateMachines.push_back(state_machine);
}
void AIConditionBase::remove(AIStateMachine* state_machine)
{
mutex().lock();
// Remove all occurances of state_machine from the queue.
queue_t::iterator const end = mWaitingStateMachines.end();
queue_t::iterator last = end;
for (queue_t::iterator iter = mWaitingStateMachines.begin(); iter != last; ++iter)
{
if (iter->get() == state_machine)
{
if (--last == iter)
{
break;
}
queue_t::value_type::swap(*iter, *last);
}
}
// This invalidates all iterators involved, including end, but not any iterators to the remaining elements.
mWaitingStateMachines.erase(last, end);
mutex().unlock();
}
void AIConditionBase::signal(int n)
{
// The condition must be locked before calling AICondition::signal or AICondition::broadcast.
llassert(mutex().isSelfLocked());
// Signal n state machines.
while (n > 0 && !mWaitingStateMachines.empty())
{
LLPointer<AIStateMachine> state_machine = mWaitingStateMachines.front();
bool success = state_machine->signalled();
// Only state machines that are actually still blocked should be in the queue:
// they are removed from the queue by calling AICondition::remove whenever
// they are unblocked for whatever reason...
llassert(success);
if (success)
{
++n;
}
else
{
// We never get here...
remove(state_machine.get());
}
}
}