Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

View 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_

View 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_

View 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_

View 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();
}
}

View 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_