mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-11 06:34:36 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
417
Engine/source/platform/async/asyncBufferedStream.h
Normal file
417
Engine/source/platform/async/asyncBufferedStream.h
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ASYNCBUFFEREDSTREAM_H_
|
||||
#define _ASYNCBUFFEREDSTREAM_H_
|
||||
|
||||
#ifndef _TSTREAM_H_
|
||||
#include "core/stream/tStream.h"
|
||||
#endif
|
||||
#ifndef _THREADPOOL_H_
|
||||
#include "platform/threads/threadPool.h"
|
||||
#endif
|
||||
#ifndef _THREADSAFEDEQUE_H_
|
||||
#include "platform/threads/threadSafeDeque.h"
|
||||
#endif
|
||||
|
||||
|
||||
// Disable nonsense warning about unreferenced
|
||||
// local function on VC.
|
||||
#ifdef TORQUE_COMPILER_VISUALC
|
||||
#pragma warning( disable: 4505 )
|
||||
#endif
|
||||
|
||||
|
||||
template< typename T, class Stream >
|
||||
class AsyncBufferedReadItem;
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// AsyncBufferedInputStream.
|
||||
//=============================================================================
|
||||
|
||||
|
||||
///
|
||||
template< typename T, class Stream = IInputStream< T >* >
|
||||
class AsyncBufferedInputStream : public IInputStreamFilter< T, Stream >,
|
||||
public ThreadSafeRefCount< AsyncBufferedInputStream< T, Stream > >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef IInputStreamFilter< T, Stream > Parent;
|
||||
|
||||
/// Type of elements read, buffered, and returned by this stream.
|
||||
typedef typename Parent::ElementType ElementType;
|
||||
|
||||
/// Type of the source stream being read by this stream.
|
||||
typedef typename Parent::SourceStreamType SourceStreamType;
|
||||
|
||||
/// Type of elements being read from the source stream.
|
||||
///
|
||||
/// @note This does not need to correspond to the type of elements buffered
|
||||
/// in this stream.
|
||||
typedef typename Parent::SourceElementType SourceElementType;
|
||||
|
||||
enum
|
||||
{
|
||||
/// The number of elements to buffer in advance by default.
|
||||
DEFAULT_STREAM_LOOKAHEAD = 3
|
||||
};
|
||||
|
||||
friend class AsyncBufferedReadItem< T, Stream >; // _onArrival
|
||||
|
||||
protected:
|
||||
|
||||
/// Stream elements are kept on deques that can be concurrently
|
||||
/// accessed by multiple threads.
|
||||
typedef ThreadSafeDeque< ElementType > ElementList;
|
||||
|
||||
/// If true, the stream will restart over from the beginning once
|
||||
/// it has been read in entirety.
|
||||
bool mIsLooping;
|
||||
|
||||
/// If true, no further requests should be issued on this stream.
|
||||
/// @note This in itself doesn't say anything about pending requests.
|
||||
bool mIsStopped;
|
||||
|
||||
/// Number of source elements remaining in the source stream.
|
||||
U32 mNumRemainingSourceElements;
|
||||
|
||||
/// Number of elements currently on buffer list.
|
||||
U32 mNumBufferedElements;
|
||||
|
||||
/// Maximum number of elements allowed on buffer list.
|
||||
U32 mMaxBufferedElements;
|
||||
|
||||
/// List of buffered elements.
|
||||
ElementList mBufferedElements;
|
||||
|
||||
/// The thread pool to which read items are queued.
|
||||
ThreadPool* mThreadPool;
|
||||
|
||||
/// The thread context used for prioritizing read items in the pool.
|
||||
ThreadContext* mThreadContext;
|
||||
|
||||
/// Request the next element from the underlying stream.
|
||||
virtual void _requestNext() = 0;
|
||||
|
||||
/// Called when an element read has been completed on the underlying stream.
|
||||
virtual void _onArrival( const ElementType& element );
|
||||
|
||||
public:
|
||||
|
||||
/// Construct a new buffered stream reading from "source".
|
||||
///
|
||||
/// @param stream The source stream from which to read the actual data elements.
|
||||
/// @param numSourceElementsToRead Total number of elements to read from "stream".
|
||||
/// @param numReadAhead Number of packets to read and buffer in advance.
|
||||
/// @param isLooping If true, the packet stream will loop infinitely over the source stream.
|
||||
/// @param pool The ThreadPool to use for asynchronous packet reads.
|
||||
/// @param context The ThreadContext to place asynchronous packet reads in.
|
||||
AsyncBufferedInputStream( const Stream& stream,
|
||||
U32 numSourceElementsToRead = 0,
|
||||
U32 numReadAhead = DEFAULT_STREAM_LOOKAHEAD,
|
||||
bool isLooping = false,
|
||||
ThreadPool* pool = &ThreadPool::GLOBAL(),
|
||||
ThreadContext* context = ThreadContext::ROOT_CONTEXT() );
|
||||
|
||||
virtual ~AsyncBufferedInputStream();
|
||||
|
||||
/// @return true if the stream is looping infinitely.
|
||||
bool isLooping() const { return mIsLooping; }
|
||||
|
||||
/// @return the number of elements that will be read and buffered in advance.
|
||||
U32 getReadAhead() const { return mMaxBufferedElements; }
|
||||
|
||||
/// Initiate the request chain of the element stream.
|
||||
void start() { _requestNext(); }
|
||||
|
||||
/// Call for the request chain of the element stream to stop at the next
|
||||
/// synchronization point.
|
||||
void stop() { mIsStopped = true; }
|
||||
|
||||
// IInputStream.
|
||||
virtual U32 read( ElementType* buffer, U32 num );
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename T, typename Stream >
|
||||
AsyncBufferedInputStream< T, Stream >::AsyncBufferedInputStream
|
||||
( const Stream& stream,
|
||||
U32 numSourceElementsToRead,
|
||||
U32 numReadAhead,
|
||||
bool isLooping,
|
||||
ThreadPool* threadPool,
|
||||
ThreadContext* threadContext )
|
||||
: Parent( stream ),
|
||||
mIsStopped( false ),
|
||||
mIsLooping( isLooping ),
|
||||
mNumRemainingSourceElements( numSourceElementsToRead ),
|
||||
mNumBufferedElements( 0 ),
|
||||
mMaxBufferedElements( numReadAhead ),
|
||||
mThreadPool( threadPool ),
|
||||
mThreadContext( threadContext )
|
||||
{
|
||||
if( mIsLooping )
|
||||
{
|
||||
// Stream is looping so we don't count down source elements.
|
||||
|
||||
mNumRemainingSourceElements = 0;
|
||||
}
|
||||
else if( !mNumRemainingSourceElements )
|
||||
{
|
||||
// If not given number of elements to read, see if the source
|
||||
// stream is sizeable. If so, take its size as the number of elements.
|
||||
|
||||
if( dynamic_cast< ISizeable<>* >( &Deref( stream ) ) )
|
||||
mNumRemainingSourceElements = ( ( ISizeable<>* ) &Deref( stream ) )->getSize();
|
||||
else
|
||||
{
|
||||
// Can't tell how many source elements there are.
|
||||
|
||||
mNumRemainingSourceElements = U32_MAX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename T, typename Stream >
|
||||
AsyncBufferedInputStream< T, Stream >::~AsyncBufferedInputStream()
|
||||
{
|
||||
ElementType element;
|
||||
while( mBufferedElements.tryPopFront( element ) )
|
||||
destructSingle( element );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename T, typename Stream >
|
||||
void AsyncBufferedInputStream< T, Stream >::_onArrival( const ElementType& element )
|
||||
{
|
||||
mBufferedElements.pushBack( element );
|
||||
|
||||
// Adjust buffer count.
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
S32 numBuffered = mNumBufferedElements;
|
||||
if( dCompareAndSwap( mNumBufferedElements, numBuffered, numBuffered + 1 ) )
|
||||
{
|
||||
// If we haven't run against the lookahead limit and haven't reached
|
||||
// the end of the stream, immediately trigger a new request.
|
||||
|
||||
if( !mIsStopped && ( numBuffered + 1 ) < mMaxBufferedElements )
|
||||
_requestNext();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename T, typename Stream >
|
||||
U32 AsyncBufferedInputStream< T, Stream >::read( ElementType* buffer, U32 num )
|
||||
{
|
||||
if( !num )
|
||||
return 0;
|
||||
|
||||
U32 numRead = 0;
|
||||
for( U32 i = 0; i < num; ++ i )
|
||||
{
|
||||
// Try to pop a element off the buffered element list.
|
||||
|
||||
ElementType element;
|
||||
if( mBufferedElements.tryPopFront( element ) )
|
||||
{
|
||||
buffer[ i ] = element;
|
||||
numRead ++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the request chain going again, if it has stopped.
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
U32 numBuffered = mNumBufferedElements;
|
||||
U32 newNumBuffered = numBuffered - numRead;
|
||||
|
||||
if( dCompareAndSwap( mNumBufferedElements, numBuffered, newNumBuffered ) )
|
||||
{
|
||||
if( numBuffered == mMaxBufferedElements )
|
||||
_requestNext();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return numRead;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// AsyncSingleBufferedInputStream.
|
||||
//=============================================================================
|
||||
|
||||
|
||||
/// Asynchronous work item for reading an element from the source stream.
|
||||
template< typename T, typename Stream = IInputStream< T >* >
|
||||
class AsyncBufferedReadItem : public ThreadWorkItem
|
||||
{
|
||||
public:
|
||||
|
||||
typedef ThreadWorkItem Parent;
|
||||
typedef ThreadSafeRef< AsyncBufferedInputStream< T, Stream > > AsyncStreamRef;
|
||||
|
||||
protected:
|
||||
|
||||
/// The issueing async state.
|
||||
AsyncStreamRef mAsyncStream;
|
||||
|
||||
///
|
||||
Stream mSourceStream;
|
||||
|
||||
/// The element read from the stream.
|
||||
T mElement;
|
||||
|
||||
// WorkItem
|
||||
virtual void execute()
|
||||
{
|
||||
if( Deref( mSourceStream ).read( &mElement, 1 ) )
|
||||
{
|
||||
// Buffer the element.
|
||||
|
||||
if( this->cancellationPoint() ) return;
|
||||
mAsyncStream->_onArrival( mElement );
|
||||
}
|
||||
}
|
||||
virtual void onCancelled()
|
||||
{
|
||||
Parent::onCancelled();
|
||||
destructSingle( mElement );
|
||||
mAsyncStream = NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
AsyncBufferedReadItem(
|
||||
const AsyncStreamRef& asyncStream,
|
||||
ThreadPool::Context* context = NULL
|
||||
)
|
||||
: Parent( context ),
|
||||
mAsyncStream( asyncStream ),
|
||||
mSourceStream( asyncStream->getSourceStream() )
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// A stream filter that performs background read-aheads on its source stream
|
||||
/// and buffers the results.
|
||||
///
|
||||
/// As each element is read in an independent threaded operation, reading an
|
||||
/// element should invole a certain amount of work for using this class to
|
||||
/// make sense.
|
||||
///
|
||||
/// @note For looping streams, the stream must implement the IResettable interface.
|
||||
///
|
||||
template< typename T, typename Stream = IInputStream< T >*, class ReadItem = AsyncBufferedReadItem< T, Stream > >
|
||||
class AsyncSingleBufferedInputStream : public AsyncBufferedInputStream< T, Stream >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef AsyncBufferedInputStream< T, Stream > Parent;
|
||||
typedef typename Parent::ElementType ElementType;
|
||||
typedef typename Parent::SourceElementType SourceElementType;
|
||||
typedef typename Parent::SourceStreamType SourceStreamType;
|
||||
|
||||
protected:
|
||||
|
||||
// AsyncBufferedInputStream.
|
||||
virtual void _requestNext();
|
||||
|
||||
/// Create a new work item that reads the next element.
|
||||
virtual void _newReadItem( ThreadSafeRef< ThreadWorkItem >& outRef )
|
||||
{
|
||||
outRef = new ReadItem( this, this->mThreadContext );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Construct a new buffered stream reading from "source".
|
||||
///
|
||||
/// @param stream The source stream from which to read the actual data elements.
|
||||
/// @param numSourceElementsToRead Total number of elements to read from "stream".
|
||||
/// @param numReadAhead Number of packets to read and buffer in advance.
|
||||
/// @param isLooping If true, the packet stream will loop infinitely over the source stream.
|
||||
/// @param pool The ThreadPool to use for asynchronous packet reads.
|
||||
/// @param context The ThreadContext to place asynchronous packet reads in.
|
||||
AsyncSingleBufferedInputStream( const Stream& stream,
|
||||
U32 numSourceElementsToRead = 0,
|
||||
U32 numReadAhead = Parent::DEFAULT_STREAM_LOOKAHEAD,
|
||||
bool isLooping = false,
|
||||
ThreadPool* pool = &ThreadPool::GLOBAL(),
|
||||
ThreadContext* context = ThreadContext::ROOT_CONTEXT() )
|
||||
: Parent( stream,
|
||||
numSourceElementsToRead,
|
||||
numReadAhead,
|
||||
isLooping,
|
||||
pool,
|
||||
context ) {}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template< typename T, typename Stream, class ReadItem >
|
||||
void AsyncSingleBufferedInputStream< T, Stream, ReadItem >::_requestNext()
|
||||
{
|
||||
Stream& stream = this->getSourceStream();
|
||||
bool isEOS = !this->mNumRemainingSourceElements;
|
||||
if( isEOS && this->mIsLooping )
|
||||
{
|
||||
SourceStreamType* s = &Deref( stream );
|
||||
dynamic_cast< IResettable* >( s )->reset();
|
||||
isEOS = false;
|
||||
}
|
||||
else if( isEOS )
|
||||
return;
|
||||
|
||||
//TODO: could scale priority depending on feed status
|
||||
|
||||
// Queue a stream packet work item.
|
||||
|
||||
if( !this->mIsLooping && this->mNumRemainingSourceElements != U32_MAX )
|
||||
-- this->mNumRemainingSourceElements;
|
||||
|
||||
ThreadSafeRef< ThreadWorkItem > workItem;
|
||||
_newReadItem( workItem );
|
||||
this->mThreadPool->queueWorkItem( workItem );
|
||||
}
|
||||
|
||||
#endif // !_ASYNCBUFFEREDSTREAM_H_
|
||||
314
Engine/source/platform/async/asyncPacketQueue.h
Normal file
314
Engine/source/platform/async/asyncPacketQueue.h
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ASYNCPACKETQUEUE_H_
|
||||
#define _ASYNCPACKETQUEUE_H_
|
||||
|
||||
#ifndef _TFIXEDSIZEQUEUE_H_
|
||||
#include "core/util/tFixedSizeDeque.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TSTREAM_H_
|
||||
#include "core/stream/tStream.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TYPETRAITS_H_
|
||||
#include "platform/typetraits.h"
|
||||
#endif
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
/// @file
|
||||
/// Time-based packet streaming.
|
||||
///
|
||||
/// The classes contained in this file can be used for any kind
|
||||
/// of continuous playback that depends on discrete samplings of
|
||||
/// a source stream (i.e. any kind of digital media streaming).
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Async packet queue.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Time-based packet stream queue.
|
||||
///
|
||||
/// AsyncPacketQueue writes data packets to a consumer stream in sync to
|
||||
/// a tick time source. Outdated packets may optionally be dropped automatically
|
||||
/// by the queue. A fixed maximum number of packets can reside in the queue
|
||||
/// concurrently at any one time.
|
||||
///
|
||||
/// Be aware that using single item queues for synchronizing to a timer
|
||||
/// will usually result in bad timing behavior when packet uploading takes
|
||||
/// any non-trivial amount of time.
|
||||
///
|
||||
/// @note While the queue associates a variable tick count with each
|
||||
/// individual packet, the queue fill status is measured in number of
|
||||
/// packets rather than in total tick time.
|
||||
///
|
||||
/// @param Packet Value type of packets passed through this queue.
|
||||
/// @param TimeSource Value type for time tick source to which the queue
|
||||
/// is synchronized.
|
||||
/// @param Consumer Value type of stream to which the packets are written.
|
||||
///
|
||||
template< typename Packet, typename TimeSource = IPositionable< U32 >*, typename Consumer = IOutputStream< Packet >*, typename Tick = U32 >
|
||||
class AsyncPacketQueue
|
||||
{
|
||||
public:
|
||||
|
||||
typedef void Parent;
|
||||
|
||||
/// The type of data packets being streamed through this queue.
|
||||
typedef typename TypeTraits< Packet >::BaseType PacketType;
|
||||
|
||||
/// The type of consumer that receives the packets from this queue.
|
||||
typedef typename TypeTraits< Consumer >::BaseType ConsumerType;
|
||||
|
||||
///
|
||||
typedef typename TypeTraits< TimeSource >::BaseType TimeSourceType;
|
||||
|
||||
/// Type for counting ticks.
|
||||
typedef Tick TickType;
|
||||
|
||||
protected:
|
||||
|
||||
/// Information about the time slice covered by an
|
||||
/// individual packet currently on the queue.
|
||||
struct QueuedPacket
|
||||
{
|
||||
/// First tick contained in this packet.
|
||||
TickType mStartTick;
|
||||
|
||||
/// First tick *not* contained in this packet anymore.
|
||||
TickType mEndTick;
|
||||
|
||||
QueuedPacket( TickType start, TickType end )
|
||||
: mStartTick( start ), mEndTick( end ) {}
|
||||
|
||||
/// Return the total number of ticks in this packet.
|
||||
TickType getNumTicks() const
|
||||
{
|
||||
return ( mEndTick - mStartTick );
|
||||
}
|
||||
};
|
||||
|
||||
typedef FixedSizeDeque< QueuedPacket > PacketQueue;
|
||||
|
||||
/// If true, packets that have missed their proper queuing timeframe
|
||||
/// will be dropped. If false, they will be queued nonetheless.
|
||||
bool mDropPackets;
|
||||
|
||||
/// Total number of ticks spanned by the total queue playback time.
|
||||
/// If this is zero, the total queue time is considered to be infinite.
|
||||
TickType mTotalTicks;
|
||||
|
||||
/// Total number of ticks submitted to the queue so far.
|
||||
TickType mTotalQueuedTicks;
|
||||
|
||||
/// Queue that holds records for each packet currently in the queue. New packets
|
||||
/// are added to back.
|
||||
PacketQueue mPacketQueue;
|
||||
|
||||
/// The time source to which we are sync'ing.
|
||||
TimeSource mTimeSource;
|
||||
|
||||
/// The output stream that this queue feeds into.
|
||||
Consumer mConsumer;
|
||||
|
||||
/// Total number of packets queued so far.
|
||||
U32 mTotalQueuedPackets;
|
||||
|
||||
public:
|
||||
|
||||
/// Construct an AsyncPacketQueue of the given length.
|
||||
///
|
||||
/// @param maxQueuedPackets The length of the queue in packets. Only a maximum of
|
||||
/// 'maxQueuedPackets' packets can be concurrently in the queue at any one time.
|
||||
/// @param timeSource The tick time source to which the queue synchronizes.
|
||||
/// @param consumer The output stream that receives the packets in sync to timeSource.
|
||||
/// @param totalTicks The total number of ticks that will be played back through the
|
||||
/// queue; if 0, the length is considered indefinite.
|
||||
/// @param dropPackets Whether the queue should drop outdated packets; if dropped, a
|
||||
/// packet will not reach the consumer.
|
||||
AsyncPacketQueue( U32 maxQueuedPackets,
|
||||
TimeSource timeSource,
|
||||
Consumer consumer,
|
||||
TickType totalTicks = 0,
|
||||
bool dropPackets = false )
|
||||
: mTotalTicks( totalTicks ),
|
||||
mTotalQueuedTicks( 0 ),
|
||||
mPacketQueue( maxQueuedPackets ),
|
||||
mTimeSource( timeSource ),
|
||||
mConsumer( consumer ),
|
||||
mDropPackets( dropPackets )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
mTotalQueuedPackets = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Return true if there are currently
|
||||
bool isEmpty() const { return mPacketQueue.isEmpty(); }
|
||||
|
||||
/// Return true if all packets have been streamed.
|
||||
bool isAtEnd() const;
|
||||
|
||||
/// Return true if the queue needs one or more new packets to be submitted.
|
||||
bool needPacket();
|
||||
|
||||
/// Submit a data packet to the queue.
|
||||
///
|
||||
/// @param packet The data packet.
|
||||
/// @param packetTicks The duration of the packet in ticks.
|
||||
/// @param isLast If true, the packet is the last one in the stream.
|
||||
/// @param packetPos The absolute position of the packet in the stream; if this is not supplied
|
||||
/// the packet is assumed to immediately follow the preceding packet.
|
||||
///
|
||||
/// @return true if the packet has been queued or false if it has been dropped.
|
||||
bool submitPacket( Packet packet,
|
||||
TickType packetTicks,
|
||||
bool isLast = false,
|
||||
TickType packetPos = TypeTraits< TickType >::MAX );
|
||||
|
||||
/// Return the current playback position according to the time source.
|
||||
TickType getCurrentTick() const { return Deref( mTimeSource ).getPosition(); }
|
||||
|
||||
/// Return the total number of ticks that have been queued so far.
|
||||
TickType getTotalQueuedTicks() const { return mTotalQueuedTicks; }
|
||||
|
||||
/// Return the total number of packets that have been queued so far.
|
||||
U32 getTotalQueuedPackets() const { return mTotalQueuedPackets; }
|
||||
};
|
||||
|
||||
template< typename Packet, typename TimeSource, typename Consumer, typename Tick >
|
||||
inline bool AsyncPacketQueue< Packet, TimeSource, Consumer, Tick >::isAtEnd() const
|
||||
{
|
||||
// Never at end if infinite.
|
||||
|
||||
if( !mTotalTicks )
|
||||
return false;
|
||||
|
||||
// Otherwise, we're at end if we're past the total tick count.
|
||||
|
||||
return ( getCurrentTick() >= mTotalTicks
|
||||
&& ( mDropPackets || mTotalQueuedTicks >= mTotalTicks ) );
|
||||
}
|
||||
|
||||
template< typename Packet, typename TimeSource, typename Consumer, typename Tick >
|
||||
bool AsyncPacketQueue< Packet, TimeSource, Consumer, Tick >::needPacket()
|
||||
{
|
||||
// Never need more packets once we have reached the
|
||||
// end.
|
||||
|
||||
if( isAtEnd() )
|
||||
return false;
|
||||
|
||||
// Always needs packets while the queue is not
|
||||
// filled up completely.
|
||||
|
||||
if( mPacketQueue.capacity() != 0 )
|
||||
return true;
|
||||
|
||||
// Unqueue packets that have expired their playtime.
|
||||
|
||||
TickType currentTick = getCurrentTick();
|
||||
while( mPacketQueue.size() && currentTick >= mPacketQueue.front().mEndTick )
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[AsyncPacketQueue] expired packet #%i: %i-%i (tick: %i; queue: %i)",
|
||||
mTotalQueuedPackets - mPacketQueue.size(),
|
||||
U32( mPacketQueue.front().mStartTick ),
|
||||
U32( mPacketQueue.front().mEndTick ),
|
||||
U32( currentTick ),
|
||||
mPacketQueue.size() );
|
||||
#endif
|
||||
|
||||
mPacketQueue.popFront();
|
||||
}
|
||||
|
||||
// Need more packets if the queue isn't full anymore.
|
||||
|
||||
return ( mPacketQueue.capacity() != 0 );
|
||||
}
|
||||
|
||||
template< typename Packet, typename TimeSource, typename Consumer, typename Tick >
|
||||
bool AsyncPacketQueue< Packet, TimeSource, Consumer, Tick >::submitPacket( Packet packet, TickType packetTicks, bool isLast, TickType packetPos )
|
||||
{
|
||||
AssertFatal( mPacketQueue.capacity() != 0,
|
||||
"AsyncPacketQueue::submitPacket() - Queue is full!" );
|
||||
|
||||
TickType packetStartPos;
|
||||
TickType packetEndPos;
|
||||
|
||||
if( packetPos != TypeTraits< TickType >::MAX )
|
||||
{
|
||||
packetStartPos = packetPos;
|
||||
packetEndPos = packetPos + packetTicks;
|
||||
}
|
||||
else
|
||||
{
|
||||
packetStartPos = mTotalQueuedTicks;
|
||||
packetEndPos = mTotalQueuedTicks + packetTicks;
|
||||
}
|
||||
|
||||
// Check whether the packet is outdated, if enabled.
|
||||
|
||||
bool dropPacket = false;
|
||||
if( mDropPackets )
|
||||
{
|
||||
TickType currentTick = getCurrentTick();
|
||||
if( currentTick >= packetEndPos )
|
||||
dropPacket = true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[AsyncPacketQueue] new packet #%i: %i-%i (ticks: %i, current: %i, queue: %i)%s",
|
||||
mTotalQueuedPackets,
|
||||
U32( mTotalQueuedTicks ),
|
||||
U32( packetEndPos ),
|
||||
U32( packetTicks ),
|
||||
U32( getCurrentTick() ),
|
||||
mPacketQueue.size(),
|
||||
dropPacket ? " !! DROPPED !!" : "" );
|
||||
#endif
|
||||
|
||||
// Queue the packet.
|
||||
|
||||
if( !dropPacket )
|
||||
{
|
||||
mPacketQueue.pushBack( QueuedPacket( packetStartPos, packetEndPos ) );
|
||||
Deref( mConsumer ).write( &packet, 1 );
|
||||
}
|
||||
|
||||
mTotalQueuedTicks = packetEndPos;
|
||||
if( isLast && !mTotalTicks )
|
||||
mTotalTicks = mTotalQueuedTicks;
|
||||
|
||||
mTotalQueuedPackets ++;
|
||||
|
||||
return !dropPacket;
|
||||
}
|
||||
|
||||
#undef DEBUG_SPEW
|
||||
#endif // _ASYNCPACKETQUEUE_H_
|
||||
327
Engine/source/platform/async/asyncPacketStream.h
Normal file
327
Engine/source/platform/async/asyncPacketStream.h
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ASYNCPACKETSTREAM_H_
|
||||
#define _ASYNCPACKETSTREAM_H_
|
||||
|
||||
#ifndef _ASYNCBUFFEREDSTREAM_H_
|
||||
#include "platform/async/asyncBufferedStream.h"
|
||||
#endif
|
||||
#ifndef _RAWDATA_H_
|
||||
#include "core/util/rawData.h"
|
||||
#endif
|
||||
#ifndef _THREADPOOLASYNCIO_H_
|
||||
#include "platform/threads/threadPoolAsyncIO.h"
|
||||
#endif
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
/// @file
|
||||
/// Input stream filter definitions for turning linear streams into
|
||||
/// streams that yield data in discrete packets using background
|
||||
/// reads.
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Async stream packets.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Stream packet read by an asynchronous packet stream.
|
||||
template< typename T >
|
||||
class AsyncPacket : public RawDataT< T >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef RawDataT< T > Parent;
|
||||
|
||||
AsyncPacket()
|
||||
: mIndex( 0 ), mIsLast( false ), mSizeActual( 0 ) {}
|
||||
AsyncPacket( T* data, U32 size, bool ownMemory = false )
|
||||
: Parent( data, size, ownMemory ),
|
||||
mIndex( 0 ), mIsLast( false ), mSizeActual( 0 ) {}
|
||||
|
||||
/// Running number in stream.
|
||||
U32 mIndex;
|
||||
|
||||
/// Number of items that have actually been read into the packet.
|
||||
/// This may be less than "size" for end-of-stream packets in non-looping
|
||||
/// streams.
|
||||
///
|
||||
/// @note Extraneous space at the end of the packet will be cleared using
|
||||
/// constructArray() calls.
|
||||
U32 mSizeActual;
|
||||
|
||||
/// If true this is the last packet in the stream.
|
||||
bool mIsLast;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Async packet streams.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// A packet stream turns a continuous stream of elements into a
|
||||
/// stream of discrete packets of elements.
|
||||
///
|
||||
/// All packets are of the exact same size even if, for end-of-stream
|
||||
/// packets, they actually contain less data than their actual size.
|
||||
/// Extraneous space is cleared.
|
||||
///
|
||||
/// @note For looping streams, the stream must implement the
|
||||
/// IResettable interface.
|
||||
template< typename Stream, class Packet = AsyncPacket< typename TypeTraits< Stream >::BaseType::ElementType > >
|
||||
class AsyncPacketBufferedInputStream : public AsyncBufferedInputStream< Packet*, Stream >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef AsyncBufferedInputStream< Packet*, Stream > Parent;
|
||||
typedef Packet PacketType;
|
||||
typedef typename TypeTraits< Stream >::BaseType StreamType;
|
||||
|
||||
protected:
|
||||
|
||||
class PacketReadItem;
|
||||
friend class PacketReadItem; // _onArrival
|
||||
|
||||
/// Asynchronous work item for reading a packet from the source stream.
|
||||
class PacketReadItem : public AsyncReadItem< typename Parent::SourceElementType, StreamType >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef AsyncReadItem< typename AsyncPacketBufferedInputStream< Stream, Packet >::SourceElementType, StreamType > Parent;
|
||||
|
||||
PacketReadItem( const ThreadSafeRef< AsyncPacketBufferedInputStream< Stream, Packet > >& asyncStream,
|
||||
PacketType* packet,
|
||||
U32 numElements,
|
||||
ThreadPool::Context* context = NULL )
|
||||
: Parent( asyncStream->getSourceStream(), numElements, 0, *packet, false, 0, context ),
|
||||
mAsyncStream( asyncStream ),
|
||||
mPacket( packet ) {}
|
||||
|
||||
protected:
|
||||
|
||||
typedef ThreadSafeRef< AsyncPacketBufferedInputStream< Stream, Packet > > AsyncPacketStreamPtr;
|
||||
|
||||
/// The issueing async state.
|
||||
AsyncPacketStreamPtr mAsyncStream;
|
||||
|
||||
/// The packet that receives the data.
|
||||
PacketType* mPacket;
|
||||
|
||||
// WorkItem
|
||||
virtual void execute()
|
||||
{
|
||||
Parent::execute();
|
||||
mPacket->mSizeActual += this->mNumElementsRead;
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[AsyncPacketStream] read %i elements into packet #%i with size %i",
|
||||
this->mNumElementsRead, mPacket->mIndex, mPacket->size );
|
||||
#endif
|
||||
|
||||
// Handle extraneous space at end of packet.
|
||||
|
||||
if( this->cancellationPoint() ) return;
|
||||
U32 numExtraElements = mPacket->size - this->mNumElementsRead;
|
||||
if( numExtraElements )
|
||||
{
|
||||
if( mAsyncStream->mIsLooping
|
||||
&& dynamic_cast< IResettable* >( &Deref( this->getStream() ) ) )
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[AsyncPacketStream] resetting stream and reading %i more elements", numExtraElements );
|
||||
#endif
|
||||
|
||||
// Wrap around and start re-reading from beginning of stream.
|
||||
|
||||
dynamic_cast< IResettable* >( &Deref( this->getStream() ) )->reset();
|
||||
|
||||
this->mOffsetInBuffer += this->mNumElementsRead;
|
||||
this->mOffsetInStream = 0;
|
||||
this->mNumElements = numExtraElements;
|
||||
|
||||
this->_prep();
|
||||
Parent::execute();
|
||||
|
||||
mPacket->mSizeActual += this->mNumElementsRead;
|
||||
}
|
||||
else
|
||||
constructArray( &mPacket->data[ this->mNumElementsRead ], numExtraElements );
|
||||
}
|
||||
|
||||
// Buffer the packet.
|
||||
|
||||
if( this->cancellationPoint() ) return;
|
||||
mAsyncStream->_onArrival( mPacket );
|
||||
}
|
||||
virtual void onCancelled()
|
||||
{
|
||||
Parent::onCancelled();
|
||||
destructSingle< PacketType* >( mPacket );
|
||||
mAsyncStream = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
typedef ThreadSafeRef< PacketReadItem > PacketReadItemRef;
|
||||
|
||||
/// Number of elements to read per packet.
|
||||
U32 mPacketSize;
|
||||
|
||||
/// Running number of next stream packet.
|
||||
U32 mNextPacketIndex;
|
||||
|
||||
/// Total number of elements in the source stream.
|
||||
U32 mNumTotalSourceElements;
|
||||
|
||||
/// Create a new stream packet of the given size.
|
||||
virtual PacketType* _newPacket( U32 packetSize ) { return constructSingle< PacketType* >( packetSize ); }
|
||||
|
||||
/// Request the next packet from the underlying stream.
|
||||
virtual void _requestNext();
|
||||
|
||||
/// Create a new work item that reads "numElements" into "packet".
|
||||
virtual void _newReadItem( PacketReadItemRef& outRef,
|
||||
PacketType* packet,
|
||||
U32 numElements )
|
||||
{
|
||||
outRef = new PacketReadItem( this, packet, numElements, this->mThreadContext );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Construct a new packet stream reading from "stream".
|
||||
///
|
||||
/// @note If looping is used and "stream" is not read from the beginning, "stream" should
|
||||
/// implement IPositionable<U32> or ISizeable<U32> so the async stream can tell how many elements
|
||||
/// there actually are in the stream after resetting.
|
||||
///
|
||||
/// @param stream The source stream from which to read the actual data elements.
|
||||
/// @param packetSize Size of stream packets returned by the stream in number of elements.
|
||||
/// @param numSourceElementsToRead Number of elements to read from "stream".
|
||||
/// @param numReadAhead Number of packets to read and buffer in advance.
|
||||
/// @param isLooping If true, the packet stream will loop infinitely over the source stream.
|
||||
/// @param pool The ThreadPool to use for asynchronous packet reads.
|
||||
/// @param context The ThreadContext to place asynchronous packet reads in.
|
||||
AsyncPacketBufferedInputStream( const Stream& stream,
|
||||
U32 packetSize,
|
||||
U32 numSourceElementsToRead = 0,
|
||||
U32 numReadAhead = Parent::DEFAULT_STREAM_LOOKAHEAD,
|
||||
bool isLooping = false,
|
||||
ThreadPool* pool = &ThreadPool::GLOBAL(),
|
||||
ThreadContext* context = ThreadContext::ROOT_CONTEXT() );
|
||||
|
||||
/// @return the size of stream packets returned by this stream in number of elements.
|
||||
U32 getPacketSize() const { return mPacketSize; }
|
||||
};
|
||||
|
||||
template< typename Stream, class Packet >
|
||||
AsyncPacketBufferedInputStream< Stream, Packet >::AsyncPacketBufferedInputStream
|
||||
( const Stream& stream,
|
||||
U32 packetSize,
|
||||
U32 numSourceElementsToRead,
|
||||
U32 numReadAhead,
|
||||
bool isLooping,
|
||||
ThreadPool* threadPool,
|
||||
ThreadContext* threadContext )
|
||||
: Parent( stream, numSourceElementsToRead, numReadAhead, isLooping, threadPool, threadContext ),
|
||||
mPacketSize( packetSize ),
|
||||
mNumTotalSourceElements( numSourceElementsToRead ),
|
||||
mNextPacketIndex( 0 )
|
||||
{
|
||||
AssertFatal( mPacketSize > 0,
|
||||
"AsyncPacketStream::AsyncPacketStream() - packet size cannot be zero" );
|
||||
|
||||
// Determine total number of elements in stream, if possible.
|
||||
|
||||
IPositionable< U32 >* positionable = dynamic_cast< IPositionable< U32 >* >( &Deref( stream ) );
|
||||
if( positionable )
|
||||
mNumTotalSourceElements += positionable->getPosition();
|
||||
else
|
||||
{
|
||||
ISizeable< U32 >* sizeable = dynamic_cast< ISizeable< U32 >* >( &Deref( stream ) );
|
||||
if( sizeable )
|
||||
mNumTotalSourceElements = sizeable->getSize();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[AsyncPacketStream] %i remaining, %i total (%i packets)",
|
||||
this->mNumRemainingSourceElements, mNumTotalSourceElements,
|
||||
( this->mNumRemainingSourceElements / mPacketSize ) + ( this->mNumRemainingSourceElements % mPacketSize ? 1 : 0 ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
template< typename Stream, class Packet >
|
||||
void AsyncPacketBufferedInputStream< Stream, Packet >::_requestNext()
|
||||
{
|
||||
Stream& stream = this->getSourceStream();
|
||||
bool isEOS = !this->mNumRemainingSourceElements;
|
||||
if( isEOS && this->mIsLooping )
|
||||
{
|
||||
StreamType* s = &Deref( stream );
|
||||
IResettable* resettable = dynamic_cast< IResettable* >( s );
|
||||
if( resettable )
|
||||
{
|
||||
resettable->reset();
|
||||
isEOS = false;
|
||||
this->mNumRemainingSourceElements = mNumTotalSourceElements;
|
||||
}
|
||||
}
|
||||
else if( isEOS )
|
||||
return;
|
||||
|
||||
//TODO: scale priority depending on feed status
|
||||
|
||||
// Allocate a packet.
|
||||
|
||||
U32 numElements = mPacketSize;
|
||||
PacketType* packet = _newPacket( numElements );
|
||||
packet->mIndex = mNextPacketIndex;
|
||||
mNextPacketIndex ++;
|
||||
|
||||
// Queue a stream packet work item.
|
||||
|
||||
if( numElements >= this->mNumRemainingSourceElements )
|
||||
{
|
||||
if( !this->mIsLooping )
|
||||
{
|
||||
this->mNumRemainingSourceElements = 0;
|
||||
packet->mIsLast = true;
|
||||
}
|
||||
else
|
||||
this->mNumRemainingSourceElements = ( this->mNumTotalSourceElements - numElements + this->mNumRemainingSourceElements );
|
||||
}
|
||||
else
|
||||
this->mNumRemainingSourceElements -= numElements;
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Platform::outputDebugString( "[AsyncPacketStream] packet %i, %i remaining, %i total",
|
||||
packet->mIndex, this->mNumRemainingSourceElements, mNumTotalSourceElements );
|
||||
#endif
|
||||
|
||||
ThreadSafeRef< PacketReadItem > workItem;
|
||||
_newReadItem( workItem, packet, numElements );
|
||||
this->mThreadPool->queueWorkItem( workItem );
|
||||
}
|
||||
|
||||
#undef DEBUG_SPEW
|
||||
#endif // !_ASYNCPACKETSTREAM_H_
|
||||
83
Engine/source/platform/async/asyncUpdate.cpp
Normal file
83
Engine/source/platform/async/asyncUpdate.cpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/async/asyncUpdate.h"
|
||||
#include "core/stream/tStream.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// AsyncUpdateList implementation.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AsyncUpdateList::process( S32 timeOut )
|
||||
{
|
||||
U32 endTime = 0;
|
||||
if( timeOut != -1 )
|
||||
endTime = Platform::getRealMilliseconds() + timeOut;
|
||||
|
||||
// Flush the process list.
|
||||
|
||||
IPolled* ptr;
|
||||
IPolled* firstProcessedPtr = 0;
|
||||
|
||||
while( mUpdateList.tryPopFront( ptr ) )
|
||||
{
|
||||
if( ptr == firstProcessedPtr )
|
||||
{
|
||||
// We've wrapped around. Stop.
|
||||
|
||||
mUpdateList.pushFront( ptr );
|
||||
break;
|
||||
}
|
||||
|
||||
if( ptr->update() )
|
||||
{
|
||||
mUpdateList.pushBack( ptr );
|
||||
|
||||
if( !firstProcessedPtr )
|
||||
firstProcessedPtr = ptr;
|
||||
}
|
||||
|
||||
// Stop if we have exceeded our processing time budget.
|
||||
|
||||
if( timeOut != -1
|
||||
&& Platform::getRealMilliseconds() >= endTime )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// AsyncUpdateThread implementation.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void AsyncUpdateThread::run( void* )
|
||||
{
|
||||
_setName( getName() );
|
||||
|
||||
while( !checkForStop() )
|
||||
{
|
||||
_waitForEventAndReset();
|
||||
|
||||
if( !checkForStop() )
|
||||
mUpdateList->process();
|
||||
}
|
||||
}
|
||||
162
Engine/source/platform/async/asyncUpdate.h
Normal file
162
Engine/source/platform/async/asyncUpdate.h
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ASYNCUPDATE_H_
|
||||
#define _ASYNCUPDATE_H_
|
||||
|
||||
#ifndef _PLATFORM_THREADS_THREAD_H_
|
||||
# include "platform/threads/thread.h"
|
||||
#endif
|
||||
#ifndef _THREADSAFEREFCOUNT_H_
|
||||
# include "platform/threads/threadSafeRefCount.h"
|
||||
#endif
|
||||
#ifndef _THREADSAFEDEQUE_H_
|
||||
# include "platform/threads/threadSafeDeque.h"
|
||||
#endif
|
||||
|
||||
|
||||
class IPolled;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Async update list.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// This structure keeps track of the objects that need
|
||||
/// updating.
|
||||
class AsyncUpdateList : public ThreadSafeRefCount< AsyncUpdateList >
|
||||
{
|
||||
protected:
|
||||
|
||||
typedef ThreadSafeDeque< IPolled* > UpdateList;
|
||||
|
||||
/// List of structures currently in the update loop.
|
||||
UpdateList mUpdateList;
|
||||
|
||||
public:
|
||||
|
||||
virtual ~AsyncUpdateList() {}
|
||||
|
||||
/// Update the structures currently on the processing list.
|
||||
///
|
||||
/// @param timeOut Soft limit in milliseconds on the time
|
||||
/// spent on flushing the list. Default of -1 means no
|
||||
/// limit and function will only return, if update list
|
||||
/// has been fully flushed.
|
||||
virtual void process( S32 timeOut = -1 );
|
||||
|
||||
/// Add the structure to the update list. It will stay
|
||||
/// on this list, until its update() method returns false.
|
||||
///
|
||||
/// @note This can be called on different threads.
|
||||
virtual void add( IPolled* ptr )
|
||||
{
|
||||
mUpdateList.pushBack( ptr );
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Async update thread.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Abstract baseclass for async update threads.
|
||||
class AsyncUpdateThread : public Thread, public ThreadSafeRefCount< AsyncUpdateThread >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Thread Parent;
|
||||
|
||||
protected:
|
||||
|
||||
/// Name of this thread.
|
||||
String mName;
|
||||
|
||||
/// Platform-dependent event data.
|
||||
void* mUpdateEvent;
|
||||
|
||||
/// The update list processed on this thread.
|
||||
ThreadSafeRef< AsyncUpdateList > mUpdateList;
|
||||
|
||||
/// Wait for an update event being triggered and
|
||||
/// immediately reset the event.
|
||||
///
|
||||
/// @note Note that this must be an atomic operation to avoid
|
||||
/// a race condition. Immediately resetting the event shields
|
||||
/// us from event releases happening during us updating getting
|
||||
/// ignored.
|
||||
virtual void _waitForEventAndReset();
|
||||
|
||||
public:
|
||||
|
||||
/// Create the update thread.
|
||||
/// The thread won't immediately start (we have virtual functions
|
||||
/// so construction needs to finish first) and will not auto-delete
|
||||
/// itself.
|
||||
AsyncUpdateThread( String name, AsyncUpdateList* updateList );
|
||||
|
||||
virtual ~AsyncUpdateThread();
|
||||
|
||||
virtual void run( void* );
|
||||
|
||||
/// Trigger the update event to notify the thread about
|
||||
/// pending updates.
|
||||
virtual void triggerUpdate();
|
||||
|
||||
///
|
||||
const String& getName() const { return mName; }
|
||||
|
||||
///
|
||||
void* getUpdateEvent() const { return mUpdateEvent; }
|
||||
};
|
||||
|
||||
/// Extension to update thread that also does automatic
|
||||
/// periodic updates.
|
||||
class AsyncPeriodicUpdateThread : public AsyncUpdateThread
|
||||
{
|
||||
typedef AsyncUpdateThread Parent;
|
||||
|
||||
protected:
|
||||
|
||||
/// Platform-dependent timer event.
|
||||
void* mUpdateTimer;
|
||||
|
||||
/// Time between periodic updates in milliseconds.
|
||||
U32 mIntervalMS;
|
||||
|
||||
virtual void _waitForEventAndReset();
|
||||
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
/// Default interval between periodic updates in milliseconds.
|
||||
DEFAULT_UPDATE_INTERVAL = 4000
|
||||
};
|
||||
|
||||
///
|
||||
AsyncPeriodicUpdateThread( String name,
|
||||
AsyncUpdateList* updateList,
|
||||
U32 intervalMS = DEFAULT_UPDATE_INTERVAL );
|
||||
|
||||
virtual ~AsyncPeriodicUpdateThread();
|
||||
};
|
||||
|
||||
#endif // _TORQUE_CORE_ASYNC_ASYNCUPDATE_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue