![]() |
VR-Forces 4.6 Class Documentation
|
This subclass of DtSimComponent shows the template of a thread safe component.
Header file:
/*******************************************************************************
** Copyright (c) 2016 MAK Technologies, Inc.
** All rights reserved.
*******************************************************************************/
#ifndef MyParallelTickComponent_H_
#define MyParallelTickComponent_H_
#include "vrfobjcore/simComponent.h"
class MyParallelTickComponent : public DtSimComponent
{
public:
// "my-parallel-tick-component" - tag used in SMS.
static const char* Tag;
//constructor
MyParallelTickComponent(
const DtString& name, DtVrfObject* object,
DtSimManager* simManager, DtComponentDescriptor* compDescriptor,
DtReaderWriterRegistry* parentRegistry = 0);
//Returns a string from compTypes.h, identifying the type of component
virtual const char* type() const;
//tick function
virtual void tick();
public:
//Creator function to be registered with the DtSimComponentFactory --
//see main.cxx
static DtSimComponent* creator(
const DtString& name, DtVrfObject* object,
DtSimManager* simManager, DtComponentDescriptor* compDescriptor,
DtReaderWriterRegistry* parentRegistry);
};
#endif
/*******************************************************************************
** Copyright (c) 2010 MAK Technologies, Inc.
** All rights reserved.
*******************************************************************************/
#include "parallelTickActuator.h"
#include "vrfobjcore/simComponentTypes.h"
#include "vrfcore/simManager.h"
#include "vrfobjcore/vrfObject.h"
#include "vrfcore/parallelTickManager.h"
const char* MyParallelTickComponent::Tag = "my-parallel-tick-component";
MyParallelTickComponent::MyParallelTickComponent(
const DtString& name,
DtVrfObject* object,
DtSimManager* simManager,
DtComponentDescriptor* compDescriptor,
DtReaderWriterRegistry* const parentRegistry)
: DtSimComponent(name, object, simManager, compDescriptor, parentRegistry)
{
}
const char* MyParallelTickComponent::type() const
{
//This is string that will be used to register this actuator's creator function. If you want to make up
//your own name, then modify the .ope file for the affected entities
//to reference the name of your new actuator.
//See chapter 6 of the VRForces Backend developer's guide for more information.
return Tag;
}
// The parallel tick functionality uses a single global read write mutex to guard the entire
// global vrf state. Each object is ticked in its own thread, so it is guaranteed that no two
// components from the same object tick at the same time.
// All functions which read any vrf data are considered to read the global state
// unless explicitly stated otherwise. All functions that modify any vrf data are considered
// to mutate the global state unless explicitly stated otherwise. If an actuator is marked as
// thread safe (see the creator function) then it must use DtCriticalSection objects to mark
// sections of code with the type of access they need. This will only have an effect during the
// normal tick function call (because the parallel tick manager is enabled then). DtCriticalSection
// can still be used in code that is not called from tick, it just won't do anything.
// This tick function is a template for the most common form that a thread safe
// tick function takes, which consists of reading data from the global state, computing values
// from it, and then updating the global state with the new values.
// Not all tick function designs will work well with this form. Some components (like sensors)
// typically spend most of their time querying the global state, and then do more
// queries based on the results. In cases like this you usually do not want to unlock
// the shared lock between queries because it will result in a huge amount of mutex
// state changes which will usually result in poor performance. In general it is
// a bad idea to write an actuator which will change the state of the mutex many times.
void MyParallelTickComponent::tick()
{
DtCriticalSection lock(simManager(), DtParallelTickManager::SHARED);
// Read access to global VRF state allowed here.
// Code here will not execute until no other code is modifying the global state, and any
// code that wants to will have to wait until we are done.
// Write access not allowed - do not change any global state.
// Other components which are in the unlocked or shared state may
// be running concurrently.
// In this section components will usually read data they need from the global
// state and copy it onto the stack.
DtWarn("Accessing global state\n");
lock.unlock();
// No access to global VRF state allowed here.
// Components can access (read and write) their ports (this is safe from anywhere).
// Components can access the terrain interface and features.
// In this section components will usually use the data they read in the previous step
// to do expensive calculations (terrain intersections, feature queries).
DtWarn("Parallel ticking\n");
// Important Note: As soon as we release the lock, we cannot get it back without possibly
// allowing other components to tick. This means that any data we've read from the global state
// may have changed, because it has been ticked after we first looked at it. Care must be taken
// so that only consistent data (i.e. all pre-tick or all post-tick) is used. The easiest way
// to do this is to do all the reading you need up front and then only operate on data from
// the stack.
lock.reset(DtParallelTickManager::EXCLUSIVE);
DtWarn("Accessing global state\n");
// Can read or write to any VRF state.
// Only other components which are unlocked can run concurrently, they
// may be doing thread safe operations like terrain intersections but
// they will not be accessing any global VRF state.
// In this section components will usually update the global VRF state with
// the new values it has computed.
// If the component is only updating ports then it does not (and should not)
// have to get an exclusive lock.
}
// Passing the created component through the MarkThreadSafe function and
// returning the result is what turns on the parallel ticking functionality
// for this actuator. All actuators are created by default with this functionality off.
// This way base classes can be made thread safe while previously existing derived classes
// won't have to also become thread safe.
DtSimComponent* MyParallelTickComponent::creator(const DtString& name,
DtVrfObject* vrfObject, DtSimManager* simManager,
DtComponentDescriptor* compDescriptor,
DtReaderWriterRegistry* parentRegistry)
{
return MarkThreadSafe(new MyParallelTickComponent(
name, vrfObject, simManager, compDescriptor, parentRegistry));
}