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_

View file

@ -0,0 +1,392 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
/// @file
/// Library-wide input events
///
/// All external events are converted into system events, which are defined
/// in this file.
///
#ifndef _EVENT_H_
#define _EVENT_H_
#include "platform/types.h"
#include "core/util/journal/journaledSignal.h"
/// @defgroup input_constants Input system constants
/// @{
/// Input event constants:
enum InputObjectInstances
{
KEY_NULL = 0x000, ///< Invalid KeyCode
KEY_BACKSPACE = 0x001,
KEY_TAB = 0x002,
KEY_RETURN = 0x003,
KEY_CONTROL = 0x004,
KEY_ALT = 0x005,
KEY_SHIFT = 0x006,
KEY_PAUSE = 0x007,
KEY_CAPSLOCK = 0x008,
KEY_ESCAPE = 0x009,
KEY_SPACE = 0x00a,
KEY_PAGE_DOWN = 0x00b,
KEY_PAGE_UP = 0x00c,
KEY_END = 0x00d,
KEY_HOME = 0x00e,
KEY_LEFT = 0x00f,
KEY_UP = 0x010,
KEY_RIGHT = 0x011,
KEY_DOWN = 0x012,
KEY_PRINT = 0x013,
KEY_INSERT = 0x014,
KEY_DELETE = 0x015,
KEY_HELP = 0x016,
KEY_0 = 0x017,
KEY_1 = 0x018,
KEY_2 = 0x019,
KEY_3 = 0x01a,
KEY_4 = 0x01b,
KEY_5 = 0x01c,
KEY_6 = 0x01d,
KEY_7 = 0x01e,
KEY_8 = 0x01f,
KEY_9 = 0x020,
KEY_A = 0x021,
KEY_B = 0x022,
KEY_C = 0x023,
KEY_D = 0x024,
KEY_E = 0x025,
KEY_F = 0x026,
KEY_G = 0x027,
KEY_H = 0x028,
KEY_I = 0x029,
KEY_J = 0x02a,
KEY_K = 0x02b,
KEY_L = 0x02c,
KEY_M = 0x02d,
KEY_N = 0x02e,
KEY_O = 0x02f,
KEY_P = 0x030,
KEY_Q = 0x031,
KEY_R = 0x032,
KEY_S = 0x033,
KEY_T = 0x034,
KEY_U = 0x035,
KEY_V = 0x036,
KEY_W = 0x037,
KEY_X = 0x038,
KEY_Y = 0x039,
KEY_Z = 0x03a,
KEY_TILDE = 0x03b,
KEY_MINUS = 0x03c,
KEY_EQUALS = 0x03d,
KEY_LBRACKET = 0x03e,
KEY_RBRACKET = 0x03f,
KEY_BACKSLASH = 0x040,
KEY_SEMICOLON = 0x041,
KEY_APOSTROPHE = 0x042,
KEY_COMMA = 0x043,
KEY_PERIOD = 0x044,
KEY_SLASH = 0x045,
KEY_NUMPAD0 = 0x046,
KEY_NUMPAD1 = 0x047,
KEY_NUMPAD2 = 0x048,
KEY_NUMPAD3 = 0x049,
KEY_NUMPAD4 = 0x04a,
KEY_NUMPAD5 = 0x04b,
KEY_NUMPAD6 = 0x04c,
KEY_NUMPAD7 = 0x04d,
KEY_NUMPAD8 = 0x04e,
KEY_NUMPAD9 = 0x04f,
KEY_MULTIPLY = 0x050,
KEY_ADD = 0x051,
KEY_SEPARATOR = 0x052,
KEY_SUBTRACT = 0x053,
KEY_DECIMAL = 0x054,
KEY_DIVIDE = 0x055,
KEY_NUMPADENTER = 0x056,
KEY_F1 = 0x057,
KEY_F2 = 0x058,
KEY_F3 = 0x059,
KEY_F4 = 0x05a,
KEY_F5 = 0x05b,
KEY_F6 = 0x05c,
KEY_F7 = 0x05d,
KEY_F8 = 0x05e,
KEY_F9 = 0x05f,
KEY_F10 = 0x060,
KEY_F11 = 0x061,
KEY_F12 = 0x062,
KEY_F13 = 0x063,
KEY_F14 = 0x064,
KEY_F15 = 0x065,
KEY_F16 = 0x066,
KEY_F17 = 0x067,
KEY_F18 = 0x068,
KEY_F19 = 0x069,
KEY_F20 = 0x06a,
KEY_F21 = 0x06b,
KEY_F22 = 0x06c,
KEY_F23 = 0x06d,
KEY_F24 = 0x06e,
KEY_NUMLOCK = 0x06f,
KEY_SCROLLLOCK = 0x070,
KEY_LCONTROL = 0x071,
KEY_RCONTROL = 0x072,
KEY_LALT = 0x073,
KEY_RALT = 0x074,
KEY_LSHIFT = 0x075,
KEY_RSHIFT = 0x076,
KEY_WIN_LWINDOW = 0x077,
KEY_WIN_RWINDOW = 0x078,
KEY_WIN_APPS = 0x079,
KEY_OEM_102 = 0x080,
KEY_MAC_OPT = 0x090,
KEY_MAC_LOPT = 0x091,
KEY_MAC_ROPT = 0x092,
KEY_BUTTON0 = 0x0100,
KEY_BUTTON1 = 0x0101,
KEY_BUTTON2 = 0x0102,
KEY_BUTTON3 = 0x0103,
KEY_BUTTON4 = 0x0104,
KEY_BUTTON5 = 0x0105,
KEY_BUTTON6 = 0x0106,
KEY_BUTTON7 = 0x0107,
KEY_BUTTON8 = 0x0108,
KEY_BUTTON9 = 0x0109,
KEY_BUTTON10 = 0x010A,
KEY_BUTTON11 = 0x010B,
KEY_BUTTON12 = 0x010C,
KEY_BUTTON13 = 0x010D,
KEY_BUTTON14 = 0x010E,
KEY_BUTTON15 = 0x010F,
KEY_BUTTON16 = 0x0110,
KEY_BUTTON17 = 0x0111,
KEY_BUTTON18 = 0x0112,
KEY_BUTTON19 = 0x0113,
KEY_BUTTON20 = 0x0114,
KEY_BUTTON21 = 0x0115,
KEY_BUTTON22 = 0x0116,
KEY_BUTTON23 = 0x0117,
KEY_BUTTON24 = 0x0118,
KEY_BUTTON25 = 0x0119,
KEY_BUTTON26 = 0x011A,
KEY_BUTTON27 = 0x011B,
KEY_BUTTON28 = 0x011C,
KEY_BUTTON29 = 0x011D,
KEY_BUTTON30 = 0x011E,
KEY_BUTTON31 = 0x011F,
KEY_ANYKEY = 0xfffe,
/// Joystick event codes.
SI_XPOV = 0x204,
SI_YPOV = 0x205,
SI_UPOV = 0x206,
SI_DPOV = 0x207,
SI_LPOV = 0x208,
SI_RPOV = 0x209,
SI_XAXIS = 0x20B,
SI_YAXIS = 0x20C,
SI_ZAXIS = 0x20D,
SI_RXAXIS = 0x20E,
SI_RYAXIS = 0x20F,
SI_RZAXIS = 0x210,
SI_SLIDER = 0x211,
SI_XPOV2 = 0x212,
SI_YPOV2 = 0x213,
SI_UPOV2 = 0x214,
SI_DPOV2 = 0x215,
SI_LPOV2 = 0x216,
SI_RPOV2 = 0x217,
XI_CONNECT = 0x300,
XI_THUMBLX = 0x301,
XI_THUMBLY = 0x302,
XI_THUMBRX = 0x303,
XI_THUMBRY = 0x304,
XI_LEFT_TRIGGER = 0x305,
XI_RIGHT_TRIGGER = 0x306,
/*XI_DPAD_UP = 0x307,
XI_DPAD_DOWN = 0x308,
XI_DPAD_LEFT = 0x309,
XI_DPAD_RIGHT = 0x310,*/
XI_START = 0x311,
XI_BACK = 0x312,
XI_LEFT_THUMB = 0x313,
XI_RIGHT_THUMB = 0x314,
XI_LEFT_SHOULDER = 0x315,
XI_RIGHT_SHOULDER = 0x316,
XI_A = 0x317,
XI_B = 0x318,
XI_X = 0x319,
XI_Y = 0x320,
};
/// Input device types
enum InputDeviceTypes
{
UnknownDeviceType,
MouseDeviceType,
KeyboardDeviceType,
JoystickDeviceType,
GamepadDeviceType,
XInputDeviceType,
NUM_INPUT_DEVICE_TYPES
};
/// Device Event Action Types
enum InputActionType
{
/// Button was depressed.
SI_MAKE = 0x01,
/// Button was released.
SI_BREAK = 0x02,
/// An axis moved.
SI_MOVE = 0x03,
/// A key repeat occurred. Happens in between a SI_MAKE and SI_BREAK.
SI_REPEAT = 0x04,
};
///Device Event Types
enum InputEventType
{
SI_UNKNOWN = 0x01,
SI_BUTTON = 0x02,
SI_POV = 0x03,
SI_AXIS = 0x04,
SI_KEY = 0x0A,
};
/// Wildcard match used by the input system.
#define SI_ANY 0xff
// Modifier Keys
enum InputModifiers
{
/// shift and ctrl are the same between platforms.
SI_LSHIFT = BIT(0),
SI_RSHIFT = BIT(1),
SI_SHIFT = (SI_LSHIFT|SI_RSHIFT),
SI_LCTRL = BIT(2),
SI_RCTRL = BIT(3),
SI_CTRL = (SI_LCTRL|SI_RCTRL),
/// win altkey, mapped to mac cmdkey.
SI_LALT = BIT(4),
SI_RALT = BIT(5),
SI_ALT = (SI_LALT|SI_RALT),
/// mac optionkey
SI_MAC_LOPT = BIT(6),
SI_MAC_ROPT = BIT(7),
SI_MAC_OPT = (SI_MAC_LOPT|SI_MAC_ROPT),
/// modifier keys used for common operations
#if defined(TORQUE_OS_MAC)
SI_COPYPASTE = SI_ALT,
SI_MULTISELECT = SI_ALT,
SI_RANGESELECT = SI_SHIFT,
SI_PRIMARY_ALT = SI_MAC_OPT, ///< Primary key used for toggling into alternates of commands.
SI_PRIMARY_CTRL = SI_ALT, ///< Primary key used for triggering commands.
#else
SI_COPYPASTE = SI_CTRL,
SI_MULTISELECT = SI_CTRL,
SI_RANGESELECT = SI_SHIFT,
SI_PRIMARY_ALT = SI_ALT,
SI_PRIMARY_CTRL = SI_CTRL,
#endif
/// modfier key used in conjunction w/ arrow keys to move cursor to next word
#if defined(TORQUE_OS_MAC)
SI_WORDJUMP = SI_MAC_OPT,
#else
SI_WORDJUMP = SI_CTRL,
#endif
/// modifier key used in conjunction w/ arrow keys to move cursor to beginning / end of line
SI_LINEJUMP = SI_ALT,
/// modifier key used in conjunction w/ home & end to jump to the top or bottom of a document
#if defined(TORQUE_OS_MAC)
SI_DOCJUMP = SI_ANY,
#else
SI_DOCJUMP = SI_CTRL,
#endif
};
/// @}
/// Generic input event.
struct InputEventInfo
{
InputEventInfo()
{
deviceInst = 0;
fValue = 0.f;
deviceType = (InputDeviceTypes)0;
objType = (InputEventType)0;
ascii = 0;
objInst = (InputObjectInstances)0;
action = (InputActionType)0;
modifier = (InputModifiers)0;
}
/// Device instance: joystick0, joystick1, etc
U32 deviceInst;
/// Value ranges from -1.0 to 1.0
F32 fValue;
/// What was the action? (MAKE/BREAK/MOVE)
InputActionType action;
InputDeviceTypes deviceType;
InputEventType objType;
InputObjectInstances objInst;
/// ASCII character code if this is a keyboard event.
U16 ascii;
/// Modifiers to action: SI_LSHIFT, SI_LCTRL, etc.
InputModifiers modifier;
inline void postToSignal(InputEvent &ie)
{
ie.trigger(deviceInst, fValue, deviceType, objType, ascii, objInst, action, modifier);
}
};
#endif

View file

@ -0,0 +1,127 @@
//-----------------------------------------------------------------------------
// 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/platform.h"
#include "platform/menus/menuBar.h"
#include "platform/menus/popupMenu.h"
#include "gui/core/guiCanvas.h"
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
MenuBar::MenuBar()
{
createPlatformPopupMenuData();
mCanvas = NULL;
}
MenuBar::~MenuBar()
{
removeFromCanvas();
deletePlatformPopupMenuData();
}
IMPLEMENT_CONOBJECT(MenuBar);
ConsoleDocClass( MenuBar,
"@brief Used for rendering platform menu bars\n\n"
"Internal use only\n\n"
"@internal"
);
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
void MenuBar::addObject(SimObject *obj)
{
Parent::addObject(obj);
updateMenuBar(dynamic_cast<PopupMenu *>(obj));
}
void MenuBar::removeObject(SimObject *obj)
{
Parent::removeObject(obj);
updateMenuBar(dynamic_cast<PopupMenu *>(obj));
}
void MenuBar::insertObject(SimObject *obj, S32 pos)
{
Parent::addObject(obj);
if(pos >= size())
pos = size() - 1;
if(pos < size())
{
if(pos < 0) pos = 0;
Parent::reOrder(obj, at(pos));
}
updateMenuBar(dynamic_cast<PopupMenu *>(obj));
}
void MenuBar::pushObject(SimObject *obj)
{
Parent::pushObject(obj);
updateMenuBar(dynamic_cast<PopupMenu *>(obj));
}
void MenuBar::popObject()
{
Parent::popObject();
updateMenuBar();
}
bool MenuBar::reOrder(SimObject *obj, SimObject *target /*= 0*/)
{
bool ret = Parent::reOrder(obj, target);
if(ret)
updateMenuBar(dynamic_cast<PopupMenu *>(obj));
return ret;
}
//-----------------------------------------------------------------------------
// Console Methods
//-----------------------------------------------------------------------------
ConsoleMethod(MenuBar, attachToCanvas, void, 4, 4, "(GuiCanvas, pos)")
{
object->attachToCanvas(dynamic_cast<GuiCanvas*>(Sim::findObject(argv[2])), dAtoi(argv[3]));
}
ConsoleMethod(MenuBar, removeFromCanvas, void, 2, 2, "()")
{
object->removeFromCanvas();
}
//-----------------------------------------------------------------------------
ConsoleMethod(MenuBar, insert, void, 4, 4,"(object, pos) insert object at position")
{
SimObject* pObject = Sim::findObject(argv[2]);
if(pObject)
object->insertObject(pObject, dAtoi(argv[3]));
}

View file

@ -0,0 +1,71 @@
//-----------------------------------------------------------------------------
// 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 "console/simBase.h"
#ifndef _MENUBAR_H_
#define _MENUBAR_H_
// Forward Refs
class PlatformMenuBarData;
class PopupMenu;
class GuiCanvas;
class MenuBar : public SimSet
{
typedef SimSet Parent;
protected:
PlatformMenuBarData *mData;
GuiCanvas *mCanvas;
/// Update the native menu bar to ensure consistency with the set
void updateMenuBar(PopupMenu *menu = NULL);
void createPlatformPopupMenuData();
void deletePlatformPopupMenuData();
public:
MenuBar();
virtual ~MenuBar();
DECLARE_CONOBJECT(MenuBar);
/// Attach this menu bar to the native menu bar
void attachToCanvas(GuiCanvas *owner, S32 pos);
/// Remove this menu bar from the native menu bar
void removeFromCanvas();
/// Returns true if this menu is attached to the menu bar
bool isAttachedToCanvas() { return mCanvas != NULL; }
virtual void insertObject(SimObject *obj, S32 pos);
// Overridden SimSet methods to ensure menu bar consistency when attached
virtual void addObject(SimObject *obj);
virtual void removeObject(SimObject *obj);
virtual void pushObject(SimObject *obj);
virtual void popObject();
virtual bool reOrder(SimObject *obj, SimObject *target = 0);
};
#endif // _MENUBAR_H_

View file

@ -0,0 +1,270 @@
//-----------------------------------------------------------------------------
// 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/menus/popupMenu.h"
#include "console/consoleTypes.h"
#include "gui/core/guiCanvas.h"
#include "core/util/safeDelete.h"
static U32 sMaxPopupGUID = 0;
PopupMenuEvent PopupMenu::smPopupMenuEvent;
bool PopupMenu::smSelectionEventHandled = false;
/// Event class used to remove popup menus from the event notification in a safe way
class PopUpNotifyRemoveEvent : public SimEvent
{
public:
void process(SimObject *object)
{
PopupMenu::smPopupMenuEvent.remove((PopupMenu *)object, &PopupMenu::handleSelectEvent);
}
};
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
PopupMenu::PopupMenu() : mCanvas(NULL)
{
createPlatformPopupMenuData();
mSubmenus = new SimSet;
mSubmenus->registerObject();
mBarTitle = StringTable->insert("");
mIsPopup = false;
mPopupGUID = sMaxPopupGUID++;
}
PopupMenu::~PopupMenu()
{
// This searches the menu bar so is safe to call for menus
// that aren't on it, since nothing will happen.
removeFromMenuBar();
SimSet::iterator i;
while((i = mSubmenus->begin()) != mSubmenus->end())
{
(*i)->deleteObject();
}
mSubmenus->deleteObject();
deletePlatformPopupMenuData();
PopupMenu::smPopupMenuEvent.remove(this, &PopupMenu::handleSelectEvent);
}
IMPLEMENT_CONOBJECT(PopupMenu);
ConsoleDocClass( PopupMenu,
"@brief PopupMenu represents a system menu.\n\n"
"You can add menu items to the menu, but there is no torque object associated "
"with these menu items, they exist only in a platform specific manner.\n\n"
"@note Internal use only\n\n"
"@internal"
);
//-----------------------------------------------------------------------------
void PopupMenu::initPersistFields()
{
addField("isPopup", TypeBool, Offset(mIsPopup, PopupMenu), "true if this is a pop-up/context menu. defaults to false.");
addField("barTitle", TypeCaseString, Offset(mBarTitle, PopupMenu), "the title of this menu when attached to a menu bar");
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
bool PopupMenu::onAdd()
{
if(! Parent::onAdd())
return false;
createPlatformMenu();
Con::executef(this, "onAdd");
return true;
}
void PopupMenu::onRemove()
{
Con::executef(this, "onRemove");
Parent::onRemove();
}
//-----------------------------------------------------------------------------
void PopupMenu::onMenuSelect()
{
Con::executef(this, "onMenuSelect");
}
//-----------------------------------------------------------------------------
void PopupMenu::handleSelectEvent(U32 popID, U32 command)
{
if (popID == mPopupGUID && canHandleID(command))
if (handleSelect(command))
smSelectionEventHandled = true;
}
//-----------------------------------------------------------------------------
void PopupMenu::onAttachToMenuBar(GuiCanvas *canvas, S32 pos, const char *title)
{
mCanvas = canvas;
// Attached menus must be notified of menu events
smPopupMenuEvent.notify(this, &PopupMenu::handleSelectEvent);
// Pass on to sub menus
for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i)
{
PopupMenu *mnu = dynamic_cast<PopupMenu *>(*i);
if(mnu == NULL)
continue;
mnu->onAttachToMenuBar(canvas, pos, title);
}
// Call script
if(isProperlyAdded())
Con::executef(this, "onAttachToMenuBar", Con::getIntArg(canvas ? canvas->getId() : 0), Con::getIntArg(pos), title);
}
void PopupMenu::onRemoveFromMenuBar(GuiCanvas *canvas)
{
mCanvas = NULL;
// We are no longer interested in select events, remove ourselves from the notification list in a safe way
Sim::postCurrentEvent(this, new PopUpNotifyRemoveEvent());
// Pass on to sub menus
for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i)
{
PopupMenu *mnu = dynamic_cast<PopupMenu *>(*i);
if(mnu == NULL)
continue;
mnu->onRemoveFromMenuBar(canvas);
}
// Call script
if(isProperlyAdded())
Con::executef(this, "onRemoveFromMenuBar", Con::getIntArg(canvas ? canvas->getId() : 0));
}
//-----------------------------------------------------------------------------
bool PopupMenu::onMessageReceived(StringTableEntry queue, const char* event, const char* data)
{
return Con::executef(this, "onMessageReceived", queue, event, data);
}
bool PopupMenu::onMessageObjectReceived(StringTableEntry queue, Message *msg )
{
return Con::executef(this, "onMessageReceived", queue, Con::getIntArg(msg->getId()));
}
//-----------------------------------------------------------------------------
// Console Methods
//-----------------------------------------------------------------------------
ConsoleMethod(PopupMenu, insertItem, S32, 3, 5, "(pos[, title][, accelerator])")
{
return object->insertItem(dAtoi(argv[2]), argc < 4 ? NULL : argv[3], argc < 5 ? "" : argv[4]);
}
ConsoleMethod(PopupMenu, removeItem, void, 3, 3, "(pos)")
{
object->removeItem(dAtoi(argv[2]));
}
ConsoleMethod(PopupMenu, insertSubMenu, S32, 5, 5, "(pos, title, subMenu)")
{
PopupMenu *mnu = dynamic_cast<PopupMenu *>(Sim::findObject(argv[4]));
if(mnu == NULL)
{
Con::errorf("PopupMenu::insertSubMenu - Invalid PopupMenu object specified for submenu");
return -1;
}
return object->insertSubMenu(dAtoi(argv[2]), argv[3], mnu);
}
ConsoleMethod(PopupMenu, setItem, bool, 4, 5, "(pos, title[, accelerator])")
{
return object->setItem(dAtoi(argv[2]), argv[3], argc < 5 ? "" : argv[4]);
}
//-----------------------------------------------------------------------------
ConsoleMethod(PopupMenu, enableItem, void, 4, 4, "(pos, enabled)")
{
object->enableItem(dAtoi(argv[2]), dAtob(argv[3]));
}
ConsoleMethod(PopupMenu, checkItem, void, 4, 4, "(pos, checked)")
{
object->checkItem(dAtoi(argv[2]), dAtob(argv[3]));
}
ConsoleMethod(PopupMenu, checkRadioItem, void, 5, 5, "(firstPos, lastPos, checkPos)")
{
object->checkRadioItem(dAtoi(argv[2]), dAtoi(argv[3]), dAtoi(argv[4]));
}
ConsoleMethod(PopupMenu, isItemChecked, bool, 3, 3, "(pos)")
{
return object->isItemChecked(dAtoi(argv[2]));
}
ConsoleMethod(PopupMenu, getItemCount, S32, 2, 2, "()")
{
return object->getItemCount();
}
//-----------------------------------------------------------------------------
ConsoleMethod(PopupMenu, attachToMenuBar, void, 5, 5, "(GuiCanvas, pos, title)")
{
object->attachToMenuBar(dynamic_cast<GuiCanvas*>(Sim::findObject(argv[2])),dAtoi(argv[3]), argv[4]);
}
ConsoleMethod(PopupMenu, removeFromMenuBar, void, 2, 2, "()")
{
object->removeFromMenuBar();
}
//-----------------------------------------------------------------------------
ConsoleMethod(PopupMenu, showPopup, void, 3, 5, "(Canvas,[x, y])")
{
GuiCanvas *pCanvas = dynamic_cast<GuiCanvas*>(Sim::findObject(argv[2]));
S32 x = argc >= 4 ? dAtoi(argv[3]) : -1;
S32 y = argc >= 5 ? dAtoi(argv[4]) : -1;
object->showPopup(pCanvas, x, y);
}

View file

@ -0,0 +1,189 @@
//-----------------------------------------------------------------------------
// 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 "console/simBase.h"
#include "core/util/tVector.h"
#include "util/messaging/dispatcher.h"
#include "gui/core/guiCanvas.h"
#ifndef _POPUPMENU_H_
#define _POPUPMENU_H_
// Forward ref used by the platform code
struct PlatformPopupMenuData;
class MenuBar;
// PopupMenu represents a menu.
// You can add menu items to the menu, but there is no torque object associated
// with these menu items, they exist only in a platform specific manner.
class PopupMenu : public SimObject, public virtual Dispatcher::IMessageListener
{
typedef SimObject Parent;
friend class MenuBar;
private:
/// Used by MenuBar to attach the menu to the menu bar. Do not use anywhere else.
void attachToMenuBar(GuiCanvas *owner, S32 pos);
protected:
PlatformPopupMenuData *mData;
SimSet *mSubmenus;
SimObjectPtr<GuiCanvas> mCanvas;
StringTableEntry mBarTitle;
U32 mPopupGUID;
bool mIsPopup;
public:
PopupMenu();
virtual ~PopupMenu();
void createPlatformPopupMenuData();
void deletePlatformPopupMenuData();
DECLARE_CONOBJECT(PopupMenu);
static void initPersistFields();
virtual bool onAdd();
virtual void onRemove();
static PopupMenuEvent smPopupMenuEvent;
static bool smSelectionEventHandled; /// Set to true if any menu or submenu handles a selection event
/// Creates the platform specific menu object, a peer to this object.
/// The platform menu *must* exist before calling any method that manipulates
/// menu items or displays the menu.
/// implementd on a per-platform basis.
void createPlatformMenu();
void setBarTitle(const char * val) { mBarTitle = StringTable->insert(val, true); }
StringTableEntry getBarTitle() const { return mBarTitle; }
/// pass NULL for @p title to insert a separator
/// returns the menu item's ID, or -1 on failure.
/// implementd on a per-platform basis.
/// TODO: factor out common code
S32 insertItem(S32 pos, const char *title, const char* accelerator);
/// Sets the name title and accelerator for
/// an existing item.
bool setItem(S32 pos, const char *title, const char* accelerator);
/// pass NULL for @p title to insert a separator
/// returns the menu item's ID, or -1 on failure.
/// adds the submenu to the mSubmenus vector.
/// implemented on a per-platform basis.
/// TODO: factor out common code
S32 insertSubMenu(S32 pos, const char *title, PopupMenu *submenu);
/// remove the menu item at @p itemPos
/// if the item has a submenu, it is removed from the mSubmenus list.
/// implemented on a per-platform basis.
/// TODO: factor out common code
void removeItem(S32 itemPos);
/// implemented on a per-platform basis.
void enableItem(S32 pos, bool enable);
/// implemented on a per-platform basis.
void checkItem(S32 pos, bool checked);
/// All items at positions firstPos through lastPos are unchecked, and the
/// item at checkPos is checked.
/// implemented on a per-platform basis.
void checkRadioItem(S32 firstPos, S32 lastPos, S32 checkPos);
bool isItemChecked(S32 pos);
/// Returns the number of items in the menu.
U32 getItemCount();
/// Returns the popup GUID
U32 getPopupGUID() { return mPopupGUID; }
//-----------------------------------------------------------------------------
// New code should not use these methods directly, use the menu bar instead.
//
// They remain for compatibility with old code and will be changing/going away
// once the existing code is moved over to the menu bar.
//-----------------------------------------------------------------------------
/// Places this menu in the menu bar of the application's main window.
/// @param owner The GuiCanvas that owns the PlatformWindow that this call is associated with
/// @param pos The relative position at which to place the menu.
/// @param title The name of the menu
void attachToMenuBar(GuiCanvas *owner, S32 pos, const char *title);
/// Removes this menu from the menu bar.
void removeFromMenuBar();
//-----------------------------------------------------------------------------
/// Called when the menu has been attached to the menu bar
void onAttachToMenuBar(GuiCanvas *canvas, S32 pos, const char *title);
/// Called when the menu has been removed from the menu bar
void onRemoveFromMenuBar(GuiCanvas *canvas);
/// Returns the position index of this menu on the bar.
S32 getPosOnMenuBar();
/// Returns true if this menu is attached to the menu bar
bool isAttachedToMenuBar() { return mCanvas != NULL; }
/// Displays this menu as a popup menu and blocks until the user has selected
/// an item.
/// @param canvas the owner to show this popup associated with
/// @param x window local x coordinate at which to display the popup menu
/// @param y window local y coordinate at which to display the popup menu
/// implemented on a per-platform basis.
void showPopup(GuiCanvas *owner, S32 x = -1, S32 y = -1);
/// Returns true iff this menu contains an item that matches @p iD.
/// implemented on a per-platform basis.
/// TODO: factor out common code
bool canHandleID(U32 iD);
/// A menu item in this menu has been selected by id.
/// Submenus are given a chance to respond to the command first.
/// If no submenu can handle the command id, this menu handles it.
/// The script callback this::onSelectItem( position, text) is called.
/// If @p text is null, then the text arg passed to script is the text of
/// the selected menu item.
/// implemented on a per-platform basis.
/// TODO: factor out common code
bool handleSelect(U32 command, const char *text = NULL);
void onMenuSelect();
/// Helper function to allow menu selections from signal events.
/// Wraps canHandleID() and handleSelect() in one function
/// without changing their internal functionality, so
/// it should work regardless of platform.
void handleSelectEvent(U32 popID, U32 command);
virtual bool onMessageReceived(StringTableEntry queue, const char* event, const char* data );
virtual bool onMessageObjectReceived(StringTableEntry queue, Message *msg );
};
#endif // _POPUPMENU_H_

View file

@ -0,0 +1,208 @@
//-----------------------------------------------------------------------------
// 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 _FILEDIALOG_H_
#define _FILEDIALOG_H_
#include "console/simBase.h"
// [03/14/07] The file dialogs need refactoring, and will be refactored in Jugg.
// Things that might need to change:
// - The interface is not in fact platform agnotsic, it is win32 oriented.
// - Filter format is highly windows specific, and is a little fragile, both for
// win32 and for other platforms.
// - Platform specific path strings are exposed to the console, because the
// protected validators save them as such.
// - Several of the FDS_XXX values are not options we want to give the user, such
// as NOT warning on file overwrite. The values FDS_OVERWRITEPROMPT,
// FDS_MUSTEXIST, and FDS_CHANGEPATH are not good things to give the user.
// - The Execute method is virtual for good reason. It should be implemented for
// each subclass. If common behavior is needed for Execute(), it can be
// factored out in hidden platform specific code.
/// @defgroup SystemDialogs Using System Dialogs
/// @ingroup SystemDialogs
/// FileDialogOpaqueData is both defined and implemented on a platform specific
/// basis.
class FileDialogOpaqueData;
/// @ingroup SystemDialogs
/// @internal
/// Platform Agnostic Structure for holding information about a file dialog.
struct FileDialogData
{
public:
FileDialogData();
~FileDialogData();
enum DialogStyle
{
FDS_OPEN = BIT(0),///< This is an open dialog.
FDS_SAVE = BIT(1),///< This is a save dialog.
FDS_OVERWRITEPROMPT = BIT(2),///< Can only be used in conjunction with style SaveDialog: prompt for a confirmation if a file will be overwritten.
FDS_MUSTEXIST = BIT(3),///< The user may only select files that actually exist.
FDS_MULTIPLEFILES = BIT(4),///< Can only be used in conjunction with style OpenDialog: allows selecting multiple files.
FDS_CHANGEPATH = BIT(5),///< Change the current working path to the directory where the file(s) chosen by the user are.
FDS_BROWSEFOLDER = BIT(6) ///< Select folders instead of files
};
U8 mStyle; ///< Specifies the Style of the File Dialog @see DialogStyle
StringTableEntry mFilters; ///< List of Filters pipe separated e.g. "BMP Files (*.bmp)|*.bmp|JPG Files (*.jpg)|*.jpg"
//StringTableEntry mFiles; // this is never used ///< Should only be referenced when using dialogStyle OpenDialog AND MultipleFiles: List of Files returned pipe separated
StringTableEntry mFile; ///< Should be referenced when dialogStyle MultipleFiles is NOT used: the file path of the user selected file.
StringTableEntry mDefaultPath; ///< Default path of dialog
StringTableEntry mDefaultFile; ///< Default selected file of dialog
StringTableEntry mTitle; ///< Title to display in file dialog
FileDialogOpaqueData *mOpaqueData; ///< Stores platform specific info about the dialog
};
/// @ingroup SystemDialogs
/// FileDialog is a platform agnostic dialog interface for querying the user for
/// file locations. It is designed to be used through the exposed
/// scripting interface.
///
/// FileDialog is the base class for Native File Dialog controls in Torque. It provides these
/// basic areas of functionality:
///
/// - Inherits from SimObject and is exposed to the scripting interface
/// - Provides blocking interface to allow instant return to script execution
/// - Simple object configuration makes practical use easy and effective
///
/// @attention
/// FileDialog is *NOT* intended to be used directly in script and is only exposed to script
/// to expose generic file dialog attributes.
/// @see OpenFileDialog for a practical example on opening a file
/// @see SaveFileDialog for a practical example of saving a file
///
///
/// @{
class FileDialog : public SimObject
{
typedef SimObject Parent;
protected:
FileDialogData mData; ///< Stores platform agnostic information about the dialogs properties
bool mChangePath; ///< Exposed ChangePath Property
bool mBoolTranslator; ///< Internally used to translate boolean values into their respective bits of dialog style
public:
FileDialog();
virtual ~FileDialog();
DECLARE_CONOBJECT(FileDialog);
static void initPersistFields();
virtual bool Execute();
FileDialogData &getData() { return mData; };
protected:
/// @name FileDialog Properties
/// @{
/// @@property DefaultPath (String) : <i>Path to use as the default when the dialog is shown.</i>
/// @code %fd.DefaultPath = "/source/myGameProject/data/images"; @endcode
///
/// @li @b ChangePath (bool) : <c>Will change the working path of the tools to the selected path when not canceled</c>
/// @code %fd.ChangePath = true; // Change Working Path on Success @endcode
/// @internal
static bool setDefaultPath( void *object, const char *index, const char *data );
static bool setDefaultFile( void *object, const char *index, const char *data );
static bool setFilters( void *object, const char *index, const char *data );
static bool setChangePath( void *object, const char *index, const char *data );
static const char* getChangePath(void* obj, const char* data);
///
/// @}
static bool setFile( void *object, const char *index, const char *data );
};
/// @}
class OpenFileDialog : public FileDialog
{
typedef FileDialog Parent;
/// Field Values
/// @{
/// @internal
bool mMustExist; ///< Corresponds to FDS_MUSTEXIST flag on the PlatformFileDlgData structure
bool mMultipleFiles; ///< Corresponds to the FDS_MULTIPLEFILES flag on the PlatformFileDlgData structure
/// @}
public:
OpenFileDialog();
virtual ~OpenFileDialog();
DECLARE_CONOBJECT(OpenFileDialog); /// @internal
static void initPersistFields();
protected:
///
/// @}
/// Must Exist Property
static bool setMustExist( void *object, const char *index, const char *data );
static const char*getMustExist(void* obj, const char* data);
/// Multiple Files Property
static bool setMultipleFiles( void *object, const char *index, const char *data );
static const char* getMultipleFiles(void* obj, const char* data);
};
class OpenFolderDialog : public OpenFileDialog
{
typedef OpenFileDialog Parent;
public:
StringTableEntry mMustExistInDir;
OpenFolderDialog();
DECLARE_CONOBJECT(OpenFolderDialog);
static void initPersistFields();
};
class SaveFileDialog : public FileDialog
{
typedef FileDialog Parent;
public:
SaveFileDialog();
virtual ~SaveFileDialog();
DECLARE_CONOBJECT(SaveFileDialog);
bool mOverwritePrompt;
static void initPersistFields();
protected:
// Overwrite Prompt Property
static bool setOverwritePrompt( void *object, const char *index, const char *data );
static const char* getOverwritePrompt(void* obj, const char* data);
};
#endif // _FILEDIALOG_H_

View file

@ -0,0 +1,101 @@
//-----------------------------------------------------------------------------
// 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 "core/strings/stringFunctions.h"
#include "core/module.h"
#include "console/console.h"
#include "console/engineAPI.h"
#include "platform/nativeDialogs/msgBox.h"
DefineEnumType( MBButtons );
DefineEnumType( MBIcons );
DefineEnumType( MBReturnVal );
static const MBReturnVal gsOK = MROk;
static const MBReturnVal gsCancel = MRCancel;
static const MBReturnVal gsRetry = MRRetry;
static const MBReturnVal gsDontSave = MRDontSave;
AFTER_MODULE_INIT( Sim )
{
#if !defined( _XBOX ) && !defined( TORQUE_DEDICATED )
Con::addConstant( "$MROk", TypeS32, &gsOK, "Determines the ok button press state in a message box.\n"
"@ingroup Platform" );
Con::addConstant( "$MRCancel", TypeS32, &gsCancel, "Determines the cancel button press state in a message box.\n"
"@ingroup Platform" );
Con::addConstant( "$MRRetry", TypeS32, &gsRetry, "Determines the retry button press state in a message box.\n"
"@ingroup Platform");
Con::addConstant( "$MRDontSave", TypeS32, &gsDontSave, "Determines the don't save button press state in a message box.\n"
"@ingroup Platform" );
#endif
}
//-----------------------------------------------------------------------------
ImplementEnumType( MBButtons,
"Which buttons to display on a message box.\n\n"
"@ingroup Platform" )
{ MBOk, "Ok" },
{ MBOkCancel, "OkCancel" },
{ MBRetryCancel, "RetryCancel" },
{ MBSaveDontSave, "SaveDontSave" }, // maps to yes/no on win, to save/discard on mac.
{ MBSaveDontSaveCancel, "SaveDontSaveCancel" }, // maps to yes/no/cancel on win, to save/cancel/don'tsave on mac.
EndImplementEnumType;
ImplementEnumType( MBIcons,
"What icon to show on a message box.\n\n"
"@ingroup Platform" )
{ MIInformation, "Information" },// win: blue i, mac: app icon or talking head
{ MIWarning, "Warning" }, // win & mac: yellow triangle with exclamation pt
{ MIStop, "Stop" }, // win: red x, mac: app icon or stop icon, depending on version
{ MIQuestion, "Question" }, // win: blue ?, mac: app icon
EndImplementEnumType;
ImplementEnumType( MBReturnVal,
"Return value for messageBox() indicating which button was pressed by the user.\n\n"
"@ingroup Platform" )
{ MROk, "OK" },
{ MRCancel, "Cancelled" },
{ MRRetry, "Retry" },
{ MRDontSave, "DontSave" }
EndImplementEnumType;
//-----------------------------------------------------------------------------
DefineEngineFunction( messageBox, S32, ( const char* title, const char* message, MBButtons buttons, MBIcons icons ), ( MBOkCancel, MIInformation ),
"Display a modal message box using the platform's native message box implementation.\n\n"
"@param title The title to display on the message box window.\n"
"@param message The text message to display in the box.\n"
"@param buttons Which buttons to put on the message box.\n"
"@param icons Which icon to show next to the message.\n"
"@return One of $MROK, $MRCancel, $MRRetry, and $MRDontSave identifying the button that the user pressed.\n"
"@tsexample\n"
"messageBox( \"Error\", \"\" );\n" //TODO
"@endtsexample\n\n"
"@ingroup Platform" )
{
return Platform::messageBox( title, message, buttons, icons );
}

View file

@ -0,0 +1,62 @@
//-----------------------------------------------------------------------------
// 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 _MSGBOX_H_
#define _MSGBOX_H_
// [tom, 10/17/2006] Note: If you change either of these enums, make sure you
// update the relevant code in the all the platform layers.
// [pauls, 3/20/2007] Reduced the available types of dialog boxes in order to
// maintain a consistent but platform - appropriate look and feel in Torque.
enum MBButtons
{
MBOk,
MBOkCancel,
MBRetryCancel,
MBSaveDontSave,
MBSaveDontSaveCancel,
};
enum MBIcons
{
MIWarning,
MIInformation,
MIQuestion,
MIStop,
};
enum MBReturnVal
{
MROk = 1, // Start from 1 to allow use of 0 for errors
MRCancel,
MRRetry,
MRDontSave,
};
extern void initMessageBoxVars();
#endif // _MSGBOX_H_

View file

@ -0,0 +1,155 @@
//-----------------------------------------------------------------------------
// 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 <limits>
#include "platform/platform.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "platform/threads/mutex.h"
#include "app/mainLoop.h"
#include "platform/event.h"
#include "platform/typetraits.h"
const F32 TypeTraits< F32 >::MIN = - F32_MAX;
const F32 TypeTraits< F32 >::MAX = F32_MAX;
const F32 TypeTraits< F32 >::ZERO = 0;
const F32 Float_Inf = std::numeric_limits< F32 >::infinity();
// The tools prefer to allow the CPU time to process
#ifndef TORQUE_TOOLS
S32 sgBackgroundProcessSleepTime = 25;
#else
S32 sgBackgroundProcessSleepTime = 200;
#endif
S32 sgTimeManagerProcessInterval = 1;
Vector<Platform::KeyboardInputExclusion> gKeyboardExclusionList;
bool gInitKeyboardExclusionList = false;
static bool gWebDeployment = false;
void Platform::initConsole()
{
Con::addVariable("$platform::backgroundSleepTime", TypeS32, &sgBackgroundProcessSleepTime, "Controls processor time usage when the game window is out of focus.\n"
"@ingroup Platform\n");
Con::addVariable("$platform::timeManagerProcessInterval", TypeS32, &sgTimeManagerProcessInterval, "Controls processor time usage when the game window is in focus.\n"
"@ingroup Platform\n");
}
S32 Platform::getBackgroundSleepTime()
{
return sgBackgroundProcessSleepTime;
}
ConsoleToolFunction(restartInstance, void, 1, 1, "restartInstance()")
{
StandardMainLoop::setRestart(true);
Platform::postQuitMessage( 0 );
}
void Platform::clearKeyboardInputExclusion()
{
gKeyboardExclusionList.clear();
gInitKeyboardExclusionList = true;
}
void Platform::addKeyboardInputExclusion(const KeyboardInputExclusion &kie)
{
gKeyboardExclusionList.push_back(kie);
}
const bool Platform::checkKeyboardInputExclusion(const InputEventInfo *info)
{
// Do one-time initialization of platform defaults.
if(!gInitKeyboardExclusionList)
{
gInitKeyboardExclusionList = true;
// CodeReview Looks like we don't even need to do #ifdefs here since
// things like cmd-tab don't appear on windows, and alt-tab is an unlikely
// desired bind on other platforms - might be best to simply have a
// global exclusion list and keep it standard on all platforms.
// This might not be so, but it's the current assumption. [bjg 5/4/07]
// Alt-tab
{
KeyboardInputExclusion kie;
kie.key = KEY_TAB;
kie.andModifierMask = SI_ALT;
addKeyboardInputExclusion(kie);
}
// ... others go here...
}
// Walk the list and look for matches.
for(S32 i=0; i<gKeyboardExclusionList.size(); i++)
{
if(gKeyboardExclusionList[i].checkAgainstInput(info))
return true;
}
return false;
}
const bool Platform::KeyboardInputExclusion::checkAgainstInput( const InputEventInfo *info ) const
{
if(info->objType != SI_KEY)
return false;
if(info->objInst != key)
return false;
if((info->modifier & andModifierMask) != andModifierMask)
return false;
if(info->modifier & !(info->modifier & orModifierMask))
return false;
return true;
}
S32 Platform::compareModifiedTimes( const char *firstPath, const char *secondPath )
{
FileTime firstModTime;
if ( !getFileTimes( firstPath, NULL, &firstModTime ) )
return -1;
FileTime secondModTime;
if ( !getFileTimes( secondPath, NULL, &secondModTime ) )
return -1;
return compareFileTimes( firstModTime, secondModTime );
}
bool Platform::getWebDeployment()
{
return gWebDeployment;
}
void Platform::setWebDeployment(bool v)
{
gWebDeployment = v;
}

View file

@ -0,0 +1,590 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORM_H_
#define _PLATFORM_H_
#include <stdlib.h>
#ifndef _TORQUECONFIG_H_
#include "torqueConfig.h"
#endif
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
#ifndef _PLATFORMASSERT_H_
#include "platform/platformAssert.h"
#endif
#ifndef _MSGBOX_H_
#include "platform/nativeDialogs/msgBox.h"
#endif
#ifndef _VERSION_H_
#include "app/version.h"
#endif
#ifndef _TORQUE_STRING_H_
#include "core/util/str.h"
#endif
#ifndef _TORQUE_SAFEDELETE_H_
#include "core/util/safeDelete.h"
#endif
#include <new>
#include <typeinfo>
/// Global processor identifiers.
///
/// @note These enums must be globally scoped so that they work with the inline assembly
enum ProcessorType
{
// x86
CPU_X86Compatible,
CPU_Intel_Unknown,
CPU_Intel_486,
CPU_Intel_Pentium,
CPU_Intel_PentiumMMX,
CPU_Intel_PentiumPro,
CPU_Intel_PentiumII,
CPU_Intel_PentiumCeleron,
CPU_Intel_PentiumIII,
CPU_Intel_Pentium4,
CPU_Intel_PentiumM,
CPU_Intel_Core,
CPU_Intel_Core2,
CPU_Intel_Corei7Xeon, // Core i7 or Xeon
CPU_AMD_K6,
CPU_AMD_K6_2,
CPU_AMD_K6_3,
CPU_AMD_Athlon,
CPU_AMD_Unknown,
CPU_Cyrix_6x86,
CPU_Cyrix_MediaGX,
CPU_Cyrix_6x86MX,
CPU_Cyrix_GXm, ///< Media GX w/ MMX
CPU_Cyrix_Unknown,
// PowerPC
CPU_PowerPC_Unknown,
CPU_PowerPC_601,
CPU_PowerPC_603,
CPU_PowerPC_603e,
CPU_PowerPC_603ev,
CPU_PowerPC_604,
CPU_PowerPC_604e,
CPU_PowerPC_604ev,
CPU_PowerPC_G3,
CPU_PowerPC_G4,
CPU_PowerPC_G4_7450,
CPU_PowerPC_G4_7455,
CPU_PowerPC_G4_7447,
CPU_PowerPC_G5,
// Xenon
CPU_Xenon,
};
/// Properties for CPU.
enum ProcessorProperties
{
CPU_PROP_C = (1<<0), ///< We should use C fallback math functions.
CPU_PROP_FPU = (1<<1), ///< Has an FPU. (It better!)
CPU_PROP_MMX = (1<<2), ///< Supports MMX instruction set extension.
CPU_PROP_3DNOW = (1<<3), ///< Supports AMD 3dNow! instruction set extension.
CPU_PROP_SSE = (1<<4), ///< Supports SSE instruction set extension.
CPU_PROP_RDTSC = (1<<5), ///< Supports Read Time Stamp Counter op.
CPU_PROP_SSE2 = (1<<6), ///< Supports SSE2 instruction set extension.
CPU_PROP_SSE3 = (1<<7), ///< Supports SSE3 instruction set extension.
CPU_PROP_SSE3xt = (1<<8), ///< Supports extended SSE3 instruction set
CPU_PROP_SSE4_1 = (1<<9), ///< Supports SSE4_1 instruction set extension.
CPU_PROP_SSE4_2 = (1<<10), ///< Supports SSE4_2 instruction set extension.
CPU_PROP_MP = (1<<11), ///< This is a multi-processor system.
CPU_PROP_LE = (1<<12), ///< This processor is LITTLE ENDIAN.
CPU_PROP_64bit = (1<<13), ///< This processor is 64-bit capable
CPU_PROP_ALTIVEC = (1<<14), ///< Supports AltiVec instruction set extension (PPC only).
};
/// Processor info manager.
struct Processor
{
/// Gather processor state information.
static void init();
};
#if defined(TORQUE_SUPPORTS_GCC_INLINE_X86_ASM)
#define TORQUE_DEBUGBREAK() { asm ( "int 3"); }
#elif defined (TORQUE_SUPPORTS_VC_INLINE_X86_ASM) // put this test second so that the __asm syntax doesn't break the Visual Studio Intellisense parser
#define TORQUE_DEBUGBREAK() { __asm { int 3 }; }
#else
/// Macro to do in-line debug breaks, used for asserts. Does inline assembly when possible.
#define TORQUE_DEBUGBREAK() Platform::debugBreak();
#endif
/// Physical type of a drive.
enum DriveType
{
DRIVETYPE_FIXED = 0, ///< Non-removable fixed drive.
DRIVETYPE_REMOVABLE = 1, ///< Removable drive.
DRIVETYPE_REMOTE = 2, ///< Networked/remote drive.
DRIVETYPE_CDROM = 3, ///< CD-Rom.
DRIVETYPE_RAMDISK = 4, ///< A ramdisk!
DRIVETYPE_UNKNOWN = 5 ///< Don't know.
};
// Some forward declares for later.
class Point2I;
template<class T> class Vector;
template<typename Signature> class Signal;
struct InputEventInfo;
namespace Platform
{
// Time
struct LocalTime
{
U8 sec; ///< Seconds after minute (0-59)
U8 min; ///< Minutes after hour (0-59)
U8 hour; ///< Hours after midnight (0-23)
U8 month; ///< Month (0-11; 0=january)
U8 monthday; ///< Day of the month (1-31)
U8 weekday; ///< Day of the week (0-6, 6=sunday)
U16 year; ///< Current year minus 1900
U16 yearday; ///< Day of year (0-365)
bool isdst; ///< True if daylight savings time is active
};
void getLocalTime(LocalTime &);
/// Converts the local time to a formatted string appropriate
/// for the current platform.
String localTimeToString( const LocalTime &lt );
U32 getTime();
U32 getVirtualMilliseconds();
/// Returns the milliseconds since the system was started. You should
/// not depend on this for high precision timing.
/// @see PlatformTimer
U32 getRealMilliseconds();
void advanceTime(U32 delta);
S32 getBackgroundSleepTime();
// Platform control
void init();
void initConsole();
void shutdown();
void process();
// Math control state
U32 getMathControlState();
void setMathControlState(U32 state);
void setMathControlStateKnown();
// Process control
void sleep(U32 ms);
bool excludeOtherInstances(const char *string);
bool checkOtherInstances(const char *string);
void restartInstance();
void postQuitMessage(const U32 in_quitVal);
void forceShutdown(S32 returnValue);
// Debug
void outputDebugString(const char *string, ...);
void debugBreak();
// Random
float getRandom();
// Window state
void setWindowLocked(bool locked);
void minimizeWindow();
//const Point2I &getWindowSize();
void setWindowSize( U32 newWidth, U32 newHeight, bool fullScreen );
void closeWindow();
// File stuff
bool doCDCheck();
StringTableEntry createPlatformFriendlyFilename(const char *filename);
struct FileInfo
{
const char* pFullPath;
const char* pFileName;
U32 fileSize;
};
bool cdFileExists(const char *filePath, const char *volumeName, S32 serialNum);
void fileToLocalTime(const FileTime &ft, LocalTime *lt);
/// compare file times returns < 0 if a is earlier than b, >0 if b is earlier than a
S32 compareFileTimes(const FileTime &a, const FileTime &b);
bool stringToFileTime(const char * string, FileTime * time);
bool fileTimeToString(FileTime * time, char * string, U32 strLen);
/// Compares the last modified time between two file paths. Returns < 0 if
/// the first file is earlier than the second, > 0 if the second file is earlier
/// than the first, and 0 if the files are equal.
///
/// If either of the files doesn't exist it returns -1.
S32 compareModifiedTimes( const char *firstPath, const char *secondPath );
// Directory functions. Dump path returns false iff the directory cannot be
// opened.
StringTableEntry getCurrentDirectory();
bool setCurrentDirectory(StringTableEntry newDir);
StringTableEntry getTemporaryDirectory();
StringTableEntry getTemporaryFileName();
/// Returns the filename of the torque executable.
/// On Win32, this is the .exe file.
/// On Mac, this is the .app/ directory bundle.
StringTableEntry getExecutableName();
/// Returns full pathname of the torque executable without filename
StringTableEntry getExecutablePath();
/// Returns the full path to the directory that contains main.cs.
/// Tools scripts are validated as such if they are in this directory or a
/// subdirectory of this directory.
StringTableEntry getMainDotCsDir();
/// Set main.cs directory. Used in runEntryScript()
void setMainDotCsDir(const char *dir);
StringTableEntry getPrefsPath(const char *file = NULL);
char *makeFullPathName(const char *path, char *buffer, U32 size, const char *cwd = NULL);
StringTableEntry stripBasePath(const char *path);
bool isFullPath(const char *path);
StringTableEntry makeRelativePathName(const char *path, const char *to);
String stripExtension( String fileName, Vector< String >& validExtensions );
bool dumpPath(const char *in_pBasePath, Vector<FileInfo>& out_rFileVector, S32 recurseDepth = -1);
bool dumpDirectories( const char *path, Vector<StringTableEntry> &directoryVector, S32 depth = 0, bool noBasePath = false );
bool hasSubDirectory( const char *pPath );
bool getFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime);
bool isFile(const char *pFilePath);
S32 getFileSize(const char *pFilePath);
bool isDirectory(const char *pDirPath);
bool isSubDirectory(const char *pParent, const char *pDir);
void addExcludedDirectory(const char *pDir);
void clearExcludedDirectories();
bool isExcludedDirectory(const char *pDir);
/// Given a directory path, create all necessary directories for that path to exist.
bool createPath(const char *path); // create a directory path
// Alerts
void AlertOK(const char *windowTitle, const char *message);
bool AlertOKCancel(const char *windowTitle, const char *message);
bool AlertRetry(const char *windowTitle, const char *message);
// Volumes
struct VolumeInformation
{
StringTableEntry RootPath;
StringTableEntry Name;
StringTableEntry FileSystem;
U32 SerialNumber;
U32 Type;
bool ReadOnly;
};
extern struct VolumeInformation *PVolumeInformation;
// Volume functions.
void getVolumeNamesList( Vector<const char*>& out_rNameVector, bool bOnlyFixedDrives = false );
void getVolumeInformationList( Vector<VolumeInformation>& out_rVolumeInfoVector, bool bOnlyFixedDrives = false );
struct SystemInfo_struct
{
struct Processor
{
ProcessorType type;
const char* name;
U32 mhz;
bool isMultiCore;
bool isHyperThreaded;
U32 numLogicalProcessors;
U32 numPhysicalProcessors;
U32 numAvailableCores;
U32 properties; // CPU type specific enum
} processor;
};
extern Signal<void(void)> SystemInfoReady;
extern SystemInfo_struct SystemInfo;
// Web page launch function:
bool openWebBrowser( const char* webAddress );
// display Splash Window
bool displaySplashWindow( );
void openFolder( const char* path );
// Open file at the OS level, according to registered file-types.
void openFile( const char* path );
const char* getLoginPassword();
bool setLoginPassword( const char* password );
const char* getClipboard();
bool setClipboard(const char *text);
// User Specific Functions
StringTableEntry getUserHomeDirectory();
StringTableEntry getUserDataDirectory();
bool getUserIsAdministrator();
// Displays a fancy platform specific message box
S32 messageBox(const UTF8 *title, const UTF8 *message, MBButtons buttons = MBOkCancel, MBIcons icon = MIInformation);
/// Description of a keyboard input we want to ignore.
struct KeyboardInputExclusion
{
KeyboardInputExclusion()
{
key = 0;
orModifierMask = 0;
andModifierMask = 0;
}
/// The key code to ignore, e.g. KEY_TAB. If this and the other
/// conditions match, ignore the key.
S32 key;
/// if(modifiers | orModifierMask) and the other conditions match,
/// ignore the key.
U32 orModifierMask;
/// if((modifiers & andModifierMask) == andModifierMask) and the
/// other conditions match, ignore the key stroke.
U32 andModifierMask;
/// Based on the values above, determine if a given input event
/// matchs this exclusion rule.
const bool checkAgainstInput(const InputEventInfo *info) const;
};
/// Reset the keyboard input exclusion list.
void clearKeyboardInputExclusion();
/// Add a new keyboard exclusion.
void addKeyboardInputExclusion(const KeyboardInputExclusion &kie);
/// Check if a given input event should be excluded.
const bool checkKeyboardInputExclusion(const InputEventInfo *info);
/// Set/Get whether this is a web deployment
bool getWebDeployment();
void setWebDeployment(bool v);
};
//------------------------------------------------------------------------------
// Unicode string conversions
// UNICODE is a windows platform API switching flag. Don't define it on other platforms.
#ifdef UNICODE
#define dT(s) L##s
#else
#define dT(s) s
#endif
//------------------------------------------------------------------------------
// Misc StdLib functions
#define QSORT_CALLBACK FN_CDECL
inline void dQsort(void *base, U32 nelem, U32 width, int (QSORT_CALLBACK *fcmp)(const void *, const void *))
{
qsort(base, nelem, width, fcmp);
}
//-------------------------------------- Some all-around useful inlines and globals
//
///@defgroup ObjTrickery Object Management Trickery
///
/// These functions are to construct and destruct objects in memory
/// without causing a free or malloc call to occur. This is so that
/// we don't have to worry about allocating, say, space for a hundred
/// NetAddresses with a single malloc call, calling delete on a single
/// NetAdress, and having it try to free memory out from under us.
///
/// @{
/// Constructs an object that already has memory allocated for it.
template <class T>
inline T* constructInPlace(T* p)
{
return new ( p ) T;
}
template< class T >
inline T* constructArrayInPlace( T* p, U32 num )
{
return new ( p ) T[ num ];
}
/// Copy constructs an object that already has memory allocated for it.
template <class T>
inline T* constructInPlace(T* p, const T* copy)
{
return new ( p ) T( *copy );
}
template <class T, class T2> inline T* constructInPlace(T* ptr, T2 t2)
{
return new ( ptr ) T( t2 );
}
template <class T, class T2, class T3> inline T* constructInPlace(T* ptr, T2 t2, T3 t3)
{
return new ( ptr ) T( t2, t3 );
}
template <class T, class T2, class T3, class T4> inline T* constructInPlace(T* ptr, T2 t2, T3 t3, T4 t4)
{
return new ( ptr ) T( t2, t3, t4 );
}
template <class T, class T2, class T3, class T4, class T5> inline T* constructInPlace(T* ptr, T2 t2, T3 t3, T4 t4, T5 t5)
{
return new ( ptr ) T( t2, t3, t4, t5 );
}
/// Destructs an object without freeing the memory associated with it.
template <class T>
inline void destructInPlace(T* p)
{
p->~T();
}
//------------------------------------------------------------------------------
/// Memory functions
#if !defined(TORQUE_DISABLE_MEMORY_MANAGER)
# define TORQUE_TMM_ARGS_DECL , const char* fileName, const U32 lineNum
# define TORQUE_TMM_ARGS , fileName, lineNum
# define TORQUE_TMM_LOC , __FILE__, __LINE__
extern void* FN_CDECL operator new(dsize_t size, const char*, const U32);
extern void* FN_CDECL operator new[](dsize_t size, const char*, const U32);
extern void FN_CDECL operator delete(void* ptr);
extern void FN_CDECL operator delete[](void* ptr);
# define _new new(__FILE__, __LINE__)
# define new _new
#else
# define TORQUE_TMM_ARGS_DECL
# define TORQUE_TMM_ARGS
# define TORQUE_TMM_LOC
#endif
#define dMalloc(x) dMalloc_r(x, __FILE__, __LINE__)
#define dRealloc(x, y) dRealloc_r(x, y, __FILE__, __LINE__)
extern void setBreakAlloc(dsize_t);
extern void setMinimumAllocUnit(U32);
extern void* dMalloc_r(dsize_t in_size, const char*, const dsize_t);
extern void dFree(void* in_pFree);
extern void* dRealloc_r(void* in_pResize, dsize_t in_size, const char*, const dsize_t);
extern void* dRealMalloc(dsize_t);
extern void dRealFree(void*);
extern void *dMalloc_aligned(dsize_t in_size, int alignment);
extern void dFree_aligned(void *);
inline void dFree( const void* p )
{
dFree( ( void* ) p );
}
// Helper function to copy one array into another of different type
template<class T,class S> void dCopyArray(T *dst, const S *src, dsize_t size)
{
for (dsize_t i = 0; i < size; i++)
dst[i] = (T)src[i];
}
// Special case of the above function when the arrays are the same type (use memcpy)
template<class T> void dCopyArray(T *dst, const T *src, dsize_t size)
{
dMemcpy(dst, src, size * sizeof(T));
}
extern void* dMemcpy(void *dst, const void *src, dsize_t size);
extern void* dMemmove(void *dst, const void *src, dsize_t size);
extern void* dMemset(void *dst, int c, dsize_t size);
extern int dMemcmp(const void *ptr1, const void *ptr2, dsize_t size);
/// The dALIGN macro ensures the passed declaration is
/// data aligned at 16 byte boundaries.
#if defined( TORQUE_COMPILER_VISUALC )
#define dALIGN( decl ) __declspec( align( 16 ) ) decl
#define dALIGN_BEGIN __declspec( align( 16 ) )
#define dALIGN_END
#elif defined( TORQUE_COMPILER_GCC )
#define dALIGN( decl ) decl __attribute__( ( aligned( 16 ) ) )
#define dALIGN_BEGIN
#define dALIGN_END __attribute__( ( aligned( 16 ) ) )
#else
#define dALIGN( decl ) decl
#define dALIGN_BEGIN()
#define dALIGN_END()
#endif
//------------------------------------------------------------------------------
// FileIO functions
extern bool dFileDelete(const char *name);
extern bool dFileRename(const char *oldName, const char *newName);
extern bool dFileTouch(const char *name);
extern bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite = true);
typedef void* FILE_HANDLE;
enum DFILE_STATUS
{
DFILE_OK = 1
};
extern FILE_HANDLE dOpenFileRead(const char *name, DFILE_STATUS &error);
extern FILE_HANDLE dOpenFileReadWrite(const char *name, bool append, DFILE_STATUS &error);
extern int dFileRead(FILE_HANDLE handle, U32 bytes, char *dst, DFILE_STATUS &error);
extern int dFileWrite(FILE_HANDLE handle, U32 bytes, const char *dst, DFILE_STATUS &error);
extern void dFileClose(FILE_HANDLE handle);
extern StringTableEntry osGetTemporaryDirectory();
//------------------------------------------------------------------------------
struct Math
{
/// Initialize the math library with the appropriate libraries
/// to support hardware acceleration features.
///
/// @param properties Leave zero to detect available hardware. Otherwise,
/// pass CPU instruction set flags that you want to load
/// support for.
static void init(U32 properties = 0);
};
/// @}
#endif

View file

@ -0,0 +1,172 @@
//-----------------------------------------------------------------------------
// 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 <stdarg.h>
#include "core/strings/stringFunctions.h"
#include "console/console.h"
//-------------------------------------- STATIC Declaration
PlatformAssert *PlatformAssert::platformAssert = NULL;
//--------------------------------------
PlatformAssert::PlatformAssert()
{
processing = false;
}
//--------------------------------------
PlatformAssert::~PlatformAssert()
{
}
//--------------------------------------
void PlatformAssert::create( PlatformAssert* newAssertClass )
{
if (!platformAssert)
platformAssert = newAssertClass ? newAssertClass : new PlatformAssert;
}
//--------------------------------------
void PlatformAssert::destroy()
{
if (platformAssert)
delete platformAssert;
platformAssert = NULL;
}
//--------------------------------------
bool PlatformAssert::displayMessageBox(const char *title, const char *message, bool retry)
{
if (retry)
return Platform::AlertRetry(title, message);
Platform::AlertOK(title, message);
return false;
}
static const char *typeName[] = { "Unknown", "Fatal-ISV", "Fatal", "Warning" };
//------------------------------------------------------------------------------
static bool askToEnterDebugger(const char* message )
{
static bool haveAsked = false;
static bool useDebugger = true;
if(!haveAsked )
{
static char tempBuff[1024];
dSprintf( tempBuff, 1024, "Torque has encountered an assertion with message\n\n"
"%s\n\n"
"Would you like to use the debugger? If you cancel, you won't be asked"
" again until you restart Torque.", message);
useDebugger = Platform::AlertOKCancel("Use debugger?", tempBuff );
haveAsked = true;
}
return useDebugger;
}
//--------------------------------------
bool PlatformAssert::process(Type assertType,
const char *filename,
U32 lineNumber,
const char *message)
{
// If we're somehow recursing, just die.
if(processing)
Platform::debugBreak();
processing = true;
bool ret = true;
// always dump to the Assert to the Console
if (Con::isActive())
{
if (assertType == Warning)
Con::warnf(ConsoleLogEntry::Assert, "%s(%ld) : %s - %s", filename, lineNumber, typeName[assertType], message);
else
Con::errorf(ConsoleLogEntry::Assert, "%s(%ld) : %s - %s", filename, lineNumber, typeName[assertType], message);
}
// if not a WARNING pop-up a dialog box
if (assertType != Warning)
{
// used for processing navGraphs (an assert won't botch the whole build)
if(Con::getBoolVariable("$FP::DisableAsserts", false) == true)
Platform::forceShutdown(1);
char buffer[2048];
dSprintf(buffer, 2048, "%s(%ld) : %s", filename, lineNumber, typeName[assertType] );
#ifdef TORQUE_DEBUG
// In debug versions, allow a retry even for ISVs...
bool retry = displayMessageBox(buffer, message, true);
#else
bool retry = displayMessageBox(buffer, message, ((assertType == Fatal) ? true : false) );
#endif
if(!retry)
Platform::forceShutdown(1);
ret = askToEnterDebugger(message);
}
processing = false;
return ret;
}
bool PlatformAssert::processingAssert()
{
return platformAssert ? platformAssert->processing : false;
}
//--------------------------------------
bool PlatformAssert::processAssert(Type assertType,
const char *filename,
U32 lineNumber,
const char *message)
{
if (platformAssert)
return platformAssert->process(assertType, filename, lineNumber, message);
else // when platAssert NULL (during _start/_exit) try direct output...
dPrintf("\n%s: (%s @ %ld) %s\n", typeName[assertType], filename, lineNumber, message);
// this could also be platform-specific: OutputDebugString on PC, DebugStr on Mac.
// Will raw printfs do the job? In the worst case, it's a break-pointable line of code.
// would have preferred Con but due to race conditions, it might not be around...
// Con::errorf(ConsoleLogEntry::Assert, "%s: (%s @ %ld) %s", typeName[assertType], filename, lineNumber, message);
return true;
}
//--------------------------------------
const char* avar(const char *message, ...)
{
static char buffer[4096];
va_list args;
va_start(args, message);
dVsprintf(buffer, sizeof(buffer), message, args);
return( buffer );
}

View file

@ -0,0 +1,141 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORMASSERT_H_
#define _PLATFORMASSERT_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
class PlatformAssert
{
public:
enum Type
{
Warning = 3,
Fatal = 2,
Fatal_ISV = 1
};
private:
static PlatformAssert *platformAssert;
bool processing;
virtual bool displayMessageBox(const char *title, const char *message, bool retry);
virtual bool process(Type assertType,
const char* filename,
U32 lineNumber,
const char* message);
PlatformAssert();
virtual ~PlatformAssert();
public:
static void create( PlatformAssert* newAssertClass = NULL );
static void destroy();
static bool processAssert(Type assertType,
const char* filename,
U32 lineNumber,
const char* message);
static char *message(const char *message, ...);
static bool processingAssert();
};
#ifdef TORQUE_ENABLE_ASSERTS
/*!
Assert that the statement x is true, and continue processing.
If the statment x is true, continue processing.
If the statement x is false, log the file and line where the assert occured,
the message y and continue processing.
These asserts are only present in DEBUG builds.
*/
#define AssertWarn(x, y) \
{ if ((x)==0) \
::PlatformAssert::processAssert(::PlatformAssert::Warning, __FILE__, __LINE__, y); }
/*!
Assert that the statement x is true, otherwise halt.
If the statement x is true, continue processing.
If the statement x is false, log the file and line where the assert occured,
the message y and displaying a dialog containing the message y. The user then
has the option to halt or continue causing the debugger to break.
These asserts are only present in DEBUG builds.
This assert is very useful for verifying data as well as function entry and
exit conditions.
*/
#define AssertFatal(x, y) \
{ if (((bool)(x))==(bool)0) \
{ if ( ::PlatformAssert::processAssert(::PlatformAssert::Fatal, __FILE__, __LINE__, y) ) { ::Platform::debugBreak(); } } }
#else
#define AssertFatal(x, y) { (void)sizeof(x); (void)sizeof(y); }
#define AssertWarn(x, y) { (void)sizeof(x); (void)sizeof(y); }
#endif
/*!
Assert (In Shipping Version) that the statement x is true, otherwise halt.
If the statement x is true, continue processing.
If the statement x is false, log the file and line where the assert occurred,
the message y and exit the program displaying a dialog containing the message y.
These asserts are present in both OPTIMIZED and DEBUG builds.
This assert should only be used for rare conditions where the application cannot continue
execution without seg-faulting and you want to display a nice exit message.
*/
#define AssertISV(x, y) \
{ if ((x)==0) \
{ if ( ::PlatformAssert::processAssert(::PlatformAssert::Fatal_ISV, __FILE__, __LINE__, y) ) { ::Platform::debugBreak(); } } }
/*!
Sprintf style string formating into a fixed temporary buffer.
@param in_msg sprintf style format string
@returns pointer to fixed buffer containing formatted string
\b Example:
\code
U8 a = 5;
S16 b = -10;
char *output = avar("hello %s! a=%u, b=%d", "world");
ouput = "hello world! a=5, b=-10"
\endcode
@warning avar uses a static fixed buffer. Treat the buffer as volatile data
and use it immediately. Other functions my use avar too and clobber the buffer.
*/
const char* avar(const char *in_msg, ...);
#endif // _PLATFORM_ASSERT_H_

View file

@ -0,0 +1,269 @@
//-----------------------------------------------------------------------------
// 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/platform.h"
#include "platform/platformCPUCount.h"
#include "core/strings/stringFunctions.h"
#include "core/stringTable.h"
#include "core/util/tSignal.h"
Signal<void(void)> Platform::SystemInfoReady;
enum CPUFlags
{
BIT_FPU = BIT(0),
BIT_RDTSC = BIT(4),
BIT_MMX = BIT(23),
BIT_SSE = BIT(25),
BIT_SSE2 = BIT(26),
BIT_3DNOW = BIT(31),
// These use a different value for comparison than the above flags
BIT_SSE3 = BIT(0),
BIT_SSE3xt = BIT(9),
BIT_SSE4_1 = BIT(19),
BIT_SSE4_2 = BIT(20),
};
// fill the specified structure with information obtained from asm code
void SetProcessorInfo(Platform::SystemInfo_struct::Processor& pInfo,
char* vendor, U32 processor, U32 properties, U32 properties2)
{
Platform::SystemInfo.processor.properties |= (properties & BIT_FPU) ? CPU_PROP_FPU : 0;
Platform::SystemInfo.processor.properties |= (properties & BIT_RDTSC) ? CPU_PROP_RDTSC : 0;
Platform::SystemInfo.processor.properties |= (properties & BIT_MMX) ? CPU_PROP_MMX : 0;
if (dStricmp(vendor, "GenuineIntel") == 0)
{
pInfo.properties |= (properties & BIT_SSE) ? CPU_PROP_SSE : 0;
pInfo.properties |= (properties & BIT_SSE2) ? CPU_PROP_SSE2 : 0;
pInfo.properties |= (properties2 & BIT_SSE3) ? CPU_PROP_SSE3 : 0;
pInfo.properties |= (properties2 & BIT_SSE3xt) ? CPU_PROP_SSE3xt : 0;
pInfo.properties |= (properties2 & BIT_SSE4_1) ? CPU_PROP_SSE4_1 : 0;
pInfo.properties |= (properties2 & BIT_SSE4_2) ? CPU_PROP_SSE4_2 : 0;
pInfo.type = CPU_Intel_Unknown;
// switch on processor family code
switch ((processor >> 8) & 0x0f)
{
case 4:
pInfo.type = CPU_Intel_486;
pInfo.name = StringTable->insert("Intel 486 class");
break;
// Pentium Family
case 5:
// switch on processor model code
switch ((processor >> 4) & 0xf)
{
case 1:
case 2:
case 3:
pInfo.type = CPU_Intel_Pentium;
pInfo.name = StringTable->insert("Intel Pentium");
break;
case 4:
pInfo.type = CPU_Intel_PentiumMMX;
pInfo.name = StringTable->insert("Intel Pentium MMX");
break;
default:
pInfo.type = CPU_Intel_Pentium;
pInfo.name = StringTable->insert( "Intel (unknown)" );
break;
}
break;
// Pentium Pro/II/II family
case 6:
{
U32 extendedModel = ( processor & 0xf0000 ) >> 16;
// switch on processor model code
switch ((processor >> 4) & 0xf)
{
case 1:
pInfo.type = CPU_Intel_PentiumPro;
pInfo.name = StringTable->insert("Intel Pentium Pro");
break;
case 3:
case 5:
pInfo.type = CPU_Intel_PentiumII;
pInfo.name = StringTable->insert("Intel Pentium II");
break;
case 6:
pInfo.type = CPU_Intel_PentiumCeleron;
pInfo.name = StringTable->insert("Intel Pentium Celeron");
break;
case 7:
case 8:
case 11:
pInfo.type = CPU_Intel_PentiumIII;
pInfo.name = StringTable->insert("Intel Pentium III");
break;
case 0xA:
if( extendedModel == 1)
{
pInfo.type = CPU_Intel_Corei7Xeon;
pInfo.name = StringTable->insert( "Intel Core i7 / Xeon" );
}
else
{
pInfo.type = CPU_Intel_PentiumIII;
pInfo.name = StringTable->insert( "Intel Pentium III Xeon" );
}
break;
case 0xD:
if( extendedModel == 1 )
{
pInfo.type = CPU_Intel_Corei7Xeon;
pInfo.name = StringTable->insert( "Intel Core i7 / Xeon" );
}
else
{
pInfo.type = CPU_Intel_PentiumM;
pInfo.name = StringTable->insert( "Intel Pentium/Celeron M" );
}
break;
case 0xE:
pInfo.type = CPU_Intel_Core;
pInfo.name = StringTable->insert( "Intel Core" );
break;
case 0xF:
pInfo.type = CPU_Intel_Core2;
pInfo.name = StringTable->insert( "Intel Core 2" );
break;
default:
pInfo.type = CPU_Intel_PentiumPro;
pInfo.name = StringTable->insert( "Intel (unknown)" );
break;
}
break;
}
// Pentium4 Family
case 0xf:
pInfo.type = CPU_Intel_Pentium4;
pInfo.name = StringTable->insert( "Intel Pentium 4" );
break;
default:
pInfo.type = CPU_Intel_Unknown;
pInfo.name = StringTable->insert( "Intel (unknown)" );
break;
}
}
//--------------------------------------
else
if (dStricmp(vendor, "AuthenticAMD") == 0)
{
// AthlonXP processors support SSE
pInfo.properties |= (properties & BIT_SSE) ? CPU_PROP_SSE : 0;
pInfo.properties |= ( properties & BIT_SSE2 ) ? CPU_PROP_SSE2 : 0;
pInfo.properties |= (properties & BIT_3DNOW) ? CPU_PROP_3DNOW : 0;
// switch on processor family code
switch ((processor >> 8) & 0xf)
{
// K6 Family
case 5:
// switch on processor model code
switch ((processor >> 4) & 0xf)
{
case 0:
case 1:
case 2:
case 3:
pInfo.type = CPU_AMD_K6_3;
pInfo.name = StringTable->insert("AMD K5");
break;
case 4:
case 5:
case 6:
case 7:
pInfo.type = CPU_AMD_K6;
pInfo.name = StringTable->insert("AMD K6");
break;
case 8:
pInfo.type = CPU_AMD_K6_2;
pInfo.name = StringTable->insert("AMD K6-2");
break;
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
pInfo.type = CPU_AMD_K6_3;
pInfo.name = StringTable->insert("AMD K6-3");
break;
}
break;
// Athlon Family
case 6:
pInfo.type = CPU_AMD_Athlon;
pInfo.name = StringTable->insert("AMD Athlon");
break;
default:
pInfo.type = CPU_AMD_Unknown;
pInfo.name = StringTable->insert("AMD (unknown)");
break;
}
}
//--------------------------------------
else
if (dStricmp(vendor, "CyrixInstead") == 0)
{
switch (processor)
{
case 0x520:
pInfo.type = CPU_Cyrix_6x86;
pInfo.name = StringTable->insert("Cyrix 6x86");
break;
case 0x440:
pInfo.type = CPU_Cyrix_MediaGX;
pInfo.name = StringTable->insert("Cyrix Media GX");
break;
case 0x600:
pInfo.type = CPU_Cyrix_6x86MX;
pInfo.name = StringTable->insert("Cyrix 6x86mx/MII");
break;
case 0x540:
pInfo.type = CPU_Cyrix_GXm;
pInfo.name = StringTable->insert("Cyrix GXm");
break;
default:
pInfo.type = CPU_Cyrix_Unknown;
pInfo.name = StringTable->insert("Cyrix (unknown)");
break;
}
}
// Get multithreading caps.
CPUInfo::EConfig config = CPUInfo::CPUCount( pInfo.numLogicalProcessors, pInfo.numAvailableCores, pInfo.numPhysicalProcessors );
pInfo.isHyperThreaded = CPUInfo::isHyperThreaded( config );
pInfo.isMultiCore = CPUInfo::isMultiCore( config );
// Trigger the signal
Platform::SystemInfoReady.trigger();
}

View file

@ -0,0 +1,667 @@
// Original code is:
// Copyright (c) 2005 Intel Corporation
// All Rights Reserved
//
// CPUCount.cpp : Detects three forms of hardware multi-threading support across IA-32 platform
// The three forms of HW multithreading are: Multi-processor, Multi-core, and
// HyperThreading Technology.
// This application enumerates all the logical processors enabled by OS and BIOS,
// determine the HW topology of these enabled logical processors in the system
// using information provided by CPUID instruction.
// A multi-processing system can support any combination of the three forms of HW
// multi-threading support. The relevant topology can be identified using a
// three level decomposition of the "initial APIC ID" into
// Package_id, core_id, and SMT_id. Such decomposition provides a three-level map of
// the topology of hardware resources and
// allow multi-threaded software to manage shared hardware resources in
// the platform to reduce resource contention
// Multicore detection algorithm for processor and cache topology requires
// all leaf functions of CPUID instructions be available. System administrator
// must ensure BIOS settings is not configured to restrict CPUID functionalities.
//-------------------------------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/platformCPUCount.h"
// Consoles don't need this
#if defined(TORQUE_OS_XENON) || defined(TORQUE_OS_PS3)
namespace CPUInfo
{
EConfig CPUCount(U32& TotAvailLogical, U32& TotAvailCore, U32& PhysicalNum)
{
TotAvailLogical = 6;
TotAvailCore = 6;
PhysicalNum = 3;
return CONFIG_MultiCoreAndHTEnabled;
}
}; // namespace
#else
#ifdef TORQUE_OS_LINUX
// The Linux source code listing can be compiled using Linux kernel verison 2.6
// or higher (e.g. RH 4AS-2.8 using GCC 3.4.4).
// Due to syntax variances of Linux affinity APIs with earlier kernel versions
// and dependence on glibc library versions, compilation on Linux environment
// with older kernels and compilers may require kernel patches or compiler upgrades.
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sched.h>
#define DWORD unsigned long
#elif defined( TORQUE_OS_WIN32 )
#include <windows.h>
#elif defined( TORQUE_OS_MAC )
# include <sys/types.h>
# include <sys/sysctl.h>
#else
#error Not implemented on platform.
#endif
#include <stdio.h>
#include <assert.h>
namespace CPUInfo {
#define HWD_MT_BIT 0x10000000 // EDX[28] Bit 28 is set if HT or multi-core is supported
#define NUM_LOGICAL_BITS 0x00FF0000 // EBX[23:16] Bit 16-23 in ebx contains the number of logical
// processors per physical processor when execute cpuid with
// eax set to 1
#define NUM_CORE_BITS 0xFC000000 // EAX[31:26] Bit 26-31 in eax contains the number of cores minus one
// per physical processor when execute cpuid with
// eax set to 4.
#define INITIAL_APIC_ID_BITS 0xFF000000 // EBX[31:24] Bits 24-31 (8 bits) return the 8-bit unique
// initial APIC ID for the processor this code is running on.
#ifndef TORQUE_OS_MAC
static unsigned int CpuIDSupported(void);
static unsigned int find_maskwidth(unsigned int);
static unsigned int HWD_MTSupported(void);
static unsigned int MaxLogicalProcPerPhysicalProc(void);
static unsigned int MaxCorePerPhysicalProc(void);
static unsigned char GetAPIC_ID(void);
static unsigned char GetNzbSubID(unsigned char, unsigned char, unsigned char);
#endif
static char g_s3Levels[2048];
#ifndef TORQUE_OS_MAC
//
// CpuIDSupported will return 0 if CPUID instruction is unavailable. Otherwise, it will return
// the maximum supported standard function.
//
static unsigned int CpuIDSupported(void)
{
unsigned int MaxInputValue;
// If CPUID instruction is supported
#ifdef TORQUE_COMPILER_GCC
try
{
MaxInputValue = 0;
// call cpuid with eax = 0
asm
(
"pushl %%ebx\n\t"
"xorl %%eax,%%eax\n\t"
"cpuid\n\t"
"popl %%ebx\n\t"
: "=a" (MaxInputValue)
:
: "%ecx", "%edx"
);
}
catch (...)
{
return(0); // cpuid instruction is unavailable
}
#elif defined( TORQUE_COMPILER_VISUALC )
try
{
MaxInputValue = 0;
// call cpuid with eax = 0
__asm
{
xor eax, eax
cpuid
mov MaxInputValue, eax
}
}
catch (...)
{
return(0); // cpuid instruction is unavailable
}
#else
# error Not implemented.
#endif
return MaxInputValue;
}
//
// Function returns the maximum cores per physical package. Note that the number of
// AVAILABLE cores per physical to be used by an application might be less than this
// maximum value.
//
static unsigned int MaxCorePerPhysicalProc(void)
{
unsigned int Regeax = 0;
if (!HWD_MTSupported()) return (unsigned int) 1; // Single core
#ifdef TORQUE_COMPILER_GCC
{
asm
(
"pushl %ebx\n\t"
"xorl %eax, %eax\n\t"
"cpuid\n\t"
"cmpl $4, %eax\n\t" // check if cpuid supports leaf 4
"jl .single_core\n\t" // Single core
"movl $4, %eax\n\t"
"movl $0, %ecx\n\t" // start with index = 0; Leaf 4 reports
"popl %ebx\n\t"
); // at least one valid cache level
asm
(
"cpuid"
: "=a" (Regeax)
:
: "%ecx", "%edx"
);
asm
(
"jmp .multi_core\n"
".single_core:\n\t"
"xor %eax, %eax\n"
".multi_core:"
);
}
#elif defined( TORQUE_COMPILER_VISUALC )
__asm
{
xor eax, eax
cpuid
cmp eax, 4 // check if cpuid supports leaf 4
jl single_core // Single core
mov eax, 4
mov ecx, 0 // start with index = 0; Leaf 4 reports
cpuid // at least one valid cache level
mov Regeax, eax
jmp multi_core
single_core:
xor eax, eax
multi_core:
}
#else
# error Not implemented.
#endif
return (unsigned int)((Regeax & NUM_CORE_BITS) >> 26)+1;
}
//
// The function returns 0 when the hardware multi-threaded bit is not set.
//
static unsigned int HWD_MTSupported(void)
{
unsigned int Regedx = 0;
if ((CpuIDSupported() >= 1))
{
#ifdef TORQUE_COMPILER_GCC
asm
(
"pushl %%ebx\n\t"
"movl $1,%%eax\n\t"
"cpuid\n\t"
"popl %%ebx\n\t"
: "=d" (Regedx)
:
: "%eax","%ecx"
);
#elif defined( TORQUE_COMPILER_VISUALC )
__asm
{
mov eax, 1
cpuid
mov Regedx, edx
}
#else
# error Not implemented.
#endif
}
return (Regedx & HWD_MT_BIT);
}
//
// Function returns the maximum logical processors per physical package. Note that the number of
// AVAILABLE logical processors per physical to be used by an application might be less than this
// maximum value.
//
static unsigned int MaxLogicalProcPerPhysicalProc(void)
{
unsigned int Regebx = 0;
if (!HWD_MTSupported()) return (unsigned int) 1;
#ifdef TORQUE_COMPILER_GCC
asm
(
"movl $1,%%eax\n\t"
"cpuid"
: "=b" (Regebx)
:
: "%eax","%ecx","%edx"
);
#elif defined( TORQUE_COMPILER_VISUALC )
__asm
{
mov eax, 1
cpuid
mov Regebx, ebx
}
#else
# error Not implemented.
#endif
return (unsigned int) ((Regebx & NUM_LOGICAL_BITS) >> 16);
}
static unsigned char GetAPIC_ID(void)
{
unsigned int Regebx = 0;
#ifdef TORQUE_COMPILER_GCC
asm
(
"movl $1, %%eax\n\t"
"cpuid"
: "=b" (Regebx)
:
: "%eax","%ecx","%edx"
);
#elif defined( TORQUE_COMPILER_VISUALC )
__asm
{
mov eax, 1
cpuid
mov Regebx, ebx
}
#else
# error Not implemented.
#endif
return (unsigned char) ((Regebx & INITIAL_APIC_ID_BITS) >> 24);
}
//
// Determine the width of the bit field that can represent the value count_item.
//
unsigned int find_maskwidth(unsigned int CountItem)
{
unsigned int MaskWidth,
count = CountItem;
#ifdef TORQUE_COMPILER_GCC
asm
(
#ifdef __x86_64__ // define constant to compile
"push %%rcx\n\t" // under 64-bit Linux
"push %%rax\n\t"
#else
"pushl %%ecx\n\t"
"pushl %%eax\n\t"
#endif
// "movl $count, %%eax\n\t" //done by Assembler below
"xorl %%ecx, %%ecx"
// "movl %%ecx, MaskWidth\n\t" //done by Assembler below
: "=c" (MaskWidth)
: "a" (count)
// : "%ecx", "%eax" We don't list these as clobbered because we don't want the assembler
//to put them back when we are done
);
asm
(
"decl %%eax\n\t"
"bsrw %%ax,%%cx\n\t"
"jz next\n\t"
"incw %%cx\n\t"
// "movl %%ecx, MaskWidth\n" //done by Assembler below
: "=c" (MaskWidth)
:
);
asm
(
"next:\n\t"
#ifdef __x86_64__
"pop %rax\n\t"
"pop %rcx"
#else
"popl %eax\n\t"
"popl %ecx"
#endif
);
#elif defined( TORQUE_COMPILER_VISUALC )
__asm
{
mov eax, count
mov ecx, 0
mov MaskWidth, ecx
dec eax
bsr cx, ax
jz next
inc cx
mov MaskWidth, ecx
next:
}
#else
# error Not implemented.
#endif
return MaskWidth;
}
//
// Extract the subset of bit field from the 8-bit value FullID. It returns the 8-bit sub ID value
//
static unsigned char GetNzbSubID(unsigned char FullID,
unsigned char MaxSubIDValue,
unsigned char ShiftCount)
{
unsigned int MaskWidth;
unsigned char MaskBits;
MaskWidth = find_maskwidth((unsigned int) MaxSubIDValue);
MaskBits = (0xff << ShiftCount) ^
((unsigned char) (0xff << (ShiftCount + MaskWidth)));
return (FullID & MaskBits);
}
#endif
//
//
//
EConfig CPUCount(U32& TotAvailLogical, U32& TotAvailCore, U32& PhysicalNum)
{
EConfig StatusFlag = CONFIG_UserConfigIssue;
g_s3Levels[0] = 0;
TotAvailCore = 1;
PhysicalNum = 1;
unsigned int numLPEnabled = 0;
int MaxLPPerCore = 1;
#ifdef TORQUE_OS_MAC
//FIXME: This isn't a proper port but more or less just some sneaky cheating
// to get around having to mess with yet another crap UNIX-style API. Seems
// like there isn't a way to do this that's working across all OSX incarnations
// and machine configurations anyway.
int numCPUs;
int numPackages;
// Get the number of CPUs.
size_t len = sizeof( numCPUs );
if( sysctlbyname( "hw.ncpu", &numCPUs, &len, 0, 0 ) == -1 )
return CONFIG_UserConfigIssue;
// Get the number of packages.
len = sizeof( numPackages );
if( sysctlbyname( "hw.packages", &numPackages, &len, 0, 0 ) == -1 )
return CONFIG_UserConfigIssue;
TotAvailCore = numCPUs;
TotAvailLogical = numCPUs;
PhysicalNum = numPackages;
#else
U32 dwAffinityMask;
int j = 0;
unsigned char apicID, PackageIDMask;
unsigned char tblPkgID[256], tblCoreID[256], tblSMTID[256];
char tmp[256];
#ifdef TORQUE_OS_LINUX
//we need to make sure that this process is allowed to run on
//all of the logical processors that the OS itself can run on.
//A process could acquire/inherit affinity settings that restricts the
// current process to run on a subset of all logical processor visible to OS.
// Linux doesn't easily allow us to look at the Affinity Bitmask directly,
// but it does provide an API to test affinity maskbits of the current process
// against each logical processor visible under OS.
int sysNumProcs = sysconf(_SC_NPROCESSORS_CONF); //This will tell us how many
//CPUs are currently enabled.
//this will tell us which processors this process can run on.
cpu_set_t allowedCPUs;
sched_getaffinity(0, sizeof(allowedCPUs), &allowedCPUs);
for (int i = 0; i < sysNumProcs; i++ )
{
if ( CPU_ISSET(i, &allowedCPUs) == 0 )
return CONFIG_UserConfigIssue;
}
#elif defined( TORQUE_OS_WIN32 )
DWORD dwProcessAffinity, dwSystemAffinity;
GetProcessAffinityMask(GetCurrentProcess(),
&dwProcessAffinity,
&dwSystemAffinity);
if (dwProcessAffinity != dwSystemAffinity) // not all CPUs are enabled
return CONFIG_UserConfigIssue;
#else
# error Not implemented.
#endif
// Assume that cores within a package have the SAME number of
// logical processors. Also, values returned by
// MaxLogicalProcPerPhysicalProc and MaxCorePerPhysicalProc do not have
// to be power of 2.
MaxLPPerCore = MaxLogicalProcPerPhysicalProc() / MaxCorePerPhysicalProc();
dwAffinityMask = 1;
#ifdef TORQUE_OS_LINUX
cpu_set_t currentCPU;
while ( j < sysNumProcs )
{
CPU_ZERO(&currentCPU);
CPU_SET(j, &currentCPU);
if ( sched_setaffinity (0, sizeof(currentCPU), &currentCPU) == 0 )
{
sleep(0); // Ensure system to switch to the right CPU
#elif defined( TORQUE_OS_WIN32 )
while (dwAffinityMask && dwAffinityMask <= dwSystemAffinity)
{
if (SetThreadAffinityMask(GetCurrentThread(), dwAffinityMask))
{
Sleep(0); // Ensure system to switch to the right CPU
#else
# error Not implemented.
#endif
apicID = GetAPIC_ID();
// Store SMT ID and core ID of each logical processor
// Shift vlaue for SMT ID is 0
// Shift value for core ID is the mask width for maximum logical
// processors per core
tblSMTID[j] = GetNzbSubID(apicID, MaxLPPerCore, 0);
tblCoreID[j] = GetNzbSubID(apicID,
MaxCorePerPhysicalProc(),
(unsigned char) find_maskwidth(MaxLPPerCore));
// Extract package ID, assume single cluster.
// Shift value is the mask width for max Logical per package
PackageIDMask = (unsigned char) (0xff <<
find_maskwidth(MaxLogicalProcPerPhysicalProc()));
tblPkgID[j] = apicID & PackageIDMask;
sprintf(tmp," AffinityMask = %d; Initial APIC = %d; Physical ID = %d, Core ID = %d, SMT ID = %d\n",
dwAffinityMask, apicID, tblPkgID[j], tblCoreID[j], tblSMTID[j]);
strcat(g_s3Levels, tmp);
numLPEnabled ++; // Number of available logical processors in the system.
} // if
j++;
dwAffinityMask = 1 << j;
} // while
// restore the affinity setting to its original state
#ifdef TORQUE_OS_LINUX
sched_setaffinity (0, sizeof(allowedCPUs), &allowedCPUs);
sleep(0);
#elif defined( TORQUE_OS_WIN32 )
SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinity);
Sleep(0);
#else
# error Not implemented.
#endif
TotAvailLogical = numLPEnabled;
//
// Count available cores (TotAvailCore) in the system
//
unsigned char CoreIDBucket[256];
DWORD ProcessorMask, pCoreMask[256];
unsigned int i, ProcessorNum;
CoreIDBucket[0] = tblPkgID[0] | tblCoreID[0];
ProcessorMask = 1;
pCoreMask[0] = ProcessorMask;
for (ProcessorNum = 1; ProcessorNum < numLPEnabled; ProcessorNum++)
{
ProcessorMask <<= 1;
for (i = 0; i < TotAvailCore; i++)
{
// Comparing bit-fields of logical processors residing in different packages
// Assuming the bit-masks are the same on all processors in the system.
if ((tblPkgID[ProcessorNum] | tblCoreID[ProcessorNum]) == CoreIDBucket[i])
{
pCoreMask[i] |= ProcessorMask;
break;
}
} // for i
if (i == TotAvailCore) // did not match any bucket. Start a new one.
{
CoreIDBucket[i] = tblPkgID[ProcessorNum] | tblCoreID[ProcessorNum];
pCoreMask[i] = ProcessorMask;
TotAvailCore++; // Number of available cores in the system
}
} // for ProcessorNum
//
// Count physical processor (PhysicalNum) in the system
//
unsigned char PackageIDBucket[256];
DWORD pPackageMask[256];
PackageIDBucket[0] = tblPkgID[0];
ProcessorMask = 1;
pPackageMask[0] = ProcessorMask;
for (ProcessorNum = 1; ProcessorNum < numLPEnabled; ProcessorNum++)
{
ProcessorMask <<= 1;
for (i = 0; i < PhysicalNum; i++)
{
// Comparing bit-fields of logical processors residing in different packages
// Assuming the bit-masks are the same on all processors in the system.
if (tblPkgID[ProcessorNum]== PackageIDBucket[i])
{
pPackageMask[i] |= ProcessorMask;
break;
}
} // for i
if (i == PhysicalNum) // did not match any bucket. Start a new one.
{
PackageIDBucket[i] = tblPkgID[ProcessorNum];
pPackageMask[i] = ProcessorMask;
PhysicalNum++; // Total number of physical processors in the system
}
} // for ProcessorNum
#endif
//
// Check to see if the system is multi-core
// Check if the system is hyper-threading
//
if (TotAvailCore > PhysicalNum)
{
// Multi-core
if (MaxLPPerCore == 1)
StatusFlag = CONFIG_MultiCoreAndHTNotCapable;
else if (numLPEnabled > TotAvailCore)
StatusFlag = CONFIG_MultiCoreAndHTEnabled;
else StatusFlag = CONFIG_MultiCoreAndHTDisabled;
}
else
{
// Single-core
if (MaxLPPerCore == 1)
StatusFlag = CONFIG_SingleCoreAndHTNotCapable;
else if (numLPEnabled > TotAvailCore)
StatusFlag = CONFIG_SingleCoreHTEnabled;
else StatusFlag = CONFIG_SingleCoreHTDisabled;
}
return StatusFlag;
}
} // namespace CPUInfo
#endif

View file

@ -0,0 +1,75 @@
//-----------------------------------------------------------------------------
// 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 _TORQUE_PLATFORM_PLATFORMCPUCOUNT_H_
#define _TORQUE_PLATFORM_PLATFORMCPUCOUNT_H_
#include "platform/platform.h"
namespace CPUInfo
{
enum EConfig
{
CONFIG_UserConfigIssue,
CONFIG_SingleCoreHTEnabled,
CONFIG_SingleCoreHTDisabled,
CONFIG_SingleCoreAndHTNotCapable,
CONFIG_MultiCoreAndHTNotCapable,
CONFIG_MultiCoreAndHTEnabled,
CONFIG_MultiCoreAndHTDisabled,
};
inline bool isMultiCore( EConfig config )
{
switch( config )
{
case CONFIG_MultiCoreAndHTNotCapable:
case CONFIG_MultiCoreAndHTEnabled:
case CONFIG_MultiCoreAndHTDisabled:
return true;
default:
return false;
}
}
inline bool isHyperThreaded( EConfig config )
{
switch( config )
{
case CONFIG_SingleCoreHTEnabled:
case CONFIG_MultiCoreAndHTEnabled:
return true;
default:
return false;
}
}
EConfig CPUCount( U32& totalAvailableLogical,
U32& totalAvailableCores,
U32& numPhysical );
} // namespace CPUInfo
#endif // _TORQUE_PLATFORM_PLATFORMCOUNT_H_

View file

@ -0,0 +1,128 @@
;-----------------------------------------------------------------------------
; 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.
;-----------------------------------------------------------------------------
segment .text
; syntax: export_fn <function name>
%macro export_fn 1
%ifidn __OUTPUT_FORMAT__, elf
; No underscore needed for ELF object files
global %1
%1:
%else
global _%1
_%1:
%endif
%endmacro
; push registers
%macro pushreg 0
; pushad
push ebx
push ebp
push esi
push edi
%endmacro
; pop registers
%macro popreg 0
pop edi
pop esi
pop ebp
pop ebx
; popad
%endmacro
; void detectX86CPUInfo(char *vendor, U32 *processor, U32 *properties);
export_fn detectX86CPUInfo
push ebp
mov ebp, esp
pushreg
push edx
push ecx
pushfd
pushfd ; save EFLAGS to stack
pop eax ; move EFLAGS into EAX
mov ebx, eax
xor eax, 0x200000 ; flip bit 21
push eax
popfd ; restore EFLAGS
pushfd
pop eax
cmp eax, ebx
jz EXIT ; doesn't support CPUID instruction
;
; get vendor information using CPUID eax == 0
xor eax, eax
cpuid
; store the vendor tag (12 bytes in ebx, edx, ecx) in the first parameter,
; which should be a char[13]
push eax ; save eax
mov eax, [ebp+8] ; store the char* address in eax
mov [eax], ebx ; move ebx into the first 4 bytes
add eax, 4 ; advance the char* 4 bytes
mov [eax], edx ; move edx into the next 4 bytes
add eax, 4 ; advance the char* 4 bytes
mov [eax], ecx ; move ecx into the last 4 bytes
pop eax ; restore eax
; get generic extended CPUID info
mov eax, 1
cpuid ; eax=1, so cpuid queries feature information
and eax, 0x0fff3fff
push ecx
mov ecx, [ebp+12]
mov [ecx], eax ; just store the model bits in processor param
mov ecx, [ebp+16]
mov [ecx], edx ; set properties param
pop ecx
; want to check for 3DNow(tm).
; need to see if extended cpuid functions present.
mov eax, 0x80000000
cpuid
cmp eax, 0x80000000
jbe MAYBE_3DLATER
mov eax, 0x80000001
cpuid
; 3DNow if bit 31 set -> put bit in our properties
and edx, 0x80000000
push eax
mov eax, [ebp+16]
or [eax], edx
pop eax
MAYBE_3DLATER:
EXIT:
popfd
pop ecx
pop edx
popreg
pop ebp
ret

View file

@ -0,0 +1,72 @@
//-----------------------------------------------------------------------------
// 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 OS_DLIBRARY_H
#define OS_DLIBRARY_H
#include "core/util/refBase.h"
// DLLs use the standard calling convention
#define DLL_CALL __stdcall
#define DLL_EXPORT_CALL __declspec(dllexport)
#define DLL_IMPORT_CALL __declspec(dllimport)
// Export functions from the DLL
#if defined(DLL_CODE)
#define DLL_DECL DLL_EXPORT_CALL
#else
#define DLL_DECL DLL_IMPORT_CALL
#endif
//-----------------------------------------------------------------------------
///@defgroup KernelDLL Loadable Libraries
/// Basic DLL handling and symbol resolving. When a library is first loaded
/// it's "open" function will be called, and it's "close" function is called
/// right before the library is unloaded.
///@ingroup OsModule
///@{
/// Dynamic Library
/// Interface for library objects loaded using the loadLibrary() function.
class DLibrary: public StrongRefBase
{
public:
virtual ~DLibrary() {}
virtual void *bind( const char *name ) = 0;
};
typedef StrongRefPtr<DLibrary> DLibraryRef;
/// Load a library
/// Returns 0 if the library fails to load. Symbols are
/// resolved through the DLibrary interface.
DLibraryRef OsLoadLibrary( const char *file );
///@}
//-----------------------------------------------------------------------------
#endif

View file

@ -0,0 +1,531 @@
//-----------------------------------------------------------------------------
// 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 "core/strings/stringFunctions.h"
#include "util/tempAlloc.h"
#include "console/console.h"
#include "core/stringTable.h"
//-----------------------------------------------------------------------------
StringTableEntry Platform::getTemporaryDirectory()
{
StringTableEntry path = osGetTemporaryDirectory();
if(! Platform::isDirectory(path))
path = Platform::getCurrentDirectory();
return path;
}
ConsoleFunction(getTemporaryDirectory, const char *, 1, 1, "()"
"@brief Returns the OS temporary directory, \"C:/Users/Mich/AppData/Local/Temp\" for example\n\n"
"@note This can be useful to adhering to OS standards and practices, "
"but not really used in Torque 3D right now.\n"
"@note Be very careful when getting into OS level File I/O."
"@return String containing path to OS temp directory\n"
"@note This is legacy function brought over from TGB, and does not appear "
"to have much use. Possibly deprecate?\n"
"@ingroup FileSystem\n"
"@internal")
{
return Platform::getTemporaryDirectory();
}
StringTableEntry Platform::getTemporaryFileName()
{
char buf[512];
StringTableEntry path = Platform::getTemporaryDirectory();
dSprintf(buf, sizeof(buf), "%s/tgb.%08x.%02x.tmp", path, Platform::getRealMilliseconds(), U32(Platform::getRandom() * 255));
// [tom, 9/7/2006] This shouldn't be needed, but just in case
if(Platform::isFile(buf))
return Platform::getTemporaryFileName();
return StringTable->insert(buf);
}
ConsoleFunction(getTemporaryFileName, const char *, 1, 1, "()"
"@brief Creates a name and extension for a potential temporary file\n\n"
"This does not create the actual file. It simply creates a random name "
"for a file that does not exist.\n\n"
"@note This is legacy function brought over from TGB, and does not appear "
"to have much use. Possibly deprecate?\n"
"@ingroup FileSystem\n"
"@internal")
{
return Platform::getTemporaryFileName();
}
//-----------------------------------------------------------------------------
static StringTableEntry sgMainCSDir = NULL;
StringTableEntry Platform::getMainDotCsDir()
{
if(sgMainCSDir == NULL)
sgMainCSDir = Platform::getExecutablePath();
return sgMainCSDir;
}
void Platform::setMainDotCsDir(const char *dir)
{
sgMainCSDir = StringTable->insert(dir);
}
//-----------------------------------------------------------------------------
typedef Vector<char*> CharVector;
static CharVector gPlatformDirectoryExcludeList( __FILE__, __LINE__ );
void Platform::addExcludedDirectory(const char *pDir)
{
gPlatformDirectoryExcludeList.push_back(dStrdup(pDir));
}
void Platform::clearExcludedDirectories()
{
while(gPlatformDirectoryExcludeList.size())
{
dFree(gPlatformDirectoryExcludeList.last());
gPlatformDirectoryExcludeList.pop_back();
}
}
bool Platform::isExcludedDirectory(const char *pDir)
{
for(CharVector::iterator i=gPlatformDirectoryExcludeList.begin(); i!=gPlatformDirectoryExcludeList.end(); i++)
if(!dStrcmp(pDir, *i))
return true;
return false;
}
//-----------------------------------------------------------------------------
inline void catPath(char *dst, const char *src, U32 len)
{
if(*dst != '/')
{
++dst; --len;
*dst = '/';
}
++dst; --len;
dStrncpy(dst, src, len);
dst[len - 1] = 0;
}
// converts the posix root path "/" to "c:/" for win32
// FIXME: this is not ideal. the c: drive is not guaranteed to exist.
#if defined(TORQUE_OS_WIN32)
static inline void _resolveLeadingSlash(char* buf, U32 size)
{
if(buf[0] != '/')
return;
AssertFatal(dStrlen(buf) + 2 < size, "Expanded path would be too long");
dMemmove(buf + 2, buf, dStrlen(buf));
buf[0] = 'c';
buf[1] = ':';
}
#endif
static void makeCleanPathInPlace( char* path )
{
U32 pathDepth = 0;
char* fromPtr = path;
char* toPtr = path;
bool isAbsolute = false;
if( *fromPtr == '/' )
{
fromPtr ++;
toPtr ++;
isAbsolute = true;
}
else if( fromPtr[ 0 ] != '\0' && fromPtr[ 1 ] == ':' )
{
toPtr += 3;
fromPtr += 3;
isAbsolute = true;
}
while( *fromPtr )
{
if( fromPtr[ 0 ] == '.' && fromPtr[ 1 ] == '.' && fromPtr[ 2 ] == '/' )
{
// Back up from '../'
if( pathDepth > 0 )
{
pathDepth --;
toPtr -= 2;
while( toPtr >= path && *toPtr != '/' )
toPtr --;
toPtr ++;
}
else if( !isAbsolute )
{
dMemcpy( toPtr, fromPtr, 3 );
toPtr += 3;
}
fromPtr += 3;
}
else if( fromPtr[ 0 ] == '.' && fromPtr[ 1 ] == '/' )
{
// Ignore.
fromPtr += 2;
}
else
{
if( fromPtr[ 0 ] == '/' )
pathDepth ++;
*toPtr ++ = *fromPtr ++;
}
}
*toPtr = '\0';
}
char * Platform::makeFullPathName(const char *path, char *buffer, U32 size, const char *cwd /* = NULL */)
{
char bspath[1024];
dStrncpy(bspath, path, sizeof(bspath));
bspath[sizeof(bspath)-1] = 0;
for(S32 i = 0;i < dStrlen(bspath);++i)
{
if(bspath[i] == '\\')
bspath[i] = '/';
}
if(Platform::isFullPath(bspath))
{
// Already a full path
#if defined(TORQUE_OS_WIN32)
_resolveLeadingSlash(bspath, sizeof(bspath));
#endif
dStrncpy(buffer, bspath, size);
buffer[size-1] = 0;
return buffer;
}
// [rene, 05/05/2008] Based on overall file handling in Torque, it does not seem to make
// that much sense to me to base things off the current working directory here.
if(cwd == NULL)
cwd = Con::isCurrentScriptToolScript() ? Platform::getMainDotCsDir() : Platform::getCurrentDirectory();
dStrncpy(buffer, cwd, size);
buffer[size-1] = 0;
const char* defaultDir = Con::getVariable("defaultGame");
char *ptr = bspath;
char *slash = NULL;
char *endptr = buffer + dStrlen(buffer) - 1;
do
{
slash = dStrchr(ptr, '/');
if(slash)
{
*slash = 0;
// Directory
if(dStrcmp(ptr, "..") == 0)
{
// Parent
endptr = dStrrchr(buffer, '/');
if (endptr)
*endptr-- = 0;
}
else if(dStrcmp(ptr, ".") == 0)
{
// Current dir
}
else if(dStrcmp(ptr, "~") == 0)
{
catPath(endptr, defaultDir, size - (endptr - buffer));
endptr += dStrlen(endptr) - 1;
}
else if(endptr)
{
catPath(endptr, ptr, size - (endptr - buffer));
endptr += dStrlen(endptr) - 1;
}
ptr = slash + 1;
}
else if(endptr)
{
// File
catPath(endptr, ptr, size - (endptr - buffer));
endptr += dStrlen(endptr) - 1;
}
} while(slash);
return buffer;
}
bool Platform::isFullPath(const char *path)
{
// Quick way out
if(path[0] == '/' || path[1] == ':')
return true;
return false;
}
//-----------------------------------------------------------------------------
/// Return "fileName" stripped of its extension. Only extensions contained
/// in "validExtensions" will be stripped from the filename.
///
/// @note Extensions in "validExtension" should include the dot.
String Platform::stripExtension( String fileName, Vector< String >& validExtensions )
{
// See if we have a valid extension to strip off
String ext;
S32 dotPos = fileName.find( '.', 0, String::Right );
if( dotPos != String::NPos )
ext = fileName.substr( dotPos );
U32 numValidExt = validExtensions.size();
if( ext.isNotEmpty() && numValidExt )
{
bool validExt = false;
for( U32 i = 0; i < numValidExt; i++ )
{
if( ext.equal( validExtensions[ i ], String::NoCase ) )
{
validExt = true;
break;
}
}
if( !validExt )
ext = String::EmptyString;
}
if( ext.isEmpty() )
return fileName;
else
return fileName.substr( 0, fileName.length() - ext.length() );
}
//-----------------------------------------------------------------------------
// TODO: wow really shouldn't be adding everything to the string table, use the string class!
StringTableEntry Platform::makeRelativePathName(const char *path, const char *to)
{
// Make sure 'to' is a proper absolute path terminated with a forward slash.
char buffer[ 2048 ];
if( !to )
{
dSprintf( buffer, sizeof( buffer ), "%s/", Platform::getMainDotCsDir() );
to = buffer;
}
else if( !Platform::isFullPath( to ) )
{
dSprintf( buffer, sizeof( buffer ), "%s/%s/", Platform::getMainDotCsDir(), to );
makeCleanPathInPlace( buffer );
to = buffer;
}
else if( to[ dStrlen( to ) - 1 ] != '/' )
{
U32 length = getMin( (U32)dStrlen( to ), sizeof( buffer ) - 2 );
dMemcpy( buffer, to, length );
buffer[ length ] = '/';
buffer[ length + 1 ] = '\0';
to = buffer;
}
// If 'path' isn't absolute, make it now. Let's us use a single
// absolute/absolute merge path from here on.
char buffer2[ 1024 ];
if( !Platform::isFullPath( path ) )
{
dSprintf( buffer2, sizeof( buffer2 ), "%s/%s", Platform::getMainDotCsDir(), path );
makeCleanPathInPlace( buffer2 );
path = buffer2;
}
// First, find the common prefix and see where 'path' branches off from 'to'.
const char *pathPtr, *toPtr, *branch = path;
for(pathPtr = path, toPtr = to;*pathPtr && *toPtr && dTolower(*pathPtr) == dTolower(*toPtr);++pathPtr, ++toPtr)
{
if(*pathPtr == '/')
branch = pathPtr;
}
// If there's no common part, the two paths are on different drives and
// there's nothing we can do.
if( pathPtr == path )
return StringTable->insert( path );
// If 'path' and 'to' are identical (minus trailing slash or so), we can just return './'.
else if((*pathPtr == 0 || (*pathPtr == '/' && *(pathPtr + 1) == 0)) &&
(*toPtr == 0 || (*toPtr == '/' && *(toPtr + 1) == 0)))
{
char* bufPtr = buffer;
*bufPtr ++ = '.';
if(*pathPtr == '/' || *(pathPtr - 1) == '/')
*bufPtr++ = '/';
*bufPtr = 0;
return StringTable->insert(buffer);
}
// If 'to' is a proper prefix of 'path', the remainder of 'path' is our relative path.
else if( *toPtr == '\0' && toPtr[ -1 ] == '/' )
return StringTable->insert( pathPtr );
// Otherwise have to step up the remaining directories in 'to' and then
// append the remainder of 'path'.
else
{
if((*pathPtr == 0 && *toPtr == '/') || (*toPtr == '/' && *pathPtr == 0))
branch = pathPtr;
// Allocate a new temp so we aren't prone to buffer overruns.
TempAlloc< char > temp( dStrlen( toPtr ) + dStrlen( branch ) + 1 );
char* bufPtr = temp;
// Figure out parent dirs
for(toPtr = to + (branch - path);*toPtr;++toPtr)
{
if(*toPtr == '/' && *(toPtr + 1) != 0)
{
*bufPtr++ = '.';
*bufPtr++ = '.';
*bufPtr++ = '/';
}
}
*bufPtr = 0;
// Copy the rest
if(*branch)
dStrcpy(bufPtr, branch + 1);
else
*--bufPtr = 0;
return StringTable->insert( temp );
}
}
//-----------------------------------------------------------------------------
static StringTableEntry tryStripBasePath(const char *path, const char *base)
{
U32 len = dStrlen(base);
if(dStrnicmp(path, base, len) == 0)
{
if(*(path + len) == '/') ++len;
return StringTable->insert(path + len, true);
}
return NULL;
}
StringTableEntry Platform::stripBasePath(const char *path)
{
if(path == NULL)
return NULL;
StringTableEntry str = tryStripBasePath(path, Platform::getMainDotCsDir());
if(str != NULL )
return str;
str = tryStripBasePath(path, Platform::getCurrentDirectory());
if(str != NULL )
return str;
str = tryStripBasePath(path, Platform::getPrefsPath());
if(str != NULL )
return str;
return path;
}
//-----------------------------------------------------------------------------
StringTableEntry Platform::getPrefsPath(const char *file /* = NULL */)
{
#ifndef TORQUE2D_TOOLS_FIXME
return StringTable->insert(file ? file : "");
#else
char buf[1024];
const char *company = Con::getVariable("$Game::CompanyName");
if(company == NULL || *company == 0)
company = "GarageGames";
const char *appName = Con::getVariable("$Game::GameName");
if(appName == NULL || *appName == 0)
appName = TORQUE_APP_NAME;
if(file)
{
if(dStrstr(file, ".."))
{
Con::errorf("getPrefsPath - filename cannot be relative");
return NULL;
}
dSprintf(buf, sizeof(buf), "%s/%s/%s/%s", Platform::getUserDataDirectory(), company, appName, file);
}
else
dSprintf(buf, sizeof(buf), "%s/%s/%s", Platform::getUserDataDirectory(), company, appName);
return StringTable->insert(buf, true);
#endif
}
//-----------------------------------------------------------------------------
ConsoleFunction(getUserDataDirectory, const char*, 1, 1, "getUserDataDirectory()")
{
return Platform::getUserDataDirectory();
}
ConsoleFunction(getUserHomeDirectory, const char*, 1, 1, "getUserHomeDirectory()")
{
return Platform::getUserHomeDirectory();
}

View file

@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// 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/platform.h"
#include "platform/platformFont.h"
const char *getCharSetName(const U32 charSet)
{
switch(charSet)
{
case TGE_ANSI_CHARSET: return "ansi";
case TGE_SYMBOL_CHARSET: return "symbol";
case TGE_SHIFTJIS_CHARSET: return "shiftjis";
case TGE_HANGEUL_CHARSET: return "hangeul";
case TGE_HANGUL_CHARSET: return "hangul";
case TGE_GB2312_CHARSET: return "gb2312";
case TGE_CHINESEBIG5_CHARSET: return "chinesebig5";
case TGE_OEM_CHARSET: return "oem";
case TGE_JOHAB_CHARSET: return "johab";
case TGE_HEBREW_CHARSET: return "hebrew";
case TGE_ARABIC_CHARSET: return "arabic";
case TGE_GREEK_CHARSET: return "greek";
case TGE_TURKISH_CHARSET: return "turkish";
case TGE_VIETNAMESE_CHARSET: return "vietnamese";
case TGE_THAI_CHARSET: return "thai";
case TGE_EASTEUROPE_CHARSET: return "easteurope";
case TGE_RUSSIAN_CHARSET: return "russian";
case TGE_MAC_CHARSET: return "mac";
case TGE_BALTIC_CHARSET: return "baltic";
}
AssertISV(false, "getCharSetName - unknown charset! Update table in platformString.cc!");
return "";
}

View file

@ -0,0 +1,97 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _PLATFORMFONT_H_
#define _PLATFORMFONT_H_
// Charsets for fonts
// [tom, 7/27/2005] These are intended to map to their Win32 equivalents. This
// enumeration may require changes to accommodate other platforms.
enum FontCharset
{
TGE_ANSI_CHARSET = 0,
TGE_SYMBOL_CHARSET,
TGE_SHIFTJIS_CHARSET,
TGE_HANGEUL_CHARSET,
TGE_HANGUL_CHARSET,
TGE_GB2312_CHARSET,
TGE_CHINESEBIG5_CHARSET,
TGE_OEM_CHARSET,
TGE_JOHAB_CHARSET,
TGE_HEBREW_CHARSET,
TGE_ARABIC_CHARSET,
TGE_GREEK_CHARSET,
TGE_TURKISH_CHARSET,
TGE_VIETNAMESE_CHARSET,
TGE_THAI_CHARSET,
TGE_EASTEUROPE_CHARSET,
TGE_RUSSIAN_CHARSET,
TGE_MAC_CHARSET,
TGE_BALTIC_CHARSET
};
extern const char *getCharSetName(const U32 charSet);
class PlatformFont
{
public:
struct CharInfo
{
S16 bitmapIndex; ///< @note -1 indicates character is NOT to be
/// rendered, i.e., \n, \r, etc.
U32 xOffset; ///< x offset into bitmap sheet
U32 yOffset; ///< y offset into bitmap sheet
U32 width; ///< width of character (pixels)
U32 height; ///< height of character (pixels)
S32 xOrigin;
S32 yOrigin;
S32 xIncrement;
U8 *bitmapData; ///< temp storage for bitmap data
};
virtual ~PlatformFont() {}
/// Is the specified character valid for rendering?
virtual bool isValidChar(const UTF16 ch) const = 0;
virtual bool isValidChar(const UTF8 *str) const = 0;
virtual U32 getFontHeight() const = 0;
virtual U32 getFontBaseLine() const = 0;
virtual PlatformFont::CharInfo &getCharInfo(const UTF16 ch) const = 0;
virtual PlatformFont::CharInfo &getCharInfo(const UTF8 *str) const = 0;
/// This is just for createPlatformFont to call.
///
/// @todo Rethink this so we don't have a private public.
virtual bool create(const char *name, U32 size, U32 charset = TGE_ANSI_CHARSET) = 0;
static void enumeratePlatformFonts( Vector<StringTableEntry>& fonts, UTF16* fontFamily = NULL );
};
extern PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset = TGE_ANSI_CHARSET);
#endif // _PLATFORMFONT_H_

View file

@ -0,0 +1,134 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORMINPUT_H_
#define _PLATFORMINPUT_H_
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#include "platform/event.h"
//------------------------------------------------------------------------------
U8 TranslateOSKeyCode( U8 vcode );
U8 TranslateKeyCodeToOS(U8 keycode);
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
class InputDevice : public SimObject
{
protected:
char mName[30];
public:
struct ObjInfo
{
InputEventType mType;
InputObjectInstances mInst;
S32 mMin, mMax;
};
inline const char* getDeviceName()
{
return mName;
}
virtual bool process() = 0;
};
//------------------------------------------------------------------------------
class InputManager : public SimGroup
{
protected:
bool mEnabled;
public:
inline bool isEnabled()
{
return mEnabled;
}
virtual bool enable() = 0;
virtual void disable() = 0;
virtual void process() = 0;
};
enum KEY_STATE
{
STATE_LOWER,
STATE_UPPER,
STATE_GOOFY
};
//------------------------------------------------------------------------------
class Input
{
protected:
static InputManager* smManager;
static bool smActive;
/// Current modifier keys.
static U8 smModifierKeys;
static bool smLastKeyboardActivated;
static bool smLastMouseActivated;
static bool smLastJoystickActivated;
public:
static void init();
static void destroy();
static bool enable();
static void disable();
static void activate();
static void deactivate();
static U16 getAscii( U16 keyCode, KEY_STATE keyState );
static U16 getKeyCode( U16 asciiCode );
static bool isEnabled();
static bool isActive();
static void process();
static InputManager* getManager();
static U8 getModifierKeys() {return smModifierKeys;}
static void setModifierKeys(U8 mod) {smModifierKeys = mod;}
#ifdef LOG_INPUT
static void log( const char* format, ... );
#endif
#ifdef TORQUE_OS_XENON
static S32 getLockedController();
#endif
/// Global input routing JournaledSignal; post input events here for
/// processing.
static InputEvent smInputEvent;
};
#endif // _H_PLATFORMINPUT_

View file

@ -0,0 +1,101 @@
//-----------------------------------------------------------------------------
// 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 _TORQUE_PLATFORM_PLATFORMINTRINSICS_GCC_H_
#define _TORQUE_PLATFORM_PLATFORMINTRINSICS_GCC_H_
/// @file
/// Compiler intrinsics for GCC.
#ifdef TORQUE_OS_MAC
#include <libkern/OSAtomic.h>
#elif defined(TORQUE_OS_PS3)
#include <cell/atomic.h>
#endif
// Fetch-And-Add
//
// NOTE: These do not return the pre-add value because
// not all platforms (damn you OSX) can do that.
//
inline void dFetchAndAdd( volatile U32& ref, U32 val )
{
#if defined(TORQUE_OS_PS3)
cellAtomicAdd32( (std::uint32_t *)&ref, val );
#elif !defined(TORQUE_OS_MAC)
__sync_fetch_and_add( ( volatile long* ) &ref, val );
#else
OSAtomicAdd32( val, (int32_t* ) &ref);
#endif
}
inline void dFetchAndAdd( volatile S32& ref, S32 val )
{
#if defined(TORQUE_OS_PS3)
cellAtomicAdd32( (std::uint32_t *)&ref, val );
#elif !defined(TORQUE_OS_MAC)
__sync_fetch_and_add( ( volatile long* ) &ref, val );
#else
OSAtomicAdd32( val, (int32_t* ) &ref);
#endif
}
// Compare-And-Swap
inline bool dCompareAndSwap( volatile U32& ref, U32 oldVal, U32 newVal )
{
// bool
//OSAtomicCompareAndSwap32(int32_t oldValue, int32_t newValue, volatile int32_t *theValue);
#if defined(TORQUE_OS_PS3)
return ( cellAtomicCompareAndSwap32( (std::uint32_t *)&ref, newVal, oldVal ) == oldVal );
#elif !defined(TORQUE_OS_MAC)
return ( __sync_val_compare_and_swap( ( volatile long* ) &ref, oldVal, newVal ) == oldVal );
#else
return OSAtomicCompareAndSwap32(oldVal, newVal, (int32_t *) &ref);
#endif
}
inline bool dCompareAndSwap( volatile U64& ref, U64 oldVal, U64 newVal )
{
#if defined(TORQUE_OS_PS3)
return ( cellAtomicCompareAndSwap32( (std::uint32_t *)&ref, newVal, oldVal ) == oldVal );
#elif !defined(TORQUE_OS_MAC)
return ( __sync_val_compare_and_swap( ( volatile long long* ) &ref, oldVal, newVal ) == oldVal );
#else
return OSAtomicCompareAndSwap64(oldVal, newVal, (int64_t *) &ref);
#endif
}
/// Performs an atomic read operation.
inline U32 dAtomicRead( volatile U32 &ref )
{
#if defined(TORQUE_OS_PS3)
return cellAtomicAdd32( (std::uint32_t *)&ref, 0 );
#elif !defined(TORQUE_OS_MAC)
return __sync_fetch_and_add( ( volatile long* ) &ref, 0 );
#else
return OSAtomicAdd32( 0, (int32_t* ) &ref);
#endif
}
#endif // _TORQUE_PLATFORM_PLATFORMINTRINSICS_GCC_H_

View file

@ -0,0 +1,57 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORMINTRINSICS_H_
#define _PLATFORMINTRINSICS_H_
#ifndef _TORQUE_TYPES_H_
# include "platform/types.h"
#endif
#if defined( TORQUE_COMPILER_VISUALC )
# include "platform/platformIntrinsics.visualc.h"
#elif defined ( TORQUE_COMPILER_GCC )
# include "platform/platformIntrinsics.gcc.h"
#else
# error No intrinsics implemented for compiler.
#endif
//TODO: 64bit safe
template< typename T >
inline bool dCompareAndSwap( T* volatile& refPtr, T* oldPtr, T* newPtr )
{
return dCompareAndSwap( *reinterpret_cast< volatile U32* >( &refPtr ), ( U32 ) oldPtr, ( U32 ) newPtr );
}
// Test-And-Set
inline bool dTestAndSet( volatile U32& ref )
{
return dCompareAndSwap( ref, 0, 1 );
}
inline bool dTestAndSet( volatile U64& ref )
{
return dCompareAndSwap( ref, 0, 1 );
}
#endif // _PLATFORMINTRINSICS_H_

View file

@ -0,0 +1,76 @@
//-----------------------------------------------------------------------------
// 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 _TORQUE_PLATFORM_PLATFORMINTRINSICS_VISUALC_H_
#define _TORQUE_PLATFORM_PLATFORMINTRINSICS_VISUALC_H_
/// @file
/// Compiler intrinsics for Visual C++.
#if defined(TORQUE_OS_XENON)
# include <Xtl.h>
# define _InterlockedExchangeAdd InterlockedExchangeAdd
# define _InterlockedExchangeAdd64 InterlockedExchangeAdd64
#else
# include <intrin.h>
#endif
// Fetch-And-Add
//
// NOTE: These do not return the pre-add value because
// not all platforms (damn you OSX) can do that.
//
inline void dFetchAndAdd( volatile U32& ref, U32 val )
{
_InterlockedExchangeAdd( ( volatile long* ) &ref, val );
}
inline void dFetchAndAdd( volatile S32& ref, S32 val )
{
_InterlockedExchangeAdd( ( volatile long* ) &ref, val );
}
#if defined(TORQUE_OS_XENON)
// Not available on x86
inline void dFetchAndAdd( volatile U64& ref, U64 val )
{
_InterlockedExchangeAdd64( ( volatile __int64* ) &ref, val );
}
#endif
// Compare-And-Swap
inline bool dCompareAndSwap( volatile U32& ref, U32 oldVal, U32 newVal )
{
return ( _InterlockedCompareExchange( ( volatile long* ) &ref, newVal, oldVal ) == oldVal );
}
inline bool dCompareAndSwap( volatile U64& ref, U64 oldVal, U64 newVal )
{
return ( _InterlockedCompareExchange64( ( volatile __int64* ) &ref, newVal, oldVal ) == oldVal );
}
/// Performs an atomic read operation.
inline U32 dAtomicRead( volatile U32 &ref )
{
return _InterlockedExchangeAdd( ( volatile long* )&ref, 0 );
}
#endif // _TORQUE_PLATFORM_PLATFORMINTRINSICS_VISUALC_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,59 @@
//-----------------------------------------------------------------------------
// 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 _TORQUE_PLATFORM_PLATFORMMEMORY_H_
#define _TORQUE_PLATFORM_PLATFORMMEMORY_H_
#include "platform/platform.h"
namespace Memory
{
enum EFlag
{
FLAG_Debug,
FLAG_Global,
FLAG_Static
};
struct Info
{
U32 mAllocNumber;
U32 mAllocSize;
const char* mFileName;
U32 mLineNumber;
bool mIsArray;
bool mIsGlobal;
bool mIsStatic;
};
void checkPtr( void* ptr );
void flagCurrentAllocs( EFlag flag = FLAG_Debug );
void ensureAllFreed();
void dumpUnflaggedAllocs(const char *file, EFlag flag = FLAG_Debug );
S32 countUnflaggedAllocs(const char *file, S32 *outUnflaggedRealloc = NULL, EFlag flag = FLAG_Debug );
dsize_t getMemoryUsed();
dsize_t getMemoryAllocated();
void getMemoryInfo( void* ptr, Info& info );
void validate();
}
#endif // _TORQUE_PLATFORM_PLATFORMMEMORY_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,150 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORM_PLATFORMNET_H_
#define _PLATFORM_PLATFORMNET_H_
#include "platform/platform.h"
#include "core/util/rawData.h"
#include "core/util/journal/journaledSignal.h"
#ifndef MAXPACKETSIZE
#define MAXPACKETSIZE 1500
#endif
typedef int NetConnectionId;
/// Generic network address
///
/// This is used to represent IP addresses.
struct NetAddress
{
int type; ///< Type of address (IPAddress currently)
/// Acceptable NetAddress types.
enum
{
IPAddress,
};
U8 netNum[4]; ///< For IP: sin_addr<br>
U8 nodeNum[6]; ///< For IP: Not used.<br>
U16 port; ///< For IP: sin_port<br>
};
typedef S32 NetSocket;
const NetSocket InvalidSocket = -1;
/// void event(NetSocket sock, U32 state)
typedef JournaledSignal<void(NetSocket,U32)> ConnectionNotifyEvent;
/// void event(NetSocket listeningPort, NetSocket newConnection, NetAddress originatingAddress)
typedef JournaledSignal<void(NetSocket,NetSocket,NetAddress)> ConnectionAcceptedEvent;
/// void event(NetSocket connection, RawData incomingData)
typedef JournaledSignal<void(NetSocket,RawData)> ConnectionReceiveEvent;
/// void event(NetAddress originator, RawData incomingData)
typedef JournaledSignal<void(NetAddress,RawData)> PacketReceiveEvent;
/// Platform-specific network operations.
struct Net
{
enum Error
{
NoError,
WrongProtocolType,
InvalidPacketProtocol,
WouldBlock,
NotASocket,
UnknownError
};
enum ConnectionState {
DNSResolved,
DNSFailed,
Connected,
ConnectFailed,
Disconnected
};
enum Protocol
{
UDPProtocol,
TCPProtocol
};
static const int MaxPacketDataSize = MAXPACKETSIZE;
static ConnectionNotifyEvent smConnectionNotify;
static ConnectionAcceptedEvent smConnectionAccept;
static ConnectionReceiveEvent smConnectionReceive;
static PacketReceiveEvent smPacketReceive;
static bool init();
static void shutdown();
// Unreliable net functions (UDP)
// sendto is for sending data
// all incoming data comes in on packetReceiveEventType
// App can only open one unreliable port... who needs more? ;)
static bool openPort(S32 connectPort, bool doBind = true);
static NetSocket getPort();
static void closePort();
static Error sendto(const NetAddress *address, const U8 *buffer, S32 bufferSize);
// Reliable net functions (TCP)
// all incoming messages come in on the Connected* events
static NetSocket openListenPort(U16 port);
static NetSocket openConnectTo(const char *stringAddress); // does the DNS resolve etc.
static void closeConnectTo(NetSocket socket);
static Error sendtoSocket(NetSocket socket, const U8 *buffer, S32 bufferSize);
static bool compareAddresses(const NetAddress *a1, const NetAddress *a2);
static bool stringToAddress(const char *addressString, NetAddress *address);
static void addressToString(const NetAddress *address, char addressString[256]);
// lower level socked based network functions
static NetSocket openSocket();
static Error closeSocket(NetSocket socket);
static Error send(NetSocket socket, const U8 *buffer, S32 bufferSize);
static Error recv(NetSocket socket, U8 *buffer, S32 bufferSize, S32 *bytesRead);
static Error connect(NetSocket socket, const NetAddress *address);
static Error listen(NetSocket socket, S32 maxConcurrentListens);
static NetSocket accept(NetSocket acceptSocket, NetAddress *remoteAddress);
static Error bind(NetSocket socket, U16 port);
static Error setBufferSize(NetSocket socket, S32 bufferSize);
static Error setBroadcast(NetSocket socket, bool broadcastEnable);
static Error setBlocking(NetSocket socket, bool blockingIO);
private:
static void process();
};
#endif

View file

@ -0,0 +1,187 @@
//-----------------------------------------------------------------------------
// 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/platformNetAsync.h"
#include "core/strings/stringFunctions.h"
#include "platform/threads/threadPool.h"
#include "console/console.h"
#if defined(TORQUE_OS_WIN32)
# include <winsock.h>
#elif defined(TORQUE_OS_XENON)
# include <Xtl.h>
#else
# include <netdb.h>
# include <unistd.h>
#endif
#include <errno.h>
NetAsync gNetAsync;
//--------------------------------------------------------------------------
// NetAsync::NameLookupRequest.
//--------------------------------------------------------------------------
// internal structure for storing information about a name lookup request
struct NetAsync::NameLookupRequest
{
NetSocket sock;
char remoteAddr[4096];
char out_h_addr[4096];
int out_h_length;
bool complete;
NameLookupRequest()
{
sock = InvalidSocket;
remoteAddr[0] = 0;
out_h_addr[0] = 0;
out_h_length = -1;
complete = false;
}
};
//--------------------------------------------------------------------------
// NetAsync::NameLookupWorkItem.
//--------------------------------------------------------------------------
/// Work item issued to the thread pool for each lookup request.
struct NetAsync::NameLookupWorkItem : public ThreadPool::WorkItem
{
typedef ThreadPool::WorkItem Parent;
NameLookupWorkItem( NameLookupRequest& request, ThreadPool::Context* context = 0 )
: Parent( context ),
mRequest( request )
{
}
protected:
virtual void execute()
{
#ifndef TORQUE_OS_XENON
// do it
struct hostent* hostent = gethostbyname(mRequest.remoteAddr);
if (hostent == NULL)
{
// oh well! leave the lookup data unmodified (h_length) should
// still be -1 from initialization
mRequest.complete = true;
}
else
{
// copy the stuff we need from the hostent
dMemset(mRequest.out_h_addr, 0,
sizeof(mRequest.out_h_addr));
dMemcpy(mRequest.out_h_addr, hostent->h_addr, hostent->h_length);
mRequest.out_h_length = hostent->h_length;
mRequest.complete = true;
}
#else
XNDNS *pxndns = NULL;
HANDLE hEvent = CreateEvent(NULL, false, false, NULL);
XNetDnsLookup(mRequest.remoteAddr, hEvent, &pxndns);
while(pxndns->iStatus == WSAEINPROGRESS)
WaitForSingleObject(hEvent, INFINITE);
if(pxndns->iStatus == 0 && pxndns->cina > 0)
{
dMemset(mRequest.out_h_addr, 0, sizeof(mRequest.out_h_addr));
// This is a suspect section. I need to revisit. [2/22/2010 Pat]
dMemcpy(mRequest.out_h_addr, pxndns->aina, sizeof(IN_ADDR));
mRequest.out_h_length = sizeof(IN_ADDR);
}
mRequest.complete = true;
XNetDnsRelease(pxndns);
CloseHandle(hEvent);
#endif
}
private:
NameLookupRequest& mRequest;
};
//--------------------------------------------------------------------------
// NetAsync.
//--------------------------------------------------------------------------
NetAsync::NetAsync()
{
VECTOR_SET_ASSOCIATION( mLookupRequests );
}
void NetAsync::queueLookup(const char* remoteAddr, NetSocket socket)
{
// do we have it already?
unsigned int i = 0;
for (i = 0; i < mLookupRequests.size(); ++i)
{
if (mLookupRequests[i].sock == socket)
// found it. ignore more than one lookup at a time for a socket.
return;
}
// not found, so add it
mLookupRequests.increment();
NameLookupRequest& lookupRequest = mLookupRequests.last();
lookupRequest.sock = socket;
dStrncpy(lookupRequest.remoteAddr, remoteAddr, sizeof(lookupRequest.remoteAddr));
ThreadSafeRef< NameLookupWorkItem > workItem( new NameLookupWorkItem( lookupRequest ) );
ThreadPool::GLOBAL().queueWorkItem( workItem );
}
bool NetAsync::checkLookup(NetSocket socket, char* out_h_addr,
int* out_h_length, int out_h_addr_size)
{
bool found = false;
// search for the socket
RequestIterator iter;
for (iter = mLookupRequests.begin();
iter != mLookupRequests.end();
++iter)
// if we found it and it is complete...
if (socket == iter->sock && iter->complete)
{
// copy the lookup data to the callers parameters
dMemcpy(out_h_addr, iter->out_h_addr, out_h_addr_size);
*out_h_length = iter->out_h_length;
found = true;
break;
}
// we found the socket, so we are done with it. erase.
if (found)
mLookupRequests.erase(iter);
return found;
}

View file

@ -0,0 +1,63 @@
//-----------------------------------------------------------------------------
// 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 PLATFORM_NET_ASYNC_H
#define PLATFORM_NET_ASYNC_H
#include "platform/platform.h"
#include "platform/platformNet.h"
#include "core/util/tVector.h"
// class for doing asynchronous network operations on unix (linux and
// hopefully osx) platforms. right now it only implements dns lookups
class NetAsync
{
private:
struct NameLookupRequest;
struct NameLookupWorkItem;
typedef Vector< NameLookupRequest > RequestVector;
typedef RequestVector::iterator RequestIterator;
RequestVector mLookupRequests;
public:
NetAsync();
// queue a DNS lookup. only one dns lookup can be queued per socket at
// a time. subsequent queue request for the socket are ignored. use
// checkLookup() to check the status of a request.
void queueLookup(const char* remoteAddr, NetSocket socket);
// check on the status of a dns lookup for a socket. if the lookup is
// not yet complete, the function will return false. if it is
// complete, the function will return true, and out_h_addr and
// out_h_length will be set appropriately. if out_h_length is -1, then
// name could not be resolved. otherwise, it provides the number of
// address bytes copied into out_h_addr.
bool checkLookup(NetSocket socket, char* out_h_addr, int* out_h_length, int out_h_addr_size);
};
// the global net async object
extern NetAsync gNetAsync;
#endif

View file

@ -0,0 +1,291 @@
//-----------------------------------------------------------------------------
// 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 "core/strings/stringFunctions.h"
#include "console/console.h"
#include "platform/platformRedBook.h"
//------------------------------------------------------------------------------
// Class: RedBookDevice
//------------------------------------------------------------------------------
RedBookDevice::RedBookDevice()
{
mAcquired = false;
mDeviceName = 0;
}
RedBookDevice::~RedBookDevice()
{
delete [] mDeviceName;
}
//------------------------------------------------------------------------------
// Class: RedBook
//------------------------------------------------------------------------------
Vector<RedBookDevice *> RedBook::smDeviceList(__FILE__, __LINE__);
RedBookDevice * RedBook::smCurrentDevice;
char RedBook::smLastError[1024];
//------------------------------------------------------------------------------
void RedBook::init()
{
}
void RedBook::destroy()
{
close();
for( Vector<RedBookDevice*>::iterator i = smDeviceList.begin( ); i != smDeviceList.end( ); i++ ) {
delete *i;
}
smDeviceList.clear( );
}
//------------------------------------------------------------------------------
void RedBook::installDevice(RedBookDevice * device)
{
smDeviceList.push_back(device);
}
RedBookDevice * RedBook::getCurrentDevice()
{
return(smCurrentDevice);
}
U32 RedBook::getDeviceCount()
{
return(smDeviceList.size());
}
const char * RedBook::getDeviceName(U32 idx)
{
if(idx >= getDeviceCount())
{
setLastError("Invalid device index");
return("");
}
return(smDeviceList[idx]->mDeviceName);
}
void RedBook::setLastError(const char * error)
{
if(!error || dStrlen(error) >= sizeof(smLastError))
setLastError("Invalid error string passed");
else
dStrcpy(smLastError, error);
}
const char * RedBook::getLastError()
{
return(smLastError);
}
void RedBook::handleCallback(U32 type)
{
switch(type)
{
case PlayFinished:
Con::executef("RedBookCallback", "PlayFinished");
break;
}
}
//------------------------------------------------------------------------------
bool RedBook::open(const char * deviceName)
{
if(!deviceName)
{
setLastError("Invalid device name");
return(false);
}
for(U32 i = 0; i < smDeviceList.size(); i++)
if(!dStricmp(deviceName, smDeviceList[i]->mDeviceName))
return(open(smDeviceList[i]));
setLastError("Failed to find device");
return(false);
}
bool RedBook::open(RedBookDevice * device)
{
if(!device)
{
setLastError("Invalid device passed");
return(false);
}
close();
smCurrentDevice = device;
return(smCurrentDevice->open());
}
bool RedBook::close()
{
if(smCurrentDevice)
{
bool ret = smCurrentDevice->close();
smCurrentDevice = 0;
return(ret);
}
setLastError("No device is currently open");
return(false);
}
bool RedBook::play(U32 track)
{
if(!smCurrentDevice)
{
setLastError("No device is currently open");
return(false);
}
return(smCurrentDevice->play(track));
}
bool RedBook::stop()
{
if(!smCurrentDevice)
{
setLastError("No device is currently open");
return(false);
}
return(smCurrentDevice->stop());
}
bool RedBook::getTrackCount(U32 * trackCount)
{
if(!smCurrentDevice)
{
setLastError("No device is currently open");
return(false);
}
return(smCurrentDevice->getTrackCount(trackCount));
}
bool RedBook::getVolume(F32 * volume)
{
if(!smCurrentDevice)
{
setLastError("No device is currently open");
return(false);
}
return(smCurrentDevice->getVolume(volume));
}
bool RedBook::setVolume(F32 volume)
{
if(!smCurrentDevice)
{
setLastError("No device is currently open");
return(false);
}
return(smCurrentDevice->setVolume(volume));
}
//------------------------------------------------------------------------------
// console methods
//------------------------------------------------------------------------------
ConsoleFunctionGroupBegin( Redbook, "Control functions for Redbook audio (ie, CD audio).");
ConsoleFunction(redbookOpen, bool, 1, 2, "(string device=NULL)"
"@brief Deprecated\n\n"
"@internal")
{
if(argc == 1)
return(RedBook::open(RedBook::getDeviceName(0)));
else
return(RedBook::open(argv[1]));
}
ConsoleFunction(redbookClose, bool, 1, 1, "Close the current Redbook device."
"@brief Deprecated\n\n"
"@internal")
{
return(RedBook::close());
}
ConsoleFunction( redbookPlay, bool, 2, 2, "(int track) Play the selected track."
"@brief Deprecated\n\n"
"@internal")
{
return(RedBook::play(dAtoi(argv[1])));
}
ConsoleFunction( redbookStop, bool, 1, 1, "Stop playing."
"@brief Deprecated\n\n"
"@internal")
{
return(RedBook::stop());
}
ConsoleFunction(redbookGetTrackCount, S32, 1, 1, "Return the number of tracks."
"@brief Deprecated\n\n"
"@internal")
{
U32 trackCount;
if(!RedBook::getTrackCount(&trackCount))
return(0);
return(trackCount);
}
ConsoleFunction(redbookGetVolume, F32, 1, 1, "Get the volume."
"@brief Deprecated\n\n"
"@internal")
{
F32 vol;
if(!RedBook::getVolume(&vol))
return(0.f);
else
return(vol);
}
ConsoleFunction(redbookSetVolume, bool, 2, 2, "(float volume) Set playback volume."
"@brief Deprecated\n\n"
"@internal")
{
return(RedBook::setVolume(dAtof(argv[1])));
}
ConsoleFunction( redbookGetDeviceCount, S32, 1, 1, "get the number of redbook devices."
"@brief Deprecated\n\n"
"@internal")
{
return(RedBook::getDeviceCount());
}
ConsoleFunction( redbookGetDeviceName, const char *, 2, 2, "(int index) Get name of specified Redbook device."
"@brief Deprecated\n\n"
"@internal")
{
return(RedBook::getDeviceName(dAtoi(argv[1])));
}
ConsoleFunction( redbookGetLastError, const char*, 1, 1, "Get a string explaining the last redbook error."
"@brief Deprecated\n\n"
"@internal")
{
return(RedBook::getLastError());
}
ConsoleFunctionGroupEnd( Redbook );

View file

@ -0,0 +1,87 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORMREDBOOK_H_
#define _PLATFORMREDBOOK_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
class RedBookDevice
{
public:
RedBookDevice();
virtual ~RedBookDevice();
bool mAcquired;
char * mDeviceName;
virtual bool open() = 0;
virtual bool close() = 0;
virtual bool play(U32) = 0;
virtual bool stop() = 0;
virtual bool getTrackCount(U32 *) = 0;
virtual bool getVolume(F32 *) = 0;
virtual bool setVolume(F32) = 0;
};
class RedBook
{
private:
static Vector<RedBookDevice *> smDeviceList;
static RedBookDevice * smCurrentDevice;
static char smLastError[];
public:
enum {
PlayFinished = 0,
};
static void handleCallback(U32);
static void init();
static void destroy();
static void installDevice(RedBookDevice *);
static U32 getDeviceCount();
static const char * getDeviceName(U32);
static RedBookDevice * getCurrentDevice();
static void setLastError(const char *);
static const char * getLastError();
static bool open(const char *);
static bool open(RedBookDevice *);
static bool close();
static bool play(U32);
static bool stop();
static bool getTrackCount(U32 *);
static bool getVolume(F32 *);
static bool setVolume(F32);
};
//------------------------------------------------------------------------------
#endif

View file

@ -0,0 +1,55 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORMTLS_H_
#define _PLATFORMTLS_H_
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
struct PlatformThreadStorage;
/// Platform independent per-thread storage class.
class ThreadStorage
{
enum
{
PlatformThreadStorageStorageSize = 32,
};
PlatformThreadStorage *mThreadStorage;
U8 mStorage[PlatformThreadStorageStorageSize];
public:
/// ThreadStorage constructor.
ThreadStorage();
/// ThreadStorage destructor.
~ThreadStorage();
/// returns the per-thread stored void pointer for this ThreadStorage. The default value is NULL.
void *get();
/// sets the per-thread stored void pointer for this ThreadStorage object.
void set(void *data);
};
#endif

View file

@ -0,0 +1,179 @@
//-----------------------------------------------------------------------------
// 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/platformTimer.h"
#include "core/util/journal/process.h"
void TimeManager::_updateTime()
{
// Calculate & filter time delta since last event.
// How long since last update?
S32 delta = mTimer->getElapsedMs();
// Now - we want to try to sleep until the time threshold will hit.
S32 msTillThresh = (mBackground ? mBackgroundThreshold : mForegroundThreshold) - delta;
if(msTillThresh > 0)
{
// There's some time to go, so let's sleep.
Platform::sleep( msTillThresh );
}
// Ok - let's grab the new elapsed and send that out.
S32 finalDelta = mTimer->getElapsedMs();
mTimer->reset();
timeEvent.trigger(finalDelta);
}
TimeManager::TimeManager()
{
mBackground = false;
mTimer = PlatformTimer::create();
Process::notify(this, &TimeManager::_updateTime, PROCESS_TIME_ORDER);
mForegroundThreshold = 5;
mBackgroundThreshold = 10;
}
TimeManager::~TimeManager()
{
Process::remove(this, &TimeManager::_updateTime);
delete mTimer;
}
void TimeManager::setForegroundThreshold(const S32 msInterval)
{
AssertFatal(msInterval > 0, "TimeManager::setForegroundThreshold - should have at least 1 ms between time events to avoid math problems!");
mForegroundThreshold = msInterval;
}
const S32 TimeManager::getForegroundThreshold() const
{
return mForegroundThreshold;
}
void TimeManager::setBackgroundThreshold(const S32 msInterval)
{
AssertFatal(msInterval > 0, "TimeManager::setBackgroundThreshold - should have at least 1 ms between time events to avoid math problems!");
mBackgroundThreshold = msInterval;
}
const S32 TimeManager::getBackgroundThreshold() const
{
return mBackgroundThreshold;
}
//----------------------------------------------------------------------------------
#pragma message("Mac/Lunix will need to implement this or get unresolved externals.")
#pragma message(" It was previously defined here with a Win32 ifdef which goes against")
#pragma message(" how torque implements its platform agnostic systems - JDD")
//PlatformTimer *PlatformTimer::create()
//{
// return new DefaultPlatformTimer();
//}
PlatformTimer::PlatformTimer()
{
}
PlatformTimer::~PlatformTimer()
{
}
// Exposes PlatformTimer to script for when high precision is needed.
#include "core/util/tDictionary.h"
#include "console/console.h"
class ScriptTimerMan
{
public:
ScriptTimerMan();
~ScriptTimerMan();
S32 startTimer();
S32 stopTimer( S32 id );
protected:
static S32 smNextId;
typedef Map<S32,PlatformTimer*> TimerMap;
TimerMap mTimers;
};
S32 ScriptTimerMan::smNextId = 1;
ScriptTimerMan::ScriptTimerMan()
{
}
ScriptTimerMan::~ScriptTimerMan()
{
TimerMap::Iterator itr = mTimers.begin();
for ( ; itr != mTimers.end(); itr++ )
delete itr->value;
mTimers.clear();
}
S32 ScriptTimerMan::startTimer()
{
PlatformTimer *timer = PlatformTimer::create();
mTimers.insert( smNextId, timer );
smNextId++;
return ( smNextId - 1 );
}
S32 ScriptTimerMan::stopTimer( S32 id )
{
TimerMap::Iterator itr = mTimers.find( id );
if ( itr == mTimers.end() )
return -1;
PlatformTimer *timer = itr->value;
S32 elapsed = timer->getElapsedMs();
mTimers.erase( itr );
delete timer;
return elapsed;
}
ScriptTimerMan gScriptTimerMan;
ConsoleFunction( startPrecisionTimer, S32, 1, 1, "startPrecisionTimer() - Create and start a high resolution platform timer. Returns the timer id." )
{
return gScriptTimerMan.startTimer();
}
ConsoleFunction( stopPrecisionTimer, S32, 2, 2, "stopPrecisionTimer( S32 id ) - Stop and destroy timer with the passed id. Returns the elapsed milliseconds." )
{
return gScriptTimerMan.stopTimer( dAtoi( argv[1] ) );
}

View file

@ -0,0 +1,111 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORM_PLATFORMTIMER_H_
#define _PLATFORM_PLATFORMTIMER_H_
#include "platform/platform.h"
#include "core/util/journal/journaledSignal.h"
/// Platform-specific timer class.
///
/// This exists primarily as support for the TimeManager, but may be useful
/// elsewhere.
class PlatformTimer
{
protected:
PlatformTimer();
public:
virtual ~PlatformTimer();
/// Get the number of MS that have elapsed since creation or the last
/// reset call.
virtual const S32 getElapsedMs()=0;
/// Reset elapsed ms back to zero.
virtual void reset()=0;
/// Create a new PlatformTimer.
static PlatformTimer *create();
};
/// Utility class to fire journalled time-delta events at regular intervals.
///
/// Most games and simulations need the ability to update their state based on
/// a time-delta. However, tracking time accurately and sending out well-conditioned
/// events (for instance, allowing no events with delta=0) tends to be platform
/// specific. This class provides an abstraction around this platform mojo.
///
/// In addition, a well behaved application may want to alter how frequently
/// it processes time advancement depending on its execution state. For instance,
/// a game running in the background can significantly reduce CPU usage
/// by only updating every 100ms, instead of trying to maintain a 1ms update
/// update rate.
class TimeManager
{
PlatformTimer *mTimer;
S32 mForegroundThreshold, mBackgroundThreshold;
bool mBackground;
void _updateTime();
public:
TimeManagerEvent timeEvent;
TimeManager();
~TimeManager();
void setForegroundThreshold(const S32 msInterval);
const S32 getForegroundThreshold() const;
void setBackgroundThreshold(const S32 msInterval);
const S32 getBackgroundThreshold() const;
void setBackground(const bool isBackground) { mBackground = isBackground; };
const bool getBackground() const { return mBackground; };
};
class DefaultPlatformTimer : public PlatformTimer
{
S32 mLastTime, mNextTime;
public:
DefaultPlatformTimer()
{
mLastTime = mNextTime = Platform::getRealMilliseconds();
}
const S32 getElapsedMs()
{
mNextTime = Platform::getRealMilliseconds();
return (mNextTime - mLastTime);
}
void reset()
{
mLastTime = mNextTime;
}
};
#endif

View file

@ -0,0 +1,34 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORMVFS_H_
#define _PLATFORMVFS_H_
namespace Zip
{
class ZipArchive;
}
extern Zip::ZipArchive *openEmbeddedVFSArchive();
extern void closeEmbeddedVFSArchive();
#endif // _PLATFORMVFS_H_

View file

@ -0,0 +1,99 @@
//-----------------------------------------------------------------------------
// 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/platformVideoInfo.h"
#include "core/strings/stringFunctions.h"
//------------------------------------------------------------------------------
PlatformVideoInfo::PlatformVideoInfo()
{
VECTOR_SET_ASSOCIATION( mAdapters );
}
//------------------------------------------------------------------------------
PlatformVideoInfo::~PlatformVideoInfo()
{
}
//------------------------------------------------------------------------------
bool PlatformVideoInfo::profileAdapters()
{
// Initialize the child class
if( !_initialize() )
return false;
mAdapters.clear();
// Query the number of adapters
String tempString;
mAdapters.increment( 1 );
//if( !_queryProperty( PVI_NumAdapters, 0, &tempString ) )
// return false;
//mAdapters.increment( dAtoi( tempString ) );
U32 adapterNum = 0;
for( Vector<PVIAdapter>::iterator itr = mAdapters.begin(); itr != mAdapters.end(); itr++ )
{
PVIAdapter &adapter = *itr;
U32 querySuccessFlags = U32_MAX;
AssertFatal( PVI_QueryCount < sizeof( querySuccessFlags ) * 8, "Not enough bits in query success mask." );
querySuccessFlags -= ( ( 1 << PVI_QueryCount ) - 1 );
// Fill in adapter information
#define _QUERY_MASK_HELPER( querytype, outstringaddr ) \
querySuccessFlags |= ( _queryProperty( querytype, adapterNum, outstringaddr ) ? 1 << querytype : 0 )
_QUERY_MASK_HELPER( PVI_NumDevices, &tempString );
adapter.numDevices = dAtoi( tempString );
_QUERY_MASK_HELPER( PVI_VRAM, &tempString );
adapter.vram = dAtoi( tempString );
_QUERY_MASK_HELPER( PVI_Description, &adapter.description );
_QUERY_MASK_HELPER( PVI_Name, &adapter.name );
_QUERY_MASK_HELPER( PVI_ChipSet, &adapter.chipSet );
_QUERY_MASK_HELPER( PVI_DriverVersion, &adapter.driverVersion );
#undef _QUERY_MASK_HELPER
// Test flags here for success
}
return true;
}
//------------------------------------------------------------------------------
const PlatformVideoInfo::PVIAdapter &PlatformVideoInfo::getAdapterInformation( const U32 adapterIndex ) const
{
AssertFatal( adapterIndex < mAdapters.size(), "Not that many adapters" );
return mAdapters[adapterIndex];
}

View file

@ -0,0 +1,99 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORM_VIDEOINFO_H_
#define _PLATFORM_VIDEOINFO_H_
#include "platform/platform.h"
#include "core/util/str.h"
#include "core/util/tVector.h"
// The purpose of this class is to abstract the gathering of video adapter information.
// This information is not specific to the API being used to do the rendering, or
// the capabilities of that renderer. That information is queried in a different
// class.
class PlatformVideoInfo
{
// # of devices
// description
// manufacturer
// chip set
// driver version
// VRAM
public:
enum PVIQueryType
{
PVI_QueryStart = 0, ///< Start of the enum for looping
// The NumAdapters query is the only non-adapter specific query, the following
// queries are all specific to an adapter.
PVI_NumDevices = 0, ///< Number of sub adapters
PVI_Description, ///< String description of the adapter
PVI_Name, ///< Card name string
PVI_ChipSet, ///< Chipset string
PVI_DriverVersion, ///< Driver version string
PVI_VRAM, ///< Dedicated video memory in megabytes
// Please add query types above this value
PVI_QueryCount, ///< Counter so that this enum can be looped over
PVI_NumAdapters, ///< Number of adapters on the system
};
struct PVIAdapter
{
U32 numDevices;
String description;
String name;
String chipSet;
String driverVersion;
U32 vram;
};
private:
Vector<PVIAdapter> mAdapters; ///< Vector of adapters
/// Signal handling method for GFX signals
// bool processGFXSignal( GFXDevice::GFXDeviceEventType gfxEvent );
protected:
/// This method will be called before any queries are made. All initialization,
/// for example Win32 COM startup, should be done in this method. If the return
/// value is false, no querys will be made.
virtual bool _initialize() = 0;
/// This is the query method which subclasses must implement. The querys made
/// are all found in the PVIQueryType enum. If NULL is specified for outValue,
/// the sub class should simply return true if the query type is supported, and
/// false otherwise.
virtual bool _queryProperty( const PVIQueryType queryType, const U32 adapterId, String *outValue ) = 0;
public:
PlatformVideoInfo();
virtual ~PlatformVideoInfo();
bool profileAdapters();
const PVIAdapter &getAdapterInformation( const U32 adapterIndex ) const;
};
#endif

View file

@ -0,0 +1,91 @@
//-----------------------------------------------------------------------------
// 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/platform.h"
#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
#include <sys/utime.h>
#else
#include <sys/time.h>
#endif
#include "platform/platformVolume.h"
#include "core/util/zip/zipVolume.h"
using namespace Torque;
using namespace Torque::FS;
namespace Platform
{
namespace FS
{
bool MountDefaults()
{
String path = getAssetDir();
bool mounted = Mount( "game", createNativeFS( path ));
if ( !mounted )
return false;
#ifndef TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM
// Note that the VirtualMountSystem must be enabled in volume.cpp for zip support to work.
return MountZips("game");
#else
return true;
#endif
}
bool MountZips(const String &root)
{
Path basePath;
basePath.setRoot(root);
Vector<String> outList;
S32 num = FindByPattern(basePath, "*.zip", true, outList);
if(num == 0)
return true; // not an error
S32 mounted = 0;
for(S32 i = 0;i < outList.size();++i)
{
String &zipfile = outList[i];
mounted += (S32)Mount(root, new ZipFileSystem(zipfile, true));
}
return mounted == outList.size();
}
//-----------------------------------------------------------------------------
bool Touch( const Path &path )
{
#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
return( utime( path.getFullPath(), 0 ) != -1 );
#else
return( utimes( path.getFullPath(), NULL) == 0 ); // utimes returns 0 on success.
#endif
}
} // namespace FS
} // namespace Platform

View file

@ -0,0 +1,55 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORMVOLUME_H_
#define _PLATFORMVOLUME_H_
#include "core/volume.h"
namespace Platform
{
namespace FS
{
using namespace Torque;
using namespace Torque::FS;
FileSystemRef createNativeFS( const String &volume );
String getAssetDir();
/// Mount default OS file systems.
/// On POSIX environment this means mounting a root FileSystem "/", mounting
/// the $HOME environment variable as the "home:/" file system and setting the
/// current working directory to the current OS working directory.
bool InstallFileSystems();
bool MountDefaults();
bool MountZips(const String &root);
bool Touch( const Path &path );
} // Namespace FS
} // Namespace Platform
#endif

View file

@ -0,0 +1,760 @@
//-----------------------------------------------------------------------------
// 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/platform.h"
#if defined(TORQUE_OS_WIN32)
#include<Windows.h> // for SetThreadAffinityMask
#endif
#if defined(TORQUE_OS_MAC)
#include <CoreServices/CoreServices.h> // For high resolution timer
#endif
#include "core/stream/fileStream.h"
#include "core/frameAllocator.h"
#include "core/strings/stringFunctions.h"
#include "core/stringTable.h"
#include "platform/profiler.h"
#include "platform/threads/thread.h"
#include "console/engineAPI.h"
#ifdef TORQUE_ENABLE_PROFILER
ProfilerRootData *ProfilerRootData::sRootList = NULL;
Profiler *gProfiler = NULL;
// Uncomment the following line to enable a debugging aid for mismatched profiler blocks.
//#define TORQUE_PROFILER_DEBUG
// Machinery to record the stack of node names, as a debugging aid to find
// mismatched PROFILE_START and PROFILE_END blocks. We profile from the
// beginning to catch profile block errors that occur when torque is starting up.
#ifdef TORQUE_PROFILER_DEBUG
Vector<StringTableEntry> gProfilerNodeStack;
#define TORQUE_PROFILE_AT_ENGINE_START true
#define PROFILER_DEBUG_PUSH_NODE( nodename ) \
gProfilerNodeStack.push_back( nodename );
#define PROFILER_DEBUG_POP_NODE() \
gProfilerNodeStack.pop_back();
#else
#define TORQUE_PROFILE_AT_ENGINE_START false
#define PROFILER_DEBUG_PUSH_NODE( nodename ) ;
#define PROFILER_DEBUG_POP_NODE() ;
#endif
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
// platform specific get hires times...
void startHighResolutionTimer(U32 time[2])
{
//time[0] = Platform::getRealMilliseconds();
__asm
{
push eax
push edx
push ecx
rdtsc
mov ecx, time
mov DWORD PTR [ecx], eax
mov DWORD PTR [ecx + 4], edx
pop ecx
pop edx
pop eax
}
}
U32 endHighResolutionTimer(U32 time[2])
{
U32 ticks;
//ticks = Platform::getRealMilliseconds() - time[0];
//return ticks;
__asm
{
push eax
push edx
push ecx
//db 0fh, 31h
rdtsc
mov ecx, time
sub edx, DWORD PTR [ecx+4]
sbb eax, DWORD PTR [ecx]
mov DWORD PTR ticks, eax
pop ecx
pop edx
pop eax
}
return ticks;
}
#elif defined(TORQUE_SUPPORTS_GCC_INLINE_X86_ASM)
// platform specific get hires times...
void startHighResolutionTimer(U32 time[2])
{
__asm__ __volatile__(
"rdtsc\n"
: "=a" (time[0]), "=d" (time[1])
);
}
U32 endHighResolutionTimer(U32 time[2])
{
U32 ticks;
__asm__ __volatile__(
"rdtsc\n"
"sub 0x4(%%ecx), %%edx\n"
"sbb (%%ecx), %%eax\n"
: "=a" (ticks) : "c" (time)
);
return ticks;
}
#elif defined(TORQUE_OS_MAC)
void startHighResolutionTimer(U32 time[2]) {
UnsignedWide t;
Microseconds(&t);
time[0] = t.lo;
time[1] = t.hi;
}
U32 endHighResolutionTimer(U32 time[2]) {
UnsignedWide t;
Microseconds(&t);
return t.lo - time[0];
// given that we're returning a 32 bit integer, and this is unsigned subtraction...
// it will just wrap around, we don't need the upper word of the time.
// NOTE: the code assumes that more than 3 hrs will not go by between calls to startHighResolutionTimer() and endHighResolutionTimer().
// I mean... that damn well better not happen anyway.
}
#else
void startHighResolutionTimer(U32 time[2])
{
}
U32 endHighResolutionTimer(U32 time[2])
{
return 1;
}
#endif
Profiler::Profiler()
{
mMaxStackDepth = MaxStackDepth;
mCurrentHash = 0;
mCurrentProfilerData = (ProfilerData *) malloc(sizeof(ProfilerData));
mCurrentProfilerData->mRoot = NULL;
mCurrentProfilerData->mNextForRoot = NULL;
mCurrentProfilerData->mNextProfilerData = NULL;
mCurrentProfilerData->mNextHash = NULL;
mCurrentProfilerData->mParent = NULL;
mCurrentProfilerData->mNextSibling = NULL;
mCurrentProfilerData->mFirstChild = NULL;
mCurrentProfilerData->mLastSeenProfiler = NULL;
mCurrentProfilerData->mHash = 0;
mCurrentProfilerData->mSubDepth = 0;
mCurrentProfilerData->mInvokeCount = 0;
mCurrentProfilerData->mTotalTime = 0;
mCurrentProfilerData->mSubTime = 0;
#ifdef TORQUE_ENABLE_PROFILE_PATH
mCurrentProfilerData->mPath = "";
#endif
mRootProfilerData = mCurrentProfilerData;
for(U32 i = 0; i < ProfilerData::HashTableSize; i++)
mCurrentProfilerData->mChildHash[i] = 0;
mProfileList = NULL;
mEnabled = TORQUE_PROFILE_AT_ENGINE_START;
mNextEnable = TORQUE_PROFILE_AT_ENGINE_START;
mStackDepth = 0;
gProfiler = this;
mDumpToConsole = false;
mDumpToFile = false;
mDumpFileName[0] = '\0';
}
Profiler::~Profiler()
{
reset();
free(mRootProfilerData);
gProfiler = NULL;
}
void Profiler::reset()
{
mEnabled = false; // in case we're in a profiler call.
while(mProfileList)
{
free(mProfileList);
mProfileList = NULL;
}
for(ProfilerRootData *walk = ProfilerRootData::sRootList; walk; walk = walk->mNextRoot)
{
walk->mFirstProfilerData = 0;
walk->mTotalTime = 0;
walk->mSubTime = 0;
walk->mTotalInvokeCount = 0;
}
mCurrentProfilerData = mRootProfilerData;
mCurrentProfilerData->mNextForRoot = 0;
mCurrentProfilerData->mFirstChild = 0;
for(U32 i = 0; i < ProfilerData::HashTableSize; i++)
mCurrentProfilerData->mChildHash[i] = 0;
mCurrentProfilerData->mInvokeCount = 0;
mCurrentProfilerData->mTotalTime = 0;
mCurrentProfilerData->mSubTime = 0;
mCurrentProfilerData->mSubDepth = 0;
mCurrentProfilerData->mLastSeenProfiler = 0;
}
static Profiler aProfiler; // allocate the global profiler
ProfilerRootData::ProfilerRootData(const char *name)
{
for(ProfilerRootData *walk = sRootList; walk; walk = walk->mNextRoot)
if(!dStrcmp(walk->mName, name))
AssertFatal( false, avar( "Duplicate profile name: %s", name ) );
mName = name;
mNameHash = _StringTable::hashString(name);
mNextRoot = sRootList;
sRootList = this;
mTotalTime = 0;
mTotalInvokeCount = 0;
mFirstProfilerData = NULL;
mEnabled = true;
}
void Profiler::validate()
{
for(ProfilerRootData *walk = ProfilerRootData::sRootList; walk; walk = walk->mNextRoot)
{
for(ProfilerData *dp = walk->mFirstProfilerData; dp; dp = dp->mNextForRoot)
{
if(dp->mRoot != walk)
Platform::debugBreak();
// check if it's in the parent's list...
ProfilerData *wk;
for(wk = dp->mParent->mFirstChild; wk; wk = wk->mNextSibling)
if(wk == dp)
break;
if(!wk)
Platform::debugBreak();
for(wk = dp->mParent->mChildHash[walk->mNameHash & (ProfilerData::HashTableSize - 1)] ;
wk; wk = wk->mNextHash)
if(wk == dp)
break;
if(!wk)
Platform::debugBreak();
}
}
}
#ifdef TORQUE_ENABLE_PROFILE_PATH
const char * Profiler::getProfilePath()
{
#ifdef TORQUE_MULTITHREAD
// Ignore non-main-thread profiler activity.
if( !ThreadManager::isMainThread() )
return "[non-main thread]";
#endif
return (mEnabled && mCurrentProfilerData) ? mCurrentProfilerData->mPath : "na";
}
#endif
#ifdef TORQUE_ENABLE_PROFILE_PATH
const char * Profiler::constructProfilePath(ProfilerData * pd)
{
if (pd->mParent)
{
const bool saveEnable = gProfiler->mEnabled;
gProfiler->mEnabled = false;
const char * connector = " -> ";
U32 len = dStrlen(pd->mParent->mPath);
if (!len)
connector = "";
len += dStrlen(connector);
len += dStrlen(pd->mRoot->mName);
U32 mark = FrameAllocator::getWaterMark();
char * buf = (char*)FrameAllocator::alloc(len+1);
dStrcpy(buf,pd->mParent->mPath);
dStrcat(buf,connector);
dStrcat(buf,pd->mRoot->mName);
const char * ret = StringTable->insert(buf);
FrameAllocator::setWaterMark(mark);
gProfiler->mEnabled = saveEnable;
return ret;
}
return "root";
}
#endif
void Profiler::hashPush(ProfilerRootData *root)
{
#ifdef TORQUE_MULTITHREAD
// Ignore non-main-thread profiler activity.
if( !ThreadManager::isMainThread() )
return;
#endif
mStackDepth++;
PROFILER_DEBUG_PUSH_NODE(root->mName);
AssertFatal(mStackDepth <= mMaxStackDepth,
"Stack overflow in profiler. You may have mismatched PROFILE_START and PROFILE_ENDs");
if(!mEnabled)
return;
ProfilerData *nextProfiler = NULL;
if(!root->mEnabled || mCurrentProfilerData->mRoot == root)
{
mCurrentProfilerData->mSubDepth++;
return;
}
if(mCurrentProfilerData->mLastSeenProfiler &&
mCurrentProfilerData->mLastSeenProfiler->mRoot == root)
nextProfiler = mCurrentProfilerData->mLastSeenProfiler;
if(!nextProfiler)
{
// first see if it's in the hash table...
U32 index = root->mNameHash & (ProfilerData::HashTableSize - 1);
nextProfiler = mCurrentProfilerData->mChildHash[index];
while(nextProfiler)
{
if(nextProfiler->mRoot == root)
break;
nextProfiler = nextProfiler->mNextHash;
}
if(!nextProfiler)
{
nextProfiler = (ProfilerData *) malloc(sizeof(ProfilerData));
for(U32 i = 0; i < ProfilerData::HashTableSize; i++)
nextProfiler->mChildHash[i] = 0;
nextProfiler->mRoot = root;
nextProfiler->mNextForRoot = root->mFirstProfilerData;
root->mFirstProfilerData = nextProfiler;
nextProfiler->mNextProfilerData = mProfileList;
mProfileList = nextProfiler;
nextProfiler->mNextHash = mCurrentProfilerData->mChildHash[index];
mCurrentProfilerData->mChildHash[index] = nextProfiler;
nextProfiler->mParent = mCurrentProfilerData;
nextProfiler->mNextSibling = mCurrentProfilerData->mFirstChild;
mCurrentProfilerData->mFirstChild = nextProfiler;
nextProfiler->mFirstChild = NULL;
nextProfiler->mLastSeenProfiler = NULL;
nextProfiler->mHash = root->mNameHash;
nextProfiler->mInvokeCount = 0;
nextProfiler->mTotalTime = 0;
nextProfiler->mSubTime = 0;
nextProfiler->mSubDepth = 0;
#ifdef TORQUE_ENABLE_PROFILE_PATH
nextProfiler->mPath = constructProfilePath(nextProfiler);
#endif
}
}
root->mTotalInvokeCount++;
nextProfiler->mInvokeCount++;
startHighResolutionTimer(nextProfiler->mStartTime);
mCurrentProfilerData->mLastSeenProfiler = nextProfiler;
mCurrentProfilerData = nextProfiler;
}
void Profiler::enable(bool enabled)
{
mNextEnable = enabled;
}
void Profiler::dumpToConsole()
{
mDumpToConsole = true;
mDumpToFile = false;
mDumpFileName[0] = '\0';
}
void Profiler::dumpToFile(const char* fileName)
{
AssertFatal(dStrlen(fileName) < DumpFileNameLength, "Error, dump filename too long");
mDumpToFile = true;
mDumpToConsole = false;
dStrcpy(mDumpFileName, fileName);
}
void Profiler::hashPop(ProfilerRootData *expected)
{
#ifdef TORQUE_MULTITHREAD
// Ignore non-main-thread profiler activity.
if( !ThreadManager::isMainThread() )
return;
#endif
mStackDepth--;
PROFILER_DEBUG_POP_NODE();
AssertFatal(mStackDepth >= 0, "Stack underflow in profiler. You may have mismatched PROFILE_START and PROFILE_ENDs");
if(mEnabled)
{
if(mCurrentProfilerData->mSubDepth)
{
mCurrentProfilerData->mSubDepth--;
return;
}
if(expected)
{
AssertISV(expected == mCurrentProfilerData->mRoot, "Profiler::hashPop - didn't get expected ProfilerRoot!");
}
F64 fElapsed = endHighResolutionTimer(mCurrentProfilerData->mStartTime);
mCurrentProfilerData->mTotalTime += fElapsed;
mCurrentProfilerData->mParent->mSubTime += fElapsed; // mark it in the parent as well...
mCurrentProfilerData->mRoot->mTotalTime += fElapsed;
if(mCurrentProfilerData->mParent->mRoot)
mCurrentProfilerData->mParent->mRoot->mSubTime += fElapsed; // mark it in the parent as well...
mCurrentProfilerData = mCurrentProfilerData->mParent;
}
if(mStackDepth == 0)
{
// apply the next enable...
if(mDumpToConsole || mDumpToFile)
{
dump();
startHighResolutionTimer(mCurrentProfilerData->mStartTime);
}
if(!mEnabled && mNextEnable)
startHighResolutionTimer(mCurrentProfilerData->mStartTime);
#if defined(TORQUE_OS_WIN32)
// The high performance counters under win32 are unreliable when running on multiple
// processors. When the profiler is enabled, we restrict Torque to a single processor.
if(mNextEnable != mEnabled)
{
if(mNextEnable)
{
Con::warnf("Warning: forcing the Torque profiler thread to run only on cpu 1.");
SetThreadAffinityMask(GetCurrentThread(), 1);
}
else
{
Con::warnf("Warning: the Torque profiler thread may now run on any cpu.");
DWORD procMask;
DWORD sysMask;
GetProcessAffinityMask( GetCurrentProcess(), &procMask, &sysMask);
SetThreadAffinityMask( GetCurrentThread(), procMask);
}
}
#endif
mEnabled = mNextEnable;
}
}
static S32 QSORT_CALLBACK rootDataCompare(const void *s1, const void *s2)
{
const ProfilerRootData *r1 = *((ProfilerRootData **) s1);
const ProfilerRootData *r2 = *((ProfilerRootData **) s2);
if((r2->mTotalTime - r2->mSubTime) > (r1->mTotalTime - r1->mSubTime))
return 1;
return -1;
}
static void profilerDataDumpRecurse(ProfilerData *data, char *buffer, U32 bufferLen, F64 totalTime)
{
// dump out this one:
Con::printf("%7.3f %7.3f %8d %s%s",
100 * data->mTotalTime / totalTime,
100 * (data->mTotalTime - data->mSubTime) / totalTime,
data->mInvokeCount,
buffer,
data->mRoot ? data->mRoot->mName : "ROOT" );
data->mTotalTime = 0;
data->mSubTime = 0;
data->mInvokeCount = 0;
buffer[bufferLen] = ' ';
buffer[bufferLen+1] = ' ';
buffer[bufferLen+2] = 0;
// sort data's children...
ProfilerData *list = NULL;
while(data->mFirstChild)
{
ProfilerData *ins = data->mFirstChild;
data->mFirstChild = ins->mNextSibling;
ProfilerData **walk = &list;
while(*walk && (*walk)->mTotalTime > ins->mTotalTime)
walk = &(*walk)->mNextSibling;
ins->mNextSibling = *walk;
*walk = ins;
}
data->mFirstChild = list;
while(list)
{
if(list->mInvokeCount)
profilerDataDumpRecurse(list, buffer, bufferLen + 2, totalTime);
list = list->mNextSibling;
}
buffer[bufferLen] = 0;
}
static void profilerDataDumpRecurseFile(ProfilerData *data, char *buffer, U32 bufferLen, F64 totalTime, FileStream& fws)
{
char pbuffer[256];
dSprintf(pbuffer, 255, "%7.3f %7.3f %8d %s%s\n",
100 * data->mTotalTime / totalTime,
100 * (data->mTotalTime - data->mSubTime) / totalTime,
data->mInvokeCount,
buffer,
data->mRoot ? data->mRoot->mName : "ROOT" );
fws.write(dStrlen(pbuffer), pbuffer);
data->mTotalTime = 0;
data->mSubTime = 0;
data->mInvokeCount = 0;
buffer[bufferLen] = ' ';
buffer[bufferLen+1] = ' ';
buffer[bufferLen+2] = 0;
// sort data's children...
ProfilerData *list = NULL;
while(data->mFirstChild)
{
ProfilerData *ins = data->mFirstChild;
data->mFirstChild = ins->mNextSibling;
ProfilerData **walk = &list;
while(*walk && (*walk)->mTotalTime > ins->mTotalTime)
walk = &(*walk)->mNextSibling;
ins->mNextSibling = *walk;
*walk = ins;
}
data->mFirstChild = list;
while(list)
{
if(list->mInvokeCount)
profilerDataDumpRecurseFile(list, buffer, bufferLen + 2, totalTime, fws);
list = list->mNextSibling;
}
buffer[bufferLen] = 0;
}
void Profiler::dump()
{
bool enableSave = mEnabled;
mEnabled = false;
mStackDepth++;
// may have some profiled calls... gotta turn em off.
Vector<ProfilerRootData *> rootVector;
F64 totalTime = 0;
for(ProfilerRootData *walk = ProfilerRootData::sRootList; walk; walk = walk->mNextRoot)
{
totalTime += walk->mTotalTime - walk->mSubTime;
rootVector.push_back(walk);
}
dQsort((void *) &rootVector[0], rootVector.size(), sizeof(ProfilerRootData *), rootDataCompare);
if (mDumpToConsole == true)
{
Con::printf("Profiler Data Dump:");
Con::printf("Ordered by non-sub total time -");
Con::printf("%%NSTime %% Time Invoke # Name");
for(U32 i = 0; i < rootVector.size(); i++)
{
Con::printf("%7.3f %7.3f %8d %s",
100 * (rootVector[i]->mTotalTime - rootVector[i]->mSubTime) / totalTime,
100 * rootVector[i]->mTotalTime / totalTime,
rootVector[i]->mTotalInvokeCount,
rootVector[i]->mName);
rootVector[i]->mTotalInvokeCount = 0;
rootVector[i]->mTotalTime = 0;
rootVector[i]->mSubTime = 0;
}
Con::printf("");
Con::printf("Ordered by stack trace total time -");
Con::printf("%% Time %% NSTime Invoke # Name");
mCurrentProfilerData->mTotalTime = endHighResolutionTimer(mCurrentProfilerData->mStartTime);
char depthBuffer[MaxStackDepth * 2 + 1];
depthBuffer[0] = 0;
profilerDataDumpRecurse(mCurrentProfilerData, depthBuffer, 0, totalTime);
mEnabled = enableSave;
mStackDepth--;
}
else if (mDumpToFile == true && mDumpFileName[0] != '\0')
{
FileStream fws;
bool success = fws.open(mDumpFileName, Torque::FS::File::Write);
AssertFatal(success, "Cannot write profile dump to specified file!");
char buffer[1024];
dStrcpy(buffer, "Profiler Data Dump:\n");
fws.write(dStrlen(buffer), buffer);
dStrcpy(buffer, "Ordered by non-sub total time -\n");
fws.write(dStrlen(buffer), buffer);
dStrcpy(buffer, "%%NSTime %% Time Invoke # Name\n");
fws.write(dStrlen(buffer), buffer);
for(U32 i = 0; i < rootVector.size(); i++)
{
dSprintf(buffer, 1023, "%7.3f %7.3f %8d %s\n",
100 * (rootVector[i]->mTotalTime - rootVector[i]->mSubTime) / totalTime,
100 * rootVector[i]->mTotalTime / totalTime,
rootVector[i]->mTotalInvokeCount,
rootVector[i]->mName);
fws.write(dStrlen(buffer), buffer);
rootVector[i]->mTotalInvokeCount = 0;
rootVector[i]->mTotalTime = 0;
rootVector[i]->mSubTime = 0;
}
dStrcpy(buffer, "\nOrdered by non-sub total time -\n");
fws.write(dStrlen(buffer), buffer);
dStrcpy(buffer, "%%NSTime %% Time Invoke # Name\n");
fws.write(dStrlen(buffer), buffer);
mCurrentProfilerData->mTotalTime = endHighResolutionTimer(mCurrentProfilerData->mStartTime);
char depthBuffer[MaxStackDepth * 2 + 1];
depthBuffer[0] = 0;
profilerDataDumpRecurseFile(mCurrentProfilerData, depthBuffer, 0, totalTime, fws);
mEnabled = enableSave;
mStackDepth--;
fws.close();
}
mDumpToConsole = false;
mDumpToFile = false;
mDumpFileName[0] = '\0';
}
void Profiler::enableMarker(const char *marker, bool enable)
{
reset();
U32 markerLen = dStrlen(marker);
if(markerLen == 0)
return;
bool sn = marker[markerLen - 1] == '*';
for(ProfilerRootData *data = ProfilerRootData::sRootList; data; data = data->mNextRoot)
{
if(sn)
{
if(!dStrncmp(marker, data->mName, markerLen - 1))
data->mEnabled = enable;
}
else
{
if(!dStrcmp(marker, data->mName))
data->mEnabled = enable;
}
}
}
//=============================================================================
// Console Functions.
//=============================================================================
// MARK: ---- Console Functions ----
//-----------------------------------------------------------------------------
DefineEngineFunction( profilerMarkerEnable, void, ( const char* markerName, bool enable ), ( true ),
"@brief Enable or disable a specific profile.\n\n"
"@param enable Optional paramater to enable or disable the profile.\n"
"@param markerName Name of a specific marker to enable or disable.\n"
"@note Calling this function will first call profilerReset(), clearing all data from profiler. "
"All profile markers are enabled by default.\n\n"
"@ingroup Debugging")
{
if( gProfiler )
gProfiler->enableMarker( markerName, enable );
}
//-----------------------------------------------------------------------------
DefineEngineFunction( profilerEnable, void, ( bool enable ),,
"@brief Enables or disables the profiler.\n\n"
"Data is only gathered while the profiler is enabled.\n\n"
"@note Profiler is not available in shipping builds.\n"
"T3D has predefined profiling areas surrounded by markers, "
"but you may need to define additional markers (in C++) around areas you wish to profile,"
" by using the PROFILE_START( markerName ); and PROFILE_END(); macros.\n\n"
"@ingroup Debugging\n" )
{
if(gProfiler)
gProfiler->enable(enable);
}
DefineEngineFunction(profilerDump, void, (),,
"@brief Dumps current profiling stats to the console window.\n\n"
"@note Markers disabled with profilerMarkerEnable() will be skipped over. "
"If the profiler is currently running, it will be disabled.\n"
"@ingroup Debugging")
{
if(gProfiler)
gProfiler->dumpToConsole();
}
DefineEngineFunction( profilerDumpToFile, void, ( const char* fileName ),,
"@brief Dumps current profiling stats to a file.\n\n"
"@note If the profiler is currently running, it will be disabled.\n"
"@param fileName Name and path of file to save profiling stats to. Must use forward slashes (/). "
"Will attempt to create the file if it does not already exist.\n"
"@tsexample\n"
"profilerDumpToFile( \"C:/Torque/log1.txt\" );\n"
"@endtsexample\n\n"
"@ingroup Debugging" )
{
if(gProfiler)
gProfiler->dumpToFile(fileName);
}
DefineEngineFunction( profilerReset, void, (),,
"@brief Resets the profiler, clearing it of all its data.\n\n"
"If the profiler is currently running, it will first be disabled. "
"All markers will retain their current enabled/disabled status.\n\n"
"@ingroup Debugging" )
{
if(gProfiler)
gProfiler->reset();
}
#endif

View file

@ -0,0 +1,194 @@
//-----------------------------------------------------------------------------
// 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 _PROFILER_H_
#define _PROFILER_H_
#ifndef _TORQUECONFIG_H_
#include "torqueConfig.h"
#endif
#ifdef TORQUE_ENABLE_PROFILER
struct ProfilerData;
struct ProfilerRootData;
/// The Profiler is used to see how long a specific chunk of code takes to execute.
/// All values outputted by the profiler are percentages of the time that it takes
/// to run entire main loop.
///
/// First, you must #define TORQUE_ENABLE_PROFILER in profiler.h in order to
/// active it. Examples of script use:
/// @code
/// //enables or disables profiling. Data is only gathered when the profiler is enabled.
/// profilerEnable(bool enable);
/// profilerReset(); //resets the data gathered by the profiler
/// profilerDump(); //dumps all profiler data to the console
/// profilerDumpToFile(string filename); //dumps all profiler data to a given file
/// profilerMarkerEnable((string markerName, bool enable); //enables or disables a given profile tag
/// @endcode
///
/// The C++ code side of the profiler uses pairs of PROFILE_START() and PROFILE_END().
///
/// When using these macros, make sure there is a PROFILE_END() for every PROFILE_START
/// and a PROFILE_START() for every PROFILE_END(). It is fine to nest these macros, however,
/// you must make sure that no matter what execution path the code takes, the PROFILE macros
/// will be balanced.
///
/// The profiler can be used to locate areas of code that are slow or should be considered for
/// optimization. Since it tracks the relative time of execution of that code to the execution
/// of the main loop, it is possible to benchmark any given code to see if changes made will
/// actually improve performance.
///
/// Here are some examples:
/// @code
/// PROFILE_START(TerrainRender);
/// //some code here
/// PROFILE_START(TerrainRenderGridSquare);
/// //some code here
/// PROFILE_END();
/// //possibly some code here
/// PROFILE_END();
/// @endcode
class Profiler
{
enum {
MaxStackDepth = 256,
DumpFileNameLength = 256
};
U32 mCurrentHash;
ProfilerData *mCurrentProfilerData;
ProfilerData *mProfileList;
ProfilerData *mRootProfilerData;
bool mEnabled;
S32 mStackDepth;
bool mNextEnable;
U32 mMaxStackDepth;
bool mDumpToConsole;
bool mDumpToFile;
char mDumpFileName[DumpFileNameLength];
void dump();
void validate();
public:
Profiler();
~Profiler();
/// Reset the data in the profiler
void reset();
/// Dumps the profile to console
void dumpToConsole();
/// Dumps the profile data to a file
/// @param fileName filename to dump data to
void dumpToFile(const char *fileName);
/// Enable profiling
void enable(bool enabled);
bool isEnabled() { return mNextEnable; }
/// Helper function for macro definition PROFILE_START
void hashPush(ProfilerRootData *data);
/// Helper function for macro definition PROFILE_END
void hashPop(ProfilerRootData *expected=NULL);
/// Enable a profiler marker
void enableMarker(const char *marker, bool enabled);
#ifdef TORQUE_ENABLE_PROFILE_PATH
/// Get current profile path
const char * getProfilePath();
/// Construct profile path of given profiler data
const char * constructProfilePath(ProfilerData * pd);
#endif
};
extern Profiler *gProfiler;
struct ProfilerRootData
{
const char *mName;
U32 mNameHash;
ProfilerData *mFirstProfilerData;
ProfilerRootData *mNextRoot;
F64 mTotalTime;
F64 mSubTime;
U32 mTotalInvokeCount;
bool mEnabled;
static ProfilerRootData *sRootList;
ProfilerRootData(const char *name);
};
struct ProfilerData
{
ProfilerRootData *mRoot; ///< link to root node.
ProfilerData *mNextForRoot; ///< links together all ProfilerData's for a particular root
ProfilerData *mNextProfilerData; ///< links all the profilerDatas
ProfilerData *mNextHash;
ProfilerData *mParent;
ProfilerData *mNextSibling;
ProfilerData *mFirstChild;
enum {
HashTableSize = 32,
};
ProfilerData *mChildHash[HashTableSize];
ProfilerData *mLastSeenProfiler;
U32 mHash;
U32 mSubDepth;
U32 mInvokeCount;
U32 mStartTime[2];
F64 mTotalTime;
F64 mSubTime;
#ifdef TORQUE_ENABLE_PROFILE_PATH
const char * mPath;
#endif
};
#define PROFILE_START(name) \
static ProfilerRootData pdata##name##obj (#name); \
if(gProfiler) gProfiler->hashPush(& pdata##name##obj )
#define PROFILE_END() if(gProfiler) gProfiler->hashPop()
#define PROFILE_END_NAMED(name) if(gProfiler) gProfiler->hashPop(& pdata##name##obj)
class ScopedProfiler {
public:
ScopedProfiler(ProfilerRootData *data) {
if (gProfiler) gProfiler->hashPush(data);
}
~ScopedProfiler() {
if (gProfiler) gProfiler->hashPop();
}
};
#define PROFILE_SCOPE(name) \
static ProfilerRootData pdata##name##obj (#name); \
ScopedProfiler scopedProfiler##name##obj(&pdata##name##obj);
#else
#define PROFILE_START(x)
#define PROFILE_END()
#define PROFILE_SCOPE(x)
#define PROFILE_END_NAMED(x)
#endif
#endif

View file

@ -0,0 +1,50 @@
//-----------------------------------------------------------------------------
// 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/platform.h"
#include "unit/test.h"
using namespace UnitTesting;
CreateInteractiveTest(CheckPlatformAlerts, "Platform/Alerts")
{
void run()
{
// Run through all the alert types.
Platform::AlertOK("Test #1 - AlertOK", "This is a test of Platform::AlertOK. I am a blocking dialog with an OK button. Please hit OK to continue.");
test(true, "AlertOK should return when the user clicks on it..."); // <-- gratuitous test point.
bool res;
res = Platform::AlertOKCancel("Test #2 - AlertOKCancel", "This is a test of Platform::alertOKCancel. I am a blocking dialog with an OK and a Cancel button. Please hit Cancel to continue.");
test(res==false,"AlertOKCancel - Didn't get cancel. User error, or just bad code?");
res = Platform::AlertOKCancel("Test #3 - AlertOKCancel", "This is a test of Platform::alertOKCancel. I am a blocking dialog with an OK and a Cancel button. Please hit OK to continue.");
test(res==true,"AlertOKCancel - Didn't get ok. User error, or just bad code?");
res = Platform::AlertRetry("Test #4 - AlertRetry", "This is a test of Platform::AlertRetry. I am a blocking dialog with an Retry and a Cancel button. Please hit Retry to continue.");
test(res==true,"AlertRetry - Didn't get retry. User error, or just bad code?");
res = Platform::AlertRetry("Test #5 - AlertRetry", "This is a test of Platform::AlertRetry. I am a blocking dialog with an Retry and a Cancel button. Please hit Cancel to continue.");
test(res==false,"AlertRetry - Didn't get cancel. User error, or just bad code?");
}
};

View file

@ -0,0 +1,151 @@
//-----------------------------------------------------------------------------
// 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 "unit/test.h"
#include "platform/async/asyncPacketQueue.h"
#include "console/console.h"
#include "core/util/tVector.h"
#ifndef TORQUE_SHIPPING
using namespace UnitTesting;
#define TEST( x ) test( ( x ), "FAIL: " #x )
CreateUnitTest( TestAsyncPacketQueue, "Platform/AsyncPacketQueue" )
{
struct Packet
{
typedef void Parent;
StringChar mChar;
U32 mDuration;
S32 mWriteIndex;
U32 mWriteTime;
Packet() {}
Packet( StringChar ch, U32 duration )
: mChar( ch ), mDuration( duration ), mWriteIndex( -2 ), mWriteTime( 0 ) {}
};
struct TimeSource
{
typedef void Parent;
U32 mStartTime;
TimeSource()
: mStartTime( Platform::getRealMilliseconds() ) {}
U32 getPosition()
{
return ( Platform::getRealMilliseconds() - mStartTime );
}
};
struct Consumer : public IOutputStream< Packet* >
{
typedef IOutputStream< Packet* > Parent;
U32 mIndex;
Consumer()
: mIndex( 0 ) {}
virtual void write( Packet* const* packets, U32 num )
{
for( U32 i = 0; i < num; ++ i )
{
Packet* p = packets[ i ];
Con::printf( "%c", p->mChar );
p->mWriteTime = Platform::getRealMilliseconds();
p->mWriteIndex = mIndex;
mIndex ++;
}
}
};
void test1( bool dropPackets, U32 queueLength )
{
F32 factor = Con::getFloatVariable( "$testAsyncPacketQueue::timeFactor", 100.0f );
String str = Con::getVariable( "$testAsyncPacketQueue::string" );
if( str.isEmpty() )
str = "This is a test string";
Vector< Packet > packets;
for( U32 i = 0; i < str.size(); ++ i )
packets.push_back( Packet( str[ i ], U32( Platform::getRandom() * factor ) ) );
U32 totalTime = 0;
for( U32 i = 0; i < packets.size(); ++ i )
totalTime += packets[ i ].mDuration;
TimeSource timeSource;
Consumer consumer;
AsyncPacketQueue< Packet*, TimeSource* > queue( queueLength, &timeSource, &consumer, totalTime, dropPackets );
U32 index = 0;
while( !queue.isAtEnd() )
{
if( queue.needPacket()
&& index < packets.size() )
{
Packet* packet = &packets[ index ];
index ++;
queue.submitPacket( packet, packet->mDuration );
}
}
U32 time = timeSource.mStartTime;
S32 lastIndex = -1;
for( U32 i = 0; i < packets.size(); ++ i )
{
TEST( ( packets[ i ].mWriteIndex == -2 && dropPackets ) // not written = dropped
|| packets[ i ].mWriteIndex == lastIndex + 1 );
if( packets[ i ].mWriteIndex != -2 )
lastIndex ++;
if( queueLength == 1 )
TEST( packets[ i ].mWriteTime >= time || dropPackets ); // start time okay
time += packets[ i ].mDuration;
if( dropPackets )
TEST( packets[ i ].mWriteTime < time ); // end time okay (if not dropping)
}
}
void run()
{
test1( false, 1 );
test1( true, 1 );
test1( false, 4 );
test1( true, 4 );
}
};
#endif // !TORQUE_SHIPPING

View file

@ -0,0 +1,124 @@
//-----------------------------------------------------------------------------
// 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/platform.h"
#include "core/util/endian.h"
#include "unit/test.h"
using namespace UnitTesting;
CreateUnitTest(CheckTypeSizes, "Platform/Types/Sizes")
{
void run()
{
// Run through all the types and ensure they're the right size.
#define CheckType(typeName, expectedSize) \
test( sizeof(typeName) == expectedSize, "Wrong size for a " #typeName ", expected " #expectedSize);
// One byte types.
CheckType(bool, 1);
CheckType(U8, 1);
CheckType(S8, 1);
CheckType(UTF8, 1);
// Two byte types.
CheckType(U16, 2);
CheckType(S16, 2);
CheckType(UTF16, 2);
// Four byte types.
CheckType(U32, 4);
CheckType(S32, 4);
CheckType(F32, 4);
CheckType(UTF32, 4);
// Eight byte types.
CheckType(U64, 8);
CheckType(S64, 8);
CheckType(F64, 8);
// 16 byte (128bit) types will go here, when we get some.
#undef CheckType
}
};
CreateUnitTest(CheckEndianConversion, "Platform/Types/EndianRoundTrip")
{
void run()
{
// Convenient and non-palindrome byte patterns to test with.
const U16 U16Test = 0xA1B2;
const S16 S16Test = 0x52A1;
const U32 U32Test = 0xA1B2C3D4;
const S32 S32Test = 0xD4C3B2A1;
const F32 F32Test = 1234.5678f;
//const U64 U64Test = 0xA1B2C3D4E3F2E10A;
//const S64 S64Test = 0x1A2B3C4D3E2F1EA0;
const F64 F64Test = 12345678.9101112131415;
// Run through all the conversions - bump stuff from host to little or big
// endian and back again.
#define CheckEndianRoundTrip(type, b_or_l) \
test( type##Test == convert##b_or_l##EndianToHost(convertHostTo##b_or_l##Endian(type##Test)), "Failed to convert the " #type " test value to " #b_or_l " endian and back to host endian order.");
#define CheckTypeBothWays(type) \
CheckEndianRoundTrip(type, B); \
CheckEndianRoundTrip(type, L);
#define CheckIntsForBitSize(bits) \
CheckTypeBothWays( U##bits ); \
CheckTypeBothWays( S##bits );
// Don't check 8-bit types - they aren't affected by endian issues.
// Check the >1 byte int types, though.
CheckIntsForBitSize(16);
CheckIntsForBitSize(32);
// CheckIntsForBitSize(64); // don't have convertHostToLEndian(const U64/S64) so this doesn't work
// And check the float types.
CheckTypeBothWays(F32);
CheckTypeBothWays(F64);
// We'd check 128bit types here, if we had any.
#undef CheckIntsForBitSize
#undef CheckTypeBothWays
#undef CheckEndianRoundTrip
}
};
CreateUnitTest(CheckEndianSwap, "Platform/Types/EndianSwap")
{
void run()
{
U32 swap32 = 0xABCDEF12;
U16 swap16 = 0xABCD;
test(endianSwap(swap32) == 0x12EFCDAB, "32 bit endianSwap should reverse byte order, but didn't.");
test(endianSwap(swap16) == 0xCDAB, "16 bit endianSwap should reverse byte order, but didn't.");
}
};

View file

@ -0,0 +1,150 @@
//-----------------------------------------------------------------------------
// 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/platform.h"
#include "core/fileio.h"
#include "unit/test.h"
#include "core/util/tVector.h"
#include "console/console.h"
using namespace UnitTesting;
CreateUnitTest(CheckFileListingAndExclusion, "File/ListDirectoryAndExclusions")
{
void run()
{
// Just dump everything under the current directory. We should
// find at least one file.
// Exclude .svn and CVS
Platform::clearExcludedDirectories();
Platform::addExcludedDirectory(".svn");
Platform::addExcludedDirectory("CVS");
test(Platform::isExcludedDirectory("foo") == false, "Doesn't match list, shouldn't be excluded.");
test(Platform::isExcludedDirectory(".svn") == true, "On list, should be excluded.");
test(Platform::isExcludedDirectory("CVS") == true, "On list, should be excluded.");
test(Platform::isExcludedDirectory(".svnCVS") == false, "Looks like a duck, but it shouldn't be excluded cuz it's distinct from all entries on the exclusion list.");
// Ok, now our exclusion list is setup, so let's dump some paths.
Vector < Platform::FileInfo > pathInfo;
Platform::dumpPath (Platform::getCurrentDirectory(), pathInfo, 2);
Con::printf("Dump of files in '%s', up to 2 levels deep...", Platform::getCurrentDirectory());
for(S32 i=0; i<pathInfo.size(); i++)
{
Platform::FileInfo &file = pathInfo[i];
Con::printf(" %s (%s) %d bytes", file.pFullPath, file.pFileName, file.fileSize);
}
test(pathInfo.size() > 0, "Should find at least SOMETHING in the current directory!");
// This'll nuke info if we run it in a live situation... so don't run unit
// tests in a live situation. ;)
Platform::clearExcludedDirectories();
}
};
CreateUnitTest(CheckFileTouchAndTime, "File/TouchAndTime")
{
void run()
{
FileTime create[2], modify[2];
// Create a file and sleep for a second.
File f;
f.open("testTouch.file", File::WriteAppend);
f.close();
Platform::sleep(2000);
// Touch a file and note its last-modified.
dFileTouch("testTouch.file");
test(Platform::isFile("testTouch.file"), "We just touched this file - it should exist.");
test(Platform::getFileTimes("testTouch.file", &create[0], &modify[0]), "Failed to get filetimes for a file we just created.");
// Sleep for a few seconds...
Platform::sleep(5000);
// Touch it again, and compare the last-modifieds.
test(Platform::isFile("testTouch.file"), "We just touched this file - it should exist.");
dFileTouch("testTouch.file");
test(Platform::isFile("testTouch.file"), "We just touched this file - it should exist.");
test(Platform::getFileTimes("testTouch.file", &create[1], &modify[1]), "Failed to get filetimes for a file we just created.");
// Now compare the times...
test(Platform::compareFileTimes(modify[0], modify[1]) < 0, "Timestamps are wrong - modify[0] should be before modify[1]!");
// This seems to fail even on a valid case...
// test(Platform::compareFileTimes(create[0], create[1]) == 0, "Create timestamps should match - we didn't delete the file during this test.");
// Clean up..
dFileDelete("testTouch.file");
test(!Platform::isFile("testTouch.file"), "Somehow failed to delete our test file.");
}
};
// Mac has no implementations for these functions, so we 'def it out for now.
#if 0
CreateUnitTest(CheckVolumes, "File/Volumes")
{
void run()
{
Con::printf("Dumping volumes by name:");
Vector<const char*> names;
Platform::getVolumeNamesList(names);
test(names.size() > 0, "We should have at least one volume...");
for(S32 i=0; i<names.size(); i++)
Con::printf(" %s", names[i]);
Con::printf("Dumping volume info:");
Vector<Platform::VolumeInformation> info;
Platform::getVolumeInformationList(info);
test(names.size() == info.size(), "Got inconsistent number of volumes back from info vs. name list functions!");
for(S32 i=0; i<info.size(); i++)
Con::printf(" %s rootPath = %s filesystem = %s ser. num. = %d type = %d readonly = %s",
info[i].Name,
info[i].RootPath,
info[i].FileSystem,
info[i].SerialNumber,
info[i].Type,
info[i].ReadOnly ? "true" : "false");
}
};
#endif
CreateUnitTest(CheckFileWriteAndRead, "File/ReadAndWrite")
{
void run()
{
// Open a file, write some junk to it, close it,
// check size is correct, and open it again.
}
};

View file

@ -0,0 +1,194 @@
//-----------------------------------------------------------------------------
// 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/platformNet.h"
#include "unit/test.h"
#include "core/util/journal/process.h"
using namespace UnitTesting;
CreateUnitTest( TestTCPRequest, "Platform/Net/TCPRequest")
{
NetSocket mSocket;
S32 mDataRecved;
void handleNotify(NetSocket sock, U32 state)
{
// Only consider our own socket.
if(mSocket != sock)
return;
// Ok - what's the state? We do some dumb responses to given states
// in order to fulfill the request.
if(state == Net::Connected)
{
U8 reqBuffer[] = {
"GET / HTTP/1.0\nUser-Agent: Torque/1.0\n\n"
};
Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer));
test(e == Net::NoError, "Got an error sending our HTTP request!");
}
else if(state == Net::Disconnected)
{
Process::requestShutdown();
mSocket = NULL;
}
}
void handleReceive(NetSocket sock, RawData incomingData)
{
// Only consider our own socket.
if(mSocket != sock)
return;
char buff[4096];
dMemcpy(buff, incomingData.data, incomingData.size);
buff[incomingData.size] = 0;
UnitPrint("Got a message...\n");
UnitPrint(buff);
UnitPrint("------\n");
mDataRecved += incomingData.size;
}
void run()
{
mSocket = InvalidSocket;
mDataRecved = 0;
// Initialize networking - done by initLibraries currently
//test(Net::init(), "Failed to initialize networking!");
// Hook into the signals.
Net::smConnectionNotify. notify(this, &TestTCPRequest::handleNotify);
Net::smConnectionReceive.notify(this, &TestTCPRequest::handleReceive);
// Open a TCP connection to garagegames.com
mSocket = Net::openConnectTo("ip:72.246.107.193:80");
while(Process::processEvents())
;
// Unhook from the signals.
Net::smConnectionNotify. remove(this, &TestTCPRequest::handleNotify);
Net::smConnectionReceive.remove(this, &TestTCPRequest::handleReceive);
test(mDataRecved > 0, "Didn't get any data back!");
}
};
CreateUnitTest( TestTCPRequestJournal, "Platform/Net/JournalTCPRequest")
{
NetSocket mSocket;
S32 mDataRecved;
void handleNotify(NetSocket sock, U32 state)
{
// Only consider our own socket.
if(mSocket != sock)
return;
// Ok - what's the state? We do some dumb responses to given states
// in order to fulfill the request.
if(state == Net::Connected)
{
U8 reqBuffer[] = {
"GET / HTTP/1.0\nUser-Agent: Torque/1.0\n\n"
};
Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer));
test(e == Net::NoError, "Got an error sending our HTTP request!");
}
else if(state == Net::Disconnected)
{
Process::requestShutdown();
mSocket = NULL;
}
}
void handleReceive(NetSocket sock, RawData incomingData)
{
// Only consider our own socket.
if(mSocket != sock)
return;
char buff[4096];
dMemcpy(buff, incomingData.data, incomingData.size);
buff[incomingData.size] = 0;
UnitPrint("Got a message...\n");
UnitPrint(buff);
UnitPrint("------\n");
mDataRecved += incomingData.size;
}
void makeRequest()
{
mSocket = InvalidSocket;
mDataRecved = 0;
// Initialize networking - done by initLibraries currently
//test(Net::init(), "Failed to initialize networking!");
// Hook into the signals.
Net::smConnectionNotify. notify(this, &TestTCPRequestJournal::handleNotify);
Net::smConnectionReceive.notify(this, &TestTCPRequestJournal::handleReceive);
// Open a TCP connection to garagegames.com
mSocket = Net::openConnectTo("ip:72.246.107.193:80");
// Let the callbacks enable things to process.
while(Process::processEvents())
;
// Unhook from the signals.
Net::smConnectionNotify. remove(this, &TestTCPRequestJournal::handleNotify);
Net::smConnectionReceive.remove(this, &TestTCPRequestJournal::handleReceive);
test(mDataRecved > 0, "Didn't get any data back!");
}
void run()
{
Journal::Record("journalTCP.jrn");
makeRequest();
S32 bytesRead = mDataRecved;
Journal::Stop();
Journal::Play("journalTCP.jrn");
makeRequest();
Journal::Stop();
test(bytesRead == mDataRecved, "Didn't get same data back from journal playback.");
}
};

View file

@ -0,0 +1,85 @@
//-----------------------------------------------------------------------------
// 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 "unit/test.h"
#include "platform/threads/threadPool.h"
#include "console/console.h"
#include "core/util/tVector.h"
#ifndef TORQUE_SHIPPING
using namespace UnitTesting;
#define TEST( x ) test( ( x ), "FAIL: " #x )
// Simple test that creates and verifies an array of numbers using
// thread pool work items.
CreateUnitTest( TestThreadPool, "Platform/ThreadPool/Simple" )
{
enum { DEFAULT_NUM_ITEMS = 4000 };
static Vector< U32 > results;
struct TestItem : public ThreadPool::WorkItem
{
typedef ThreadPool::WorkItem Parent;
U32 mIndex;
TestItem( U32 index )
: mIndex( index ) {}
protected:
virtual void execute()
{
results[ mIndex ] = mIndex;
}
};
void run()
{
U32 numItems = Con::getIntVariable( "$testThreadPool::numValues", DEFAULT_NUM_ITEMS );
ThreadPool* pool = &ThreadPool::GLOBAL();
results.setSize( numItems );
for( U32 i = 0; i < numItems; ++ i )
results[ i ] = U32( -1 );
for( U32 i = 0; i < numItems; ++ i )
{
ThreadSafeRef< TestItem > item( new TestItem( i ) );
pool->queueWorkItem( item );
}
pool->flushWorkItems();
for( U32 i = 0; i < numItems; ++ i )
test( results[ i ] == i, "result mismatch" );
results.clear();
}
};
Vector< U32 > TestThreadPool::results( __FILE__, __LINE__ );
#endif // !TORQUE_SHIPPING

View file

@ -0,0 +1,403 @@
//-----------------------------------------------------------------------------
// 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 "unit/test.h"
#include "platform/threads/threadSafeDeque.h"
#include "platform/threads/thread.h"
#include "core/util/tVector.h"
#include "console/console.h"
#ifndef TORQUE_SHIPPING
using namespace UnitTesting;
#define TEST( x ) test( ( x ), "FAIL: " #x )
#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x )
// Test deque without concurrency.
CreateUnitTest( TestThreadSafeDequeSerial, "Platform/ThreadSafeDeque/Serial" )
{
void test1()
{
ThreadSafeDeque< char > deque;
String str = "teststring";
for( U32 i = 0; i < str.length(); ++ i )
deque.pushBack( str[ i ] );
TEST( !deque.isEmpty() );
for( U32 i = 0; i < str.length(); ++ i )
{
char ch;
TEST( deque.tryPopFront( ch ) && ch == str[ i ] );
}
}
void test2()
{
ThreadSafeDeque< char > deque;
String str = "teststring";
const char* p1 = str.c_str() + 4;
const char* p2 = p1 + 1;
while( *p2 )
{
deque.pushFront( *p1 );
deque.pushBack( *p2 );
-- p1;
++ p2;
}
#ifdef TORQUE_DEBUG
deque.dumpDebug();
#endif
for( U32 i = 0; i < str.length(); ++ i )
{
char ch;
TEST( deque.tryPopFront( ch ) && ch == str[ i ] );
}
}
void test3()
{
ThreadSafeDeque< char > deque;
String str = "teststring";
const char* p1 = str.c_str() + 4;
const char* p2 = p1 + 1;
while( *p2 )
{
deque.pushFront( *p1 );
deque.pushBack( *p2 );
-- p1;
++ p2;
}
#ifdef TORQUE_DEBUG
deque.dumpDebug();
#endif
for( S32 i = ( str.length() - 1 ); i >= 0; -- i )
{
char ch;
TEST( deque.tryPopBack( ch ) && ch == str[ i ] );
}
}
void test4()
{
ThreadSafeDeque< char > deque;
char ch;
TEST( deque.isEmpty() );
deque.pushFront( 'a' );
TEST( !deque.isEmpty() );
TEST( deque.tryPopFront( ch ) );
TEST( ch == 'a' );
deque.pushBack( 'a' );
TEST( !deque.isEmpty() );
TEST( deque.tryPopFront( ch ) );
TEST( ch == 'a' );
deque.pushBack( 'a' );
TEST( !deque.isEmpty() );
TEST( deque.tryPopBack( ch ) );
TEST( ch == 'a' );
deque.pushFront( 'a' );
TEST( !deque.isEmpty() );
TEST( deque.tryPopBack( ch ) );
TEST( ch == 'a' );
}
void run()
{
test1();
test2();
test3();
test4();
}
};
// Test deque in a concurrent setting.
CreateUnitTest( TestThreadSafeDequeConcurrentSimple, "Platform/ThreadSafeDeque/ConcurrentSimple" )
{
public:
typedef TestThreadSafeDequeConcurrentSimple TestType;
enum
{
DEFAULT_NUM_VALUES = 100000,
};
struct Value : public ThreadSafeRefCount< Value >
{
U32 mIndex;
U32 mTick;
Value() {}
Value( U32 index, U32 tick )
: mIndex( index ), mTick( tick ) {}
};
typedef ThreadSafeRef< Value > ValueRef;
struct Deque : public ThreadSafeDeque< ValueRef >
{
typedef ThreadSafeDeque<ValueRef> Parent;
U32 mPushIndex;
U32 mPopIndex;
Deque()
: mPushIndex( 0 ), mPopIndex( 0 ) {}
void pushBack( const ValueRef& value )
{
AssertFatal( value->mIndex == mPushIndex, "index out of line" );
mPushIndex ++;
Parent::pushBack( value );
}
bool tryPopFront( ValueRef& outValue )
{
if( Parent::tryPopFront( outValue ) )
{
AssertFatal( outValue->mIndex == mPopIndex, "index out of line" );
mPopIndex ++;
return true;
}
else
return false;
}
};
Deque mDeque;
Vector< U32 > mValues;
struct ProducerThread : public Thread
{
ProducerThread( TestType* test )
: Thread( 0, test ) {}
virtual void run( void* arg )
{
_setName( "ProducerThread" );
Platform::outputDebugString( "Starting ProducerThread" );
TestType* test = ( TestType* ) arg;
for( U32 i = 0; i < test->mValues.size(); ++ i )
{
U32 tick = Platform::getRealMilliseconds();
test->mValues[ i ] = tick;
ValueRef val = new Value( i, tick );
test->mDeque.pushBack( val );
}
Platform::outputDebugString( "Stopping ProducerThread" );
}
};
struct ConsumerThread : public Thread
{
ConsumerThread( TestType* test )
: Thread( 0, test ) {}
virtual void run( void* arg )
{
_setName( "ConsumerThread" );
Platform::outputDebugString( "Starting CosumerThread" );
TestType* t = ( TestType* ) arg;
for( U32 i = 0; i < t->mValues.size(); ++ i )
{
ValueRef value;
while( !t->mDeque.tryPopFront( value ) );
XTEST( t, value->mIndex == i );
XTEST( t, t->mValues[ i ] == value->mTick );
}
Platform::outputDebugString( "Stopping ConsumerThread" );
}
};
void run()
{
U32 numValues = Con::getIntVariable( "$testThreadSafeDeque::numValues", DEFAULT_NUM_VALUES );
mValues.setSize( numValues );
ProducerThread pThread( this );
ConsumerThread cThread( this );
pThread.start();
cThread.start();
pThread.join();
cThread.join();
mValues.clear();
}
};
CreateUnitTest( TestThreadSafeDequeConcurrent, "Platform/ThreadSafeDeque/Concurrent" )
{
public:
typedef TestThreadSafeDequeConcurrent TestType;
enum
{
DEFAULT_NUM_VALUES = 100000,
DEFAULT_NUM_CONSUMERS = 10,
DEFAULT_NUM_PRODUCERS = 10
};
struct Value : public ThreadSafeRefCount< Value >
{
U32 mIndex;
U32 mTick;
Value() {}
Value( U32 index, U32 tick )
: mIndex( index ), mTick( tick ) {}
};
typedef ThreadSafeRef< Value > ValueRef;
U32 mProducerIndex;
U32 mConsumerIndex;
ThreadSafeDeque< ValueRef > mDeque;
Vector< U32 > mValues;
struct ProducerThread : public Thread
{
ProducerThread( TestType* test )
: Thread( 0, test ) {}
virtual void run( void* arg )
{
_setName( "ProducerThread" );
Platform::outputDebugString( "Starting ProducerThread" );
TestType* test = ( TestType* ) arg;
while( 1 )
{
U32 index = test->mProducerIndex;
if( index == test->mValues.size() )
break;
if( dCompareAndSwap( test->mProducerIndex, index, index + 1 ) )
{
U32 tick = Platform::getRealMilliseconds();
test->mValues[ index ] = tick;
ValueRef val = new Value( index, tick );
test->mDeque.pushBack( val );
}
}
Platform::outputDebugString( "Stopping ProducerThread" );
}
};
struct ConsumerThread : public Thread
{
ConsumerThread( TestType* test )
: Thread( 0, test ) {}
virtual void run( void* arg )
{
_setName( "ConsumerThread" );
Platform::outputDebugString( "Starting ConsumerThread" );
TestType* t = ( TestType* ) arg;
while( t->mConsumerIndex < t->mValues.size() )
{
ValueRef value;
if( t->mDeque.tryPopFront( value ) )
{
dFetchAndAdd( t->mConsumerIndex, 1 );
XTEST( t, t->mValues[ value->mIndex ] == value->mTick );
t->mValues[ value->mIndex ] = 0;
}
}
Platform::outputDebugString( "Stopping ConsumerThread" );
}
};
void run()
{
U32 numValues = Con::getIntVariable( "$testThreadSafeDeque::numValues", DEFAULT_NUM_VALUES );
U32 numConsumers = Con::getIntVariable( "$testThreadSafeDeque::numConsumers", DEFAULT_NUM_CONSUMERS );
U32 numProducers = Con::getIntVariable( "$testThreadSafeDeque::numProducers", DEFAULT_NUM_PRODUCERS );
mProducerIndex = 0;
mConsumerIndex = 0;
mValues.setSize( numValues );
U32 tick = Platform::getRealMilliseconds();
for( U32 i = 0; i < numValues; ++ i )
mValues[ i ] = tick;
Vector< ProducerThread* > producers;
Vector< ConsumerThread* > consumers;
producers.setSize( numProducers );
consumers.setSize( numConsumers );
for( U32 i = 0; i < numProducers; ++ i )
{
producers[ i ] = new ProducerThread( this );
producers[ i ]->start();
}
for( U32 i = 0; i < numConsumers; ++ i )
{
consumers[ i ] = new ConsumerThread( this );
consumers[ i ]->start();
}
for( U32 i = 0; i < numProducers; ++ i )
{
producers[ i ]->join();
delete producers[ i ];
}
for( U32 i = 0; i < numConsumers; ++ i )
{
consumers[ i ]->join();
delete consumers[ i ];
}
for( U32 i = 0; i < mValues.size(); ++ i )
TEST( mValues[ i ] == 0 );
mValues.clear();
}
};
#endif // !TORQUE_SHIPPING

View file

@ -0,0 +1,245 @@
//-----------------------------------------------------------------------------
// 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 "unit/test.h"
#include "platform/threads/threadSafePriorityQueue.h"
#include "platform/threads/thread.h"
#include "core/util/tVector.h"
#include "console/console.h"
#ifndef TORQUE_SHIPPING
using namespace UnitTesting;
#define TEST( x ) test( ( x ), "FAIL: " #x )
#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x )
// Test queue without concurrency.
CreateUnitTest( TestThreadSafePriorityQueueSerial, "Platform/ThreadSafePriorityQueue/Serial" )
{
struct Value
{
F32 mPriority;
U32 mIndex;
Value() {}
Value( F32 priority, U32 index )
: mPriority( priority ), mIndex( index ) {}
};
template< bool SORT_MIN_TO_MAX >
void test1()
{
Vector< Value > values;
values.push_back( Value( 0.2f, 2 ) );
values.push_back( Value( 0.7f, 7 ) );
values.push_back( Value( 0.4f, 4 ) );
values.push_back( Value( 0.6f, 6 ) );
values.push_back( Value( 0.1f, 1 ) );
values.push_back( Value( 0.5f, 5 ) );
values.push_back( Value( 0.3f, 3 ) );
values.push_back( Value( 0.8f, 8 ) );
values.push_back( Value( 0.6f, 6 ) );
values.push_back( Value( 0.9f, 9 ) );
values.push_back( Value( 0.0f, 0 ) );
const S32 min = 0;
const S32 max = 9;
ThreadSafePriorityQueue< U32, F32, SORT_MIN_TO_MAX > queue;
for( U32 i = 0; i < values.size(); ++ i )
queue.insert( values[ i ].mPriority, values[ i ].mIndex );
TEST( !queue.isEmpty() );
S32 index;
if( SORT_MIN_TO_MAX )
index = min - 1;
else
index = max + 1;
for( U32 i = 0; i < values.size(); ++ i )
{
U32 value;
TEST( queue.takeNext( value ) );
if( value != index )
{
if( SORT_MIN_TO_MAX )
index ++;
else
index --;
}
TEST( value == index );
}
}
void run()
{
test1< true >();
test1< false >();
}
};
// Test queue with concurrency.
CreateUnitTest( TestThreadSafePriorityQueueConcurrent, "Platform/ThreadSafePriorityQueue/Concurrent" )
{
public:
typedef TestThreadSafePriorityQueueConcurrent TestType;
enum
{
DEFAULT_NUM_VALUES = 100000,
DEFAULT_NUM_CONSUMERS = 10,
DEFAULT_NUM_PRODUCERS = 10
};
struct Value : public ThreadSafeRefCount< Value >
{
U32 mIndex;
F32 mPriority;
bool mCheck;
Value() : mCheck( false ) {}
Value( U32 index, F32 priority )
: mIndex( index ), mPriority( priority ), mCheck( false ) {}
};
typedef ThreadSafeRef< Value > ValueRef;
U32 mProducerIndex;
U32 mConsumerIndex;
ThreadSafePriorityQueue< ValueRef > mQueue;
Vector< ValueRef > mValues;
struct ProducerThread : public Thread
{
ProducerThread( TestType* test )
: Thread( 0, test ) {}
virtual void run( void* arg )
{
_setName( "ProducerThread" );
Platform::outputDebugString( "Starting ProducerThread" );
TestType* test = ( TestType* ) arg;
while( 1 )
{
U32 index = test->mProducerIndex;
if( index == test->mValues.size() )
break;
if( dCompareAndSwap( test->mProducerIndex, index, index + 1 ) )
{
F32 priority = Platform::getRandom();
ValueRef val = new Value( index, priority );
test->mValues[ index ] = val;
test->mQueue.insert( priority, val );
}
}
Platform::outputDebugString( "Stopping ProducerThread" );
}
};
struct ConsumerThread : public Thread
{
ConsumerThread( TestType* test )
: Thread( 0, test ) {}
virtual void run( void* arg )
{
_setName( "ConsumerThread" );
Platform::outputDebugString( "Starting ConsumerThread" );
TestType* t = ( TestType* ) arg;
while( t->mConsumerIndex < t->mValues.size() )
{
ValueRef value;
if( t->mQueue.takeNext( value ) )
{
dFetchAndAdd( t->mConsumerIndex, 1 );
XTEST( t, t->mValues[ value->mIndex ] == value );
value->mCheck = true;
}
else
Platform::sleep( 5 );
}
Platform::outputDebugString( "Stopping ConsumerThread" );
}
};
void run()
{
U32 numValues = Con::getIntVariable( "$testThreadSafePriorityQueue::numValues", DEFAULT_NUM_VALUES );
U32 numConsumers = Con::getIntVariable( "$testThreadSafePriorityQueue::numConsumers", DEFAULT_NUM_CONSUMERS );
U32 numProducers = Con::getIntVariable( "$testThreadSafePriorityQueue::numProducers", DEFAULT_NUM_PRODUCERS );
mProducerIndex = 0;
mConsumerIndex = 0;
mValues.setSize( numValues );
Vector< ProducerThread* > producers;
Vector< ConsumerThread* > consumers;
producers.setSize( numProducers );
consumers.setSize( numConsumers );
for( U32 i = 0; i < numProducers; ++ i )
{
producers[ i ] = new ProducerThread( this );
producers[ i ]->start();
}
for( U32 i = 0; i < numConsumers; ++ i )
{
consumers[ i ] = new ConsumerThread( this );
consumers[ i ]->start();
}
for( U32 i = 0; i < numProducers; ++ i )
{
producers[ i ]->join();
delete producers[ i ];
}
for( U32 i = 0; i < numConsumers; ++ i )
{
consumers[ i ]->join();
delete consumers[ i ];
}
for( U32 i = 0; i < mValues.size(); ++ i )
{
TEST( mValues[ i ] != NULL );
if( mValues[ i ] != NULL )
TEST( mValues[ i ]->mCheck );
}
mValues.clear();
}
};
#endif // !TORQUE_SHIPPING

View file

@ -0,0 +1,227 @@
//-----------------------------------------------------------------------------
// 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 "unit/test.h"
#include "platform/threads/threadSafeRefCount.h"
#include "platform/threads/thread.h"
#include "core/util/tVector.h"
#include "console/console.h"
#ifndef TORQUE_SHIPPING
using namespace UnitTesting;
#define TEST( x ) test( ( x ), "FAIL: " #x )
CreateUnitTest( TestThreadSafeRefCountSerial, "Platform/ThreadSafeRefCount/Serial" )
{
struct TestObject : public ThreadSafeRefCount< TestObject >
{
static bool smDeleted;
TestObject()
{
smDeleted = false;
}
~TestObject()
{
smDeleted = true;
}
};
typedef ThreadSafeRef< TestObject > TestObjectRef;
void run()
{
TestObjectRef ref1 = new TestObject;
TEST( !ref1->isShared() );
TEST( ref1 != NULL );
TestObjectRef ref2 = ref1;
TEST( ref1->isShared() );
TEST( ref2->isShared() );
TEST( ref1 == ref2 );
ref1 = NULL;
TEST( !ref2->isShared() );
ref2 = NULL;
TEST( TestObject::smDeleted );
}
};
bool TestThreadSafeRefCountSerial::TestObject::smDeleted;
CreateUnitTest( TestThreadSafeRefCountConcurrent, "Platform/ThreadSafeRefCount/Concurrent" )
{
public:
typedef TestThreadSafeRefCountConcurrent TestType;
enum
{
NUM_ADD_REFS_PER_THREAD = 1000,
NUM_EXTRA_REFS_PER_THREAD = 1000,
NUM_THREADS = 10
};
class TestObject : public ThreadSafeRefCount< TestObject >
{
public:
};
ThreadSafeRef< TestObject > mRef;
class TestThread : public Thread
{
public:
TestType* mTest;
Vector< ThreadSafeRef< TestObject > > mExtraRefs;
TestThread( TestType* test )
: mTest( test ) {}
void run( void* arg )
{
if( !arg )
{
for( U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; ++ i )
mTest->mRef->addRef();
mExtraRefs.setSize( NUM_EXTRA_REFS_PER_THREAD );
for( U32 i = 0; i < NUM_EXTRA_REFS_PER_THREAD; ++ i )
mExtraRefs[ i ] = mTest->mRef;
}
else
{
mExtraRefs.clear();
for( U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; ++ i )
mTest->mRef->release();
}
}
};
void run()
{
mRef = new TestObject;
TEST( mRef->getRefCount() == 2 ); // increments of 2
Vector< TestThread* > threads;
threads.setSize( NUM_THREADS );
// Create threads.
for( U32 i = 0; i < NUM_THREADS; ++ i )
threads[ i ] = new TestThread( this );
// Run phase 1: create references.
for( U32 i = 0; i < NUM_THREADS; ++ i )
threads[ i ]->start( NULL );
// Wait for completion.
for( U32 i = 0; i < NUM_THREADS; ++ i )
threads[ i ]->join();
Con::printf( "REF: %i", mRef->getRefCount() );
TEST( mRef->getRefCount() == 2 + ( ( NUM_ADD_REFS_PER_THREAD + NUM_EXTRA_REFS_PER_THREAD ) * NUM_THREADS * 2 ) );
// Run phase 2: release references.
for( U32 i = 0; i < NUM_THREADS; ++ i )
threads[ i ]->start( ( void* ) 1 );
// Wait for completion.
for( U32 i = 0; i < NUM_THREADS; ++ i )
{
threads[ i ]->join();
delete threads[ i ];
}
TEST( mRef->getRefCount() == 2 ); // increments of two
mRef = NULL;
}
};
CreateUnitTest( TestThreadSafeRefCountTagging, "Platform/ThreadSafeRefCount/Tagging" )
{
struct TestObject : public ThreadSafeRefCount< TestObject > {};
typedef ThreadSafeRef< TestObject > TestObjectRef;
void run()
{
TestObjectRef ref;
TEST( !ref.isTagged() );
TEST( !ref );
TEST( !ref.ptr() );
TEST( ref.trySetFromTo( ref, NULL ) );
TEST( !ref.isTagged() );
TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Set ) );
TEST( ref.isTagged() );
TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Set ) );
TEST( ref.isTagged() );
TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Unset ) );
TEST( !ref.isTagged() );
TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Unset ) );
TEST( !ref.isTagged() );
TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_SetOrFail ) );
TEST( ref.isTagged() );
TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_SetOrFail ) );
TEST( ref.isTagged() );
TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_FailIfSet ) );
TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_UnsetOrFail ) );
TEST( !ref.isTagged() );
TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_UnsetOrFail ) );
TEST( !ref.isTagged() );
TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_FailIfUnset ) );
TestObjectRef objectA = new TestObject;
TestObjectRef objectB = new TestObject;
TEST( !objectA->isShared() );
TEST( !objectB->isShared() );
ref = objectA;
TEST( !ref.isTagged() );
TEST( ref == objectA );
TEST( ref == objectA.ptr() );
TEST( objectA->isShared() );
TEST( ref.trySetFromTo( objectA, objectB, TestObjectRef::TAG_Set ) );
TEST( ref.isTagged() );
TEST( ref == objectB );
TEST( ref == objectB.ptr() );
TEST( objectB->isShared() );
TEST( !objectA->isShared() );
TEST( ref.trySetFromTo( ref, objectA ) );
TEST( ref.isTagged() );
TEST( ref == objectA );
TEST( ref == objectA.ptr() );
}
};
#endif // !TORQUE_SHIPPING

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.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/threads/thread.h"
#include "platform/threads/semaphore.h"
#include "platform/threads/mutex.h"
#include "unit/test.h"
#include "core/util/tVector.h"
#include "console/console.h"
using namespace UnitTesting;
class ThreadTestHarness
{
U32 mStartTime, mEndTime, mCleanupTime;
void (*mThreadBody)(void*);
S32 mThreadCount;
Thread **mThreads;
public:
ThreadTestHarness()
{
mStartTime = mEndTime = mCleanupTime = 0;
mThreadBody = NULL;
mThreadCount = 1;
mThreads = NULL;
}
void startThreads(void (*threadBody)(void*), void *arg, U32 threadCount)
{
mThreadCount = threadCount;
mThreadBody = threadBody;
// Start up threadCount threads...
mThreads = new Thread*[threadCount];
mStartTime = Platform::getRealMilliseconds();
//Con::printf(" Running with %d threads...", threadCount);
for(S32 i=0; i<mThreadCount; i++)
{
mThreads[i] = new Thread(threadBody, arg);
mThreads[i]->start();
}
}
void waitForThreadExit(U32 checkFrequencyMs)
{
// And wait for them to complete.
bool someAlive = true;
S32 liveCount = mThreadCount;
while(someAlive)
{
//Con::printf(" - Sleeping for %dms with %d live threads.", checkFrequencyMs, liveCount);
Platform::sleep(checkFrequencyMs);
someAlive = false;
liveCount = 0;
for(S32 i=0; i<mThreadCount; i++)
{
if(!mThreads[i]->isAlive())
continue;
someAlive = true;
liveCount++;
}
}
mEndTime = Platform::getRealMilliseconds();
// Clean up memory at this point.
for(S32 i=0; i<mThreadCount; i++)
delete mThreads[i];
delete[] mThreads;
// Make sure we didn't take a long time to complete.
mCleanupTime = Platform::getRealMilliseconds();
// And dump some stats.
Con::printf(" Took approximately %dms (+/- %dms) to run %d threads, and %dms to cleanup.",
(mEndTime - mStartTime),
checkFrequencyMs,
mThreadCount,
mCleanupTime - mEndTime);
}
};
CreateUnitTest( ThreadSanityCheck, "Platform/Threads/BasicSanity")
{
const static S32 amountOfWork = 100;
const static S32 numberOfThreads = 8;
static void threadBody(void *)
{
S32 work = 0x381f4fd3;
// Spin on some work, then exit.
for(S32 i=0; i<amountOfWork; i++)
{
// Do a little computation...
work ^= (i + work | amountOfWork);
// And sleep a slightly variable bit.
Platform::sleep(10 + ((work+i) % 10));
}
}
void runNThreads(S32 threadCount)
{
ThreadTestHarness tth;
tth.startThreads(&threadBody, NULL, threadCount);
tth.waitForThreadExit(32);
}
void run()
{
for(S32 i=0; i<numberOfThreads; i++)
runNThreads(i);
}
};
CreateUnitTest( MutexStressTest, "Platform/Threads/MutexStress")
{
const static S32 numberOfLocks = 100;
const static S32 numberOfThreads = 4;
void *mMutex;
static void threadBody(void *mutex)
{
// Acquire the mutex numberOfLocks times. Sleep for 1ms, acquire, sleep, release.
S32 lockCount = numberOfLocks;
while(lockCount--)
{
Platform::sleep(1);
Mutex::lockMutex(mutex, true);
Platform::sleep(1);
Mutex::unlockMutex(mutex);
}
}
void runNThreads(S32 threadCount)
{
ThreadTestHarness tth;
mMutex = Mutex::createMutex();
tth.startThreads(&threadBody, mMutex, threadCount);
// We fudge the wait period to be about the expected time assuming
// perfect execution speed.
tth.waitForThreadExit(32); //threadCount * 2 * numberOfLocks + 100);
Mutex::destroyMutex(mMutex);
}
void run()
{
for(S32 i=0; i<numberOfThreads; i++)
runNThreads(i);
}
};
CreateUnitTest( MemoryStressTest, "Platform/Threads/MemoryStress")
{
const static S32 numberOfAllocs = 1000;
const static S32 minAllocSize = 13;
const static S32 maxAllocSize = 1024 * 1024;
const static S32 numberOfThreads = 4;
void *mMutex;
// Cheap little RNG so we can vary our allocations more uniquely per thread.
static U32 threadRandom(U32 &seed, U32 min, U32 max)
{
seed = (1664525 * seed + 1013904223);
U32 res = seed;
res %= (max - min);
return res + min;
}
static void threadBody(void *mutex)
{
// Acquire the mutex numberOfLocks times. Sleep for 1ms, acquire, sleep, release.
S32 allocCount = numberOfAllocs;
U32 seed = (U32)((U32)mutex + (U32)&allocCount);
while(allocCount--)
{
U8 *mem = new U8[threadRandom(seed, minAllocSize, maxAllocSize)];
delete[] mem;
}
}
void runNThreads(S32 threadCount)
{
ThreadTestHarness tth;
mMutex = Mutex::createMutex();
tth.startThreads(&threadBody, mMutex, threadCount);
// We fudge the wait period to be about the expected time assuming
// perfect execution speed.
tth.waitForThreadExit(32);
Mutex::destroyMutex(mMutex);
}
void run()
{
for(S32 i=0; i<numberOfThreads; i++)
runNThreads(i);
}
};
CreateUnitTest( ThreadGymnastics, "Platform/Threads/BasicSynchronization")
{
void run()
{
// We test various scenarios wrt to locking and unlocking, in a single
// thread, just to make sure our basic primitives are working in the
// most basic case.
void *mutex1 = Mutex::createMutex();
test(mutex1, "First Mutex::createMutex call failed - that's pretty bad!");
void *mutex2 = Mutex::createMutex();
test(mutex2, "Second Mutex::createMutex call failed - that's pretty bad, too!");
test(Mutex::lockMutex(mutex1, false), "Nonblocking call to brand new mutex failed - should not be.");
test(Mutex::lockMutex(mutex1, true), "Failed relocking a mutex from the same thread - should be able to do this.");
// Unlock & kill mutex 1
Mutex::unlockMutex(mutex1);
Mutex::unlockMutex(mutex1);
Mutex::destroyMutex(mutex1);
// Kill mutex2, which was never touched.
Mutex::destroyMutex(mutex2);
// Now we can test semaphores.
Semaphore *sem1 = new Semaphore(1);
Semaphore *sem2 = new Semaphore(1);
// Test that we can do non-blocking acquires that succeed.
test(sem1->acquire(false), "Should succeed at acquiring a new semaphore with count 1.");
test(sem2->acquire(false), "This one should succeed too, see previous test.");
// Test that we can do non-blocking acquires that fail.
test(sem1->acquire(false)==false, "Should failed, as we've already got the sem.");
sem1->release();
test(sem2->acquire(false)==false, "Should also fail.");
sem2->release();
// Test that we can do blocking acquires that succeed.
test(sem1->acquire(true)==true, "Should succeed as we just released.");
test(sem2->acquire(true)==true, "Should succeed as we just released.");
// Can't test blocking acquires that never happen... :)
// Clean up.
delete sem1;
delete sem2;
}
};
CreateUnitTest( SemaphoreWaitTest, "Platform/Threads/SemaphoreWaitTest")
{
static void threadBody(void *self)
{
SemaphoreWaitTest *me = (SemaphoreWaitTest*)self;
// Wait for the semaphore to get released.
me->mSemaphore->acquire();
// Increment the counter.
Mutex::lockMutex(me->mMutex);
me->mDoneCount++;
Mutex::unlockMutex(me->mMutex);
// Signal back to the main thread we're done.
me->mPostbackSemaphore->release();
}
Semaphore *mSemaphore;
Semaphore *mPostbackSemaphore;
void *mMutex;
U32 mDoneCount;
const static int csmThreadCount = 10;
void run()
{
ThreadTestHarness tth;
mDoneCount = 0;
mSemaphore = new Semaphore(0);
mPostbackSemaphore = new Semaphore(0);
mMutex = Mutex::createMutex();
tth.startThreads(&threadBody, this, csmThreadCount);
Platform::sleep(500);
Mutex::lockMutex(mMutex);
test(mDoneCount == 0, "no threads should have touched the counter yet.");
Mutex::unlockMutex(mMutex);
// Let 500 come out.
for(S32 i=0; i<csmThreadCount/2; i++)
mSemaphore->release();
// And wait for 500 postbacks.
for(S32 i=0; i<csmThreadCount/2; i++)
mPostbackSemaphore->acquire();
Mutex::lockMutex(mMutex);
test(mDoneCount == csmThreadCount / 2, "Didn't get expected number of done threads! (a)");
Mutex::unlockMutex(mMutex);
// Ok, now do the rest.
// Let 500 come out.
for(S32 i=0; i<csmThreadCount/2; i++)
mSemaphore->release();
// And wait for 500 postbacks.
for(S32 i=0; i<csmThreadCount/2; i++)
mPostbackSemaphore->acquire();
Mutex::lockMutex(mMutex);
test(mDoneCount == csmThreadCount, "Didn't get expected number of done threads! (b)");
Mutex::unlockMutex(mMutex);
// Wait for the threads to exit - shouldn't have to wait ever though.
tth.waitForThreadExit(10);
// Make sure no one touched our data after shutdown time.
Mutex::lockMutex(mMutex);
test(mDoneCount == csmThreadCount, "Didn't get expected number of done threads! (c)");
Mutex::unlockMutex(mMutex);
}
};
CreateUnitTest( MutexWaitTest, "Platform/Threads/MutexWaitTest")
{
static void threadBody(void *self)
{
MutexWaitTest *me = (MutexWaitTest*)self;
// Increment the counter. We'll block until the mutex
// is open.
Mutex::lockMutex(me->mMutex);
me->mDoneCount++;
Mutex::unlockMutex(me->mMutex);
}
void *mMutex;
U32 mDoneCount;
const static int csmThreadCount = 10;
void run()
{
mMutex = Mutex::createMutex();
mDoneCount = 0;
// We lock the mutex before we create any threads, so that all the threads
// block on the mutex. Then we unlock it and let them all work their way
// through the increment.
Mutex::lockMutex(mMutex);
ThreadTestHarness tth;
tth.startThreads(&threadBody, this, csmThreadCount);
Platform::sleep(5000);
// Check count is still zero.
test(mDoneCount == 0, "Uh oh - a thread somehow didn't get blocked by the locked mutex!");
// Open the flood gates...
Mutex::unlockMutex(mMutex);
// Wait for the threads to all finish executing.
tth.waitForThreadExit(10);
Mutex::lockMutex(mMutex);
test(mDoneCount == csmThreadCount, "Hmm - all threads reported done, but we didn't get the expected count.");
Mutex::unlockMutex(mMutex);
// Kill the mutex.
Mutex::destroyMutex(mMutex);
}
};

View file

@ -0,0 +1,105 @@
//-----------------------------------------------------------------------------
// 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/platform.h"
#include "platform/platformTimer.h"
#include "core/util/journal/journaledSignal.h"
#include "core/util/journal/process.h"
#include "math/mMath.h"
#include "console/console.h"
#include "unit/test.h"
using namespace UnitTesting;
CreateUnitTest(Check_advanceTime, "Platform/Time/advanceTime")
{
void run()
{
U32 time = Platform::getVirtualMilliseconds();
Platform::advanceTime(10);
U32 newTime = Platform::getVirtualMilliseconds();
test(newTime - time == 10, "Platform::advanceTime is borked, we advanced 10ms but didn't get a 10ms delta!");
}
};
CreateUnitTest(Check_platformSleep, "Platform/Time/Sleep")
{
const static S32 sleepTimeMs = 500;
void run()
{
U32 start = Platform::getRealMilliseconds();
Platform::sleep(sleepTimeMs);
U32 end = Platform::getRealMilliseconds();
test(end - start >= sleepTimeMs, "We didn't sleep at least as long as we requested!");
}
};
CreateUnitTest(Check_timeManager, "Platform/Time/Manager")
{
void handleTimeEvent(S32 timeDelta)
{
mElapsedTime += timeDelta;
mNumberCalls++;
if(mElapsedTime >= 1000)
Process::requestShutdown();
}
S32 mElapsedTime;
S32 mNumberCalls;
void run()
{
mElapsedTime = mNumberCalls = 0;
// Initialize the time manager...
TimeManager time;
time.timeEvent.notify(this, &Check_timeManager::handleTimeEvent);
// Event loop till at least one second has passed.
const U32 start = Platform::getRealMilliseconds();
while(Process::processEvents())
{
// If we go too long, kill it off...
if(Platform::getRealMilliseconds() - start > 30*1000)
{
test(false, "Terminated process loop due to watchdog, not due to time manager event, after 30 seconds.");
Process::requestShutdown();
}
}
const U32 end = Platform::getRealMilliseconds();
// Now, confirm we have approximately similar elapsed times.
S32 elapsedRealTime = end - start;
test(mAbs(elapsedRealTime - mElapsedTime) < 50, "Failed to elapse time to within the desired tolerance.");
test(mNumberCalls > 0, "Somehow got no event callbacks from TimeManager?");
Con::printf(" Got %d time events, and elapsed %dms from TimeManager, "
"%dms according to Platform::getRealMilliseconds()",
mNumberCalls, mElapsedTime, elapsedRealTime);
}
};

View file

@ -0,0 +1,129 @@
//-----------------------------------------------------------------------------
// 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/types.h"
#include "platform/platformAssert.h"
#ifndef _PLATFORM_THREADS_MUTEX_H_
#define _PLATFORM_THREADS_MUTEX_H_
// Forward ref used by platform code
struct PlatformMutexData;
class Mutex
{
protected:
PlatformMutexData *mData;
public:
Mutex();
virtual ~Mutex();
virtual bool lock(bool block = true);
virtual void unlock();
// Old API so that we don't have to change a load of code
static void *createMutex()
{
Mutex *mutex = new Mutex;
return (void *)mutex;
}
static void destroyMutex(void *mutex)
{
Mutex *realMutex = reinterpret_cast<Mutex *>(mutex);
delete realMutex;
}
static bool lockMutex(void *mutex, bool block = true)
{
Mutex *realMutex = reinterpret_cast<Mutex *>(mutex);
return realMutex->lock(block);
}
static void unlockMutex(void *mutex)
{
Mutex *realMutex = reinterpret_cast<Mutex *>(mutex);
realMutex->unlock();
}
};
/// Helper for simplifying mutex locking code.
///
/// This class will automatically unlock a mutex that you've
/// locked through it, saving you from managing a lot of complex
/// exit cases. For instance:
///
/// @code
/// MutexHandle handle;
/// handle.lock(myMutex);
///
/// if(error1)
/// return; // Auto-unlocked by handle if we leave here - normally would
/// // leave the mutex locked, causing much pain later.
///
/// handle.unlock();
/// @endcode
class MutexHandle
{
private:
void *mMutexPtr;
public:
MutexHandle()
: mMutexPtr(NULL)
{
}
~MutexHandle()
{
if(mMutexPtr)
unlock();
}
bool lock(void *mutex, bool blocking=false)
{
AssertFatal(!mMutexPtr, "MutexHandle::lock - shouldn't be locking things twice!");
bool ret = Mutex::lockMutex(mutex, blocking);
if(ret)
{
// We succeeded, do book-keeping.
mMutexPtr = mutex;
}
return ret;
}
void unlock()
{
if(mMutexPtr)
{
Mutex::unlockMutex(mMutexPtr);
mMutexPtr = NULL;
}
}
};
#endif // _PLATFORM_THREADS_MUTEX_H_

View file

@ -0,0 +1,55 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORM_THREAD_SEMAPHORE_H_
#define _PLATFORM_THREAD_SEMAPHORE_H_
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
// Forward ref used by platform code
class PlatformSemaphore;
class Semaphore
{
protected:
PlatformSemaphore *mData;
public:
/// Create a semaphore. initialCount defaults to 1.
Semaphore(S32 initialCount = 1);
/// Delete a semaphore, ignoring it's count.
~Semaphore();
/// Acquire the semaphore, decrementing its count.
/// if the initial count is less than 1, block until it goes above 1, then acquire.
/// Returns true if the semaphore was acquired, false if the semaphore could
/// not be acquired and block was false.
bool acquire(bool block = true, S32 timeoutMS = -1);
/// Release the semaphore, incrementing its count.
/// Never blocks.
void release();
};
#endif

View file

@ -0,0 +1,245 @@
//-----------------------------------------------------------------------------
// 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 _PLATFORM_THREADS_THREAD_H_
#define _PLATFORM_THREADS_THREAD_H_
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#ifndef _PLATFORM_THREADS_MUTEX_H_
#include "platform/threads/mutex.h"
#endif
#ifndef _TSINGLETON_H_
#include "core/util/tSingleton.h"
#endif
// Forward ref used by platform code
class PlatformThreadData;
// Typedefs
typedef void (*ThreadRunFunction)(void *data);
class Thread
{
public:
typedef void Parent;
protected:
PlatformThreadData* mData;
/// Used to signal threads need to stop.
/// Threads set this flag to false in start()
U32 shouldStop;
/// Set the name of this thread for identification in debuggers.
/// Maybe a NOP on platforms that do not support this. Always a NOP
/// in non-debug builds.
void _setName( const char* name );
public:
/// If set, the thread will delete itself once it has finished running.
bool autoDelete;
/// Create a thread.
/// @param func The starting function for the thread.
/// @param arg Data to be passed to func, when the thread starts.
/// @param start_thread Supported for compatibility. Must be false. Starting threads from
/// within the constructor is not allowed anymore as the run() method is virtual.
Thread(ThreadRunFunction func = 0, void *arg = 0, bool start_thread = false, bool autodelete = false);
/// Destroy a thread.
/// The thread MUST be allowed to exit before it is destroyed.
virtual ~Thread();
/// Start a thread.
/// Sets shouldStop to false and calls run() in a new thread of execution.
void start( void* arg = 0 );
/// Ask a thread to stop running.
void stop()
{
shouldStop = true;
}
/// Block until the thread stops running.
/// @note Don't use this in combination with auto-deletion as otherwise the thread will kill
/// itself while still executing the join() method on the waiting thread.
bool join();
/// Threads may call checkForStop() periodically to check if they've been
/// asked to stop. As soon as checkForStop() returns true, the thread should
/// clean up and return.
bool checkForStop()
{
return shouldStop;
}
/// Run the Thread's entry point function.
/// Override this method in a subclass of Thread to create threaded code in
/// an object oriented way, and without passing a function ptr to Thread().
/// Also, you can call this method directly to execute the thread's
/// code in a non-threaded way.
virtual void run(void *arg = 0);
/// Returns true if the thread is running.
bool isAlive();
/// Returns the platform specific thread id for this thread.
U32 getId();
};
///
class ThreadManager
{
Vector<Thread*> threadPool;
Mutex poolLock;
struct MainThreadId
{
U32 mId;
MainThreadId()
{
mId = ThreadManager::getCurrentThreadId();
}
U32 get()
{
// Okay, this is a bit soso. The main thread ID may get queried during
// global ctor phase before MainThreadId's ctor ran. Since global
// ctors will/should all run on the main thread, we can sort of safely
// assume here that we can just query the current thread's ID.
if( !mId )
mId = ThreadManager::getCurrentThreadId();
return mId;
}
};
static MainThreadId smMainThreadId;
public:
ThreadManager()
{
VECTOR_SET_ASSOCIATION( threadPool );
}
/// Return true if the caller is running on the main thread.
static bool isMainThread();
/// Returns true if threadId is the same as the calling thread's id.
static bool isCurrentThread(U32 threadId);
/// Returns true if the 2 thread ids represent the same thread. Some thread
/// APIs return an opaque object as a thread id, so the == operator cannot
/// reliably compare thread ids.
// this comparator is needed by pthreads and ThreadManager.
static bool compare(U32 threadId_1, U32 threadId_2);
/// Returns the platform specific thread id of the calling thread. Some
/// platforms do not guarantee that this ID stays the same over the life of
/// the thread, so use ThreadManager::compare() to compare thread ids.
static U32 getCurrentThreadId();
/// Returns the platform specific thread id ot the main thread.
static U32 getMainThreadId() { return smMainThreadId.get(); }
/// Each thread should add itself to the thread pool the first time it runs.
static void addThread(Thread* thread)
{
ThreadManager &manager = *ManagedSingleton< ThreadManager >::instance();
manager.poolLock.lock();
Thread *alreadyAdded = getThreadById(thread->getId());
if(!alreadyAdded)
manager.threadPool.push_back(thread);
manager.poolLock.unlock();
}
static void removeThread(Thread* thread)
{
ThreadManager &manager = *ManagedSingleton< ThreadManager >::instance();
manager.poolLock.lock();
U32 threadID = thread->getId();
for(U32 i = 0;i < manager.threadPool.size();++i)
{
if( compare( manager.threadPool[i]->getId(), threadID ) )
{
manager.threadPool.erase(i);
break;
}
}
manager.poolLock.unlock();
}
/// Searches the pool of known threads for a thread whose id is equivalent to
/// the given threadid. Compares thread ids with ThreadManager::compare().
static Thread* getThreadById(U32 threadid)
{
AssertFatal(threadid != 0, "ThreadManager::getThreadById() Searching for a bad thread id.");
Thread* ret = NULL;
ThreadManager &manager = *ManagedSingleton< ThreadManager >::instance();
manager.poolLock.lock();
Vector<Thread*> &pool = manager.threadPool;
for( S32 i = pool.size() - 1; i >= 0; i--)
{
Thread* p = pool[i];
if(compare(p->getId(), threadid))
{
ret = p;
break;
}
}
manager.poolLock.unlock();
return ret;
}
static Thread* getCurrentThread()
{
return getThreadById(ThreadManager::getCurrentThreadId());
}
static const char* getSingletonName()
{
return "ThreadManager";
}
};
inline bool ThreadManager::isMainThread()
{
return compare( ThreadManager::getCurrentThreadId(), smMainThreadId.get() );
}
inline bool ThreadManager::isCurrentThread(U32 threadId)
{
U32 current = getCurrentThreadId();
return compare(current, threadId);
}
#endif // _PLATFORM_THREADS_THREAD_H_

View file

@ -0,0 +1,479 @@
//-----------------------------------------------------------------------------
// 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/threads/threadPool.h"
#include "platform/threads/thread.h"
#include "platform/platformCPUCount.h"
#include "core/strings/stringFunctions.h"
#include "core/util/tSingleton.h"
//#define DEBUG_SPEW
//=============================================================================
// ThreadPool::Context.
//=============================================================================
ThreadPool::Context ThreadPool::Context::smRootContext( "ROOT", NULL, 1.0 );
//--------------------------------------------------------------------------
ThreadPool::Context::Context( const char* name, ThreadPool::Context* parent, F32 priorityBias )
: mName( name ),
mParent( parent ),
mSibling( 0 ),
mChildren( 0 ),
mPriorityBias( priorityBias ),
mAccumulatedPriorityBias( 0.0 )
{
if( parent )
{
mSibling = mParent->mChildren;
mParent->mChildren = this;
}
}
//--------------------------------------------------------------------------
ThreadPool::Context::~Context()
{
if( mParent )
for( Context* context = mParent->mChildren, *prev = 0; context != 0; prev = context, context = context->mSibling )
if( context == this )
{
if( !prev )
mParent->mChildren = this->mSibling;
else
prev->mSibling = this->mSibling;
}
}
//--------------------------------------------------------------------------
ThreadPool::Context* ThreadPool::Context::getChild( const char* name )
{
for( Context* child = getChildren(); child != 0; child = child->getSibling() )
if( dStricmp( child->getName(), name ) == 0 )
return child;
return 0;
}
//--------------------------------------------------------------------------
F32 ThreadPool::Context::getAccumulatedPriorityBias()
{
if( !mAccumulatedPriorityBias )
updateAccumulatedPriorityBiases();
return mAccumulatedPriorityBias;
}
//--------------------------------------------------------------------------
void ThreadPool::Context::setPriorityBias( F32 value )
{
mPriorityBias = value;
mAccumulatedPriorityBias = 0.0;
}
//--------------------------------------------------------------------------
void ThreadPool::Context::updateAccumulatedPriorityBiases()
{
// Update our own priority bias.
mAccumulatedPriorityBias = mPriorityBias;
for( Context* context = getParent(); context != 0; context = context->getParent() )
mAccumulatedPriorityBias *= context->getPriorityBias();
// Update our children.
for( Context* child = getChildren(); child != 0; child = child->getSibling() )
child->updateAccumulatedPriorityBiases();
}
//=============================================================================
// ThreadPool::WorkItem.
//=============================================================================
//--------------------------------------------------------------------------
void ThreadPool::WorkItem::process()
{
execute();
}
//--------------------------------------------------------------------------
bool ThreadPool::WorkItem::isCancellationRequested()
{
return false;
}
//--------------------------------------------------------------------------
bool ThreadPool::WorkItem::cancellationPoint()
{
if( isCancellationRequested() )
{
onCancelled();
return true;
}
else
return false;
}
//--------------------------------------------------------------------------
F32 ThreadPool::WorkItem::getPriority()
{
return 1.0;
}
//=============================================================================
// ThreadPool::WorkItemWrapper.
//=============================================================================
/// Value wrapper for work items while placed on priority queue.
/// Conforms to interface dictated by ThreadSafePriorityQueueWithUpdate.
///
/// @see ThreadSafePriorityQueueWithUpdate
/// @see ThreadPool::WorkItem
///
struct ThreadPool::WorkItemWrapper : public ThreadSafeRef< WorkItem >
{
typedef ThreadSafeRef< WorkItem > Parent;
WorkItemWrapper() {}
WorkItemWrapper( WorkItem* item )
: Parent( item ) {}
bool isAlive();
F32 getPriority();
};
inline bool ThreadPool::WorkItemWrapper::isAlive()
{
WorkItem* item = ptr();
if( !item )
return false;
else if( item->isCancellationRequested() )
{
( *this ) = 0;
return false;
}
else
return true;
}
inline F32 ThreadPool::WorkItemWrapper::getPriority()
{
WorkItem* item = ptr();
AssertFatal( item != 0, "ThreadPool::WorkItemWrapper::getPriority - called on dead item" );
// Compute a scaled priority value based on the item's context.
return ( item->getContext()->getAccumulatedPriorityBias() * item->getPriority() );
}
//=============================================================================
// ThreadPool::WorkerThread.
//=============================================================================
///
///
struct ThreadPool::WorkerThread : public Thread
{
WorkerThread( ThreadPool* pool, U32 index );
WorkerThread* getNext();
virtual void run( void* arg = 0 );
private:
U32 mIndex;
ThreadPool* mPool;
WorkerThread* mNext;
};
ThreadPool::WorkerThread::WorkerThread( ThreadPool* pool, U32 index )
: mPool( pool ),
mIndex( index )
{
// Link us to the pool's thread list.
mNext = pool->mThreads;
pool->mThreads = this;
}
inline ThreadPool::WorkerThread* ThreadPool::WorkerThread::getNext()
{
return mNext;
}
void ThreadPool::WorkerThread::run( void* arg )
{
#ifdef TORQUE_DEBUG
{
// Set the thread's name for debugging.
char buffer[ 2048 ];
dSprintf( buffer, sizeof( buffer ), "ThreadPool(%s) WorkerThread %i", mPool->mName.c_str(), mIndex );
_setName( buffer );
}
#endif
#if defined(TORQUE_OS_XENON)
// On Xbox 360 you must explicitly assign software threads to hardware threads.
// This will distribute job threads across the secondary CPUs leaving both
// primary CPU cores available to the "main" thread. This will help prevent
// more L2 thrashing of the main thread/core.
static U32 sCoreAssignment = 2;
XSetThreadProcessor( GetCurrentThread(), sCoreAssignment );
sCoreAssignment = sCoreAssignment < 6 ? sCoreAssignment + 1 : 2;
#endif
while( 1 )
{
if( checkForStop() )
{
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[ThreadPool::WorkerThread] thread '%i' exits", getId() );
#endif
dFetchAndAdd( mPool->mNumThreads, ( U32 ) -1 );
return;
}
// Mark us as potentially blocking.
dFetchAndAdd( mPool->mNumThreadsReady, ( U32 ) -1 );
bool waitForSignal = false;
{
// Try to take an item from the queue. Do
// this in a separate block, so we'll be
// releasing the item after we have finished.
WorkItemWrapper workItem;
if( mPool->mWorkItemQueue.takeNext( workItem ) )
{
// Mark us as non-blocking as this loop definitely
// won't wait on the semaphore.
dFetchAndAdd( mPool->mNumThreadsReady, 1 );
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[ThreadPool::WorkerThread] thread '%i' takes item '0x%x'", getId(), *workItem );
#endif
workItem->process();
}
else
waitForSignal = true;
}
if( waitForSignal )
{
dFetchAndAdd( mPool->mNumThreadsAwake, ( U32 ) -1 );
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[ThreadPool::WorkerThread] thread '%i' going to sleep", getId() );
#endif
mPool->mSemaphore.acquire();
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[ThreadPool::WorkerThread] thread '%i' waking up", getId() );
#endif
dFetchAndAdd( mPool->mNumThreadsAwake, 1 );
dFetchAndAdd( mPool->mNumThreadsReady, 1 );
}
}
}
//=============================================================================
// ThreadPool.
//=============================================================================
bool ThreadPool::smForceAllMainThread;
U32 ThreadPool::smMainThreadTimeMS;
ThreadPool::QueueType ThreadPool::smMainThreadQueue;
//--------------------------------------------------------------------------
ThreadPool::ThreadPool( const char* name, U32 numThreads )
: mName( name ),
mNumThreads( numThreads ),
mNumThreadsAwake( 0 ),
mThreads( 0 ),
mSemaphore( 0 )
{
// Number of worker threads to create.
if( !mNumThreads )
{
// Use platformCPUInfo directly as in the case of the global pool,
// Platform::SystemInfo will not yet have been initialized.
U32 numLogical;
U32 numPhysical;
U32 numCores;
CPUInfo::CPUCount( numLogical, numCores, numPhysical );
const U32 baseCount = getMax( numLogical, numCores );
if( baseCount )
mNumThreads = baseCount;
else
mNumThreads = 2;
}
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[ThreadPool] spawning %i threads", mNumThreads );
#endif
// Create the threads.
mNumThreadsAwake = mNumThreads;
mNumThreadsReady = mNumThreads;
for( U32 i = 0; i < mNumThreads; i ++ )
{
WorkerThread* thread = new WorkerThread( this, i );
thread->start();
}
}
//--------------------------------------------------------------------------
ThreadPool::~ThreadPool()
{
shutdown();
}
//--------------------------------------------------------------------------
void ThreadPool::shutdown()
{
const U32 numThreads = mNumThreads;
// Tell our worker threads to stop.
for( WorkerThread* thread = mThreads; thread != 0; thread = thread->getNext() )
thread->stop();
// Release the semaphore as many times as there are threads.
// Doing this separately guarantees we're not waking a thread
// that hasn't been set its stop flag yet.
for( U32 n = 0; n < numThreads; ++ n )
mSemaphore.release();
// Delete each worker thread. Wait until death as we're prone to
// running into issues with decomposing work item lists otherwise.
for( WorkerThread* thread = mThreads; thread != 0; )
{
WorkerThread* next = thread->getNext();
thread->join();
delete thread;
thread = next;
}
mThreads = NULL;
mNumThreads = 0;
}
//--------------------------------------------------------------------------
void ThreadPool::queueWorkItem( WorkItem* item )
{
bool executeRightAway = ( getForceAllMainThread() );
#ifdef DEBUG_SPEW
Platform::outputDebugString( "[ThreadPool] %s work item '0x%x'",
( executeRightAway ? "executing" : "queuing" ),
item );
#endif
if( executeRightAway )
item->process();
else
{
// Put the item in the queue.
mWorkItemQueue.insert( item->getPriority(), item );
// Wake up some thread, if we need to.
// Use the ready count here as the wake count does
// not correctly protect the critical section in the
// thread's run function. This may lead us to release
// the semaphore more often than necessary, but it avoids
// a race condition.
if( !dCompareAndSwap( mNumThreadsReady, mNumThreads, mNumThreads ) )
mSemaphore.release();
}
}
//--------------------------------------------------------------------------
void ThreadPool::flushWorkItems( S32 timeOut )
{
AssertFatal( mNumThreads, "ThreadPool::flushWorkItems() - no worker threads in pool" );
U32 endTime = 0;
if( timeOut != -1 )
endTime = Platform::getRealMilliseconds() + timeOut;
// Spinlock until the queue is empty.
while( !mWorkItemQueue.isEmpty() )
{
Platform::sleep( 25 );
// Stop if we have exceeded our processing time budget.
if( timeOut != -1
&& Platform::getRealMilliseconds() >= endTime )
break;
}
}
//--------------------------------------------------------------------------
void ThreadPool::queueWorkItemOnMainThread( WorkItem* item )
{
smMainThreadQueue.insert( item->getPriority(), item );
}
//--------------------------------------------------------------------------
void ThreadPool::processMainThreadWorkItems()
{
AssertFatal( ThreadManager::isMainThread(),
"ThreadPool::processMainThreadWorkItems - this function must only be called on the main thread" );
U32 timeLimit = ( Platform::getRealMilliseconds() + getMainThreadThresholdTimeMS() );
do
{
WorkItemWrapper item;
if( !smMainThreadQueue.takeNext( item ) )
break;
else
item->process();
}
while( Platform::getRealMilliseconds() < timeLimit );
}

View file

@ -0,0 +1,398 @@
//-----------------------------------------------------------------------------
// 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 _THREADPOOL_H_
#define _THREADPOOL_H_
#ifndef _THREADSAFEREFCOUNT_H_
#include "platform/threads/threadSafeRefCount.h"
#endif
#ifndef _THREADSAFEPRIORITYQUEUE_H_
#include "platform/threads/threadSafePriorityQueue.h"
#endif
#ifndef _PLATFORM_THREAD_SEMAPHORE_H_
#include "platform/threads/semaphore.h"
#endif
#ifndef _TSINGLETON_H_
#include "core/util/tSingleton.h"
#endif
/// @file
/// Interface for an asynchronous work manager.
/// Asynchronous work manager.
///
/// Thread pooling allows to submit work items for background execution.
/// Each work item will be placed on a queue and, based on a total priority
/// ordering, executed when it has the highest priority and a worker thread
/// becomes available.
///
/// @note The global pool maintains the invariant that only the main thread
/// may submit items in order to be able to flush the item queue reliably
/// from the main thread itself. If other threads were issuing items to
/// the queue, the queue may never empty out and the main thread will
/// deadlock.
///
/// Flushing is the simplest method to guarantee that no asynchronous
/// operation is pending in a specific case (deletion of the target object
/// being the most common case). However, when possible, avoid this
/// situation and design your work items to operate independently,
/// e.g. by having only a single point of access to data that may have
/// disappeared in the meantime and putting a check around that single
/// access so that the item will silently die when its target object has
/// disappeared.
///
/// The cleanest safe solution to this is to create a separate concurrently
/// reference-counted structure that holds all interfacing state and
/// functionality shared between a work item and its issueing code. This way
/// the host object can safely disappear with the interfacing structure
/// automatically being released once the last concurrent work item has been
/// processed or discarded.
///
class ThreadPool
{
public:
/// A ThreadPool context defines a logical context in which WorkItems are
/// being executed. Their primary use is for biasing priorities of
/// WorkItems.
///
/// Contexts are arranged in a tree hierarchy. Each parent node's priority
/// bias scales all the priority biases underneath it.
///
/// Note that instances of this class are meant to be instantiated
/// globally only.
///
class Context
{
protected:
/// Superordinate context; scales this context's priority bias.
Context* mParent;
/// First child.
Context* mChildren;
/// Next sibling in child chain.
Context* mSibling;
/// Name of this context. Should be unique in parent namespace.
const char* mName;
/// Priority scale factor of this context.
F32 mPriorityBias;
/// Accumulated scale factor.
F32 mAccumulatedPriorityBias;
/// The root context; does not modify priorities. All contexts should be direct or indirect children of this one.
static Context smRootContext;
/// Recursively update cached accumulated priority biases.
void updateAccumulatedPriorityBiases();
public:
Context( const char* name, Context* parent, F32 priorityBias );
~Context();
/// Return the name of the worker threading context.
const char* getName() const
{
return mName;
}
/// Return the context's own work item priority bias.
F32 getPriorityBias() const
{
return mPriorityBias;
}
/// Return the superordinate node to the current context.
Context* getParent() const
{
return mParent;
}
/// Return the next sibling to the current context.
Context* getSibling() const
{
return mSibling;
}
/// Return the first child context.
Context* getChildren() const
{
return mChildren;
}
/// Return the root context.
static Context* ROOT_CONTEXT()
{
return &smRootContext;
}
///
F32 getAccumulatedPriorityBias();
///
Context* getChild( const char* name );
///
void setPriorityBias( F32 value );
};
/// An action to execute on a worker thread from the pool.
///
/// Work items are concurrently reference-counted and will be
/// automatically released once the last reference disappears.
///
class WorkItem : public ThreadSafeRefCount< WorkItem >
{
public:
typedef ThreadSafeRefCount< WorkItem > Parent;
protected:
/// The work context of this item.
Context* mContext;
/// Mark a point in a work item's execution where the item can
/// be safely cancelled.
///
/// This method should be called by subclasses' execute() methods
/// whenever an item can be safely cancelled. When it returns true,
/// the work item should exit from its execute() method.
bool cancellationPoint();
/// Called when the item has been cancelled.
virtual void onCancelled() {}
/// Execute the actions associated with this work item.
/// This is the primary function to implement by subclasses.
virtual void execute() = 0;
public:
/// Construct a new work item.
///
/// @param context The work context in which the item should be placed.
/// If NULL, the root context will be used.
WorkItem( Context* context = 0 )
: mContext( context ? context : Context::ROOT_CONTEXT() )
{
}
virtual ~WorkItem() {}
/// Return the work context associated with the work item.
inline Context* getContext() const
{
return mContext;
}
/// Process the work item.
void process();
/// Return true if the work item should be cancelled.
///
/// This method can be overridden by subclasses. It's value will be
/// checked each time cancellationPoint() is called. When it returns
/// true, the item's process() method will exit automatically.
///
/// @return true, if item should be cancelled; default is false.
/// @see ThreadPool::WorkItem::cancellationPoint
virtual bool isCancellationRequested();
/// Return the item's base priority value.
/// @return item priority; defaults to 1.0.
virtual F32 getPriority();
};
typedef ThreadSafeRef< WorkItem > WorkItemPtr;
struct GlobalThreadPool;
protected:
struct WorkItemWrapper;
struct WorkerThread;
friend struct WorkerThread; // mSemaphore, mNumThreadsAwake, mThreads
typedef ThreadSafePriorityQueueWithUpdate< WorkItemWrapper, F32 > QueueType;
/// Name of this pool. Mainly for debugging. Used to name worker threads.
String mName;
/// Number of worker threads spawned by the pool.
U32 mNumThreads;
/// Number of worker threads in non-sleeping state.
U32 mNumThreadsAwake;
/// Number of worker threads guaranteed to be non-blocking.
U32 mNumThreadsReady;
/// Semaphore used to wake up threads, if necessary.
Semaphore mSemaphore;
/// Threaded priority queue for concurrent access by worker threads.
QueueType mWorkItemQueue;
/// List of worker threads.
WorkerThread* mThreads;
/// Force all work items to execute on main thread;
/// turns this into a single-threaded system.
/// Primarily useful to find whether malfunctions are caused
/// by parallel execution or not.
static bool smForceAllMainThread;
///
static U32 smMainThreadTimeMS;
/// Work queue for main thread; can be used to ping back work items to
/// main thread that need processing that can only happen on main thread.
static QueueType smMainThreadQueue;
public:
/// Create a new thread pool with the given number of worker threads.
///
/// If numThreads is zero (the default), the number of threads created
/// will be based on the number of CPU cores available.
///
/// @param numThreads Number of threads to create or zero for default.
ThreadPool( const char* name, U32 numThreads = 0 );
~ThreadPool();
/// Manually shutdown threads outside of static destructors.
void shutdown();
///
void queueWorkItem( WorkItem* item );
///
/// <em>For the global pool, it is very important to only ever call
/// this function on the main thread and to let work items only ever
/// come from the main thread. Otherwise this function has the potential
/// of dead-locking as new work items may constantly be fed to the queue
/// without it ever getting empty.</em>
///
/// @param timeOut Soft limit on the number of milliseconds to wait for
/// the queue to flush out. -1 = infinite.
void flushWorkItems( S32 timeOut = -1 );
/// Add a work item to the main thread's work queue.
///
/// The main thread's work queue will be processed each frame using
/// a set timeout to limit the work being done. Nonetheless, work
/// items will not be suspended in-midst of processing, so make sure
/// that whatever work you issue to the main thread is light work
/// or you may see short hangs in gameplay.
///
/// To reiterate this: any code executed through this interface directly
/// adds to frame processing time on the main thread.
///
/// This method *may* (and is meant to) be called from threads
/// other than the main thread.
static void queueWorkItemOnMainThread( WorkItem* item );
/// Process work items waiting on the main thread's work queue.
///
/// There is a soft limit imposed on the time this method is allowed
/// to run so as to balance frame-to-frame load. However, work
/// items, once their processing is initiated, will not be suspended
/// and will run for as long as they take to complete, so make sure
/// individual items perform as little work as necessary.
///
/// @see ThreadPool::getMainThreadThesholdTimeMS
static void processMainThreadWorkItems();
/// Return the interval in which item priorities are updated on the queue.
/// @return update interval in milliseconds.
U32 getQueueUpdateInterval() const
{
return mWorkItemQueue.getUpdateInterval();
}
/// Return the priority increment applied to work items on each passing of the update interval.
F32 getQueueTimeBasedPriorityBoost() const
{
return mWorkItemQueue.getTimeBasedPriorityBoost();
}
/// Set the update interval of the work item queue to the given value.
/// @param milliSeconds Time between updates in milliseconds.
void setQueueUpdateInterval( U32 milliSeconds )
{
mWorkItemQueue.setUpdateInterval( milliSeconds );
}
/// Set the priority increment applied to work items on each update interval.
/// @param value Priority increment. Set to zero to deactivate.
void setQueueTimeBasedPriorityBoost( F32 value )
{
mWorkItemQueue.setTimeBasedPriorityBoost( value );
}
///
static U32& getMainThreadThresholdTimeMS()
{
return smMainThreadTimeMS;
}
///
static bool& getForceAllMainThread()
{
return smForceAllMainThread;
}
/// Return the global thread pool singleton.
static ThreadPool& GLOBAL();
};
typedef ThreadPool::Context ThreadContext;
typedef ThreadPool::WorkItem ThreadWorkItem;
struct ThreadPool::GlobalThreadPool : public ThreadPool, public ManagedSingleton< GlobalThreadPool >
{
typedef ThreadPool Parent;
GlobalThreadPool()
: Parent( "GLOBAL" ) {}
// For ManagedSingleton.
static const char* getSingletonName() { return "GlobalThreadPool"; }
};
inline ThreadPool& ThreadPool::GLOBAL()
{
return *( GlobalThreadPool::instance() );
}
#endif // !_THREADPOOL_H_

View file

@ -0,0 +1,357 @@
//-----------------------------------------------------------------------------
// 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 _THREADPOOLASYNCIO_H_
#define _THREADPOOLASYNCIO_H_
#ifndef _THREADPOOL_H_
# include "platform/threads/threadPool.h"
#endif
#ifndef _RAWDATA_H_
# include "core/util/rawData.h"
#endif
#ifndef _TSTREAM_H_
# include "core/stream/tStream.h"
#endif
//RDTODO: I/O error handling
/// @file
/// Thread pool work items for asynchronous stream I/O.
/// Through the use of stream filters, this can be basically used for any
/// type of asynchronous stream processing.
//--------------------------------------------------------------------------
// AsyncIOItem.
//--------------------------------------------------------------------------
/// Abstract superclass of async I/O work items.
///
/// Supports both offset-based stream I/O as well as I/O on streams with
/// implicit positions. Note that if you use the latter type, make sure
/// that no other thread is messing with the stream at the same time or
/// chaos will ensue.
///
/// @param T Type of elements being streamed.
template< typename T, class Stream >
class AsyncIOItem : public ThreadPool::WorkItem
{
public:
typedef WorkItem Parent;
typedef T ValueType;
typedef RawDataT< ValueType > BufferType;
typedef U32 OffsetType;
typedef Stream StreamType;
protected:
/// Buffer keeping/receiving the data elements.
BufferType mBuffer;
/// The stream to read from/write to.
StreamType* mStream;
/// Number of elements to read from/write to the stream.
U32 mNumElements;
/// Offset in "mBuffer" from where to read/where to start writing to.
U32 mOffsetInBuffer;
/// Offset in stream from where to read/where to write to.
/// @note This is only meaningful if the stream is an offset I/O
/// stream. For a stream that is can do both types of I/O,
/// explicit offsets are preferred and this value is used.
OffsetType mOffsetInStream;
///
ValueType* getBufferPtr()
{
return &getBuffer().data[ getOffsetInBuffer() ];
}
public:
///
/// If the stream uses implicit positioning, then the supplied "offsetInStream"
/// is meaningless and ignored.
AsyncIOItem( StreamType* stream, U32 numElements, OffsetType offsetInStream,
ThreadContext* context = 0 )
: Parent( context ),
mStream( stream ),
mNumElements( numElements ),
mOffsetInStream( offsetInStream ),
mOffsetInBuffer( 0 ) {}
/// Construct a read item on "stream" that stores data into the given "buffer".
///
AsyncIOItem( StreamType* stream, BufferType& buffer, U32 offsetInBuffer,
U32 numElements, OffsetType offsetInStream, bool takeOwnershipOfBuffer = true,
ThreadContext* context = 0 )
: Parent( context ),
mStream( stream ),
mBuffer( buffer ),
mNumElements( numElements ),
mOffsetInStream( offsetInStream ),
mOffsetInBuffer( offsetInBuffer )
{
if( takeOwnershipOfBuffer )
mBuffer.ownMemory = true;
}
/// Return the stream being written to/read from.
StreamType* getStream()
{
return mStream;
}
/// Return the data buffer being written to/read from.
/// @note This may not yet have been allocated.
BufferType& getBuffer()
{
return mBuffer;
}
/// Return the number of elements involved in the transfer.
U32 getNumElements()
{
return mNumElements;
}
/// Return the position in the data buffer at which to start the transfer.
U32 getOffsetInBuffer()
{
return mOffsetInBuffer;
}
/// Return the position in the stream at which to start the transfer.
/// @note Only meaningful for streams that support offset I/O.
OffsetType getOffsetInStream()
{
return mOffsetInStream;
}
};
//--------------------------------------------------------------------------
// AsyncReadItem.
//--------------------------------------------------------------------------
//RDTODO: error handling
/// Work item to asynchronously read from a stream.
///
/// The given stream type may implement any of the input stream
/// interfaces. Preference is given to IAsyncInputStream, then to
/// IOffsetInputStream, and only if none of these are implemented
/// IInputStream is used.
///
/// For IAsyncInputStreams, the async read operation is issued immediately
/// on the constructing thread and then picked up on the worker thread.
/// This ensures optimal use of concurrency.
template< typename T, class Stream = IOffsetInputStream< T > >
class AsyncReadItem : public AsyncIOItem< T, Stream >
{
public:
typedef AsyncIOItem< T, Stream > Parent;
typedef typename Parent::StreamType StreamType;
typedef typename Parent::OffsetType OffsetType;
typedef typename Parent::BufferType BufferType;
typedef typename Parent::ValueType ValueType;
/// Construct a read item that reads "numElements" at "offsetInStream"
/// from "stream".
///
/// Since with this constructor no data buffer is supplied, it will be
/// dynamically allocated by the read() method. Note that this only makes
/// sense if this class is subclassed and processing is done on the buffer
/// after it has been read.
///
/// @param stream The stream to read from.
/// @param numElement The number of elements to read from the stream.
/// @param offsetInStream The offset at which to read from the stream;
/// ignored if the stream uses implicit positioning
/// @param context The tread pool context to place the item into.
AsyncReadItem( StreamType* stream, U32 numElements, OffsetType offsetInStream,
ThreadContext* context = 0 )
: Parent( stream, numElements, offsetInStream, context )
{
_prep();
}
AsyncReadItem( StreamType* stream, U32 numElements, OffsetType offsetInStream,
BufferType& buffer, bool takeOwnershipOfBuffer = false,
U32 offsetInBuffer = 0, ThreadContext* context = 0 )
: Parent( stream, buffer, offsetInBuffer, numElements, offsetInStream, takeOwnershipOfBuffer, context )
{
_prep();
}
/// @return The number of elements actually read from the stream.
U32 getNumElementsRead()
{
return mNumElementsRead;
}
protected:
/// Handle of asynchronous stream read, if we are using an async interface.
void* mAsyncHandle;
/// After the read operation has completed, this holds the number of
/// elements actually read from the stream.
U32 mNumElementsRead;
virtual void execute();
void _allocBuffer()
{
if( !this->getBuffer().data )
this->getBuffer().alloc( this->getNumElements() );
}
void _prep()
{
IAsyncInputStream< T >* s = dynamic_cast< IAsyncInputStream< T >* >( this->getStream() );
if( s )
{
_allocBuffer();
mAsyncHandle = s->issueReadAt( this->getOffsetInStream(), this->getBufferPtr(), this->getNumElements() );
}
}
// Helper functions to differentiate between stream types.
void _read( IInputStream< T >* stream )
{
mNumElementsRead = stream->read( this->getBufferPtr(), this->getNumElements() );
}
void _read( IOffsetInputStream< T >* stream )
{
mNumElementsRead = stream->readAt( this->getOffsetInStream(), this->getBufferPtr(), this->getNumElements() );
}
void _read( IAsyncInputStream< T >* stream )
{
stream->tryCompleteReadAt( mAsyncHandle, mNumElementsRead, true );
}
};
template< typename T, class Stream >
void AsyncReadItem< T, Stream >::execute()
{
_allocBuffer();
// Read the data. Do a dynamic cast for any of the
// interfaces we prefer.
if( this->cancellationPoint() ) return;
StreamType* stream = this->getStream();
if( dynamic_cast< IAsyncInputStream< T >* >( stream ) )
_read( ( IAsyncInputStream< T >* ) stream );
else if( dynamic_cast< IOffsetInputStream< T >* >( stream ) )
_read( ( IOffsetInputStream< T >* ) stream );
else
_read( stream );
}
//--------------------------------------------------------------------------
// AsyncWriteItem.
//--------------------------------------------------------------------------
/// Work item for writing to an output stream.
///
/// The stream being written to may implement any of the given output stream
/// interfaces. Preference is given to IAsyncOutputStream, then to
/// IOffsetOutputStream, and only if none of these is implemented IOutputStream
/// is used.
///
/// A useful feature is to yield ownership of the data buffer to the
/// write item. This way, this can be pretty much used in a fire-and-forget
/// manner where after submission, no further synchronization happens
/// between the client and the work item.
///
/// @note Be aware that if writing to an output stream that has an implicit
/// position property, multiple concurrent writes will interfere with each other.
template< typename T, class Stream = IOffsetOutputStream< T > >
class AsyncWriteItem : public AsyncIOItem< T, Stream >
{
public:
typedef AsyncIOItem< T, Stream > Parent;
typedef typename Parent::StreamType StreamType;
typedef typename Parent::OffsetType OffsetType;
typedef typename Parent::BufferType BufferType;
typedef typename Parent::ValueType ValueType;
AsyncWriteItem( StreamType* stream, U32 numElements, OffsetType offsetInStream,
BufferType& buffer, bool takeOwnershipOfBuffer = true,
U32 offsetInBuffer = 0, ThreadContext* context = 0 )
: Parent( stream, buffer, offsetInBuffer, numElements, offsetInStream, takeOwnershipOfBuffer, context )
{
_prep( stream );
}
protected:
/// Handle of asynchronous write operation, if the stream implements IAsyncOutputStream.
void* mAsyncHandle;
virtual void execute();
void _prep( StreamType* stream )
{
IAsyncOutputStream< T >* s = dynamic_cast< IAsyncOutputStream< T >* >( stream );
if( s )
mAsyncHandle = s->issueWriteAt( this->getOffset(), this->getBufferPtr(), this->getNumElements() );
}
void _write( IOutputStream< T >* stream )
{
stream->write( this->getBufferPtr(), this->getNumElements() );
}
void _write( IOffsetOutputStream< T >* stream )
{
stream->writeAt( this->getOffsetInStream(), this->getBufferPtr(), this->getNumElements() );
}
void _write( IAsyncOutputStream< T >* stream )
{
stream->tryCompleteWriteAt( mAsyncHandle, true );
}
};
template< typename T, class Stream >
void AsyncWriteItem< T, Stream >::execute()
{
if( this->cancellationPoint() ) return;
StreamType* stream = this->getStream();
if( dynamic_cast< IAsyncOutputStream< T >* >( stream ) )
_write( ( IAsyncOutputStream< T >* ) stream );
if( dynamic_cast< IOffsetOutputStream< T >* >( stream ) )
_write( ( IOffsetOutputStream< T >* ) stream );
else
_write( stream );
}
#endif // _THREADPOOLASYNCIO_H_

View file

@ -0,0 +1,474 @@
//-----------------------------------------------------------------------------
// 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 _THREADSAFEDEQUE_H_
#define _THREADSAFEDEQUE_H_
#ifndef _THREADSAFEFREELIST_H_
# include "platform/threads/threadSafeFreeList.h"
#endif
#include "platform/tmm_off.h"
/// Fast, lock-free double-ended queue for concurrent access.
///
/// @param T Type of list elements; must have default contructor.
template< typename T >
class ThreadSafeDeque
{
// Lock-free deques using just single-word atomic writes are
// very tricky as each pointer update must immediately result
// in a fully valid list state. The idea here is to maintain the
// deque's head and tail pointers unreliably but otherwise keep a
// regular double-linked list (since we never insert nodes in the
// middle, single-word writes are all we need).
//
// Deletions are a bit less straightforward and require the threads
// to work cooperatively. Since failure of a pointer update depends
// on the deletion state, the deletion flag has to be encoded into
// the link fields. However, as there are two link fields this creates
// two independent deletion flags for each single node, one on the
// next link and one on the prev link.
//
// This will not lead to a problem, though, as it only becomes relevant
// when there is only a single value in the list which, even if the
// respective node gets both deleted and appended/prepended a new node,
// will result in a valid list state.
public:
typedef T ValueType;
protected:
class Node;
class DeleteNode;
typedef ThreadSafeRef< Node > NodeRef;
/// List node.
class Node : public ThreadSafeFreeListNode< Node, DeleteNode >
{
public:
friend class DeleteNode; // mFreeList;
typedef typename ThreadSafeDeque< T >::ValueType ValueType;
/// Thread claim flag. This is to prevent two threads who concurrently
/// do a tryPopFront() and tryPopBack() respectively on a deque with just
/// a single node to both claim and return the same value (which would happen
/// without the flag as otherwise both threads would use two different
/// deletion bits for claiming the node).
U32 mIsClaimed;
/// Link to the freelist that the node has been
/// allocated from.
ThreadSafeFreeList< Node >& mFreeList;
/// Value contained in the node.
ValueType mValue;
/// Reference to next node and deletion bit.
NodeRef mNext;
/// Reference to previous node and deletion bit.
NodeRef mPrev;
/// Construct an unlinked node allocated from "freeList".
Node( ThreadSafeFreeList< Node >& freeList, const ValueType& value )
: mIsClaimed( 0 ), mFreeList( freeList ), mValue( value ) {}
};
class DeleteNode
{
public:
template< typename N >
static void destroy( N* ptr )
{
AssertFatal( ptr->mIsClaimed,
"ThreadSafeDeque::DeleteNode::destroy() - deleting unclaimed node" );
destructInPlace( ptr );
ptr->mFreeList.free( ptr );
}
};
#ifdef TORQUE_DEBUG
S32 mNumValues;
#endif
/// Reference to the head node.
NodeRef mHead;
///
NodeRef mTail;
/// Free list for list nodes.
ThreadSafeFreeList< Node > mFreeList;
/// @return the leftmost node in the list.
/// @note Updates the list state and may purge deleted nodes.
NodeRef getHead();
/// @return the rightmost node in the list.
/// @note Updates the list state and may purge deleted nodes.
NodeRef getTail();
public:
/// Construct an empty deque.
ThreadSafeDeque()
{
#ifdef TORQUE_DEBUG
mNumValues = 0;
#endif
}
~ThreadSafeDeque()
{
ValueType value;
while( tryPopFront( value ) );
AssertFatal( isEmpty(), "ThreadSafeDeque::~ThreadSafeDeque() - not empty" );
}
/// @return true if the queue is empty.
bool isEmpty()
{
return ( !getHead() && !getTail() );
}
/// Prepend the given value to the list.
void pushFront( const ValueType& value );
/// Append the given value to the list.
void pushBack( const ValueType& value );
/// Try to take the leftmost value from the deque.
/// Fails if the deque is empty at the time the method tries to
/// take a node from the list.
bool tryPopFront( ValueType& outValue );
/// Try to take the rightmost value from the deque.
/// Fails if the deque is empty at the time the method tries to
/// take a node from the list.
bool tryPopBack( ValueType& outValue );
void dumpDebug()
{
#ifdef TORQUE_DEBUG
Platform::outputDebugString( "[ThreadSafeDeque] numValues=%i", mNumValues );
mFreeList.dumpDebug();
#endif
}
};
// The getHead() and getTail() code here is pretty much brute-force in order
// to keep the complexities of synchronizing it bounded. We just let each
// thread work as if it is the only thread but require each one to start from
// scratch on each iteration.
template< typename T >
typename ThreadSafeDeque< T >::NodeRef ThreadSafeDeque< T >::getHead()
{
// Find leftmost node.
NodeRef result;
while( 1 )
{
// Iterate through to leftmost node.
{
NodeRef head = mHead;
while( head != NULL )
{
NodeRef prev = head->mPrev;
if( prev != NULL )
mHead.trySetFromTo( head, prev, NodeRef::TAG_Unset );
else
break;
head = mHead;
}
}
// Clear out dead nodes at front of list.
{
NodeRef head = mHead;
if( head && head->mPrev.isTagged() )
{
NodeRef next = head->mNext;
mHead.trySetFromTo( head, next, NodeRef::TAG_Unset );
mTail.trySetFromTo( head, next, NodeRef::TAG_Unset );
if( next != NULL )
next->mPrev.trySetFromTo( head, NULL );
head->mNext.trySetFromTo( next, NULL, NodeRef::TAG_Set );
continue; // Restart.
}
}
// Try head.
NodeRef head = mHead;
if( head != NULL && !head->mPrev.isTagged() )
{
result = head;
break;
}
// Try tail.
if( !head )
{
head = mTail;
if( !head )
break;
}
// Update head.
NodeRef prev = head->mPrev;
if( head->mPrev != NULL )
{
if( !mHead.trySetFromTo( head, prev, NodeRef::TAG_Unset ) )
mHead.trySetFromTo( NULL, prev );
}
else
mHead.trySetFromTo( NULL, head );
}
AssertFatal( !result.isTagged(), "ThreadSafeDeque::getHead() - head got tagged" );
return result;
}
template< typename T >
typename ThreadSafeDeque< T >::NodeRef ThreadSafeDeque< T >::getTail()
{
// Find rightmost node.
NodeRef result;
while( 1 )
{
// Iterate through to rightmost node.
{
NodeRef tail = mTail;
while( tail != NULL )
{
NodeRef next = tail->mNext;
if( next != NULL )
mTail.trySetFromTo( tail, next, NodeRef::TAG_Unset );
else
break;
tail = mTail;
}
}
// Clear out dead nodes at tail of list.
{
NodeRef tail = mTail;
if( tail != NULL && tail->mNext.isTagged() )
{
NodeRef prev = tail->mPrev;
mHead.trySetFromTo( tail, prev, NodeRef::TAG_Unset );
mTail.trySetFromTo( tail, prev, NodeRef::TAG_Unset );
if( prev != NULL )
prev->mNext.trySetFromTo( tail, NULL );
tail->mPrev.trySetFromTo( prev, NULL, NodeRef::TAG_Set );
continue; // Restart.
}
}
// Try tail.
NodeRef tail = mTail;
if( tail != NULL && !tail->mNext.isTagged() )
{
result = tail;
break;
}
// Try head.
if( !tail )
{
tail = mHead;
if( !tail )
break;
}
// Update tail.
NodeRef next = tail->mNext;
if( next != NULL )
{
if( !mTail.trySetFromTo( tail, next, NodeRef::TAG_Unset ) )
mTail.trySetFromTo( NULL, next );
}
else
mTail.trySetFromTo( NULL, tail );
}
AssertFatal( !result.isTagged(), "ThreadSafeDeque::getTail() - tail got tagged" );
return result;
}
template< typename T >
void ThreadSafeDeque< T >::pushFront( const ValueType& value )
{
NodeRef nextNode;
NodeRef newNode;
NodeRef::unsafeWrite( newNode, new ( mFreeList ) Node( mFreeList, value ) );
while( 1 )
{
nextNode = getHead();
if( !nextNode )
{
newNode->mNext = NULL;
if( mHead.trySetFromTo( NULL, newNode ) )
break;
}
else
{
newNode->mNext = nextNode;
if( nextNode->mPrev.trySetFromTo( NULL, newNode, NodeRef::TAG_FailIfSet ) )
break;
}
}
#ifdef TORQUE_DEBUG
dFetchAndAdd( mNumValues, 1 );
#endif
}
template< typename T >
void ThreadSafeDeque< T >::pushBack( const ValueType& value )
{
NodeRef prevNode;
NodeRef newNode;
NodeRef::unsafeWrite( newNode, new ( mFreeList ) Node( mFreeList, value ) );
while( 1 )
{
prevNode = getTail();
if( !prevNode )
{
newNode->mPrev = NULL;
if( mHead.trySetFromTo( NULL, newNode ) ) // use head so we synchronize with pushFront
break;
}
else
{
newNode->mPrev = prevNode;
if( prevNode->mNext.trySetFromTo( NULL, newNode, NodeRef::TAG_FailIfSet ) )
break;
}
}
#ifdef TORQUE_DEBUG
dFetchAndAdd( mNumValues, 1 );
#endif
}
template< typename T >
bool ThreadSafeDeque< T >::tryPopFront( ValueType& outValue )
{
NodeRef oldHead;
while( 1 )
{
oldHead = getHead();
if( !oldHead )
return false;
// Try to claim the node.
if( oldHead->mPrev.trySetFromTo( NULL, NULL, NodeRef::TAG_SetOrFail ) )
{
if( dCompareAndSwap( oldHead->mIsClaimed, 0, 1 ) )
break;
else
continue;
}
}
outValue = oldHead->mValue;
oldHead = NULL;
// Cleanup.
getHead();
#ifdef TORQUE_DEBUG
dFetchAndAdd( mNumValues, -1 );
#endif
return true;
}
template< typename T >
bool ThreadSafeDeque< T >::tryPopBack( ValueType& outValue )
{
NodeRef oldTail;
while( 1 )
{
oldTail = getTail();
if( !oldTail )
return false;
// Try to claim the node.
if( oldTail->mNext.trySetFromTo( NULL, NULL, NodeRef::TAG_SetOrFail ) )
{
if( dCompareAndSwap( oldTail->mIsClaimed, 0, 1 ) )
break;
}
}
outValue = oldTail->mValue;
oldTail = NULL;
// Cleanup.
getTail();
#ifdef TORQUE_DEBUG
dFetchAndAdd( mNumValues, -1 );
#endif
return true;
}
#include "platform/tmm_on.h"
#endif // _THREADSAFEDEQUE_H_

View file

@ -0,0 +1,192 @@
//-----------------------------------------------------------------------------
// 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 _THREADSAFEFREELIST_H_
#define _THREADSAFEFREELIST_H_
#ifndef _THREADSAFEREFCOUNT_H_
# include "platform/threads/threadSafeRefCount.h"
#endif
#ifndef _PLATFORMINTRINSICS_H_
# include "platform/platformIntrinsics.h"
#endif
#include "platform/tmm_off.h"
/// @file
/// Lock-free freelists for concurrent access.
/// Freelist for re-using allocations in a concurrent setting.
///
/// @note Make sure that there are no more allocations in use
/// when the free list is destructed.
/// @note Allocated instances come with a reference already counted
/// on the instance.
///
/// @param T Type of elements to allocate; must be derived from
/// ThreadSafeRefCount and have at least define one additional
/// pointer-sized field.
template< class T >
class ThreadSafeFreeList
{
protected:
T* mFreeList;
#ifdef TORQUE_DEBUG
S32 mNumNodesTotal;
S32 mNumNodesFree;
#endif
T*& getNext( T* ptr )
{
return *( ( T** ) &( ( U8* ) ptr )[ sizeof( T ) - sizeof( T* ) ] );
}
public:
/// Create the freelist.
///
/// @param numPreAlloc Number of instances to pre-allocate.
ThreadSafeFreeList( U32 numPreAlloc = 0 )
: mFreeList( 0 )
{
#ifdef TORQUE_DEBUG
mNumNodesTotal = 0;
mNumNodesFree = 0;
#endif
for( U32 i = 0; i < numPreAlloc; ++ i )
free( alloc() );
}
~ThreadSafeFreeList()
{
#ifdef TORQUE_DEBUG
AssertWarn( mNumNodesTotal == mNumNodesFree,
"ThreadSafeFreeList::~ThreadSafeFreeList() - still got live instances" );
#endif
// Destroy remaining nodes. Not synchronized. We assume all
// concurrent processing to have finished.
while( mFreeList )
{
T* next = getNext( mFreeList );
dFree( mFreeList );
mFreeList = next;
}
}
/// Return memory for a new instance.
void* alloc()
{
T* ptr;
while( 1 )
{
ptr = ThreadSafeRef< T >::safeRead( mFreeList );
if( !ptr )
{
ptr = ( T* ) dMalloc( sizeof( T ) );
dMemset( ptr, 0, sizeof( T ) );
#ifdef TORQUE_DEBUG
dFetchAndAdd( mNumNodesTotal, 1 );
#endif
ptr->addRef();
break;
}
else if( dCompareAndSwap( mFreeList, ptr, getNext( ptr ) ) )
{
#ifdef TORQUE_DEBUG
dFetchAndAdd( mNumNodesFree, -1 );
#endif
ptr->clearLowestBit();
break;
}
else
ptr->release();
}
return ptr;
}
/// Return the memory allocated to the given instance to the freelist.
void free( void* ptr )
{
AssertFatal( ptr, "ThreadSafeFreeList::free() - got a NULL pointer" );
T* node = ( T* ) ptr;
while( 1 )
{
T* list = mFreeList;
getNext( node ) = list;
if( dCompareAndSwap( mFreeList, list, node ) )
break;
}
#ifdef TORQUE_DEBUG
dFetchAndAdd( mNumNodesFree, 1 );
#endif
}
void dumpDebug()
{
#ifdef TORQUE_DEBUG
Platform::outputDebugString( "[ThreadSafeFreeList] total=%i, free=%i",
mNumNodesTotal, mNumNodesFree );
#endif
}
};
/// Baseclass for objects allocated from ThreadSafeFreeLists.
template< class T, class DeletePolicy = DeleteSingle >
class ThreadSafeFreeListNode : public ThreadSafeRefCount< T, DeletePolicy >
{
public:
typedef ThreadSafeRefCount< T, DeletePolicy > Parent;
ThreadSafeFreeListNode()
: Parent( false ) {}
static void* operator new( size_t size, ThreadSafeFreeList< T >& freeList )
{
AssertFatal( size <= sizeof( T ),
"ThreadSafeFreeListNode::new() - size exceeds limit of freelist" );
TORQUE_UNUSED( size );
return freeList.alloc();
}
static void operator delete( void* ptr, ThreadSafeFreeList< T >& freeList )
{
freeList.free( ptr );
}
};
#include "platform/tmm_on.h"
#endif // _THREADSAFEFREELIST_H_

View file

@ -0,0 +1,740 @@
//-----------------------------------------------------------------------------
// 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 _THREADSAFEPRIORITYQUEUE_H_
#define _THREADSAFEPRIORITYQUEUE_H_
#ifndef _PLATFORMINTRINSICS_H_
#include "platform/platformIntrinsics.h"
#endif
#ifndef _THREADSAFEREFCOUNT_H_
#include "platform/threads/threadSafeRefCount.h"
#endif
#ifndef _TYPETRAITS_H_
#include "platform/typetraits.h"
#endif
// Disable TMM's new operator grabbing.
#include "platform/tmm_off.h"
//#define DEBUG_SPEW
/// @file
/// Template code for an efficient thread-safe priority queue
/// implementation. There are two alternative implementations to
/// choose from: ThreadSafePriorityQueue and ThreadSafePriorityQueueWithUpdate
/// where the latter adds concurrent status updates of queue items to
/// the former implementation.
//--------------------------------------------------------------------------
// ThreadSafePriorityQueue.
//--------------------------------------------------------------------------
/// Fast, lock-free priority queue implementation for concurrent access.
///
/// Equal priorities are allowed and are placed <em>before</em> existing items of
/// identical priority in the queue.
///
/// Based on (but with significant deviations from) "Fast and Lock-Free Concurrent
/// Priority Queues for Multi-Thread Systems" by Hakan Sundell and Philippas Tsigas.
/// Parts of the skiplist code is based on work by William Pugh.
///
/// @param T The item value type. Must have a default constructor.
/// @param K The priority key type. Must be comparable, have a default constructor,
/// and be a valid template parameter to TypeTraits.
/// @param SORT_MIN_TO_MAX If true, the queue sorts from minimum to maximum priority or
/// the reverse if false.
/// @param MAX_LEVEL The number of levels a node can have at most.
/// @param PROBABILISTIC_BIAS The probabilistic level distribution factor for
/// the skiplist. Multiplied by 100 and turned into int to conform to restrictions
/// on non-type template parameters.
///
/// @see TypeTraits
template< typename T, typename K = F32, bool SORT_MIN_TO_MAX = false, U32 MAX_LEVEL = 4, U32 PROBABILISTIC_BIAS = 50 >
struct ThreadSafePriorityQueue
{
typedef T ValueType;
typedef K KeyType;
enum { MAX_LEVEL_CONST = MAX_LEVEL };
ThreadSafePriorityQueue();
bool isEmpty();
void insert( KeyType priority, const T& value );
bool takeNext( T& outValue, KeyType upToPriority = ( SORT_MIN_TO_MAX ? TypeTraits< KeyType >::MAX : TypeTraits< KeyType >::MIN ) );
protected:
struct Node;
typedef ThreadSafeRef< Node > NodePtr;
friend class ThreadSafeRefCount< Node >;
friend struct DeleteSingle;
/// A queue node.
///
/// Nodes are reference-counted to coordinate memory management
/// between the different threads. Reclamation happens on the
/// thread that releases the last reference.
///
/// Reference-counting and deletion requests are kept separate.
/// A given node is marked for deletion and will then have its references
/// progressively disappear and eventually be reclaimed once the
/// reference count drops to zero.
///
/// Note that 'Next' references are released by the destructor which
/// is only called when the reference count to the node itself drops to
/// zero. This is to avoid threads getting trapped in a node with no
/// link out.
struct Node : public ThreadSafeRefCount< Node >
{
typedef ThreadSafeRefCount< Node > Parent;
Node( KeyType priority, const ValueType& value );
~Node();
KeyType getPriority() { return mPriority; }
ValueType& getValue() { return mValue; }
U32 getLevel();
NodePtr& getNext( U32 level );
bool isMarkedForDeletion();
bool tryMarkForDeletion();
void clearValue() { mValue = ValueType(); }
static U32 randomLevel();
void* operator new( size_t size, S32 level = -1 );
void operator delete( void* ptr );
private:
KeyType mPriority; ///< Priority key.
U32 mLevel; ///< Level count and deletion bit (highest).
ValueType mValue;
Node* mNext[ 1 ]; ///< Variable-sized array of next pointers.
struct FreeList
{
bool mDestroyed;
Node* mNodes;
~FreeList();
};
static FreeList smFreeLists[ MAX_LEVEL ];
};
NodePtr mHead; ///< Artificial head node.
NodePtr mTail; ///< Artificial tail node.
void readNext( NodePtr& refPrev, NodePtr& refNext, U32 level );
void scan( NodePtr& refPrev, NodePtr& refNext, U32 level, KeyType priority );
void scanFromHead( NodePtr& refPrev, NodePtr& refNext, U32 level, KeyType priority );
void insert( KeyType priority, const T& value, NodePtr& outResult );
void helpDelete();
};
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
typename ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::FreeList ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::smFreeLists[ MAX_LEVEL ];
/// Construct an empty queue.
///
/// Internally, this creates a head node with maximal priority and a tail node with minimal priority,
/// both at maximum level.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::ThreadSafePriorityQueue()
{
NodePtr::unsafeWrite( mHead, new ( MAX_LEVEL - 1 )
Node( SORT_MIN_TO_MAX ? TypeTraits< KeyType >::MIN : TypeTraits< KeyType >::MAX, ValueType() ) );
NodePtr::unsafeWrite( mTail, new ( MAX_LEVEL - 1 )
Node( SORT_MIN_TO_MAX ? TypeTraits< KeyType >::MAX : TypeTraits< KeyType >::MIN, ValueType() ) );
for( U32 level = 0; level < MAX_LEVEL; level ++ )
mHead->getNext( level ) = mTail;
}
/// Return true if the queue does not currently contain an item.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
bool ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::isEmpty()
{
return ( mHead->getNext( 0 ) == mTail );
}
/// Insert the given value into the queue at the place determined by the given priority.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
inline void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::insert( KeyType priority, const ValueType& value )
{
NodePtr result;
insert( priority, value, result );
}
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::insert( KeyType priority, const ValueType& value, NodePtr& outResult )
{
// Create a new node at a random level.
outResult = NULL;
NodePtr::unsafeWrite( outResult, new Node( priority, value ) );
U32 resultNodeLevel = outResult->getLevel();
// Link up all the levels. Do this bottom-up instead of
// top-down (as would be the right way for a skiplist) so
// that our list state always remains valid. If going top-down,
// we'll insert nodes with NULL pointers at their lower levels.
U32 currentLevel = 0;
do
{
while( 1 )
{
NodePtr nextNode;
NodePtr prevNode;
scanFromHead( prevNode, nextNode, currentLevel, priority );
outResult->getNext( currentLevel ) = nextNode;
if( prevNode->getNext( currentLevel ).trySetFromTo( nextNode, outResult, NodePtr::TAG_FailIfSet ) )
break;
else
outResult->getNext( currentLevel ) = 0;
}
currentLevel ++;
}
while( currentLevel <= resultNodeLevel
&& !outResult->isMarkedForDeletion() ); // No point linking up remaining levels if another thread already took this node.
}
/// Take the item with the highest priority from the queue.
///
/// @param outValue Reference to where the resulting value should be stored.
/// @param upToPriority Priority limit (inclusive) up to which items are taken from the queue.
/// @return true if there was a matching item in the queue.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
bool ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::takeNext( T& outValue, KeyType upToPriority )
{
// Iterate through to the first unmarked node.
NodePtr prevNode = mHead;
while( 1 )
{
NodePtr node;
readNext( prevNode, node, 0 );
if( node == mTail )
return false; // End reached.
bool priorityThresholdReached = SORT_MIN_TO_MAX
? ( upToPriority >= node->getPriority() )
: ( upToPriority <= node->getPriority() );
if( !priorityThresholdReached )
return false;
else
{
// Try to mark the node for deletion. Only if that succeeds, taking the
// node was a success and we can return. If it fails, spin and try again.
if( node->tryMarkForDeletion() )
{
helpDelete();
// Node is now off the list and will disappear as soon as
// all references held by threads (including this one)
// go out of scope.
outValue = node->getValue();
node->clearValue();
return true;
}
}
}
}
/// Update the given references to the next non-deleted node at the given level.
/// refPrev will be updated to reference the immediate predecessor of the next
/// node returned. Note that this can be a node in deleted state.
///
/// @param refPrev Reference to a node of which the successor node should be
/// returned. Updated to immediate predecessor of refNext on return.
/// @param refNext Reference to update to refer to next non-deleted node on
/// the given level.
/// @param level Skiplist level to operate on.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
inline void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::readNext( NodePtr& refPrev, NodePtr& refNext, U32 level )
{
while( 1 )
{
refNext = refPrev->getNext( level );
AssertFatal( refNext != NULL, "ThreadSafePriorityQueue::readNext() - next is NULL" );
if( !refNext->isMarkedForDeletion() || refNext == mTail )
break;
refPrev = refNext;
}
}
/// Scan for the position at which to insert a node of the given priority.
/// Upon return, the position between refPrev and refNext is the one to insert at.
///
/// @param refPrev position at which to start scanning; updated to match insert position.
/// @param refNext
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::scan( NodePtr& refPrev, NodePtr& refNext, U32 level, KeyType priority )
{
while( 1 )
{
readNext( refPrev, refNext, level );
if( refNext == mTail
|| ( SORT_MIN_TO_MAX
? ( refNext->getPriority() > priority )
: ( refNext->getPriority() < priority ) ) )
break;
refPrev = refNext;
}
}
///
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::scanFromHead( NodePtr& refPrev, NodePtr& refNext, U32 level, KeyType priority )
{
// Purge dead nodes at left end of queue so
// we don't get stuck hitting the same node
// in deletable state over and over again.
helpDelete();
S32 currentLevel = MAX_LEVEL - 1;
refPrev = mHead;
do
{
scan( refPrev, refNext, currentLevel, priority );
currentLevel --;
}
while( currentLevel >= S32( level ) );
}
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::helpDelete()
{
// Clean out all the references from head.
// Spin over a given reference on each level until head
// clearly refers to a node in non-deletable state. This
// makes this code work cooperatively with other threads
// doing takeNexts on prior or later nodes while also
// guaranteeing that all next pointers to us will eventually
// disappear.
//
// Note that this is *the only place* where we will be cleaning
// out our lists.
S32 level = MAX_LEVEL - 1;
do
{
while( 1 )
{
NodePtr ptr = mHead->getNext( level );
if( !ptr->isMarkedForDeletion() )
break;
else
{
NodePtr& next = ptr->getNext( level );
next.setTag();
mHead->getNext( level ).trySetFromTo( ptr, next, NodePtr::TAG_Unset );
AssertFatal( next->getRefCount() >= 2, "ThreadSafePriorityQueue::helpDelete() - invalid refcount" );
}
}
level --;
}
while( level >= 0 );
}
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
inline ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::Node( KeyType priority, const ValueType& value )
: Parent( false ),
mPriority( priority ),
mValue( value )
{
dMemset( mNext, 0, sizeof( Node* ) * ( getLevel() + 1 ) );
// Level is already set by the allocation routines.
}
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::~Node()
{
for( U32 level = 0; level < ( getLevel() + 1 ); level ++ )
getNext( level ) = NULL;
}
/// Return the skip list level the node is at.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
inline U32 ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::getLevel()
{
// Mask out the deletion request bit.
return ( mLevel & 0x7FFFFFFF );
}
/// Return the successor node at the given level.
/// @param level The level of the desired successor node; must be within the node's level bounds.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
inline typename ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::NodePtr& ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::getNext( U32 level )
{
return *reinterpret_cast< NodePtr* >( &mNext[ level ] );
}
/// Return true if the node is marked to be deleted.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
inline bool ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::isMarkedForDeletion()
{
return ( mLevel & 0x80000000 );
}
/// Attempt to mark the node for deletion. If the mark bit has not yet been set
/// and setting it on the current thread succeeds, returns true.
///
/// @return true, if the marking succeeded.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
inline bool ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::tryMarkForDeletion()
{
U32 oldVal = mLevel & 0x7FFFFFFF;
U32 newVal = oldVal | 0x80000000;
return ( dCompareAndSwap( mLevel, oldVal, newVal ) );
}
/// Choose a random level.
///
/// The chosen level depends on the given PROBABILISTIC_BIAS and MAX_LEVEL,
/// but is not affected by the actual number of nodes in a queue.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
U32 ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::randomLevel()
{
U32 level = 0;
while( Platform::getRandom() < ( ( ( F32 ) PROBABILISTIC_BIAS ) / 100 ) && level < ( MAX_LEVEL - 1 ) )
level ++;
return level;
}
/// Allocate a new node.
/// The node comes with a reference count of one and its level already set.
///
/// @param level The level to allocate the node at. If this is -1, a random level is chosen.
/// @return a new node.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
void* ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::operator new( size_t size, S32 level )
{
if( level == -1 )
level = randomLevel();
Node* node = 0;
while( 1 )
{
// Try to take a node from the freelist. If there's none,
// allocate a new one.
if( !smFreeLists[ level ].mDestroyed )
node = Node::safeRead( smFreeLists[ level ].mNodes );
if( !node )
{
node = ( Node* ) dMalloc( sizeof( Node ) + sizeof( Node* ) * level );
dMemset( node, 0, sizeof( Node ) );
node->mLevel = level;
node->addRef();
break;
}
else if( dCompareAndSwap( smFreeLists[ level ].mNodes, node, node->mNext[ 0 ] ) )
{
node->clearLowestBit();
break;
}
else
node->release(); // Other thread was quicker than us; release.
}
AssertFatal( node->getRefCount() != 0, "ThreadSafePriorityQueue::new Node() - invalid refcount" );
AssertFatal( ( node->getRefCount() % 2 ) == 0, "ThreadSafePriorityQueue::new Node() - invalid refcount" );
return node;
}
/// Reclaim a node.
///
/// @param node The node to reclaim. Must refer to a Node instance.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::operator delete( void* ptr )
{
//TODO: limit number of nodes kept
Node* node = ( Node* ) ptr;
U32 level = node->getLevel();
node->mLevel = level; // Reset the node's deletion bit.
while( !smFreeLists[ level ].mDestroyed )
{
// Put the node on the freelist.
Node* freeList = smFreeLists[ level ].mNodes;
node->mNext[ 0 ] = freeList;
if( dCompareAndSwap( smFreeLists[ level ].mNodes, freeList, node ) )
{
node = NULL;
break;
}
}
if( node )
dFree( node );
}
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::FreeList::~FreeList()
{
mDestroyed = true;
while( mNodes )
{
//FIXME: could leak some bytes under unfortunate circumstances (this in
// combination with mDestroyed is a dependent write)
Node* next = mNodes;
if( dCompareAndSwap( mNodes, next, next->mNext[ 0 ] ) )
dFree( next );
}
}
//--------------------------------------------------------------------------
// ThreadSafePriorityQueueWithUpdate.
//--------------------------------------------------------------------------
/// Fast, lock-free priority queue implementation for concurrent access that
/// performs dynamic re-prioritization of items.
///
/// Within the bounds of a set update interval UPDATE_INTERVAL, the takeNext
/// method is guaranteed to always return the item that has the highest priority
/// at the time the method is called rather than at the time items were inserted
/// into the queue.
///
/// Values placed on the queue must implement the following interface:
///
/// @code
/// template&lt; typename K >
/// struct IThreadSafePriorityQueueItem
/// {
/// // Default constructor.
/// IThreadSafePriorityQueueItem();
///
/// // Return the current priority.
/// // This must run normally even if the item is already dead.
/// K getPriority();
///
/// // Return true if the item is still meant to be waiting in the queue.
/// bool isAlive();
/// };
/// @endcode
template< typename T, typename K, bool SORT_MIN_TO_MAX = false, U32 MAX_LEVEL = 4, U32 PROBABILISTIC_BIAS = 50 >
struct ThreadSafePriorityQueueWithUpdate : public ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >
{
typedef T ValueType;
typedef K KeyType;
enum { DEFAULT_UPDATE_INTERVAL = 256 };
ThreadSafePriorityQueueWithUpdate( U32 updateInterval = DEFAULT_UPDATE_INTERVAL );
void insert( KeyType priority, const T& value );
bool takeNext( T& outValue, KeyType upToPriority = ( SORT_MIN_TO_MAX ? TypeTraits< KeyType >::MAX : TypeTraits< KeyType >::MIN ) );
U32 getUpdateInterval() const;
void setUpdateInterval( U32 value );
KeyType getTimeBasedPriorityBoost() const;
void setTimeBasedPriorityBoost( KeyType value );
void updatePriorities();
protected:
typedef ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS > Parent;
typedef U32 TickType;
typedef typename Parent::NodePtr NodePtr;
U32 mUpdateInterval;
KeyType mPriorityBoost; ///< If this is non-zero, priorities will be boosted by this amount each update. This can be used to prevent constant high-priority inserts to starve low-priority items already in the queue.
/// Work queue for node updates.
ThreadSafePriorityQueue< NodePtr, TickType, true, MAX_LEVEL, PROBABILISTIC_BIAS > mUpdateQueue;
TickType getTick() { return Platform::getRealMilliseconds(); }
};
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::ThreadSafePriorityQueueWithUpdate( U32 updateInterval )
: mUpdateInterval( updateInterval ),
mPriorityBoost( TypeTraits< KeyType >::ZERO )
{
}
/// Return the current update interval in milliseconds.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
U32 ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::getUpdateInterval() const
{
return mUpdateInterval;
}
/// Set update interval of queue to given value.
///
/// <em>Call this method on the main thread only.</em>
///
/// @param value Time between priority updates in milliseconds.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
void ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::setUpdateInterval( U32 value )
{
mUpdateInterval = value;
}
/// Return the delta to apply to priorities on each update.
/// Set to zero to deactivate time-based priority adjustments.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
K ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::getTimeBasedPriorityBoost() const
{
return mPriorityBoost;
}
/// Set the delta for time-based priority adjustments to the given value.
///
/// <em>Call this method on the main thread only.</em>
///
/// @param value The new priority adjustment value.
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
void ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::setTimeBasedPriorityBoost( KeyType value )
{
mPriorityBoost = value;
}
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
void ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::insert( KeyType priority, const ValueType& value )
{
NodePtr node;
Parent::insert( priority, value, node );
mUpdateQueue.insert( getTick() + getUpdateInterval(), node );
}
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
bool ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::takeNext( T& outValue, KeyType upToPriority )
{
updatePriorities();
bool result = false;
do
{
result = Parent::takeNext( outValue, upToPriority );
}
while( result && !outValue.isAlive() );
return result;
}
///
template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS >
void ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::updatePriorities()
{
TickType currentTime = getTick();
U32 numNodesUpdated = 0;
U32 numNodesDead = 0;
U32 numNodesChanged = 0;
NodePtr node;
while( mUpdateQueue.takeNext( node, currentTime ) )
{
numNodesUpdated ++;
// Since we're updating nodes on the update queue only periodically,
// their associated values or main queue nodes may have died in the
// meantime. If so, we just discard them here.
if( node->getValue().isAlive()
&& !node->isMarkedForDeletion() )
{
KeyType newPriority = node->getValue().getPriority() + getTimeBasedPriorityBoost();
if( newPriority != node->getPriority() )
{
// Node is outdated. Reinsert with new priority and mark the
// old node for deletion.
insert( newPriority, node->getValue() );
node->tryMarkForDeletion();
numNodesChanged ++;
}
else
{
// Node is still current. Just move to end.
mUpdateQueue.insert( currentTime + getUpdateInterval(), node );
}
}
else
numNodesDead ++;
}
#ifdef DEBUG_SPEW
if( numNodesUpdated )
Platform::outputDebugString( "[ThreadSafePriorityQueueWithUpdate] updated %i nodes (%i changed, %i dead)",
numNodesUpdated, numNodesChanged, numNodesDead );
#endif
}
// Re-enable TMM if necessary.
#include "platform/tmm_on.h"
#undef DEBUG_SPEW
#endif // !_THREADSAFEPRIORITYQUEUE_H_

View file

@ -0,0 +1,380 @@
//-----------------------------------------------------------------------------
// 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 _THREADSAFEREFCOUNT_H_
#define _THREADSAFEREFCOUNT_H_
#ifndef _PLATFORMINTRINSICS_H_
# include "platform/platformIntrinsics.h"
#endif
#ifndef _TYPETRAITS_H_
# include "platform/typetraits.h"
#endif
/// @file
/// Templated code for concurrent reference-counting.
///
/// Part of this code is based on work by J.D. Valois, Michael M. Maged,
/// and Scott L. Michael.
//--------------------------------------------------------------------------
// ThreadSafeRefCount.
//--------------------------------------------------------------------------
/// Baseclass for concurrently reference-counted objects.
///
/// @note NOTE that freshly instantiated objects start out with a reference
/// count of ZERO! Depending on how this class is used, this may not
/// be desirable, so override this behavior in constructors if necessary.
///
/// @param T the class being reference counted; this is passed to this class,
/// so it can call the correct destructor without having to force users
/// to have virtual methods
template< class T, class DeletePolicy = DeleteSingle >
class ThreadSafeRefCount
{
public:
typedef void Parent;
ThreadSafeRefCount()
: mRefCount( 0 ) {}
ThreadSafeRefCount( bool noSet ) {}
bool isShared() const;
U32 getRefCount() const;
void addRef();
void release();
void clearLowestBit();
static T* safeRead( T* const volatile& refPtr );
protected:
U32 mRefCount; ///< Reference count and claim bit. Note that this increments in steps of two.
static U32 decrementAndTestAndSet( U32& refCount );
};
/// @return true if the object is referenced by more than a single
/// reference.
template< class T, class DeletePolicy >
inline bool ThreadSafeRefCount< T, DeletePolicy >::isShared() const
{
return ( mRefCount > 3 );
}
/// Get the current reference count. This method is mostly meant for
/// debugging and should not normally be used.
template< class T, class DeletePolicy >
inline U32 ThreadSafeRefCount< T, DeletePolicy >::getRefCount() const
{
return mRefCount;
}
/// Increase the reference count on the object.
template< class T, class DeletePolicy >
inline void ThreadSafeRefCount< T, DeletePolicy >::addRef()
{
dFetchAndAdd( mRefCount, 2 );
}
/// Decrease the object's reference count and delete the object, if the count
/// drops to zero and claiming the object by the current thread succeeds.
template< class T, class DeletePolicy >
inline void ThreadSafeRefCount< T, DeletePolicy >::release()
{
AssertFatal( mRefCount != 0, "ThreadSafeRefCount::release() - refcount of zero" );
if( decrementAndTestAndSet( mRefCount ) != 0 )
DeletePolicy::destroy( ( T* ) this );
}
/// Dereference a reference-counted pointer in a multi-thread safe way.
template< class T, class DeletePolicy >
T* ThreadSafeRefCount< T, DeletePolicy >::safeRead( T* const volatile& refPtr )
{
while( 1 )
{
// Support tagged pointers here.
T* ptr = TypeTraits< T* >::getUntaggedPtr( refPtr );
if( !ptr )
return 0;
ptr->addRef();
if( ptr == TypeTraits< T* >::getUntaggedPtr( refPtr ) )
return ptr;
else
ptr->release();
}
}
/// Decrement the given reference count. Return 1 if the count dropped to zero
/// and the claim bit has been successfully set; return 0 otherwise.
template< class T, class DeletePolicy >
U32 ThreadSafeRefCount< T, DeletePolicy >::decrementAndTestAndSet( U32& refCount )
{
U32 oldVal;
U32 newVal;
do
{
oldVal = refCount;
newVal = oldVal - 2;
AssertFatal( oldVal >= 2,
"ThreadSafeRefCount::decrementAndTestAndSet() - invalid refcount" );
if( newVal == 0 )
newVal = 1;
}
while( !dCompareAndSwap( refCount, oldVal, newVal ) );
return ( ( oldVal - newVal ) & 1 );
}
///
template< class T, class DeletePolicy >
inline void ThreadSafeRefCount< T, DeletePolicy >::clearLowestBit()
{
AssertFatal( mRefCount % 2 != 0, "ThreadSafeRefCount::clearLowestBit() - invalid refcount" );
U32 oldVal;
U32 newVal;
do
{
oldVal = mRefCount;
newVal = oldVal - 1;
}
while( !dCompareAndSwap( mRefCount, oldVal, newVal ) );
}
//--------------------------------------------------------------------------
// ThreadSafeRef.
//--------------------------------------------------------------------------
/// Reference to a concurrently reference-counted object.
///
/// This class takes care of the reference-counting as well as protecting
/// the reference itself from concurrent operations.
///
/// Tagging allows the pointer contained in the reference to be flagged.
/// Tag state is preserved through updates to the reference.
///
/// @note If you directly assign a freshly created object with a reference
/// count of zero to a ThreadSafeRef, make absolutely sure the ThreadSafeRef
/// is accessed only by a single thread. Otherwise there's a risk of the
/// object being released and freed in midst of trying to set the reference.
template< class T >
class ThreadSafeRef
{
public:
enum ETag
{
TAG_PreserveOld, ///< Preserve existing tagging state when changing pointer.
TAG_PreserveNew, ///< Preserve tagging state of new pointer when changing pointer.
TAG_Set, ///< Set tag when changing pointer; okay if already set.
TAG_Unset, ///< Unset tag when changing pointer; okay if already unset.
TAG_SetOrFail, ///< Set tag when changing pointer; fail if already set.
TAG_UnsetOrFail, ///< Unset tag when changing pointer; fail if already unset.
TAG_FailIfSet, ///< Fail changing pointer when currently tagged.
TAG_FailIfUnset ///< Fail changing pointer when currently untagged.
};
typedef ThreadSafeRef< T > ThisType;
ThreadSafeRef() : mPtr( 0 ) {}
ThreadSafeRef( T* ptr ) : mPtr( ThreadSafeRefCount< T >::safeRead( ptr ) ) {}
ThreadSafeRef( const ThisType& ref ) : mPtr( ThreadSafeRefCount< T >::safeRead( ref.mPtr ) ) {}
~ThreadSafeRef()
{
T* ptr = NULL;
while( !trySetFromTo( mPtr, ptr ) );
}
T* ptr() const { return getUntaggedPtr( mPtr ) ; }
void setTag() { while( !trySetFromTo( mPtr, mPtr, TAG_Set ) ); }
bool isTagged() const { return isTaggedPtr( mPtr ); }
bool trySetFromTo( T* oldVal, T* const volatile& newVal, ETag tag = TAG_PreserveOld );
bool trySetFromTo( T* oldVal, const ThisType& newVal, ETag tag = TAG_PreserveOld );
bool trySetFromTo( const ThisType& oldVal, const ThisType& newVal, ETag tag = TAG_PreserveOld );
static void unsafeWrite( ThisType& ref, T* ptr );
static T* safeRead( T* const volatile& refPtr ) { return ThreadSafeRefCount< T >::safeRead( refPtr ); }
bool operator ==( T* ptr ) const;
bool operator ==( const ThisType& ref ) const;
bool operator !=( T* ptr ) const { return !( *this == ptr ); }
bool operator !=( const ThisType& ref ) const { return !( *this == ref ); }
ThisType& operator =( T* ptr );
ThisType& operator =( const ThisType& ref );
bool operator !() const { return ( ptr() == 0 ); }
T& operator *() const { return *ptr(); }
T* operator ->() const { return ptr(); }
operator T*() const { return ptr(); }
protected:
T* volatile mPtr;
static bool isTaggedPtr( T* ptr ) { return TypeTraits< T* >::isTaggedPtr( ptr ); }
static T* getTaggedPtr( T* ptr ) { return TypeTraits< T* >::getTaggedPtr( ptr ); }
static T* getUntaggedPtr( T* ptr ) { return TypeTraits< T* >::getUntaggedPtr( ptr ); }
};
/// Update the reference from pointing to oldVal to point to newVal.
/// Do so in a thread-safe way.
///
/// This operation will only succeed, if, when doing the pointer-swapping,
/// the reference still points to oldVal. If, however, the reference
/// has been changed in the meantime by another thread, the operation will
/// fail.
///
/// @param oldVal The pointer assumed to currently be contained in this ThreadSafeRef.
/// @param newVal The pointer to store in this ThreadSafeRef.
/// @param tag Operation to perform on the reference's tag field.
///
/// @return true, if the reference now points to newVal.
template< class T >
bool ThreadSafeRef< T >::trySetFromTo( T* oldVal, T* const volatile& newVal, ETag tag )
{
bool setTag = false;
bool getTag = false;
bool isTagged = isTaggedPtr( oldVal );
switch( tag )
{
case TAG_PreserveOld: setTag = isTaggedPtr( oldVal ); break;
case TAG_PreserveNew: setTag = isTaggedPtr( newVal ); break;
case TAG_Set: setTag = true; break;
case TAG_Unset: setTag = false; break;
case TAG_SetOrFail: setTag = true; getTag = true; break;
case TAG_UnsetOrFail: setTag = false; getTag = true; break;
case TAG_FailIfSet: if( isTagged ) return false; break;
case TAG_FailIfUnset: if( !isTagged ) return false; break;
}
T* newValPtr = ( setTag
? getTaggedPtr( ThreadSafeRefCount< T >::safeRead( newVal ) )
: getUntaggedPtr( ThreadSafeRefCount< T >::safeRead( newVal ) ) );
if( dCompareAndSwap( mPtr,
( getTag
? ( setTag
? getUntaggedPtr( oldVal )
: getTaggedPtr( oldVal ) )
: oldVal ),
newValPtr ) )
{
if( getUntaggedPtr( oldVal ) )
getUntaggedPtr( oldVal )->release();
return true;
}
else
{
if( getUntaggedPtr( newValPtr ) )
getUntaggedPtr( newValPtr )->release();
return false;
}
}
template< class T >
inline bool ThreadSafeRef< T >::trySetFromTo( T* oldVal, const ThisType& newVal, ETag tag )
{
return trySetFromTo( oldVal, newVal.mPtr, tag );
}
template< class T >
inline bool ThreadSafeRef< T >::trySetFromTo( const ThisType& oldVal, const ThisType& newVal, ETag tag )
{
return trySetFromTo( oldVal.mPtr, newVal.mPtr, tag );
}
/// Update ref to point to ptr but <em>do not</em> release an existing
/// reference held by ref nor do the operation in a thread-safe way.
///
/// This method is <em>only</em> for when you absolutely know that your
/// thread is the only thread operating on a reference <em>and</em> you
/// are keeping track of reference counts yourself.
///
/// @param ref The reference to update.
/// @param ptr The new pointer to store in ref.
template< class T >
inline void ThreadSafeRef< T >::unsafeWrite( ThisType& ref, T* ptr )
{
ref.mPtr = ptr;
}
template< class T >
inline bool ThreadSafeRef< T >::operator ==( T* p ) const
{
return ( ptr() == p );
}
template< class T >
inline bool ThreadSafeRef< T >::operator ==( const ThisType& ref ) const
{
return ( ptr() == ref.ptr() );
}
template< class T >
inline ThreadSafeRef< T >& ThreadSafeRef< T >::operator =( T* ptr )
{
while( !trySetFromTo( mPtr, ptr, TAG_PreserveNew ) );
return *this;
}
template< class T >
inline ThreadSafeRef< T >& ThreadSafeRef< T >::operator =( const ThisType& ref )
{
while( !trySetFromTo( mPtr, ref, TAG_PreserveNew ) );
return *this;
}
template< typename T >
struct TypeTraits< ThreadSafeRef< T > > : public TypeTraits< T* > {};
template< typename T >
inline T& Deref( ThreadSafeRef< T >& ref )
{
return *ref;
}
template< typename T >
inline T& Deref( const ThreadSafeRef< T >& ref )
{
return *ref;
}
#endif // _THREADSAFEREFCOUNT_H_

View file

@ -0,0 +1,23 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#undef new

View file

@ -0,0 +1,25 @@
//-----------------------------------------------------------------------------
// 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 TORQUE_DISABLE_MEMORY_MANAGER
# define new _new
#endif

View file

@ -0,0 +1,98 @@
//-----------------------------------------------------------------------------
// 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 INCLUDED_TYPES_CODEWARRIOR_H
#define INCLUDED_TYPES_CODEWARRIOR_H
#pragma once
// If using the IDE detect if DEBUG build was requested
#if __ide_target("Torque-W32-Debug")
#define TORQUE_DEBUG
#elif __ide_target("Torque-MacCarb-Debug")
#define TORQUE_DEBUG
#elif __ide_target("Torque-MacX-Debug")
#define TORQUE_DEBUG
#endif
//--------------------------------------
// Types
typedef signed long long S64; ///< Compiler independent Signed 64-bit integer
typedef unsigned long long U64; ///< Compiler independent Unsigned 64-bit integer
//--------------------------------------
// Compiler Version
#define TORQUE_COMPILER_CODEWARRIOR __MWERKS__
#define TORQUE_COMPILER_STRING "CODEWARRIOR"
//--------------------------------------
// Identify the Operating System
#if defined(_WIN32)
# define TORQUE_OS_STRING "Win32"
# define TORQUE_OS_WIN32
# include "platform/types.win32.h"
#elif defined(macintosh) || defined(__APPLE__)
# define TORQUE_OS_STRING "Mac"
# define TORQUE_OS_MAC
# if defined(__MACH__)
# define TORQUE_OS_MAC
# endif
# include "platform/types.ppc.h"
#else
# error "CW: Unsupported Operating System"
#endif
//--------------------------------------
// Identify the CPU
#if defined(_M_IX86)
# define TORQUE_CPU_STRING "x86"
# define TORQUE_CPU_X86
# define TORQUE_LITTLE_ENDIAN
# define TORQUE_SUPPORTS_NASM
# define TORQUE_SUPPORTS_VC_INLINE_X86_ASM
// Compiling with the CW IDE we cannot use NASM :(
# if __ide_target("Torque-W32-Debug")
# undef TORQUE_SUPPORTS_NASM
# elif __ide_target("Torque-W32-Release")
# undef TORQUE_SUPPORTS_NASM
# endif
#elif defined(__POWERPC__)
# define TORQUE_CPU_STRING "PowerPC"
# define TORQUE_CPU_PPC
# define TORQUE_BIG_ENDIAN
#else
# error "CW: Unsupported Target CPU"
#endif
#endif // INCLUDED_TYPES_CODEWARRIOR_H

View file

@ -0,0 +1,163 @@
//-----------------------------------------------------------------------------
// 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 _TYPESGCC_H
#define _TYPESGCC_H
// For additional information on GCC predefined macros
// http://gcc.gnu.org/onlinedocs/gcc-3.0.2/cpp.html
//--------------------------------------
// Types
typedef signed long long S64;
typedef unsigned long long U64;
//--------------------------------------
// Compiler Version
#define TORQUE_COMPILER_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
//--------------------------------------
// Identify the compiler string
#if defined(__MINGW32__)
# define TORQUE_COMPILER_STRING "GCC (MinGW)"
# define TORQUE_COMPILER_MINGW
#elif defined(__CYGWIN__)
# define TORQUE_COMPILER_STRING "GCC (Cygwin)"
# define TORQUE_COMPILER_MINGW
#else
# define TORQUE_COMPILER_STRING "GCC "
#endif
//--------------------------------------
// Identify the Operating System
#if defined(__WIN32__) || defined(_WIN32)
# define TORQUE_OS_STRING "Win32"
# define TORQUE_OS_WIN32
# define TORQUE_SUPPORTS_NASM
# define TORQUE_SUPPORTS_GCC_INLINE_X86_ASM
# include "platform/types.win32.h"
#elif defined(SN_TARGET_PS3)
# define TORQUE_OS_STRING "PS3"
# define TORQUE_OS_PS3
# include "platform/types.posix.h"
#elif defined(linux)
# define TORQUE_OS_STRING "Linux"
# define TORQUE_OS_LINUX
# define TORQUE_SUPPORTS_NASM
# define TORQUE_SUPPORTS_GCC_INLINE_X86_ASM
# include "platform/types.posix.h"
#elif defined(__OpenBSD__)
# define TORQUE_OS_STRING "OpenBSD"
# define TORQUE_OS_OPENBSD
# define TORQUE_SUPPORTS_NASM
# define TORQUE_SUPPORTS_GCC_INLINE_X86_ASM
# include "platform/types.posix.h"
#elif defined(__FreeBSD__)
# define TORQUE_OS_STRING "FreeBSD"
# define TORQUE_OS_FREEBSD
# define TORQUE_SUPPORTS_NASM
# define TORQUE_SUPPORTS_GCC_INLINE_X86_ASM
# include "platform/types.posix.h"
#elif defined(__APPLE__)
# define TORQUE_OS_STRING "MacOS X"
# define TORQUE_OS_MAC
# include "platform/types.mac.h"
# if defined(i386)
// Disabling ASM on XCode for shared library build code relocation issues
// This could be reconfigured for static builds, though minimal impact
//# define TORQUE_SUPPORTS_NASM
# endif
#else
# error "GCC: Unsupported Operating System"
#endif
//--------------------------------------
// Identify the CPU
#if defined(i386)
# define TORQUE_CPU_STRING "Intel x86"
# define TORQUE_CPU_X86
# define TORQUE_LITTLE_ENDIAN
#elif defined(__ppc__)
# define TORQUE_CPU_STRING "PowerPC"
# define TORQUE_CPU_PPC
# define TORQUE_BIG_ENDIAN
#elif defined(SN_TARGET_PS3)
# define TORQUE_CPU_STRING "PowerPC"
# define TORQUE_CPU_PPC
# define TORQUE_BIG_ENDIAN
#else
# error "GCC: Unsupported Target CPU"
#endif
#ifndef Offset
/// Offset macro:
/// Calculates the location in memory of a given member x of class cls from the
/// start of the class. Need several definitions to account for various
/// flavors of GCC.
// now, for each compiler type, define the Offset macros that should be used.
// The Engine code usually uses the Offset macro, but OffsetNonConst is needed
// when a variable is used in the indexing of the member field (see
// TSShapeConstructor::initPersistFields for an example)
// compiler is non-GCC, or gcc < 3
#if (__GNUC__ < 3)
#define Offset(x, cls) _Offset_Normal(x, cls)
#define OffsetNonConst(x, cls) _Offset_Normal(x, cls)
// compiler is GCC 3 with minor version less than 4
#elif defined(TORQUE_COMPILER_GCC) && (__GNUC__ == 3) && (__GNUC_MINOR__ < 4)
#define Offset(x, cls) _Offset_Variant_1(x, cls)
#define OffsetNonConst(x, cls) _Offset_Variant_1(x, cls)
// compiler is GCC 3 with minor version greater than 4
#elif defined(TORQUE_COMPILER_GCC) && (__GNUC__ == 3) && (__GNUC_MINOR__ >= 4)
#include <stddef.h>
#define Offset(x, cls) _Offset_Variant_2(x, cls)
#define OffsetNonConst(x, cls) _Offset_Variant_1(x, cls)
// compiler is GCC 4
#elif defined(TORQUE_COMPILER_GCC) && (__GNUC__ == 4)
#include <stddef.h>
#define Offset(x, cls) _Offset_Normal(x, cls)
#define OffsetNonConst(x, cls) _Offset_Variant_1(x, cls)
#endif
#endif
#endif // INCLUDED_TYPES_GCC_H

View file

@ -0,0 +1,299 @@
//-----------------------------------------------------------------------------
// 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 _TORQUE_TYPES_H_
#define _TORQUE_TYPES_H_
//------------------------------------------------------------------------------
//-------------------------------------- Basic Types...
typedef signed char S8; ///< Compiler independent Signed Char
typedef unsigned char U8; ///< Compiler independent Unsigned Char
typedef signed short S16; ///< Compiler independent Signed 16-bit short
typedef unsigned short U16; ///< Compiler independent Unsigned 16-bit short
typedef signed int S32; ///< Compiler independent Signed 32-bit integer
typedef unsigned int U32; ///< Compiler independent Unsigned 32-bit integer
typedef float F32; ///< Compiler independent 32-bit float
typedef double F64; ///< Compiler independent 64-bit float
struct EmptyType {}; ///< "Null" type used by templates
#define TORQUE_UNUSED(var) (void)var
//------------------------------------------------------------------------------
//------------------------------------- String Types
typedef char UTF8; ///< Compiler independent 8 bit Unicode encoded character
typedef unsigned short UTF16; ///< Compiler independent 16 bit Unicode encoded character
typedef unsigned int UTF32; ///< Compiler independent 32 bit Unicode encoded character
typedef const char* StringTableEntry;
//------------------------------------------------------------------------------
//-------------------------------------- Type constants...
#define __EQUAL_CONST_F F32(0.000001) ///< Constant float epsilon used for F32 comparisons
extern const F32 Float_Inf;
static const F32 Float_One = F32(1.0); ///< Constant float 1.0
static const F32 Float_Half = F32(0.5); ///< Constant float 0.5
static const F32 Float_Zero = F32(0.0); ///< Constant float 0.0
static const F32 Float_Pi = F32(3.14159265358979323846); ///< Constant float PI
static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846); ///< Constant float 2*PI
static const F32 Float_InversePi = F32(1.0 / 3.14159265358979323846); ///< Constant float 1 / PI
static const F32 Float_HalfPi = F32(0.5 * 3.14159265358979323846); ///< Constant float 1/2 * PI
static const F32 Float_2InversePi = F32(2.0 / 3.14159265358979323846);///< Constant float 2 / PI
static const F32 Float_Inverse2Pi = F32(0.5 / 3.14159265358979323846);///< Constant float 0.5 / PI
static const F32 Float_Sqrt2 = F32(1.41421356237309504880f); ///< Constant float sqrt(2)
static const F32 Float_SqrtHalf = F32(0.7071067811865475244008443f); ///< Constant float sqrt(0.5)
static const S8 S8_MIN = S8(-128); ///< Constant Min Limit S8
static const S8 S8_MAX = S8(127); ///< Constant Max Limit S8
static const U8 U8_MAX = U8(255); ///< Constant Max Limit U8
static const S16 S16_MIN = S16(-32768); ///< Constant Min Limit S16
static const S16 S16_MAX = S16(32767); ///< Constant Max Limit S16
static const U16 U16_MAX = U16(65535); ///< Constant Max Limit U16
static const S32 S32_MIN = S32(-2147483647 - 1); ///< Constant Min Limit S32
static const S32 S32_MAX = S32(2147483647); ///< Constant Max Limit S32
static const U32 U32_MAX = U32(0xffffffff); ///< Constant Max Limit U32
static const F32 F32_MIN = F32(1.175494351e-38F); ///< Constant Min Limit F32
static const F32 F32_MAX = F32(3.402823466e+38F); ///< Constant Max Limit F32
// define all the variants of Offset that we might use
#define _Offset_Normal(x, cls) ((dsize_t)((const char *)&(((cls *)1)->x)-(const char *)1))
#define _Offset_Variant_1(x, cls) ((int)(&((cls *)1)->x) - 1)
#define _Offset_Variant_2(x, cls) offsetof(cls, x) // also requires #include <stddef.h>
//--------------------------------------
// Identify the compiler being used
// PC-lint
#if defined(_lint)
# include "platform/types.lint.h"
// Metrowerks CodeWarrior
#elif defined(__MWERKS__)
# include "platform/types.codewarrior.h"
// Microsoft Visual C++/Visual.NET
#elif defined(_MSC_VER)
# include "platform/types.visualc.h"
// GNU GCC
#elif defined(__GNUC__)
# include "platform/types.gcc.h"
#else
# error "Unknown Compiler"
#endif
/// Integral type matching the host's memory address width.
#ifdef TORQUE_64BITS
typedef U64 MEM_ADDRESS;
#else
typedef U32 MEM_ADDRESS;
#endif
//-------------------------------------- Some all-around useful inlines and globals
//
/// Returns power of 2 number which is as small as possible but
/// still greater than or equal to input number. Note: returns 0
/// for an input of 0 even though that is not a power of 2.
/// @param num Any U32
inline U32 getNextPow2(U32 num)
{
// Taken from: http://graphics.stanford.edu/~seander/bithacks.html
num--;
num |= num >> 1;
num |= num >> 2;
num |= num >> 4;
num |= num >> 8;
num |= num >> 16;
num++;
return num;
}
/// Return integer log2 of input number (rounding down). So, e.g.,
/// getBinLog2(7) == 2 whereas getBinLog2(8) == 3. If known
/// @param num Any U32
/// @param knownPow2 Is num a known power of 2?
inline U32 getBinLog2(U32 num, bool knownPow2 = false)
{
// Taken from: http://graphics.stanford.edu/~seander/bithacks.html
static const U32 MultiplyDeBruijnBitPosition[32] =
{
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
if (!knownPow2)
{
num |= num >> 1; // first round down to power of 2
num |= num >> 2;
num |= num >> 4;
num |= num >> 8;
num |= num >> 16;
num = (num >> 1) + 1;
}
return MultiplyDeBruijnBitPosition[(num * 0x077CB531UL) >> 27];
}
/// Determines if the given U32 is some 2^n
/// @param num Any U32
/// @returns true if in_num is a power of two, otherwise false
inline bool isPow2(const U32 num)
{
return (num & (num - 1)) == 0;
}
/// Determines the binary logarithm of the next greater power of two of the input number.
inline U32 getNextBinLog2(U32 number)
{
return getBinLog2(number) + (isPow2(number) ? 0 : 1);
}
//----------------Many versions of min and max-------------
//---not using template functions because MS VC++ chokes---
/// Returns the lesser of the two parameters: a & b.
inline U32 getMin(U32 a, U32 b)
{
return a>b ? b : a;
}
/// Returns the lesser of the two parameters: a & b.
inline U16 getMin(U16 a, U16 b)
{
return a>b ? b : a;
}
/// Returns the lesser of the two parameters: a & b.
inline U8 getMin(U8 a, U8 b)
{
return a>b ? b : a;
}
/// Returns the lesser of the two parameters: a & b.
inline S32 getMin(S32 a, S32 b)
{
return a>b ? b : a;
}
/// Returns the lesser of the two parameters: a & b.
inline S16 getMin(S16 a, S16 b)
{
return a>b ? b : a;
}
/// Returns the lesser of the two parameters: a & b.
inline S8 getMin(S8 a, S8 b)
{
return a>b ? b : a;
}
/// Returns the lesser of the two parameters: a & b.
inline float getMin(float a, float b)
{
return a>b ? b : a;
}
/// Returns the lesser of the two parameters: a & b.
inline double getMin(double a, double b)
{
return a>b ? b : a;
}
/// Returns the greater of the two parameters: a & b.
inline U32 getMax(U32 a, U32 b)
{
return a>b ? a : b;
}
/// Returns the greater of the two parameters: a & b.
inline U16 getMax(U16 a, U16 b)
{
return a>b ? a : b;
}
/// Returns the greater of the two parameters: a & b.
inline U8 getMax(U8 a, U8 b)
{
return a>b ? a : b;
}
/// Returns the greater of the two parameters: a & b.
inline S32 getMax(S32 a, S32 b)
{
return a>b ? a : b;
}
/// Returns the greater of the two parameters: a & b.
inline S16 getMax(S16 a, S16 b)
{
return a>b ? a : b;
}
/// Returns the greater of the two parameters: a & b.
inline S8 getMax(S8 a, S8 b)
{
return a>b ? a : b;
}
/// Returns the greater of the two parameters: a & b.
inline float getMax(float a, float b)
{
return a>b ? a : b;
}
/// Returns the greater of the two parameters: a & b.
inline double getMax(double a, double b)
{
return a>b ? a : b;
}
//-------------------------------------- Use this instead of Win32 FOURCC()
// macro...
//
#define makeFourCCTag(ch0, ch1, ch2, ch3) \
(((U32(ch0) & 0xFF) << 0) | \
((U32(ch1) & 0xFF) << 8) | \
((U32(ch2) & 0xFF) << 16) | \
((U32(ch3) & 0xFF) << 24) )
#define makeFourCCString(ch0, ch1, ch2, ch3) { ch0, ch1, ch2, ch3 }
#define BIT(x) (1 << (x)) ///< Returns value with bit x set (2^x)
#if defined(TORQUE_OS_WIN32)
#define STDCALL __stdcall
#else
#define STDCALL
#endif
#endif //_TORQUE_TYPES_H_

View file

@ -0,0 +1,58 @@
//-----------------------------------------------------------------------------
// 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 TORQUE_TYPES_LINT_H_
#define TORQUE_TYPES_LINT_H_
typedef signed long long S64;
typedef unsigned long long U64;
typedef unsigned int dsize_t;
struct FileTime
{
U32 v1;
U32 v2;
};
#define TORQUE_OS_STRING "Lint"
#define TORQUE_CPU_STRING "x86"
#define TORQUE_LITTLE_ENDIAN
#define TORQUE_SUPPORTS_NASM
#define TORQUE_SUPPORTS_VC_INLINE_X86_ASM
#define TORQUE_OS_WIN32
#define TORQUE_COMPILER_VISUALC 1500
#ifndef FN_CDECL
#define FN_CDECL
#endif
#ifndef Offset
#define Offset(x, cls) _Offset_Normal(x, cls)
#define OffsetNonConst(x, cls) _Offset_Normal(x, cls)
#endif
#ifndef NULL
#define NULL 0
#endif
#endif

View file

@ -0,0 +1,51 @@
//-----------------------------------------------------------------------------
// 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 _TYPESPPC_H_
#define _TYPESPPC_H_
///< Calling convention
#define FN_CDECL
#define STDCALL
// size_t is needed to overload new
// size_t tends to be OS and compiler specific and may need to
// be if/def'ed in the future
typedef unsigned long dsize_t;
/** Platform dependent file date-time structure. The defination of this structure
* will likely be different for each OS platform.
* On the PPC is a 64-bit structure for storing the date/time for a file
*/
// 64-bit structure for storing the date/time for a file
// The date and time, specified in seconds since the unix epoch.
// NOTE: currently, this is only 32-bits in value, so the upper 32 are all zeroes.
typedef U64 FileTime;
#ifndef NULL
# define NULL (0)
#endif
#endif //_TYPESPPC_H_

View file

@ -0,0 +1,46 @@
//-----------------------------------------------------------------------------
// 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 _TYPESPOSIX_H_
#define _TYPESPOSIX_H_
#define FN_CDECL ///< Calling convention
// size_t is needed to overload new
// size_t tends to be OS and compiler specific and may need to
// be if/def'ed in the future
typedef unsigned int dsize_t;
/** Platform dependent file date-time structure. The defination of this structure
* will likely be different for each OS platform.
*/
typedef S32 FileTime;
#ifndef NULL
# define NULL (0)
#endif
#endif //_TYPESPOSIX_H_

View file

@ -0,0 +1,50 @@
//-----------------------------------------------------------------------------
// 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 _TYPESPPC_H_
#define _TYPESPPC_H_
///< Calling convention
#define FN_CDECL
// size_t is needed to overload new
// size_t tends to be OS and compiler specific and may need to
// be if/def'ed in the future
typedef unsigned long dsize_t;
/** Platform dependent file date-time structure. The defination of this structure
* will likely be different for each OS platform.
* On the PPC is a 64-bit structure for storing the date/time for a file
*/
// 64-bit structure for storing the date/time for a file
// The date and time, specified in seconds since the unix epoch.
// NOTE: currently, this is only 32-bits in value, so the upper 32 are all zeroes.
typedef U64 FileTime;
#ifndef NULL
# define NULL (0)
#endif
#endif //_TYPESPPC_H_

View file

@ -0,0 +1,99 @@
//-----------------------------------------------------------------------------
// 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 INCLUDED_TYPES_VISUALC_H
#define INCLUDED_TYPES_VISUALC_H
// For more information on VisualC++ predefined macros
// http://support.microsoft.com/default.aspx?scid=kb;EN-US;q65472
//--------------------------------------
// Types
typedef signed _int64 S64;
typedef unsigned _int64 U64;
//--------------------------------------
// Compiler Version
#define TORQUE_COMPILER_VISUALC _MSC_VER
//--------------------------------------
// Identify the compiler string
#if _MSC_VER < 1200
// No support for old compilers
# error "VC: Minimum VisualC++ 6.0 or newer required"
#else _MSC_VER >= 1200
# define TORQUE_COMPILER_STRING "VisualC++"
#endif
//--------------------------------------
// Identify the Operating System
#if _XBOX_VER >= 200
# define TORQUE_OS_STRING "Xenon"
# ifndef TORQUE_OS_XENON
# define TORQUE_OS_XENON
# endif
# include "platform/types.xenon.h"
#elif defined( _XBOX_VER )
# define TORQUE_OS_STRING "Xbox"
# define TORQUE_OS_XBOX
# include "platform/types.win32.h"
#elif defined(_WIN32)
# define TORQUE_OS_STRING "Win32"
# define TORQUE_OS_WIN32
# include "platform/types.win32.h"
#else
# error "VC: Unsupported Operating System"
#endif
//--------------------------------------
// Identify the CPU
#if defined(_M_IX86)
# define TORQUE_CPU_STRING "x86"
# define TORQUE_CPU_X86
# define TORQUE_LITTLE_ENDIAN
# define TORQUE_SUPPORTS_NASM
# define TORQUE_SUPPORTS_VC_INLINE_X86_ASM
#elif defined(TORQUE_OS_XENON)
# define TORQUE_CPU_STRING "ppc"
# define TORQUE_CPU_PPC
# define TORQUE_BIG_ENDIAN
#else
# error "VC: Unsupported Target CPU"
#endif
#ifndef FN_CDECL
# define FN_CDECL __cdecl ///< Calling convention
#endif
#define for if(false) {} else for ///< Hack to work around Microsoft VC's non-C++ compliance on variable scoping
// disable warning caused by memory layer
// see msdn.microsoft.com "Compiler Warning (level 1) C4291" for more details
#pragma warning(disable: 4291)
#endif // INCLUDED_TYPES_VISUALC_H

View file

@ -0,0 +1,49 @@
//-----------------------------------------------------------------------------
// 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 _TYPESWIN32_H_
#define _TYPESWIN32_H_
#define FN_CDECL __cdecl ///< Calling convention
// size_t is needed to overload new
// size_t tends to be OS and compiler specific and may need to
// be if/def'ed in the future
typedef unsigned int dsize_t;
/// Platform dependent file date-time structure. The definition of this structure
/// will likely be different for each OS platform.
struct FileTime
{
U32 v1;
U32 v2;
};
#ifndef NULL
# define NULL 0
#endif
#endif //_TYPESWIN32_H_

View file

@ -0,0 +1,49 @@
//-----------------------------------------------------------------------------
// 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 _TYPESXENON_H_
#define _TYPESXENON_H_
///< Calling convention
#ifdef FN_CDECL
# undef FN_CDECL
#endif
#define FN_CDECL __cdecl
// size_t is needed to overload new
// size_t tends to be OS and compiler specific and may need to
// be if/def'ed in the future
typedef size_t dsize_t;
struct FileTime
{
U32 v1;
U32 v2;
};
#ifndef NULL
# define NULL (0)
#endif
#endif //_TYPESXENON_H_

View file

@ -0,0 +1,80 @@
//-----------------------------------------------------------------------------
// 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 _TYPESLINUX_H_
#define _TYPESLINUX_H_
/* eek. */
#ifndef NULL
#define NULL 0
#endif
#define PLATFORM_LITTLE_ENDIAN
#define FN_CDECL
typedef signed char S8;
typedef unsigned char U8;
typedef signed short S16;
typedef unsigned short U16;
typedef signed int S32;
typedef unsigned int U32;
typedef signed long long S64;
typedef unsigned long long U64;
typedef float F32;
typedef double F64;
typedef unsigned int dsize_t;
typedef const char* StringTableEntry;
typedef S32 FileTime;
#define __EQUAL_CONST_F F32(0.000001)
static const F32 Float_One = F32(1.0);
static const F32 Float_Half = F32(0.5);
static const F32 Float_Zero = F32(0.0);
static const F32 Float_Pi = F32(3.14159265358979323846);
static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846);
static const S8 S8_MIN = S8(-128);
static const S8 S8_MAX = S8(127);
static const U8 U8_MAX = U8(255);
static const S16 S16_MIN = S16(-32768);
static const S16 S16_MAX = S16(32767);
static const U16 U16_MAX = U16(65535);
static const S32 S32_MIN = S32(-2147483647 - 1);
static const S32 S32_MAX = S32(2147483647);
static const U32 U32_MAX = U32(0xffffffff);
static const F32 F32_MAX = F32(3.402823466e+38F);
static const F32 F32_MIN = F32(1.175494351e-38F);
#endif

View file

@ -0,0 +1,99 @@
//-----------------------------------------------------------------------------
// 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 _TYPES_PPC_H_
#define _TYPES_PPC_H_
// We have to check this. Since every file will eventually wind up including
// this header, but not every header includes a windows or system header...
//
#ifndef NULL
#define NULL 0
#endif
// Let's just have this in a nice central location. Again, since every file
// will wind up including this file, we can affect compilation most effectively
// from this location.
//
#define PLATFORM_BIG_ENDIAN
#define FN_CDECL
//------------------------------------------------------------------------------
//-------------------------------------- Basic Types...
typedef signed char S8;
typedef unsigned char U8;
typedef signed short S16;
typedef unsigned short U16;
typedef signed int S32;
typedef unsigned int U32;
typedef signed long long S64;
typedef unsigned long long U64;
typedef float F32;
typedef double F64;
// size_t is needed to overload new
// size_t tends to be OS and compiler specific and may need to
// be if/def'ed in the future
typedef unsigned long dsize_t;
typedef const char* StringTableEntry;
// 64-bit structure for storing the date/time for a file
// The date and time, specified in seconds since the unix epoch.
// NOTE: currently, this is only 32-bits in value, so the upper 32 are all zeroes.
typedef U64 FileTime;
//------------------------------------------------------------------------------
//-------------------------------------- Type constants...
#define __EQUAL_CONST_F F32(0.000001)
static const F32 Float_One = F32(1.0);
static const F32 Float_Half = F32(0.5);
static const F32 Float_Zero = F32(0.0);
static const F32 Float_Pi = F32(3.14159265358979323846);
static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846);
static const S8 S8_MIN = S8(-128);
static const S8 S8_MAX = S8(127);
static const U8 U8_MAX = U8(255);
static const S16 S16_MIN = S16(-32768);
static const S16 S16_MAX = S16(32767);
static const U16 U16_MAX = U16(65535);
static const S32 S32_MIN = S32(-2147483647 - 1);
static const S32 S32_MAX = S32(2147483647);
static const U32 U32_MAX = U32(0xffffffff);
static const F32 F32_MAX = F32(3.402823466e+38F);
static const F32 F32_MIN = F32(1.175494351e-38F);
#endif //_TYPES_PPC_H_

View file

@ -0,0 +1,123 @@
//-----------------------------------------------------------------------------
// 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 _TYPESWIN32_H_
#define _TYPESWIN32_H_
// We have to check this. Since every file will eventually wind up including
// this header, but not every header includes a windows or system header...
//
#ifndef NULL
#define NULL 0
#endif
// Let's just have this in a nice central location. Again, since every file
// will wind up including this file, we can affect compilation most effectively
// from this location.
//
#define PLATFORM_LITTLE_ENDIAN ///< Signals this platfrom is Little Endian
#define FN_CDECL __cdecl ///< Calling convention
//------------------------------------------------------------------------------
//-------------------------------------- Basic Types...
typedef signed char S8; ///< Compiler independent Signed Char
typedef unsigned char U8; ///< Compiler independent Unsigned Char
typedef signed short S16; ///< Compiler independent Signed 16-bit short
typedef unsigned short U16; ///< Compiler independent Unsigned 16-bit short
typedef signed int S32; ///< Compiler independent Signed 32-bit integer
typedef unsigned int U32; ///< Compiler independent Unsigned 32-bit integer
#ifdef __BORLANDC__
typedef signed __int64 S64; ///< Compiler independent Signed 64-bit integer
typedef unsigned __int64 U64; ///< Compiler independent Unsigned 64-bit integer
#elif defined(__MWERKS__) // This has to go before MSC_VER since CodeWarrior defines MSC_VER too
typedef signed long long S64; ///< Compiler independent Signed 64-bit integer
typedef unsigned long long U64; ///< Compiler independent Unsigned 64-bit integer
#elif defined(_MSC_VER)
typedef signed _int64 S64; ///< Compiler independent Signed 64-bit integer
typedef unsigned _int64 U64; ///< Compiler independent Unsigned 64-bit integer
#pragma warning(disable: 4291) // disable warning caused by memory layer...
#pragma warning(disable: 4996) // turn off "deprecation" warnings
#else
typedef signed long long S64; ///< Compiler independent Signed 64-bit integer
typedef unsigned long long U64; ///< Compiler independent Unsigned 64-bit integer
#endif
typedef float F32; ///< Compiler independent 32-bit float
typedef double F64; ///< Compiler independent 64-bit float
// size_t is needed to overload new
// size_t tends to be OS and compiler specific and may need to
// be if/def'ed in the future
typedef unsigned int dsize_t;
typedef const char* StringTableEntry;
/* Platform dependent file date-time structure. The defination of this structure
* will likely be different for each OS platform.
*/
struct FileTime
{
U32 v1;
U32 v2;
};
//------------------------------------------------------------------------------
//-------------------------------------- Type constants...
#define __EQUAL_CONST_F F32(0.000001) ///< Constant float epsilon used for F32 comparisons
static const F32 Float_One = F32(1.0); ///< Constant float 1.0
static const F32 Float_Half = F32(0.5); ///< Constant float 0.5
static const F32 Float_Zero = F32(0.0); ///< Constant float 0.0
static const F32 Float_Pi = F32(3.14159265358979323846); ///< Constant float PI
static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846); ///< Constant float 2*PI
static const S8 S8_MIN = S8(-128); ///< Constant Min Limit S8
static const S8 S8_MAX = S8(127); ///< Constant Max Limit S8
static const U8 U8_MAX = U8(255); ///< Constant Max Limit U8
static const S16 S16_MIN = S16(-32768); ///< Constant Min Limit S16
static const S16 S16_MAX = S16(32767); ///< Constant Max Limit S16
static const U16 U16_MAX = U16(65535); ///< Constant Max Limit U16
static const S32 S32_MIN = S32(-2147483647 - 1); ///< Constant Min Limit S32
static const S32 S32_MAX = S32(2147483647); ///< Constant Max Limit S32
static const U32 U32_MAX = U32(0xffffffff); ///< Constant Max Limit U32
static const F32 F32_MIN = F32(1.175494351e-38F); ///< Constant Min Limit F32
static const F32 F32_MAX = F32(3.402823466e+38F); ///< Constant Max Limit F32
#ifdef _MSC_VER
#define for if(false) {} else for ///< Hack to work around Microsoft VC's non-C++ compliance on variable scoping
#endif
#endif //_NTYPES_H_

View file

@ -0,0 +1,80 @@
//-----------------------------------------------------------------------------
// 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 _TYPESX86UNIX_H_
#define _TYPESX86UNIX_H_
/* eek. */
#ifndef NULL
#define NULL 0
#endif
#define PLATFORM_LITTLE_ENDIAN
#define FN_CDECL
typedef signed char S8;
typedef unsigned char U8;
typedef signed short S16;
typedef unsigned short U16;
typedef signed int S32;
typedef unsigned int U32;
typedef signed long long S64;
typedef unsigned long long U64;
typedef float F32;
typedef double F64;
typedef unsigned int dsize_t;
typedef const char* StringTableEntry;
typedef S32 FileTime;
#define __EQUAL_CONST_F F32(0.000001)
static const F32 Float_One = F32(1.0);
static const F32 Float_Half = F32(0.5);
static const F32 Float_Zero = F32(0.0);
static const F32 Float_Pi = F32(3.14159265358979323846);
static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846);
static const S8 S8_MIN = S8(-128);
static const S8 S8_MAX = S8(127);
static const U8 U8_MAX = U8(255);
static const S16 S16_MIN = S16(-32768);
static const S16 S16_MAX = S16(32767);
static const U16 U16_MAX = U16(65535);
static const S32 S32_MIN = S32(-2147483647 - 1);
static const S32 S32_MAX = S32(2147483647);
static const U32 U32_MAX = U32(0xffffffff);
static const F32 F32_MAX = F32(3.402823466e+38F);
static const F32 F32_MIN = F32(1.175494351e-38F);
#endif

View file

@ -0,0 +1,425 @@
//-----------------------------------------------------------------------------
// 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 _TYPETRAITS_H_
#define _TYPETRAITS_H_
#ifndef _PLATFORM_H_
# include "platform/platform.h"
#endif
/// @file
/// Template definitions for introspecting type properties.
//--------------------------------------------------------------------------
// Type Predicating.
//--------------------------------------------------------------------------
struct TrueType {};
struct FalseType {};
template< typename T >
inline bool IsTrueType()
{
return false;
}
template<>
inline bool IsTrueType< TrueType >()
{
return true;
}
template< typename T >
inline bool IsFalseType()
{
return false;
}
template<>
inline bool IsFalseType< FalseType >()
{
return true;
}
template< typename T, typename IfTrue, typename IfFalse >
struct IfTrueType : public IfFalse {};
template< typename IfTrue, typename IfFalse >
struct IfTrueType< TrueType, IfTrue, IfFalse > : public IfTrue {};
template< typename T, typename IfTrue, typename IfFalse >
struct IfFalseType : public IfTrue {};
template< typename IfTrue, typename IfFalse >
struct IfFalseType< FalseType, IfTrue, IfFalse > : public IfFalse {};
//--------------------------------------------------------------------------
// Construct.
//--------------------------------------------------------------------------
struct _ConstructDefault
{
template< typename T >
static T single()
{
return T();
}
template< typename T, typename A >
static T single( A a )
{
return T( a );
}
template< typename T, typename A, typename B >
static T single( A a, B b )
{
return T( a, b );
}
template< typename T >
static void array( T* ptr, U32 num )
{
constructArrayInPlace< T >( ptr, num );
}
template< typename T, typename A >
static void array( T* ptr, U32 num, A a )
{
for( U32 i = 0; i < num; ++ i )
ptr[ i ] = single< T >( a );
}
};
struct _ConstructPrim
{
template< typename T >
static T single()
{
return 0;
}
template< typename T, typename A >
static T single( T a )
{
return a;
}
template< typename T >
static void array( T* ptr, U32 num )
{
dMemset( ptr, 0, num * sizeof( T ) );
}
template< typename T, typename A >
static void array( T* ptr, U32 num, T a )
{
for( U32 i = 0; i < num; ++ i )
ptr[ i ] = a;
}
};
struct _ConstructPtr
{
template< typename T >
static T* single()
{
return new T;
}
template< typename T, typename A >
static T* single( A a )
{
return new T( a );
}
template< typename T, typename A, typename B >
static T* single( A a, B b )
{
return new T( a, b );
}
template< typename T >
static void array( T** ptr, U32 num )
{
for( U32 i = 0; i < num; ++ i )
ptr[ i ] = single< T >();
}
template< typename T, typename A >
static void array( T** ptr, U32 num, A a )
{
for( U32 i = 0; i < num; ++ i )
ptr[ i ] = single< T >( a );
}
};
//--------------------------------------------------------------------------
// Destruct.
//--------------------------------------------------------------------------
struct _DestructDefault
{
template< typename T >
static void single( T& val )
{
val.~T();
}
template< typename T >
static void array( T* ptr, U32 num )
{
for( U32 i = 0; i < num; ++ i )
single< T >( ptr[ i ] );
}
};
struct _DestructPrim
{
template< typename T >
static void single( T& val ) {}
template< typename T >
static void array( T* ptr, U32 num ) {}
};
struct _DestructPtr
{
template< typename T >
static void single( T*& val )
{
delete val;
val = NULL;
}
template< typename T >
static void array( T* ptr, U32 num )
{
for( U32 i = 0; i < num; ++ i )
single< T >( ptr[ i ] );
}
};
//--------------------------------------------------------------------------
// TypeTraits.
//--------------------------------------------------------------------------
template< typename T >
struct _TypeTraits
{
typedef T BaseType;
typedef const T ConstType;
typedef _ConstructDefault Construct;
typedef _DestructDefault Destruct;
};
template< typename T >
struct _TypeTraits< T* >
{
typedef T BaseType;
typedef const T ConstType;
typedef _ConstructPtr Construct;
typedef _DestructPtr Destruct;
template< typename A >
static bool isTaggedPtr( A* ptr ) { return ( U32( ptr ) & 0x1 ); } //TODO: 64bits
template< typename A >
static A* getTaggedPtr( A* ptr ) { return ( A* ) ( U32( ptr ) | 0x1 ); } //TODO: 64bits
template< typename A >
static A* getUntaggedPtr( A* ptr ) { return ( A* ) ( U32( ptr ) & 0xFFFFFFFE ); } //TODO: 64bit
};
template< typename T >
struct TypeTraits : public TypeTraits< typename T::Parent >
{
typedef T BaseType;
typedef const T ConstType;
};
template< typename T >
struct TypeTraits< T* > : public TypeTraits< typename T::Parent* >
{
typedef T BaseType;
typedef const T ConstType;
};
template< typename T >
struct TypeTraits< T* const > : public TypeTraits< typename T::Parent* >
{
typedef T BaseType;
typedef const T ConstType;
};
template<>
struct TypeTraits< void > : public _TypeTraits< void > {};
template<>
struct TypeTraits< void* > : public _TypeTraits< void* > {};
template<>
struct TypeTraits< void* const > : public _TypeTraits< void* > {};
// Type traits for primitive types.
template<>
struct TypeTraits< bool > : public _TypeTraits< bool >
{
typedef _ConstructPrim Construct;
typedef _DestructPrim Destruct;
};
template<>
struct TypeTraits< S8 > : public _TypeTraits< S8 >
{
static const S8 MIN = S8_MIN;
static const S8 MAX = S8_MAX;
static const S8 ZERO = 0;
typedef _ConstructPrim Construct;
typedef _DestructPrim Destruct;
};
template<>
struct TypeTraits< U8 > : public _TypeTraits< U8 >
{
static const U8 MIN = 0;
static const U8 MAX = U8_MAX;
static const U8 ZERO = 0;
typedef _ConstructPrim Construct;
typedef _DestructPrim Destruct;
};
template<>
struct TypeTraits< S16 > : public _TypeTraits< S16 >
{
static const S16 MIN = S16_MIN;
static const S16 MAX = S16_MAX;
static const S16 ZERO = 0;
typedef _ConstructPrim Construct;
typedef _DestructPrim Destruct;
};
template<>
struct TypeTraits< U16 > : public _TypeTraits< U16 >
{
static const U16 MIN = 0;
static const U16 MAX = U16_MAX;
static const U16 ZERO = 0;
typedef _ConstructPrim Construct;
typedef _DestructPrim Destruct;
};
template<>
struct TypeTraits< S32 > : public _TypeTraits< S32 >
{
static const S32 MIN = S32_MIN;
static const S32 MAX = S32_MAX;
static const S32 ZERO = 0;
typedef _ConstructPrim Construct;
typedef _DestructPrim Destruct;
};
template<>
struct TypeTraits< U32 > : public _TypeTraits< U32 >
{
static const U32 MIN = 0;
static const U32 MAX = U32_MAX;
static const U32 ZERO = 0;
typedef _ConstructPrim Construct;
typedef _DestructPrim Destruct;
};
template<>
struct TypeTraits< F32 > : public _TypeTraits< F32 >
{
static const F32 MIN;
static const F32 MAX;
static const F32 ZERO;
typedef _ConstructPrim Construct;
typedef _DestructPrim Destruct;
};
//--------------------------------------------------------------------------
// Utilities.
//--------------------------------------------------------------------------
template< typename T >
inline T constructSingle()
{
typedef typename TypeTraits< T >::BaseType Type;
typedef typename TypeTraits< T >::Construct Construct;
return Construct::template single< Type >();
}
template< typename T, typename A >
inline T constructSingle( A a )
{
typedef typename TypeTraits< T >::BaseType BaseType;
typedef typename TypeTraits< T >::Construct Construct;
return Construct::template single< BaseType >( a );
}
template< typename T, typename A, typename B >
inline T constructSingle( A a, B b )
{
typedef typename TypeTraits< T >::BaseType BaseType;
typedef typename TypeTraits< T >::Construct Construct;
return Construct::template single< BaseType >( a, b );
}
template< typename T >
inline void constructArray( T* ptr, U32 num )
{
typedef typename TypeTraits< T >::BaseType BaseType;
typedef typename TypeTraits< T >::Construct Construct;
Construct::template array< BaseType >( ptr, num );
}
template< typename T, typename A >
inline void constructArray( T* ptr, U32 num, A a )
{
typedef typename TypeTraits< T >::BaseType BaseType;
typedef typename TypeTraits< T >::Construct Construct;
Construct::template array< BaseType >( ptr, num, a );
}
template< typename T >
inline void destructSingle( T& val )
{
typedef typename TypeTraits< T >::BaseType BaseType;
typedef typename TypeTraits< T >::Destruct Destruct;
Destruct::template single< BaseType >( val );
}
template< typename T >
inline void destructArray( T* ptr, U32 num )
{
typedef typename TypeTraits< T >::BaseType BaseType;
typedef typename TypeTraits< T >::Destruct Destruct;
Destruct::template array< BaseType >( ptr, num );
}
template< typename T>
inline T& Deref( T& val )
{
return val;
}
template< typename T >
inline T& Deref( T* ptr )
{
return *ptr;
}
/// Delete a single object policy.
struct DeleteSingle
{
template<class T>
static void destroy(T *ptr) { delete ptr; }
};
/// Delete an array of objects policy.
struct DeleteArray
{
template<class T>
static void destroy(T *ptr) { delete [] ptr; }
};
///
template< typename T >
struct ValueHolder
{
T value;
ValueHolder( const T& value )
: value( value ) {}
operator T() const { return value; }
};
template<>
struct ValueHolder< void >
{
ValueHolder() {}
};
#endif // _TYPETRAITS_H_