mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-06 05:37:48 +00:00
GEEKING
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 1995, 1999
|
||||
* Berkeley Software Design, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp
|
||||
*/
|
||||
|
||||
#ifndef _IFADDRS_H_
|
||||
#define _IFADDRS_H_
|
||||
|
||||
struct ifaddrs {
|
||||
struct ifaddrs *ifa_next;
|
||||
char *ifa_name;
|
||||
unsigned int ifa_flags;
|
||||
struct sockaddr *ifa_addr;
|
||||
struct sockaddr *ifa_netmask;
|
||||
struct sockaddr *ifa_dstaddr;
|
||||
void *ifa_data;
|
||||
};
|
||||
|
||||
/*
|
||||
* This may have been defined in <net/if.h>. Note that if <net/if.h> is
|
||||
* to be included it must be included before this header file.
|
||||
*/
|
||||
#ifndef ifa_broadaddr
|
||||
#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */
|
||||
#endif
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
extern int getifaddrs(struct ifaddrs **ifap);
|
||||
extern void freeifaddrs(struct ifaddrs *ifa);
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,766 @@
|
||||
#pragma once
|
||||
|
||||
#include "rbx/Debug.h"
|
||||
#include "boost/utility.hpp"
|
||||
#include "boost/type_traits.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
|
||||
//
|
||||
// ArrayBase
|
||||
//
|
||||
template< class T >
|
||||
class ArrayBase
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef T* iterator;
|
||||
typedef const T* const_iterator;
|
||||
typedef T* reverse_iterator;
|
||||
typedef const T* const_reverse_iterator;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef size_t size_type;
|
||||
|
||||
inline T& operator[]( size_t i );
|
||||
inline const T& operator[]( size_t i ) const;
|
||||
inline T& at( size_t i );
|
||||
inline const T& at( size_t i ) const;
|
||||
inline size_t size() const;
|
||||
inline T& front();
|
||||
inline const T& front() const;
|
||||
inline T& back();
|
||||
inline const T& back() const;
|
||||
inline T* begin();
|
||||
inline const T* begin() const;
|
||||
inline const T* cbegin() const;
|
||||
inline T* end();
|
||||
inline const T* end() const;
|
||||
inline const T* cend() const;
|
||||
inline T* data();
|
||||
inline const T* data() const;
|
||||
inline const T* cdata() const;
|
||||
inline bool empty() const;
|
||||
|
||||
protected:
|
||||
inline ArrayBase( T* _data, size_t _size );
|
||||
inline ArrayBase( const ArrayBase< T >& a );
|
||||
|
||||
private:
|
||||
inline ArrayBase< T >& operator=( const ArrayBase< T >& src );
|
||||
|
||||
protected:
|
||||
T* mData;
|
||||
size_t mSize;
|
||||
};
|
||||
|
||||
//
|
||||
// ArrayBase Implementation
|
||||
//
|
||||
template< class T >
|
||||
ArrayBase< T >::ArrayBase( T* _data, size_t _size ): mData( _data ), mSize( _size ) { }
|
||||
|
||||
template< class T >
|
||||
ArrayBase< T >::ArrayBase( const ArrayBase< T >& a ): mData( a.mData ), mSize( a.mSize ) { }
|
||||
|
||||
template< class T >
|
||||
inline T& ArrayBase< T >::operator[]( size_t i )
|
||||
{
|
||||
RBXASSERT_VERY_FAST( i < mSize );
|
||||
return mData[ i ];
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline const T& ArrayBase< T >::operator[]( size_t i ) const
|
||||
{
|
||||
RBXASSERT_VERY_FAST( i < mSize );
|
||||
return mData[ i ];
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline T& ArrayBase< T >::at( size_t i )
|
||||
{
|
||||
RBXASSERT_VERY_FAST( i < mSize );
|
||||
return mData[ i ];
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline const T& ArrayBase< T >::at( size_t i ) const
|
||||
{
|
||||
RBXASSERT_VERY_FAST( i < mSize );
|
||||
return mData[ i ];
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline size_t ArrayBase< T >::size() const
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline T& ArrayBase< T >::front()
|
||||
{
|
||||
RBXASSERT_VERY_FAST( mSize > 0 );
|
||||
return mData[ 0 ];
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline const T& ArrayBase< T >::front() const
|
||||
{
|
||||
RBXASSERT_VERY_FAST( mSize > 0 );
|
||||
return mData[ 0 ];
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline T& ArrayBase< T >::back()
|
||||
{
|
||||
RBXASSERT_VERY_FAST( mSize > 0 );
|
||||
return mData[ mSize - 1 ];
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline const T& ArrayBase< T >::back() const
|
||||
{
|
||||
RBXASSERT_VERY_FAST( mSize > 0 );
|
||||
return mData[ mSize - 1 ];
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline T* ArrayBase< T >::begin()
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline const T* ArrayBase< T >::begin() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline const T* ArrayBase< T >::cbegin() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline T* ArrayBase< T >::end()
|
||||
{
|
||||
return mData + mSize;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline const T* ArrayBase< T >::end() const
|
||||
{
|
||||
return mData + mSize;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline const T* ArrayBase< T >::cend() const
|
||||
{
|
||||
return mData + mSize;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline T* ArrayBase< T >::data()
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline const T* ArrayBase< T >::data() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline const T* ArrayBase< T >::cdata() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline bool ArrayBase< T >::empty() const
|
||||
{
|
||||
return mSize == 0;
|
||||
}
|
||||
|
||||
struct ArrayNoInit { };
|
||||
|
||||
//
|
||||
// ArrayDynamic
|
||||
//
|
||||
template< class T >
|
||||
class ArrayDynamic: public ArrayBase< T >
|
||||
{
|
||||
public:
|
||||
static const boost::uint32_t defaultAlignment = 16;
|
||||
typedef ArrayBase< T > Base;
|
||||
inline ArrayDynamic( );
|
||||
inline explicit ArrayDynamic( size_t _size, ArrayNoInit, boost::uint32_t _align = defaultAlignment );
|
||||
inline explicit ArrayDynamic( size_t _size, boost::uint32_t _align = defaultAlignment );
|
||||
inline ArrayDynamic( const ArrayDynamic< T >& a );
|
||||
inline ArrayDynamic( const ArrayBase< T >& a );
|
||||
inline ~ArrayDynamic();
|
||||
inline void clear();
|
||||
inline ArrayDynamic< T >& operator=( const ArrayDynamic< T >& _a );
|
||||
inline ArrayDynamic< T >& operator=( const ArrayBase< T >& _a );
|
||||
inline void reserve( size_t _capacity );
|
||||
inline void resize( size_t _size );
|
||||
inline size_t capacity() const;
|
||||
inline void push_back( const T& _a );
|
||||
inline void pop_back( );
|
||||
inline void insert( size_t i, const T& val );
|
||||
inline T* insert( const T* it, const T& val );
|
||||
template< class InputType >
|
||||
inline void insert_count( T* it, InputType first, size_t count );
|
||||
template< class InputType >
|
||||
inline void insert( T* it, InputType first, InputType last );
|
||||
void assign( size_t size, const T& value );
|
||||
|
||||
private:
|
||||
template< class InputType >
|
||||
inline void copyConstruct( void* dst, InputType src, size_t count );
|
||||
inline void copyConstruct( void* dst, const T* src, size_t count );
|
||||
inline void increase_capacity( size_t requestedCapacity );
|
||||
|
||||
size_t mCapacity;
|
||||
bool mNoInit;
|
||||
boost::uint32_t mAlignment;
|
||||
};
|
||||
|
||||
//
|
||||
// ArrayDynamic implementation
|
||||
//
|
||||
|
||||
namespace array_dynamic_details
|
||||
{
|
||||
inline void* aligned_alloc(std::size_t alignment, std::size_t size) BOOST_NOEXCEPT
|
||||
{
|
||||
if (!size) {
|
||||
return 0;
|
||||
}
|
||||
if (alignment < sizeof(void*)) {
|
||||
alignment = sizeof(void*);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
void* p = _aligned_malloc( size, alignment );
|
||||
#elif defined( __ANDROID__ )
|
||||
void* p = ::memalign( alignment, size );
|
||||
#else
|
||||
void* p;
|
||||
if (::posix_memalign(&p, alignment, size) != 0) {
|
||||
p = 0;
|
||||
}
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
||||
inline void aligned_free(void* ptr)
|
||||
BOOST_NOEXCEPT
|
||||
{
|
||||
#ifdef _WIN32
|
||||
_aligned_free( ptr );
|
||||
#else
|
||||
::free(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Construct
|
||||
//
|
||||
template< class T >
|
||||
static void construct( void* dst, size_t count, const boost::true_type& hasTrivialConstructor )
|
||||
{
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void construct( void* dst, size_t count, const boost::false_type& hasTrivialConstructor )
|
||||
{
|
||||
for( size_t i = 0; i < count; i++ )
|
||||
{
|
||||
new( reinterpret_cast< char* >( dst ) + i * sizeof( T ) )T();
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void construct( void* dst, size_t count )
|
||||
{
|
||||
construct< T >( dst, count, boost::has_trivial_constructor< T >() );
|
||||
}
|
||||
|
||||
//
|
||||
// Copy
|
||||
//
|
||||
template< class T >
|
||||
static void copyTrivial( void* dst, const T* src, size_t count, const boost::false_type& isFundamentalOrPointer )
|
||||
{
|
||||
memcpy( dst, src, count * sizeof( T ) );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void copyTrivial( void* dst, const T* src, size_t count, const boost::true_type& isFundamentalOrPointer )
|
||||
{
|
||||
if( count > 16 )
|
||||
{
|
||||
copyTrivial(dst, src, count, boost::false_type() );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( size_t i = 0; i < count; i++ )
|
||||
{
|
||||
new( reinterpret_cast< char* >( dst ) + i * sizeof( T ) )T( src[ i ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool B>
|
||||
struct bool_type : boost::integral_constant<bool, B>
|
||||
{
|
||||
static const bool value = B;
|
||||
};
|
||||
|
||||
template< class T >
|
||||
static void copyTrivial( void* dst, const T* src, size_t count )
|
||||
{
|
||||
copyTrivial( dst, src, count, bool_type< boost::is_fundamental< T >::value || boost::is_pointer< T >::value >() );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void copyConstruct( void* dst, const T* src, size_t count, const boost::true_type& hasTrivialCopyConstruct )
|
||||
{
|
||||
copyTrivial( dst, src, count );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void copyConstruct( void* dst, const T* src, size_t count, const boost::false_type& hasTrivialCopyConstruct )
|
||||
{
|
||||
for( size_t i = 0; i < count; i++ )
|
||||
{
|
||||
new( reinterpret_cast< char* >( dst ) + i * sizeof( T ) )T( src[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void copyConstruct( void* dst, const T* src, size_t count )
|
||||
{
|
||||
copyConstruct( dst, src, count, boost::has_trivial_copy_constructor< T >() );
|
||||
}
|
||||
|
||||
//
|
||||
// Destroy
|
||||
//
|
||||
template< class T >
|
||||
void destroy( T* src, size_t count, const boost::false_type& hasTrivialDestructor )
|
||||
{
|
||||
for( size_t i = 0; i < count; i++ )
|
||||
{
|
||||
src[ i ].~T();
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
void destroy( T* src, size_t count, const boost::true_type& hasTrivialDestructor )
|
||||
{
|
||||
}
|
||||
|
||||
template< class T >
|
||||
void destroy( T* src, size_t count )
|
||||
{
|
||||
destroy( src, count, boost::has_trivial_destructor< T >() );
|
||||
}
|
||||
|
||||
//
|
||||
// Shift right
|
||||
//
|
||||
template< class T >
|
||||
static void shiftRightTrivialCopy( T* src, size_t count, size_t offset, const boost::false_type& isFundamentalOrPointer )
|
||||
{
|
||||
memmove( static_cast< void* >( src + offset ), src, count * sizeof( T ) );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void shiftRightTrivialCopy( T* src, size_t count, size_t offset, const boost::true_type& isFundamentalOrPointer )
|
||||
{
|
||||
if( count < 16 )
|
||||
{
|
||||
for( size_t i = 0; i < count; i++ )
|
||||
{
|
||||
new( (T*)src + count + offset - 1 - i ) T( src[ count - 1 - i ] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
shiftRightTrivialCopy( src, count, offset, boost::false_type() );
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void shiftRightTrivialCopy( T* src, size_t count, size_t offset )
|
||||
{
|
||||
shiftRightTrivialCopy( src, count, offset, boost::integral_constant< bool, boost::is_fundamental< T >::value || boost::is_pointer< T >::value >() );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void shiftRight( T* src, size_t count, size_t offset, const boost::true_type& hasTrivialCopy )
|
||||
{
|
||||
shiftRightTrivialCopy( src, count, offset, boost::integral_constant< bool, boost::is_fundamental< T >::value || boost::is_pointer< T >::value >() );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void shiftRightNonTrivialOverlapping( T* src, size_t count, size_t offset, const boost::false_type& hasTrivialDestructor )
|
||||
{
|
||||
for( size_t i = 0; i < count; i++ )
|
||||
{
|
||||
new( (T*)src + count + offset - 1 - i ) T( src[ count - 1 - i ] );
|
||||
src[ count - 1 - i ].~T();
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void shiftRightNonTrivialOverlapping( T* src, size_t count, size_t offset, const boost::true_type& hasTrivialDestructor )
|
||||
{
|
||||
for( size_t i = 0; i < count; i++ )
|
||||
{
|
||||
new( (T*)src + count + offset - 1 - i ) T( src[ count - 1 - i ] );
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void shiftRight( T* src, size_t count, size_t offset, const boost::false_type& hasTrivialCopy )
|
||||
{
|
||||
shiftRightNonTrivialOverlapping( src, count, offset, boost::has_trivial_destructor< T >() );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void shiftRight( T* src, size_t count, size_t offset )
|
||||
{
|
||||
shiftRight( src, count, offset, boost::has_trivial_copy< T >() );
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
template< class InputType >
|
||||
void ArrayDynamic<T>::copyConstruct( void* dst, InputType src, size_t count )
|
||||
{
|
||||
for( size_t i = 0; i < count; i++ )
|
||||
{
|
||||
new( reinterpret_cast< char* >( dst ) + i * sizeof( T ) )T( *src );
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
void ArrayDynamic<T>::copyConstruct( void* dst, const T* src, size_t count )
|
||||
{
|
||||
if( mNoInit )
|
||||
{
|
||||
array_dynamic_details::copyTrivial( dst, src, count );
|
||||
}
|
||||
else
|
||||
{
|
||||
array_dynamic_details::copyConstruct( dst, src, count );
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
ArrayDynamic<T>::ArrayDynamic( ): ArrayBase< T >( NULL, 0 ), mAlignment( 16 ), mNoInit( false ), mCapacity( 0 ) { }
|
||||
|
||||
template< class T >
|
||||
ArrayDynamic<T>::ArrayDynamic( size_t _size, boost::uint32_t _align ): ArrayBase< T >( NULL, 0 ), mAlignment( _align ), mNoInit( false ), mCapacity( 0 )
|
||||
{
|
||||
reserve( _size );
|
||||
Base::mSize = _size;
|
||||
|
||||
// Initialize
|
||||
array_dynamic_details::construct< T >( Base::mData, Base::mSize );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
ArrayDynamic<T>::ArrayDynamic( size_t _size, ArrayNoInit, boost::uint32_t _align ): ArrayBase< T >( NULL, 0 ), mAlignment( _align ), mNoInit( true ), mCapacity( 0 )
|
||||
{
|
||||
reserve( _size );
|
||||
Base::mSize = _size;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
ArrayDynamic<T>::ArrayDynamic( const ArrayDynamic< T >& a ): ArrayBase< T >( NULL, 0 ), mAlignment( a.mAlignment ), mNoInit( a.mNoInit ), mCapacity( 0 )
|
||||
{
|
||||
reserve( a.size() );
|
||||
copyConstruct( Base::mData, a.data(), a.size() );
|
||||
Base::mSize = a.size();
|
||||
}
|
||||
|
||||
template< class T >
|
||||
ArrayDynamic<T>::ArrayDynamic( const ArrayBase< T >& a ): ArrayBase< T >( NULL, 0 ), mAlignment( defaultAlignment ), mNoInit( false ), mCapacity( 0 )
|
||||
{
|
||||
reserve( a.size() );
|
||||
copyConstruct( Base::mData, a.data(), a.size() );
|
||||
Base::mSize = a.size();
|
||||
}
|
||||
|
||||
template< class T >
|
||||
ArrayDynamic<T>::~ArrayDynamic()
|
||||
{
|
||||
clear();
|
||||
if( mCapacity > 0 )
|
||||
{
|
||||
array_dynamic_details::aligned_free( Base::mData );
|
||||
mCapacity = 0;
|
||||
Base::mData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
void ArrayDynamic<T>::clear()
|
||||
{
|
||||
if( !mNoInit )
|
||||
{
|
||||
array_dynamic_details::destroy( Base::mData, Base::mSize );
|
||||
}
|
||||
Base::mSize = 0;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
ArrayDynamic< T >& ArrayDynamic<T>::operator=( const ArrayDynamic< T >& _a )
|
||||
{
|
||||
clear();
|
||||
if( mCapacity > 0 && mAlignment != _a.mAlignment )
|
||||
{
|
||||
array_dynamic_details::aligned_free( Base::mData );
|
||||
mCapacity = 0;
|
||||
Base::mData = NULL;
|
||||
}
|
||||
mNoInit = _a.mNoInit;
|
||||
mAlignment = _a.mAlignment;
|
||||
reserve( _a.size() );
|
||||
copyConstruct( Base::mData, _a.data(), _a.size() );
|
||||
Base::mSize = _a.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
ArrayDynamic< T >& ArrayDynamic<T>::operator=( const ArrayBase< T >& _a )
|
||||
{
|
||||
clear();
|
||||
reserve( _a.size() );
|
||||
copyConstruct( Base::mData, _a.data(), _a.size() );
|
||||
Base::mSize = _a.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
void ArrayDynamic<T>::reserve( size_t _capacity )
|
||||
{
|
||||
if( _capacity > mCapacity )
|
||||
{
|
||||
void* newData = array_dynamic_details::aligned_alloc( mAlignment, _capacity * sizeof( T ) );
|
||||
if( mNoInit )
|
||||
{
|
||||
array_dynamic_details::copyTrivial( newData, Base::mData, Base::mSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
array_dynamic_details::copyConstruct( newData, Base::mData, Base::mSize );
|
||||
array_dynamic_details::destroy( Base::mData, Base::mSize );
|
||||
}
|
||||
|
||||
if( mCapacity > 0 )
|
||||
{
|
||||
array_dynamic_details::aligned_free( Base::mData );
|
||||
}
|
||||
Base::mData = ( T* )newData;
|
||||
mCapacity = _capacity;
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
void ArrayDynamic<T>::resize( size_t _size )
|
||||
{
|
||||
if( _size <= Base::mSize )
|
||||
{
|
||||
if( !mNoInit )
|
||||
{
|
||||
array_dynamic_details::destroy( Base::mData + _size, Base::mSize - _size );
|
||||
}
|
||||
Base::mSize = _size;
|
||||
return;
|
||||
}
|
||||
|
||||
if( _size > mCapacity )
|
||||
{
|
||||
reserve( _size );
|
||||
}
|
||||
|
||||
if( !mNoInit )
|
||||
{
|
||||
array_dynamic_details::construct< T >( Base::mData + Base::mSize, _size - Base::mSize );
|
||||
}
|
||||
Base::mSize = _size;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline size_t ArrayDynamic<T>::capacity() const
|
||||
{
|
||||
return mCapacity;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline void ArrayDynamic<T>::increase_capacity( size_t requestedCapacity )
|
||||
{
|
||||
size_t newCapacity = mCapacity == 0 ? 2 : 2 * mCapacity;
|
||||
while (newCapacity < requestedCapacity) newCapacity *= 2;
|
||||
reserve( newCapacity );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline void ArrayDynamic<T>::push_back( const T& _a )
|
||||
{
|
||||
if( mCapacity == Base::mSize )
|
||||
{
|
||||
increase_capacity( mCapacity + 1 );
|
||||
}
|
||||
new( Base::data() + Base::mSize )T( _a );
|
||||
Base::mSize++;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline void ArrayDynamic<T>::pop_back( )
|
||||
{
|
||||
RBXASSERT_VERY_FAST( Base::mSize > 0 );
|
||||
Base::mSize--;
|
||||
if( mNoInit )
|
||||
{
|
||||
( Base::data()+Base::mSize )->~T();
|
||||
}
|
||||
}
|
||||
|
||||
template< class T >
|
||||
void ArrayDynamic<T>::insert( size_t i, const T& val )
|
||||
{
|
||||
RBXASSERT_VERY_FAST( i <= Base::mSize );
|
||||
if( i == Base::mSize )
|
||||
{
|
||||
push_back( val );
|
||||
return;
|
||||
}
|
||||
if( mCapacity == Base::mSize )
|
||||
{
|
||||
increase_capacity( mCapacity + 1 );
|
||||
}
|
||||
if( mNoInit )
|
||||
{
|
||||
array_dynamic_details::shiftRightTrivialCopy( Base::data() + i, Base::mSize - i, 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
array_dynamic_details::shiftRight( Base::data() + i, Base::mSize - i, 1 );
|
||||
}
|
||||
Base::mSize++;
|
||||
new( Base::data() + i )T( val );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline T* ArrayDynamic<T>::insert( const T* it, const T& val )
|
||||
{
|
||||
RBXASSERT_VERY_FAST( Base::begin() <= it );
|
||||
RBXASSERT_VERY_FAST( Base::end() >= it );
|
||||
size_t index = it - Base::begin();
|
||||
insert( index, val );
|
||||
return Base::begin() + index;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
template< class InputType >
|
||||
void ArrayDynamic<T>::insert_count( T* it, InputType first, size_t count )
|
||||
{
|
||||
RBXASSERT_VERY_FAST( Base::begin() <= it );
|
||||
RBXASSERT_VERY_FAST( Base::end() >= it );
|
||||
size_t index = it - Base::begin();
|
||||
if( mCapacity < Base::mSize + count )
|
||||
{
|
||||
increase_capacity( Base::mSize + count );
|
||||
}
|
||||
it = Base::begin() + index;
|
||||
if( it < Base::end() )
|
||||
{
|
||||
if( mNoInit )
|
||||
{
|
||||
array_dynamic_details::shiftRightTrivialCopy( it, Base::end() - it, count );
|
||||
}
|
||||
else
|
||||
{
|
||||
array_dynamic_details::shiftRight( it, Base::end() - it, count );
|
||||
}
|
||||
}
|
||||
copyConstruct( it, first, count );
|
||||
Base::mSize += count;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
template< class InputType >
|
||||
inline void ArrayDynamic<T>::insert( T* it, InputType first, InputType last )
|
||||
{
|
||||
size_t count = last - first;
|
||||
insert_count( it, first, count );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
void ArrayDynamic<T>::assign( size_t size, const T& value )
|
||||
{
|
||||
clear();
|
||||
reserve( size );
|
||||
T* it = Base::begin();
|
||||
for( size_t i = 0; i < size; i++ )
|
||||
{
|
||||
new( it + i )T( value );
|
||||
}
|
||||
Base::mSize = size;
|
||||
}
|
||||
|
||||
//
|
||||
// ArrayRef
|
||||
//
|
||||
template< class T >
|
||||
class ArrayRef : public ArrayBase< T >
|
||||
{
|
||||
public:
|
||||
typedef ArrayBase< T > Base;
|
||||
|
||||
inline ArrayRef( T* _data, size_t _size );
|
||||
inline ArrayRef( const ArrayRef< T >& a );
|
||||
inline ArrayRef( const ArrayBase< T >& a );
|
||||
inline ArrayRef< T >& operator=( const ArrayBase< T >& src );
|
||||
};
|
||||
|
||||
//
|
||||
// ArrayRef Implementation
|
||||
//
|
||||
template< class T >
|
||||
ArrayRef< T >::ArrayRef( T* _data, size_t _size ): Base( _data, _size ) { }
|
||||
|
||||
template< class T >
|
||||
ArrayRef< T >::ArrayRef( const ArrayRef< T >& a ): Base( a ) { }
|
||||
|
||||
template< class T >
|
||||
ArrayRef< T >::ArrayRef( const ArrayBase< T >& a ): Base( a ) { }
|
||||
|
||||
template< class T >
|
||||
ArrayRef< T >& ArrayRef< T >::operator=( const ArrayBase< T >& src )
|
||||
{
|
||||
Base::mData = src.mData;
|
||||
Base::mSize = src.mSize;
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* BaldPtr.h
|
||||
* Copyright (c) 2013 ROBLOX Corp. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rbx/Debug.h"
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
|
||||
/**
|
||||
* Wraps raw pointers with a layer of protection in debug builds.
|
||||
* Checks for bad pointers on access.
|
||||
*/
|
||||
template<class T>
|
||||
class BaldPtr
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Sets pointer to NULL.
|
||||
*/
|
||||
inline BaldPtr()
|
||||
: mPointer(NULL)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Pointer is also validated.
|
||||
*
|
||||
* @param Pointer pointer to wrap, may be NULL
|
||||
*/
|
||||
inline BaldPtr(T* Pointer)
|
||||
: mPointer(Pointer)
|
||||
{
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shallow copies pointer.
|
||||
* Pointer is validated.
|
||||
*
|
||||
* @param Pointer pointer to copy, may be NULL
|
||||
* @return this pointer
|
||||
*/
|
||||
inline T*& operator=(T* Pointer)
|
||||
{
|
||||
mPointer = Pointer;
|
||||
validate();
|
||||
return mPointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dereference operator.
|
||||
* Checks for NULL and validates.
|
||||
*
|
||||
* @return reference to pointer
|
||||
*/
|
||||
inline T& operator*() const
|
||||
{
|
||||
RBXASSERT_VERY_FAST(mPointer);
|
||||
validate();
|
||||
return *mPointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class pointer cast.
|
||||
* Validates pointer.
|
||||
*
|
||||
* @return pointer cast to a class *, may be NULL
|
||||
*/
|
||||
inline operator T*() const
|
||||
{
|
||||
validate();
|
||||
return mPointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arrow operator that checks and returns the pointer.
|
||||
* Checks for NULL and validates.
|
||||
*
|
||||
* @return data pointer
|
||||
*/
|
||||
inline T* operator->() const
|
||||
{
|
||||
RBXASSERT_VERY_FAST(mPointer);
|
||||
validate();
|
||||
return mPointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw pointer value.
|
||||
* Performs no checks.
|
||||
*
|
||||
* @return data pointer, may be NULL
|
||||
*/
|
||||
inline T* get() const { return mPointer; }
|
||||
|
||||
/**
|
||||
* Validates the pointer.
|
||||
* Various memory patterns are checked such as fence posts,
|
||||
* deleted, and allocated memory. Standard CRT patterns are
|
||||
* checked as well as Ogre patterns.
|
||||
*
|
||||
* @return description
|
||||
*/
|
||||
inline void validate() const
|
||||
{
|
||||
#ifdef __RBX_VERY_FAST_ASSERT
|
||||
if ( !mPointer )
|
||||
return;
|
||||
#endif
|
||||
|
||||
RBXASSERT_VERY_FAST(
|
||||
// CRT debug allocator
|
||||
(unsigned)mPointer != 0xCCCCCCCC && // uninitialized stack memory
|
||||
(unsigned)mPointer != 0xCDCDCDCD && // uninitialized heap memory
|
||||
(unsigned)mPointer != 0xFDFDFDFD && // "no man's land" guard bytes before and after allocated heap memory
|
||||
(unsigned)mPointer != 0xDDDDDDDD && // deleted heap memory
|
||||
(unsigned)mPointer != 0xFEEEFEEE && // deleted heap memory
|
||||
|
||||
// Ogre allocator
|
||||
(unsigned)mPointer != 0xBAADF00D && // before "no man's land" guard bytes
|
||||
(unsigned)mPointer != 0xDEADC0DE && // after "no man's land" guard bytes
|
||||
(unsigned)mPointer != 0xFEEDFACE && // uninitialized memory
|
||||
(unsigned)mPointer != 0xDEADBEEF ); // deleted memory
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
T* mPointer;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// struct Foo
|
||||
// {
|
||||
// int x;
|
||||
// };
|
||||
//
|
||||
// template<typename Ptr>
|
||||
// void test()
|
||||
// {
|
||||
// Ptr f = new Foo();
|
||||
// Foo* f2 = f;
|
||||
// f->x = 23;
|
||||
// (*f).x = 32;
|
||||
// void* v = f;
|
||||
// BaldPtr<Foo> b = f;
|
||||
// BaldPtr<Foo> b2(f);
|
||||
// const void* vc = f;
|
||||
//
|
||||
// delete f;
|
||||
// }
|
||||
//
|
||||
// template<typename Ptr>
|
||||
// void testConst()
|
||||
// {
|
||||
// Ptr f = new Foo();
|
||||
// const Foo* f2 = f;
|
||||
// int x = f->x;
|
||||
// int y = (*f).x;
|
||||
// const void* v = f;
|
||||
// BaldPtr<const Foo> b = f;
|
||||
// BaldPtr<const Foo> b2(f);
|
||||
//
|
||||
// delete f;
|
||||
// }
|
||||
//
|
||||
// int _tmain(int argc, _TCHAR* argv[])
|
||||
// {
|
||||
// test<BaldPtr<Foo> const>();
|
||||
// test<Foo* const>();
|
||||
// test<BaldPtr<Foo> >();
|
||||
// test<Foo* >();
|
||||
//
|
||||
// testConst<BaldPtr<const Foo> const>();
|
||||
// testConst<const Foo* const>();
|
||||
// testConst<BaldPtr<const Foo> >();
|
||||
// testConst<const Foo* >();
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // defines std::min and std::max before Windows.h takes over
|
||||
|
||||
#include "boost/config/user.hpp"
|
||||
#ifndef ROBLOX_BOOST_CONFIGS
|
||||
#error // Please re-get the full boost directory
|
||||
#endif
|
||||
#include "boost/shared_ptr.hpp"
|
||||
|
||||
|
||||
#include "boost/bind.hpp"
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
|
||||
// for placement_any
|
||||
#include <boost/type_traits/remove_reference.hpp>
|
||||
#include <boost/type_traits/is_reference.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
using boost::shared_ptr;
|
||||
using boost::scoped_ptr;
|
||||
using boost::weak_ptr;
|
||||
|
||||
#ifdef _WIN32
|
||||
//#include <windows.h>
|
||||
#else
|
||||
#include "RbxFormat.h"
|
||||
#include <pthread.h>
|
||||
// This is a hack. Truncates a pointer.
|
||||
#define GetCurrentThreadId() (static_cast<unsigned>(reinterpret_cast<long>(pthread_self())))
|
||||
#define SwitchToThread() {sched_yield();}
|
||||
// We may decide to use the following instead on Mac, but we would prefer the above.
|
||||
//#define SwitchToThread() {struct timespec req = {0, 1}; nanosleep(&req, NULL);}
|
||||
#endif
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
// TODO: Does boost have a nicer way of doing this?
|
||||
template<class T>
|
||||
void del_fun(T* t)
|
||||
{
|
||||
delete t;
|
||||
}
|
||||
|
||||
bool isFinite(double value);
|
||||
bool isFinite(int value);
|
||||
}
|
||||
|
||||
namespace rbx
|
||||
{
|
||||
|
||||
|
||||
namespace implementation
|
||||
{
|
||||
class type_holder : boost::noncopyable
|
||||
{
|
||||
public: // operations
|
||||
void (*destruct)(char* dest);
|
||||
void (*construct)(const char* src, char* dest);
|
||||
};
|
||||
|
||||
template<typename ValueType>
|
||||
class typed_holder : public type_holder
|
||||
{
|
||||
typed_holder()
|
||||
{
|
||||
construct = &construct_func;
|
||||
destruct = &destruct_func;
|
||||
}
|
||||
|
||||
public:
|
||||
static const typed_holder* singleton()
|
||||
{
|
||||
static typed_holder<ValueType> s;
|
||||
return &s;
|
||||
}
|
||||
|
||||
static void construct_func(const char* src, char* dest)
|
||||
{
|
||||
const ValueType* value = reinterpret_cast<const ValueType*>(src);
|
||||
ValueType* v = reinterpret_cast<ValueType*>(dest);
|
||||
new (v) ValueType(*value);
|
||||
}
|
||||
|
||||
static void destruct_func(char* dest)
|
||||
{
|
||||
ValueType* value = reinterpret_cast<ValueType*>(dest);
|
||||
value->~ValueType();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// placement_any is a reworking of boost::any that embeds the value inside of
|
||||
// itself, rather than in the heap. This eliminates new/delete operations. However,
|
||||
// you must know in advance how big the values are able to be. Also, placement_any
|
||||
// allocates enough memory for the largest possible object, even when it is void.
|
||||
// SizeType must be a class that is as large as the largest sized object that
|
||||
// will be placed inside placement_any
|
||||
template<typename SizeType>
|
||||
class placement_any
|
||||
{
|
||||
public: // structors
|
||||
placement_any()
|
||||
: holder(0)
|
||||
{
|
||||
}
|
||||
|
||||
placement_any(const placement_any& other)
|
||||
: holder(0)
|
||||
{
|
||||
if (other.holder)
|
||||
(*other.holder->construct)(other.data, data);
|
||||
holder = other.holder; // construct didn't throw, so we can assign the holder now
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
explicit placement_any(const ValueType& value)
|
||||
: holder(0)
|
||||
{
|
||||
// If this fails, then make ValueType the new SizeType!
|
||||
BOOST_STATIC_ASSERT((sizeof(ValueType) <= sizeof(SizeType)));
|
||||
|
||||
ValueType* v = reinterpret_cast<ValueType*>(data);
|
||||
new (v) ValueType(value);
|
||||
holder = implementation::typed_holder<ValueType>::singleton(); // construct didn't throw, so we can assign it now
|
||||
}
|
||||
|
||||
~placement_any()
|
||||
{
|
||||
if (holder)
|
||||
(*holder->destruct)(data);
|
||||
}
|
||||
|
||||
public: // modifiers
|
||||
placement_any& swap(placement_any& rhs)
|
||||
{
|
||||
placement_any temp(*this);
|
||||
*this = rhs;
|
||||
rhs = temp;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
placement_any& operator=(const ValueType& rhs)
|
||||
{
|
||||
const implementation::typed_holder<ValueType>* s = implementation::typed_holder<ValueType>::singleton();
|
||||
if (holder == s)
|
||||
{
|
||||
// Optimization. Is this worth it?
|
||||
ValueType* dest = reinterpret_cast<ValueType*>(data);
|
||||
*dest = rhs;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (holder)
|
||||
{
|
||||
(*holder->destruct)(data);
|
||||
holder = 0;
|
||||
}
|
||||
|
||||
// If this fails, then make ValueType the new SizeType!
|
||||
BOOST_STATIC_ASSERT((sizeof(ValueType) <= sizeof(SizeType)));
|
||||
|
||||
ValueType* v = reinterpret_cast<ValueType*>(data);
|
||||
new (v) ValueType(rhs);
|
||||
|
||||
holder = s;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
placement_any& operator=(const placement_any& rhs)
|
||||
{
|
||||
if (&rhs == this)
|
||||
return *this;
|
||||
|
||||
if (holder)
|
||||
{
|
||||
(*holder->destruct)(data);
|
||||
holder = 0;
|
||||
}
|
||||
if (rhs.holder)
|
||||
{
|
||||
(*rhs.holder->construct)(rhs.data, data);
|
||||
holder = rhs.holder;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
public: // queries
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return holder == 0;
|
||||
}
|
||||
|
||||
const char* getData() const
|
||||
{
|
||||
return holder ? data : NULL;
|
||||
}
|
||||
|
||||
char* getData()
|
||||
{
|
||||
return holder ? data : NULL;
|
||||
}
|
||||
|
||||
private: // representation
|
||||
|
||||
const implementation::type_holder* holder;
|
||||
char data[sizeof(SizeType)];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include "boost/noncopyable.hpp"
|
||||
#include "RbxFormat.h"
|
||||
#include "rbx/RbxTime.h"
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
|
||||
#ifndef _WIN32
|
||||
#define RBX_CEVENT_BOOST
|
||||
#endif
|
||||
|
||||
// TODO: This class is modeled heavily off of ATL::CEvent and should be
|
||||
// cleaned up. Probably it should be split into 2 classes:
|
||||
// Manual and Automatic
|
||||
class CEvent :
|
||||
public boost::noncopyable
|
||||
{
|
||||
#ifdef RBX_CEVENT_BOOST
|
||||
const bool manualReset;
|
||||
volatile bool isSet;
|
||||
boost::condition_variable cond;
|
||||
boost::mutex mut;
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
private:
|
||||
void* m_h;
|
||||
#endif
|
||||
#endif
|
||||
public:
|
||||
CEvent(bool bManualReset);
|
||||
~CEvent() throw();
|
||||
void Set() throw();
|
||||
void Wait();
|
||||
// TODO: Deprecate:
|
||||
bool Wait(int milliseconds);
|
||||
bool Wait(RBX::Time::Interval interval) { return Wait((int)(1000.0 * interval.seconds())); }
|
||||
|
||||
private:
|
||||
static const int cWAIT_OBJECT_0 = 0;
|
||||
static const int cWAIT_TIMEOUT = 258;
|
||||
static const int cINFINITE = 0xFFFFFFFF;
|
||||
static int WaitForSingleObject(CEvent& event, int milliseconds);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "rbx/atomic.h"
|
||||
#include "rbx/Declarations.h"
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
|
||||
namespace Diagnostics
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class RBXBaseClass Countable
|
||||
{
|
||||
static rbx::atomic<int> count;
|
||||
public:
|
||||
static long getCount() { return count; }
|
||||
~Countable()
|
||||
{
|
||||
--count;
|
||||
}
|
||||
protected:
|
||||
Countable()
|
||||
{
|
||||
++count;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
rbx::atomic<int> Countable<T>::count;
|
||||
|
||||
} // namespace Diagnostics
|
||||
|
||||
} // namespace RBX
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(_WIN32) && !defined(RBX_PLATFORM_DURANGO)
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
class Crypt
|
||||
{
|
||||
#if defined (_WIN32) && !defined(RBX_PLATFORM_DURANGO)
|
||||
HCRYPTPROV context;
|
||||
HCRYPTKEY key;
|
||||
#endif
|
||||
public:
|
||||
Crypt();
|
||||
~Crypt();
|
||||
void verifySignatureBase64(std::string message, std::string signatureBase64);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
#pragma once
|
||||
|
||||
#include "RbxPlatform.h"
|
||||
#include "RbxAssert.h"
|
||||
#include "RbxFormat.h"
|
||||
#include <set>
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
#include <assert.h>
|
||||
|
||||
#if (defined(_DEBUG) && defined(_WIN32))
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <typeinfo>
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#include "rbx/Declarations.h"
|
||||
#include "FastLog.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#define __noop
|
||||
inline void DebugBreak()
|
||||
{
|
||||
#if defined(__i386__)
|
||||
// gcc on intel
|
||||
__asm__ __volatile__ ( "int $3" );
|
||||
#else
|
||||
// some other gcc
|
||||
::abort();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
LOGGROUP(Asserts)
|
||||
|
||||
/* Overview of builds and switches:
|
||||
|
||||
RBXASSERT: Standard assert. Should be reasonably fast. Do not do "finds" or complex stuff here. Simple bools, simple math, a couple levels of pointer indirection, etc.
|
||||
RBXASSERT_VERY_FAST: High fr equency, extremely fast assert. Not in regular debug build because frequency too high. Mostly inner engine stuff
|
||||
RBXASSERT_SLOW: Put things like "find" here. Will always run in debug builds
|
||||
RBXASSERT_IF_VALIDATING: Very slow stuff. Only turns on if the "validating debug" switch is turned on in debug or noOpt build
|
||||
RBXASSERT_FISHING: Usually doesn't go off, should be safe - turn on for engine testing
|
||||
|
||||
RBXASSERT() RBXASSERT_VERY_FAST() RBXASSERT_SLOW() RBXASSERT_IF_VALIDATING() RBXASSERT_FISHING()
|
||||
DEBUG X X X X -
|
||||
NoOpt X X - - -
|
||||
ReleaseAssert X - - - -
|
||||
Release - - - - -
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define __RBX_VERY_FAST_ASSERT
|
||||
#define __RBX_VALIDATE_ASSERT
|
||||
// #define __RBX_SLOW_ASSERT // TODO: Hire a physics guy to enable them
|
||||
// #define __RBX_FISHING_ASSERT
|
||||
#define __RBX_NOT_RELEASE
|
||||
#endif
|
||||
|
||||
#ifdef _NOOPT
|
||||
#define __RBX_CRASH_ON_ASSERT
|
||||
#define __RBX_VERY_FAST_ASSERT
|
||||
#define __RBX_NOT_RELEASE
|
||||
#endif
|
||||
|
||||
namespace RBX {
|
||||
|
||||
// Used for memory leak detection and other stuff
|
||||
class Debugable
|
||||
{
|
||||
public:
|
||||
// this is here as a last chance way to debug an assert build, force assertions on, but not crash
|
||||
static volatile bool doCrashEnabled;
|
||||
|
||||
static void doCrash();
|
||||
static void doCrash(const char*);
|
||||
|
||||
static void* badMemory() {return reinterpret_cast<void*>(0x00000003);} // set values to this when deleting to check if ever coming back
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void RBXCRASH();
|
||||
void RBXCRASH(const char* message);
|
||||
|
||||
void ReleaseAssert(int channel, const char* msg);
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define TOSTRING(x) STRINGIFY(x)
|
||||
|
||||
// macro to convince a compiler a variable is used while not generating instructions (useful for removing warnings)
|
||||
#define RBX_UNUSED(x) (void)(sizeof((x), 0))
|
||||
|
||||
// This macro will cause a crash. Usually you don't call it directly. Use RBXASSERT instead
|
||||
#define RBX_CRASH_ASSERT(expr) \
|
||||
((void) (!!(expr) || \
|
||||
((RBX::_internal::_debugHook != NULL) && (RBX::_internal::_debugHook(#expr, __FILE__, __LINE__))) || \
|
||||
(RBX::Debugable::doCrash(#expr), 0)))
|
||||
|
||||
// This macro will just log an assert string, if we will run into crash log with the assert information will be sent to us
|
||||
#define RBX_LOG_ASSERT(expr) \
|
||||
((void) (FLog::Asserts && (!!(expr) || \
|
||||
((RBX::_internal::_debugHook != NULL) && (RBX::_internal::_debugHook(#expr, __FILE__, __LINE__))) || \
|
||||
(ReleaseAssert(FLog::Asserts,#expr " file: " __FILE__ " line: " TOSTRING(__LINE__)), 0))))
|
||||
|
||||
|
||||
// LEGACY_ASSERT should be used when we have some assert bogging us and it seems like this guy is a good candidate for removal
|
||||
// usage just replace RBXASSERT with LEGACY_ASSERT and it will gone by default, but if you need to see it temporary define FIRE_LEGACY_ASSERT
|
||||
#undef FIRE_LEGACY_ASSERT
|
||||
|
||||
#ifdef FIRE_LEGACY_ASSERT
|
||||
#define LEGACY_ASSERT(expr) RBXASSERT(expr)
|
||||
#else
|
||||
#define LEGACY_ASSERT(expr) ((void)0)
|
||||
#endif
|
||||
|
||||
#define RBXASSERTENABLED
|
||||
|
||||
// RBXASSERT()
|
||||
//
|
||||
#ifdef __RBX_CRASH_ON_ASSERT
|
||||
#define RBXASSERT RBX_CRASH_ASSERT
|
||||
#else
|
||||
#if (defined(_DEBUG) && defined(__APPLE__)) // Apple Debug
|
||||
#include "TargetConditionals.h"
|
||||
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
|
||||
#define RBXASSERT RBX_LOG_ASSERT // iOS has no way to step over asserts (makes debugging hard)
|
||||
#else
|
||||
#define RBXASSERT(expr) assert(expr)
|
||||
#define RBXASSERTENABLED
|
||||
#endif
|
||||
#elif (defined(_DEBUG) && defined(_WIN32)) // Windows Debug
|
||||
#define RBXASSERT(expr) \
|
||||
((void) (!!(expr) || \
|
||||
((RBX::_internal::_debugHook != NULL) && (RBX::_internal::_debugHook(#expr, __FILE__, __LINE__))) || \
|
||||
(_ASSERTE(expr), 0)))
|
||||
#define RBXASSERTENABLED
|
||||
#else // All Platform Release
|
||||
#define RBXASSERT RBX_LOG_ASSERT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// RBXASSERT_VERY_FAST()
|
||||
//
|
||||
#ifdef __RBX_VERY_FAST_ASSERT
|
||||
#define RBXASSERT_VERY_FAST(expr) RBXASSERT(expr)
|
||||
#else
|
||||
#define RBXASSERT_VERY_FAST(expr) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
// RBXASSERT_SLOW()
|
||||
//
|
||||
#ifdef __RBX_SLOW_ASSERT
|
||||
#define RBXASSERT_SLOW(expr) RBXASSERT(expr)
|
||||
#else
|
||||
#define RBXASSERT_SLOW(expr) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
// RBXASSERT_FISHING)
|
||||
//
|
||||
#ifdef __RBX_FISHING_ASSERT
|
||||
#define RBXASSERT_FISHING(expr) RBXASSERT(expr)
|
||||
#else
|
||||
#define RBXASSERT_FISHING(expr) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
// RBXASSERT_IF_VALIDATING()
|
||||
//
|
||||
#ifdef __RBX_VALIDATE_ASSERT
|
||||
#define RBXASSERT_IF_VALIDATING(expr) RBXASSERT( (expr) )
|
||||
|
||||
#else
|
||||
#define RBXASSERT_IF_VALIDATING(expr) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
// RBXASSERT_NOT_RELEASE() make sure this code is not being compiled in release build
|
||||
#ifdef __RBX_NOT_RELEASE
|
||||
#define RBXASSERT_NOT_RELEASE() ((void)0)
|
||||
#else
|
||||
#define RBXASSERT_NOT_RELEASE() RBXCRASH()
|
||||
#endif
|
||||
|
||||
|
||||
// Same as boost::polymorphic_downcast but with an RBXASSERT
|
||||
template<class T, class U>
|
||||
inline T rbx_static_cast(U u) {
|
||||
RBXASSERT_SLOW(dynamic_cast<T>(u)==u);
|
||||
return static_cast<T>(u);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
|
||||
http://msdn.microsoft.com/en-us/magazine/cc301398.aspx
|
||||
|
||||
These days abstract classes are
|
||||
not just common, they're ubiquitous. Think: where in Windows® do
|
||||
abstract classes appear over and over again? That's right, in COM!
|
||||
A COM interface is an abstract class with only pure virtual functions.
|
||||
As everything in Windows migrates to COM land, Windows-based programs
|
||||
have COM interfaces up the wazoo. A typical COM class might implement
|
||||
a dozen or more interfaces, each with several functions.
|
||||
Even outside COM, the notion of an interface is quite powerful
|
||||
and useful, as in the Java language. Each interface implementation might
|
||||
use several layers of classes, none intended to be used by themselves,
|
||||
but only as base classes for yet more classes. ATL provides many such
|
||||
classes using templates, another source of class proliferation. All of
|
||||
this adds up to lots of initialization code and useless vtables with
|
||||
NULL entries. The total bloat can become significant, especially when
|
||||
you're developing small objects that must load over a slow medium like
|
||||
the Internet.
|
||||
So __declspec(novtable) was invented to solve the problem. It's a
|
||||
Microsoft-specific optimization hint that tells the compiler: this class
|
||||
is never used by itself, but only as a base class for other classes, so
|
||||
don't bother with all that vtable stuff, thank you.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// Decoration to indicate a class is to be treated as an "Interface"
|
||||
// The class should contain pure virtual functions and maybe a little
|
||||
// trivial code. Otherwise, use RBXBaseClass.
|
||||
// !!! You can't define a virtual destructor for a class of this type
|
||||
#define RBXInterface __declspec(novtable)
|
||||
|
||||
// Decoration to indicate a class should not be instantiated directly
|
||||
// !!! You can't define a virtual destructor for a class of this type
|
||||
#define RBXBaseClass __declspec(novtable)
|
||||
|
||||
/****
|
||||
Note:
|
||||
|
||||
C++ doesn't have a strict "Interface" type. RBXInterface should be used
|
||||
for classes that declare only pure virtual functions and maybe a constructor
|
||||
and/or a field. Classes that define non-trivial code should use RBXBaseClass instead.
|
||||
|
||||
***/
|
||||
|
||||
#else
|
||||
|
||||
#define RBXInterface
|
||||
#define RBXBaseClass
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include "rbx/Debug.h"
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
// Internal implementation of DenseHashSet and DenseHashMap
|
||||
namespace detail
|
||||
{
|
||||
template <typename Key> struct DenseHashSetItem
|
||||
{
|
||||
Key key;
|
||||
|
||||
DenseHashSetItem(const Key& key): key(key)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Key, typename Value> struct DenseHashMapItem
|
||||
{
|
||||
Key key;
|
||||
Value value;
|
||||
|
||||
DenseHashMapItem(const Key& key): key(key), value()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Key, typename Item, typename Hash, typename Eq> class DenseHashTable
|
||||
{
|
||||
public:
|
||||
class const_iterator;
|
||||
|
||||
DenseHashTable(const Key& empty_key, size_t buckets = 0): data(buckets, Item(empty_key)), count(0), empty_key(empty_key)
|
||||
{
|
||||
// buckets has to be power-of-two or zero
|
||||
RBXASSERT((buckets & (buckets - 1)) == 0);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
data.clear();
|
||||
count = 0;
|
||||
}
|
||||
|
||||
Item* insert(const Key& key)
|
||||
{
|
||||
// It is invalid to insert empty_key into the table since it acts as a "entry does not exist" marker
|
||||
RBXASSERT(!eq(key, empty_key));
|
||||
|
||||
if (count >= data.size() * 3 / 4)
|
||||
{
|
||||
rehash();
|
||||
}
|
||||
|
||||
size_t hashmod = data.size() - 1;
|
||||
size_t bucket = hasher(key) & hashmod;
|
||||
|
||||
for (size_t probe = 0; probe <= hashmod; ++probe)
|
||||
{
|
||||
Item& probe_item = data[bucket];
|
||||
|
||||
// Element does not exist, insert here
|
||||
if (eq(probe_item.key, empty_key))
|
||||
{
|
||||
probe_item.key = key;
|
||||
count++;
|
||||
return &probe_item;
|
||||
}
|
||||
|
||||
// Element already exists
|
||||
if (eq(probe_item.key, key))
|
||||
{
|
||||
return &probe_item;
|
||||
}
|
||||
|
||||
// Hash collision, quadratic probing
|
||||
bucket = (bucket + probe + 1) & hashmod;
|
||||
}
|
||||
|
||||
// Hash table is full - this should not happen
|
||||
RBXASSERT(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const Item* find(const Key& key) const
|
||||
{
|
||||
if (data.empty()) return 0;
|
||||
if (eq(key, empty_key)) return 0;
|
||||
|
||||
size_t hashmod = data.size() - 1;
|
||||
size_t bucket = hasher(key) & hashmod;
|
||||
|
||||
for (size_t probe = 0; probe <= hashmod; ++probe)
|
||||
{
|
||||
const Item& probe_item = data[bucket];
|
||||
|
||||
// Element exists
|
||||
if (eq(probe_item.key, key))
|
||||
return &probe_item;
|
||||
|
||||
// Element does not exist
|
||||
if (eq(probe_item.key, empty_key))
|
||||
return NULL;
|
||||
|
||||
// Hash collision, quadratic probing
|
||||
bucket = (bucket + probe + 1) & hashmod;
|
||||
}
|
||||
|
||||
// Hash table is full - this should not happen
|
||||
RBXASSERT(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
size_t start = 0;
|
||||
|
||||
while (start < data.size() && eq(data[start].key, empty_key))
|
||||
start++;
|
||||
|
||||
return const_iterator(this, start);
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return const_iterator(this, data.size());
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t bucket_count() const
|
||||
{
|
||||
return data.size();
|
||||
}
|
||||
|
||||
class const_iterator
|
||||
{
|
||||
public:
|
||||
const_iterator(): set(0), index(0)
|
||||
{
|
||||
}
|
||||
|
||||
const_iterator(const DenseHashTable<Key, Item, Hash, Eq>* set, size_t index): set(set), index(index)
|
||||
{
|
||||
}
|
||||
|
||||
const Item& getItem() const
|
||||
{
|
||||
return set->data[index];
|
||||
}
|
||||
|
||||
const Key& operator*() const
|
||||
{
|
||||
return set->data[index].key;
|
||||
}
|
||||
|
||||
const Key* operator->() const
|
||||
{
|
||||
return &set->data[index].key;
|
||||
}
|
||||
|
||||
bool operator==(const const_iterator& other) const
|
||||
{
|
||||
return set == other.set && index == other.index;
|
||||
}
|
||||
|
||||
bool operator!=(const const_iterator& other) const
|
||||
{
|
||||
return set != other.set || index != other.index;
|
||||
}
|
||||
|
||||
const_iterator& operator++()
|
||||
{
|
||||
size_t size = set->data.size();
|
||||
|
||||
do
|
||||
{
|
||||
index++;
|
||||
}
|
||||
while (index < size && set->eq(set->data[index].key, set->empty_key));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator operator++(int)
|
||||
{
|
||||
const_iterator res = *this;
|
||||
++*this;
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
const DenseHashTable<Key, Item, Hash, Eq>* set;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<Item> data;
|
||||
size_t count;
|
||||
Key empty_key;
|
||||
Hash hasher;
|
||||
Eq eq;
|
||||
|
||||
void rehash()
|
||||
{
|
||||
size_t newsize = data.empty() ? 16 : data.size() * 2;
|
||||
DenseHashTable newtable(empty_key, newsize);
|
||||
|
||||
for (size_t i = 0; i < data.size(); ++i)
|
||||
if (!eq(data[i].key, empty_key))
|
||||
*newtable.insert(data[i].key) = data[i];
|
||||
|
||||
RBXASSERT(count == newtable.count);
|
||||
data.swap(newtable.data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// This is a faster alternative of boost::unordered_set, but it does not implement the same interface (i.e. it does not support erasing and has contains() instead of find())
|
||||
template <typename Key, typename Hash = boost::hash<Key>, typename Eq = std::equal_to<Key> > class DenseHashSet
|
||||
{
|
||||
typedef detail::DenseHashTable<Key, detail::DenseHashSetItem<Key>, Hash, Eq> Impl;
|
||||
Impl impl;
|
||||
|
||||
public:
|
||||
typedef typename Impl::const_iterator const_iterator;
|
||||
|
||||
DenseHashSet(const Key& empty_key, size_t buckets = 0): impl(empty_key, buckets)
|
||||
{
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
impl.clear();
|
||||
}
|
||||
|
||||
void insert(const Key& key)
|
||||
{
|
||||
impl.insert(key);
|
||||
}
|
||||
|
||||
bool contains(const Key& key) const
|
||||
{
|
||||
return impl.find(key) != 0;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return impl.size();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return impl.size() == 0;
|
||||
}
|
||||
|
||||
size_t bucket_count() const
|
||||
{
|
||||
return impl.bucket_count();
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return impl.begin();
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return impl.end();
|
||||
}
|
||||
};
|
||||
|
||||
// This is a faster alternative of boost::unordered_map, but it does not implement the same interface (i.e. it does not support erasing and has contains() instead of find())
|
||||
template <typename Key, typename Value, typename Hash = boost::hash<Key>, typename Eq = std::equal_to<Key> > class DenseHashMap
|
||||
{
|
||||
typedef detail::DenseHashTable<Key, detail::DenseHashMapItem<Key, Value>, Hash, Eq> Impl;
|
||||
Impl impl;
|
||||
|
||||
public:
|
||||
typedef typename Impl::const_iterator const_iterator;
|
||||
|
||||
DenseHashMap(const Key& empty_key, size_t buckets = 0): impl(empty_key, buckets)
|
||||
{
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
impl.clear();
|
||||
}
|
||||
|
||||
// Note: this reference is invalidated by any insert operation (i.e. operator[])
|
||||
Value& operator[](const Key& key)
|
||||
{
|
||||
return impl.insert(key)->value;
|
||||
}
|
||||
|
||||
// Note: this pointer is invalidated by any insert operation (i.e. operator[])
|
||||
const Value* find(const Key& key) const
|
||||
{
|
||||
const detail::DenseHashMapItem<Key, Value>* result = impl.find(key);
|
||||
|
||||
return result ? &result->value : NULL;
|
||||
}
|
||||
|
||||
// Note: this pointer is invalidated by any insert operation (i.e. operator[])
|
||||
Value* find(const Key& key)
|
||||
{
|
||||
const detail::DenseHashMapItem<Key, Value>* result = impl.find(key);
|
||||
|
||||
return result ? const_cast<Value*>(&result->value) : NULL;
|
||||
}
|
||||
|
||||
bool contains(const Key& key) const
|
||||
{
|
||||
return impl.find(key) != 0;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return impl.size();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return impl.size() == 0;
|
||||
}
|
||||
|
||||
size_t bucket_count() const
|
||||
{
|
||||
return impl.bucket_count();
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return impl.begin();
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return impl.end();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
|
||||
struct GlobalVectorItemBase
|
||||
{
|
||||
bool valid;
|
||||
GlobalVectorItemBase() : valid(false) {};
|
||||
};
|
||||
|
||||
// this class manages a sparse static array.
|
||||
// allocate one of these "smartptr-like" classes to get the next available entry in the list.
|
||||
// T should derive from GlobalVectorItemBase
|
||||
template<class T>
|
||||
class GlobalVectorItemPtr
|
||||
{
|
||||
T* p;
|
||||
T* newp;
|
||||
public:
|
||||
GlobalVectorItemPtr(T* list, size_t count) : p(0), newp(0)
|
||||
{
|
||||
for(size_t i = 0; i< count; ++i, ++list)
|
||||
{
|
||||
if(!list->valid)
|
||||
{
|
||||
p = list;
|
||||
p->valid = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// out of space. just allocate one.
|
||||
|
||||
newp = new T();
|
||||
p = newp;
|
||||
}
|
||||
|
||||
~GlobalVectorItemPtr()
|
||||
{
|
||||
if(newp)
|
||||
{
|
||||
delete newp;
|
||||
newp = 0;
|
||||
p = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// free slot.
|
||||
p->valid = false;
|
||||
p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
T* operator->()
|
||||
{
|
||||
return this->p;
|
||||
}
|
||||
|
||||
T* get()
|
||||
{
|
||||
return this->p;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
#pragma once
|
||||
|
||||
#include "RBX/Debug.h"
|
||||
#include "boost/noncopyable.hpp"
|
||||
|
||||
namespace RBX { namespace Intrusive {
|
||||
|
||||
// A very efficient, constant time, unordered set
|
||||
// Features:
|
||||
// Constant-time insert
|
||||
// Constant-time remove
|
||||
// Constant-time membership test
|
||||
// items can remove themselves
|
||||
// Items in the set auto-remove themselves upon destruction
|
||||
// no memory is allocated for operations
|
||||
// everything is nothrow
|
||||
//
|
||||
// TODO: Adding an item to a set silently removes it for membership in another set. Is this desirable? Should it be a runtime error?
|
||||
// TODO: Implement ConstIterator
|
||||
|
||||
template<class Item, class Tag = Item>
|
||||
class Set : boost::noncopyable
|
||||
{
|
||||
private:
|
||||
class NextRef
|
||||
{
|
||||
friend class Set;
|
||||
protected:
|
||||
Item* next;
|
||||
inline NextRef() throw()
|
||||
:next(0)
|
||||
{
|
||||
}
|
||||
inline NextRef(const NextRef& other) throw()
|
||||
:next(0)
|
||||
{
|
||||
// Copies of objects aren't automatically added to containers
|
||||
}
|
||||
inline NextRef& operator=(const NextRef& other) throw()
|
||||
{
|
||||
// This object retains its membership to its container
|
||||
}
|
||||
};
|
||||
public:
|
||||
class Hook : public NextRef
|
||||
{
|
||||
friend class Set;
|
||||
Set* _container;
|
||||
NextRef* prev;
|
||||
public:
|
||||
inline Hook() throw()
|
||||
:_container(0),prev(0)
|
||||
{
|
||||
}
|
||||
inline Hook(const Hook& other) throw()
|
||||
:_container(0),prev(0)
|
||||
{
|
||||
// Copies of objects aren't automatically added to containers
|
||||
}
|
||||
inline Hook& operator=(const Hook& other) throw()
|
||||
{
|
||||
// This object retains its membership to its container
|
||||
}
|
||||
inline ~Hook() throw()
|
||||
{
|
||||
remove();
|
||||
}
|
||||
inline void remove() throw()
|
||||
{
|
||||
if (is_linked())
|
||||
{
|
||||
RBXASSERT(prev!=0 || Set::NextRef::next!=0);
|
||||
|
||||
if (prev)
|
||||
prev->Set::NextRef::next = NextRef::next;
|
||||
if (NextRef::next)
|
||||
NextRef::next->Set::Hook::prev = prev;
|
||||
|
||||
_container->count--;
|
||||
|
||||
NextRef::next = 0;
|
||||
prev = 0;
|
||||
_container = 0;
|
||||
}
|
||||
}
|
||||
inline bool is_linked() const throw()
|
||||
{
|
||||
RBXASSERT((_container != 0) == ((Set::NextRef::next != 0) || (prev != 0)));
|
||||
return _container != 0;
|
||||
}
|
||||
inline Set* container() throw()
|
||||
{
|
||||
return _container;
|
||||
}
|
||||
};
|
||||
|
||||
class Iterator
|
||||
{
|
||||
friend class Set;
|
||||
Item* item;
|
||||
Iterator(Item* item) throw()
|
||||
:item(item)
|
||||
{
|
||||
RBXASSERT(!item || item->Set::Hook::is_linked());
|
||||
}
|
||||
public:
|
||||
inline Iterator() throw()
|
||||
:item(0) {}
|
||||
|
||||
inline bool operator==(const Iterator& other) const throw()
|
||||
{
|
||||
return item == other.item;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Iterator& other) const throw()
|
||||
{
|
||||
return item != other.item;
|
||||
}
|
||||
|
||||
inline Item* operator->() throw()
|
||||
{
|
||||
RBXASSERT(item);
|
||||
RBXASSERT(!item || item->Set::Hook::is_linked());
|
||||
return item;
|
||||
}
|
||||
|
||||
inline Item& operator*() throw()
|
||||
{
|
||||
RBXASSERT(item);
|
||||
RBXASSERT(!item || item->Set::Hook::is_linked());
|
||||
return *item;
|
||||
}
|
||||
|
||||
inline Iterator& operator++() throw()
|
||||
{
|
||||
RBXASSERT(item);
|
||||
|
||||
item = item->Set::Hook::next;
|
||||
|
||||
RBXASSERT(!item || item->Set::Hook::is_linked());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool empty() const throw()
|
||||
{
|
||||
return item == 0;
|
||||
}
|
||||
|
||||
// for std iterators:
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef Item& value_type;
|
||||
typedef void difference_type;
|
||||
typedef /*typename*/ Item* pointer;
|
||||
typedef /*typename*/ Item& reference;
|
||||
};
|
||||
|
||||
inline Set() throw()
|
||||
:count(0)
|
||||
{}
|
||||
|
||||
inline ~Set() throw()
|
||||
{
|
||||
for (Iterator iter = begin(); !iter.empty(); iter = erase(iter))
|
||||
;
|
||||
}
|
||||
|
||||
inline size_t size() const throw() { return count; }
|
||||
inline bool empty() const throw() { return count==0; }
|
||||
|
||||
Iterator erase(Iterator iter) throw()
|
||||
{
|
||||
Item& item(*iter);
|
||||
++iter;
|
||||
remove_element(item);
|
||||
return iter;
|
||||
}
|
||||
|
||||
bool remove_element(Item& item) throw()
|
||||
{
|
||||
if (item.Set::Hook::_container == this)
|
||||
{
|
||||
item.Set::Hook::remove();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void insert(Item& item) throw()
|
||||
{
|
||||
if (item.Set::Hook::_container == this)
|
||||
return;
|
||||
|
||||
// Items can be in only one list at a time
|
||||
item.Set::Hook::remove();
|
||||
|
||||
RBXASSERT(!item.Set::Hook::next);
|
||||
RBXASSERT(!item.Set::Hook::prev);
|
||||
|
||||
Item* head = head_ref.next;
|
||||
if (head)
|
||||
{
|
||||
RBXASSERT(head->Set::Hook::is_linked());
|
||||
RBXASSERT(head->Set::Hook::container() == this);
|
||||
item.Set::NextRef::next = head;
|
||||
head->Set::Hook::prev = &item;
|
||||
}
|
||||
head_ref.next = &item;
|
||||
item.Set::Hook::prev = &head_ref;
|
||||
|
||||
item.Set::Hook::_container = this;
|
||||
RBXASSERT(item.Set::Hook::next || item.Set::Hook::prev);
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
inline Iterator begin() throw()
|
||||
{
|
||||
return Iterator(head_ref.next);
|
||||
}
|
||||
|
||||
inline Iterator end() throw()
|
||||
{
|
||||
return Iterator();
|
||||
}
|
||||
|
||||
// For the std iterator pattern:
|
||||
typedef Iterator iterator;
|
||||
|
||||
// For the boost::intrusive pattern:
|
||||
inline void push_front(Item& item) throw()
|
||||
{
|
||||
insert(item);
|
||||
}
|
||||
|
||||
// For the boost::intrusive pattern:
|
||||
inline Iterator iterator_to(Item& item) throw()
|
||||
{
|
||||
return item.Set::Hook::_container == this ? Iterator(&item) : Iterator();
|
||||
}
|
||||
|
||||
private:
|
||||
size_t count;
|
||||
NextRef head_ref;
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,70 @@
|
||||
|
||||
#ifndef _A1177B91C54B40259B36DD55E5DF7726
|
||||
#define _A1177B91C54B40259B36DD55E5DF7726
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include "rbx/Debug.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
namespace RBX {
|
||||
|
||||
class Log;
|
||||
|
||||
// Returns a Log instance. Multithreaded apps should return a different
|
||||
// instance for each thread, so that Scope objects don't interact with each other
|
||||
class RBXInterface ILogProvider
|
||||
{
|
||||
public:
|
||||
virtual Log* provideLog() = 0;
|
||||
};
|
||||
|
||||
class Log
|
||||
{
|
||||
const std::string name;
|
||||
public:
|
||||
// return a string representing the amount of memory
|
||||
static std::string formatMem(unsigned int bytes);
|
||||
static std::string formatTime(double time);
|
||||
|
||||
enum Severity { Information=0, Warning=1, Error=2 };
|
||||
static Severity aggregateWorstSeverity; // The worst severity level reported by any Log
|
||||
Severity worstSeverity; // The worst severity level reported by this Log
|
||||
|
||||
void writeEntry(Severity severity, const char* message);
|
||||
void writeEntry(Severity severity, const wchar_t* message);
|
||||
void timeStamp(bool includeDate);
|
||||
|
||||
static void setLogProvider(ILogProvider* provider);
|
||||
|
||||
Log(const char* logFile, const char* name);
|
||||
virtual ~Log(void);
|
||||
|
||||
const std::string logFile;
|
||||
|
||||
static inline Log* current()
|
||||
{
|
||||
return provider ? provider->provideLog() : NULL;
|
||||
}
|
||||
|
||||
static void timeStamp(std::ofstream& stream, bool includeDate);
|
||||
private:
|
||||
std::ofstream stream;
|
||||
|
||||
|
||||
static ILogProvider* provider;
|
||||
static inline std::ofstream& currentStream()
|
||||
{
|
||||
RBXASSERT(provider->provideLog()!=NULL);
|
||||
return provider->provideLog()->stream;
|
||||
}
|
||||
|
||||
friend class Entry;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
|
||||
enum Confidence
|
||||
{
|
||||
C90,
|
||||
C95,
|
||||
C99,
|
||||
C99p9,
|
||||
ConfidenceMax
|
||||
};
|
||||
|
||||
double IsValueOutlier(double value, unsigned count, double average, double std, Confidence conf);
|
||||
|
||||
void GetConfidenceInterval(double average, double variance, Confidence conf, double* minV, double* maxV);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
|
||||
|
||||
#ifndef _0632EE02291848e49902EAA033B8C2EA
|
||||
#define _0632EE02291848e49902EAA033B8C2EA
|
||||
|
||||
#include "boost/pool/singleton_pool.hpp"
|
||||
#include "boost/scoped_ptr.hpp"
|
||||
#include <assert.h>
|
||||
#include "rbx/debug.h"
|
||||
#include "rbx/atomic.h"
|
||||
#include <vector>
|
||||
|
||||
// Note - Interlock Incs, Decs are turned off on count because of possible performance issues
|
||||
// TODO: Benchmark RBX_ALLOCATOR_COUNTS
|
||||
#ifdef _DEBUG
|
||||
#define RBX_ALLOCATOR_COUNTS
|
||||
#define RBX_POOL_ALLOCATION_STATS
|
||||
#endif
|
||||
|
||||
// TODO: Benchmark:
|
||||
#ifndef _DEBUG
|
||||
// Note: Using this option makes it harder to find memory leaks
|
||||
#define RBX_ALLOCATOR_SINGLETON_POOL
|
||||
#endif
|
||||
|
||||
// TODO: Benchmark:
|
||||
//#define RBX_MEMORY_SCALABLE_MALLOC
|
||||
|
||||
namespace RBX {
|
||||
#ifdef RBX_POOL_ALLOCATION_STATS
|
||||
extern std::vector<size_t*> poolAllocationList;
|
||||
#endif
|
||||
typedef bool (*releaseFunc)();
|
||||
extern std::vector<size_t*> poolAvailabilityList;
|
||||
extern std::vector<releaseFunc> poolReleaseMemoryFuncList;
|
||||
|
||||
inline void addToPool(size_t* allocatedSize, size_t* availableSize, size_t size)
|
||||
{
|
||||
if (size > *availableSize)
|
||||
{
|
||||
#ifdef RBX_POOL_ALLOCATION_STATS
|
||||
(*allocatedSize)+=(size);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
(*availableSize)-=(size);
|
||||
}
|
||||
}
|
||||
|
||||
inline void removeFromPool(size_t* availableSize, size_t size)
|
||||
{
|
||||
(*availableSize)+=(size);
|
||||
}
|
||||
|
||||
// You can use this allocator when using std or boost collections
|
||||
class roblox_allocator
|
||||
{
|
||||
public:
|
||||
static bool crashOnAllocationFailure; // TODO: Put this in more places, including std allocator overrides?
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
static char* malloc(const size_type bytes);
|
||||
static void free(char* const block);
|
||||
static char* realloc(char* ptr, size_t nsize);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class Allocator
|
||||
{
|
||||
#ifdef RBX_ALLOCATOR_COUNTS
|
||||
static rbx::atomic<int> count;
|
||||
#endif
|
||||
public:
|
||||
static size_t allocatedSize;
|
||||
static size_t availableSize;
|
||||
static bool initialized;
|
||||
|
||||
Allocator()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
#ifdef RBX_POOL_ALLOCATION_STATS
|
||||
poolAllocationList.push_back(&allocatedSize);
|
||||
#endif
|
||||
poolAvailabilityList.push_back(&availableSize);
|
||||
bool (*pReleaseMemory)() = releaseMemory;
|
||||
poolReleaseMemoryFuncList.push_back(pReleaseMemory);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef RBX_ALLOCATOR_SINGLETON_POOL
|
||||
// TODO: Benchmark this allocator vs. other kinds
|
||||
void* operator new(size_t nSize) {
|
||||
assert(nSize==sizeof(T));
|
||||
void* result = boost::singleton_pool<T, sizeof(T), boost::default_user_allocator_malloc_free>::malloc();
|
||||
if (!result)
|
||||
{
|
||||
if (roblox_allocator::crashOnAllocationFailure)
|
||||
RBXCRASH(); // We want a nice fat crash here so that the process quits and we can log it
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
#ifdef RBX_ALLOCATOR_COUNTS
|
||||
count++;
|
||||
#endif
|
||||
addToPool(&allocatedSize, &availableSize, nSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
void* operator new( size_t size, void* p )
|
||||
{
|
||||
addToPool(&allocatedSize, &availableSize, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
void operator delete(void*, void*)
|
||||
{
|
||||
removeFromPool(&availableSize, sizeof(T));
|
||||
}
|
||||
|
||||
static bool releaseMemory()
|
||||
{
|
||||
#ifdef RBX_POOL_ALLOCATION_STATS
|
||||
allocatedSize -= availableSize;
|
||||
#endif
|
||||
availableSize = 0;
|
||||
return boost::singleton_pool<T, sizeof(T), boost::default_user_allocator_malloc_free>::release_memory();
|
||||
}
|
||||
|
||||
static bool purgeMemory()
|
||||
{
|
||||
// Be very careful when calling this as this is singleton pool purge
|
||||
#ifdef RBX_POOL_ALLOCATION_STATS
|
||||
allocatedSize = 0;
|
||||
#endif
|
||||
availableSize = 0;
|
||||
return boost::singleton_pool<T, sizeof(T), boost::default_user_allocator_malloc_free>::purge_memory();
|
||||
}
|
||||
|
||||
void operator delete(void* p) {
|
||||
boost::singleton_pool<T, sizeof(T), boost::default_user_allocator_malloc_free>::free(p);
|
||||
#ifdef RBX_ALLOCATOR_COUNTS
|
||||
count--;
|
||||
#endif
|
||||
removeFromPool(&availableSize, sizeof(T));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
#else
|
||||
void* operator new(size_t nSize) {
|
||||
assert(nSize==sizeof(T));
|
||||
void* result = (void*)roblox_allocator::malloc(nSize);
|
||||
if (!result)
|
||||
{
|
||||
if (roblox_allocator::crashOnAllocationFailure)
|
||||
RBXCRASH(); // We want a nice fat crash here so that the process quits and we can log it
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
#ifdef RBX_ALLOCATOR_COUNTS
|
||||
count++;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
void operator delete(void* p) {
|
||||
roblox_allocator::free((char*)p);
|
||||
#ifdef RBX_ALLOCATOR_COUNTS
|
||||
count--;
|
||||
#endif
|
||||
}
|
||||
|
||||
void* operator new( size_t size, void* p )
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
void operator delete(void*, void*)
|
||||
{
|
||||
// placement delete, nothing to do
|
||||
}
|
||||
|
||||
static bool releaseMemory()
|
||||
{
|
||||
// pool not used, nothing to do
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool purgeMemory()
|
||||
{
|
||||
// pool not used, nothing to do
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef RBX_ALLOCATOR_COUNTS
|
||||
static long getCount() {return count; }
|
||||
static long getHeapSize() {return sizeof(T) * count; }
|
||||
#endif
|
||||
};
|
||||
|
||||
template<class T>
|
||||
size_t Allocator<T>::allocatedSize = 0;
|
||||
template<class T>
|
||||
size_t Allocator<T>::availableSize = 0;
|
||||
template<class T>
|
||||
bool Allocator<T>::initialized = false;
|
||||
|
||||
#ifdef RBX_ALLOCATOR_COUNTS
|
||||
template<class T>
|
||||
rbx::atomic<int> Allocator<T>::count;
|
||||
#endif
|
||||
|
||||
// This class is a wrapper for boost::pool<>. It allocates extra memory used by AutoPoolObject
|
||||
// to store a pointer back to the pool.
|
||||
class AutoMemPool
|
||||
{
|
||||
boost::scoped_ptr< boost::pool<> > pool;
|
||||
|
||||
public:
|
||||
|
||||
// A pool object that auto free itself from the pool it was allocated from
|
||||
// MUST use this with AutoMemPool
|
||||
class Object
|
||||
{
|
||||
public:
|
||||
void* operator new(size_t size, AutoMemPool* pool)
|
||||
{
|
||||
RBXASSERT(((size_t)pool->getRequestedSize()) == size + sizeof(AutoMemPool*));
|
||||
|
||||
void* mem = pool->malloc();
|
||||
*(AutoMemPool**)mem = &(*pool); // store the pool at start of memory block
|
||||
return (char*)mem + sizeof(AutoMemPool*); // skip over the pool
|
||||
}
|
||||
|
||||
void operator delete(void* p, AutoMemPool* pool)
|
||||
{
|
||||
pool->free(p);
|
||||
}
|
||||
|
||||
void operator delete(void *p)
|
||||
{
|
||||
p = (char*)p - sizeof(AutoMemPool*);
|
||||
AutoMemPool* pool = *(AutoMemPool**)p;
|
||||
pool->free(p);
|
||||
}
|
||||
};
|
||||
|
||||
AutoMemPool(int requested_size)
|
||||
{
|
||||
// allocate extra bytes to store pointer to the pool
|
||||
pool.reset(new boost::pool<>(requested_size + sizeof(this)));
|
||||
}
|
||||
|
||||
inline void* malloc()
|
||||
{
|
||||
return pool->malloc();
|
||||
}
|
||||
|
||||
inline void free(void* p)
|
||||
{
|
||||
RBXASSERT(pool->is_from(p));
|
||||
pool->free(p);
|
||||
}
|
||||
|
||||
inline int getRequestedSize()
|
||||
{
|
||||
return int(pool->get_requested_size());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
#if defined(__APPLE__)
|
||||
#ifdef nil
|
||||
#undef nil
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include "rbx/boost.hpp"
|
||||
#include "util/ScopedSingleton.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(RBX_PLATFORM_DURANGO)
|
||||
#include <pdh.h>
|
||||
|
||||
class CQuery
|
||||
{
|
||||
HQUERY handle;
|
||||
public:
|
||||
CQuery(HQUERY handle):handle(handle)
|
||||
{
|
||||
}
|
||||
CQuery():handle(0)
|
||||
{
|
||||
}
|
||||
HQUERY* operator&() { return &handle; }
|
||||
operator HQUERY() const { return handle; }
|
||||
~CQuery()
|
||||
{
|
||||
PdhCloseQuery(handle);
|
||||
}
|
||||
};
|
||||
|
||||
class PerfCounter
|
||||
{
|
||||
protected:
|
||||
PerfCounter();
|
||||
CQuery hQuery;
|
||||
static void GetData2(HCOUNTER counter, long& result);
|
||||
static void GetData2(HCOUNTER counter, double& result);
|
||||
public:
|
||||
void CollectData();
|
||||
};
|
||||
|
||||
class CProcessPerfCounter : public PerfCounter, public RBX::ScopedSingleton<CProcessPerfCounter>
|
||||
{
|
||||
public:
|
||||
CProcessPerfCounter();
|
||||
CProcessPerfCounter(int pid);
|
||||
// The number of cores used by the process
|
||||
double GetProcessCores();
|
||||
double GetElapsedTime() { double result; PerfCounter::GetData2(elapsedTimeCounter, result); return result; }
|
||||
long GetTotalProcessorTime() { long result; PerfCounter::GetData2(totalProcessorTimeCounter, result); return result; }
|
||||
long GetProcessorTime() { long result; PerfCounter::GetData2(processorTimeCounter, result); return result; }
|
||||
long GetPrivateBytes() { long result; PerfCounter::GetData2(privateBytesCounter, result); return result; }
|
||||
long GetPageFaultsPerSecond() { long result; PerfCounter::GetData2(pageFaultsPerSecondCounter, result); return result; }
|
||||
long GetPageFileBytes() { long result; PerfCounter::GetData2(pageFileBytesCounter, result); return result; }
|
||||
long GetVirtualBytes() { long result; PerfCounter::GetData2(virtualBytesCounter, result); return result; }
|
||||
long GetPrivateWorkingSetBytes() { long result; PerfCounter::GetData2(workingSetPrivateCounter, result); return result; }
|
||||
|
||||
private:
|
||||
unsigned int numCores;
|
||||
SYSTEM_INFO systemInfo;
|
||||
HCOUNTER elapsedTimeCounter;
|
||||
HCOUNTER totalProcessorTimeCounter;
|
||||
HCOUNTER processorTimeCounter;
|
||||
HCOUNTER privateBytesCounter;
|
||||
HCOUNTER pageFaultsPerSecondCounter;
|
||||
HCOUNTER pageFileBytesCounter;
|
||||
HCOUNTER virtualBytesCounter;
|
||||
HCOUNTER workingSetPrivateCounter;
|
||||
void init(int pid);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "RbxFormat.h" // for RBX_PRINTF_ATTR
|
||||
|
||||
#if defined(_WIN32) || defined(__APPLE__) || defined(__ANDROID__)
|
||||
#define RBXPROFILER
|
||||
#endif
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
namespace Profiler
|
||||
{
|
||||
typedef uint64_t Token;
|
||||
|
||||
Token getToken(const char* group, const char* name, int color = -1);
|
||||
Token getLabelToken(const char* group);
|
||||
Token getCounterToken(const char* name);
|
||||
|
||||
uint64_t enterRegion(Token token);
|
||||
void leaveRegion(Token token, uint64_t enterTimestamp);
|
||||
|
||||
void addLabel(Token token, const char* name);
|
||||
RBX_PRINTF_ATTR(2, 3) void addLabelFormat(Token token, const char* name, ...);
|
||||
|
||||
void counterAdd(Token token, long long count);
|
||||
void counterSet(Token token, long long count);
|
||||
|
||||
void onThreadCreate(const char* name);
|
||||
void onThreadExit();
|
||||
|
||||
void onFrame();
|
||||
|
||||
enum Flags
|
||||
{
|
||||
Flag_MouseMove = 1 << 0,
|
||||
Flag_MouseWheel = 1 << 1,
|
||||
Flag_MouseDown = 1 << 2,
|
||||
Flag_MouseUp = 1 << 3,
|
||||
};
|
||||
|
||||
void gpuInit(void* context);
|
||||
void gpuShutdown();
|
||||
|
||||
bool isCapturingMouseInput();
|
||||
bool handleMouse(unsigned int flags, int mouseX, int mouseY, int mouseWheel, int mouseButton);
|
||||
|
||||
bool toggleVisible();
|
||||
bool togglePause();
|
||||
|
||||
struct Renderer
|
||||
{
|
||||
virtual ~Renderer() {}
|
||||
|
||||
virtual void drawText(int x, int y, unsigned int color, const char* text, unsigned int length, unsigned int textWidth, unsigned int textHeight) = 0;
|
||||
virtual void drawBox(int x0, int y0, int x1, int y1, unsigned int color0, unsigned int color1) = 0;
|
||||
virtual void drawLine(unsigned int vertexCount, const float* vertexData, unsigned int color) = 0;
|
||||
};
|
||||
|
||||
bool isVisible();
|
||||
void render(Renderer* renderer, unsigned int width, unsigned int height);
|
||||
|
||||
struct Scope
|
||||
{
|
||||
Token token;
|
||||
uint64_t timestamp;
|
||||
|
||||
Scope(Token token): token(token)
|
||||
{
|
||||
timestamp = enterRegion(token);
|
||||
}
|
||||
|
||||
~Scope()
|
||||
{
|
||||
leaveRegion(token, timestamp);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#define RBXPROFILER_TOKEN_PASTE0(a, b) a ## b
|
||||
#define RBXPROFILER_TOKEN_PASTE(a, b) RBXPROFILER_TOKEN_PASTE0(a,b)
|
||||
|
||||
#ifdef RBXPROFILER
|
||||
#define RBXPROFILER_SCOPE(group, name, ...) static ::RBX::Profiler::Token RBXPROFILER_TOKEN_PASTE(proftoken, __LINE__) = ::RBX::Profiler::getToken(group "", name "", ## __VA_ARGS__); ::RBX::Profiler::Scope RBXPROFILER_TOKEN_PASTE(profscope, __LINE__)(RBXPROFILER_TOKEN_PASTE(proftoken, __LINE__))
|
||||
#define RBXPROFILER_LABEL(group, label) static ::RBX::Profiler::Token RBXPROFILER_TOKEN_PASTE(proftoken, __LINE__) = ::RBX::Profiler::getLabelToken(group ""); ::RBX::Profiler::addLabel(RBXPROFILER_TOKEN_PASTE(proftoken, __LINE__), label)
|
||||
#define RBXPROFILER_LABELF(group, label, ...) static ::RBX::Profiler::Token RBXPROFILER_TOKEN_PASTE(proftoken, __LINE__) = ::RBX::Profiler::getLabelToken(group ""); ::RBX::Profiler::addLabelFormat(RBXPROFILER_TOKEN_PASTE(proftoken, __LINE__), label, ## __VA_ARGS__)
|
||||
#define RBXPROFILER_COUNTER_ADD(name, count) static ::RBX::Profiler::Token RBXPROFILER_TOKEN_PASTE(proftoken, __LINE__) = ::RBX::Profiler::getCounterToken(name ""); ::RBX::Profiler::counterAdd(RBXPROFILER_TOKEN_PASTE(proftoken, __LINE__), static_cast<long long>(count))
|
||||
#define RBXPROFILER_COUNTER_SUB(name, count) static ::RBX::Profiler::Token RBXPROFILER_TOKEN_PASTE(proftoken, __LINE__) = ::RBX::Profiler::getCounterToken(name ""); ::RBX::Profiler::counterAdd(RBXPROFILER_TOKEN_PASTE(proftoken, __LINE__), -static_cast<long long>(count))
|
||||
#define RBXPROFILER_COUNTER_SET(name, count) static ::RBX::Profiler::Token RBXPROFILER_TOKEN_PASTE(proftoken, __LINE__) = ::RBX::Profiler::getCounterToken(name ""); ::RBX::Profiler::counterSet(RBXPROFILER_TOKEN_PASTE(proftoken, __LINE__), count)
|
||||
#else
|
||||
#define RBXPROFILER_SCOPE(group, name, ...) (void)0
|
||||
#define RBXPROFILER_LABEL(group, label) (void)0
|
||||
#define RBXPROFILER_LABELF(group, label, ...) (void)sizeof(0, __VA_ARGS__)
|
||||
#define RBXPROFILER_COUNTER_ADD(name, count) (void)0
|
||||
#define RBXPROFILER_COUNTER_SUB(name, count) (void)0
|
||||
#define RBXPROFILER_COUNTER_SET(name, count) (void)0
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "rbx/RbxDbgInfo.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace RBX;
|
||||
|
||||
RbxDbgInfo RbxDbgInfo::s_instance;
|
||||
|
||||
RbxDbgInfo::RbxDbgInfo()
|
||||
{
|
||||
memset(this, 0, sizeof(RbxDbgInfo));
|
||||
}
|
||||
|
||||
void RbxDbgInfo::AddPlace(long ID)
|
||||
{
|
||||
// Shift all places to upper indices
|
||||
for(int i = PLACE_HISTORY-1; i > 0; i--)
|
||||
{
|
||||
s_instance.PlaceIDs[i]=s_instance.PlaceIDs[i-1];
|
||||
}
|
||||
s_instance.PlaceIDs[0] = ID;
|
||||
s_instance.PlaceCounter++;
|
||||
}
|
||||
|
||||
void RbxDbgInfo::RemovePlace(long ID)
|
||||
{
|
||||
s_instance.PlaceCounter--;
|
||||
for(int i = 0; i < PLACE_HISTORY; i++)
|
||||
{
|
||||
if(s_instance.PlaceIDs[i] == ID)
|
||||
{
|
||||
// Shift all places after it to lower indices
|
||||
for(int j = i; j < PLACE_HISTORY-1; j++)
|
||||
{
|
||||
s_instance.PlaceIDs[j] = s_instance.PlaceIDs[j+1];
|
||||
}
|
||||
s_instance.PlaceIDs[PLACE_HISTORY-1] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4996)
|
||||
void RbxDbgInfo::SetGfxCardName(const char* s)
|
||||
{
|
||||
strncpy(s_instance.GfxCardName, s, DBG_STRING_MAX - 1);
|
||||
s_instance.GfxCardName[DBG_STRING_MAX - 1] = '\0';
|
||||
}
|
||||
|
||||
void RbxDbgInfo::SetGfxCardDriverVersion(const char* s)
|
||||
{
|
||||
strncpy(s_instance.GfxCardDriverVersion, s, DBG_STRING_MAX - 1);
|
||||
s_instance.GfxCardDriverVersion[DBG_STRING_MAX - 1] = '\0';
|
||||
}
|
||||
|
||||
void RbxDbgInfo::SetGfxCardVendor(const char* s)
|
||||
{
|
||||
strncpy(s_instance.GfxCardVendorName, s, DBG_STRING_MAX - 1);
|
||||
s_instance.GfxCardVendorName[DBG_STRING_MAX - 1] = '\0';
|
||||
}
|
||||
|
||||
void RbxDbgInfo::SetCPUName(const char* s)
|
||||
{
|
||||
strncpy(s_instance.CPUName, s, DBG_STRING_MAX - 1);
|
||||
s_instance.CPUName[DBG_STRING_MAX - 1] = '\0';
|
||||
}
|
||||
|
||||
void RbxDbgInfo::SetServerIP(const char* s)
|
||||
{
|
||||
strncpy(s_instance.ServerIP, s, DBG_STRING_MAX - 1);
|
||||
s_instance.ServerIP[DBG_STRING_MAX - 1] = '\0';
|
||||
}
|
||||
#pragma warning(pop)
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#define PLACE_HISTORY 4
|
||||
#define DBG_STRING_MAX 128
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
|
||||
// Global struct with the sole purpose of being accessible in minidump
|
||||
|
||||
struct RbxDbgInfo
|
||||
{
|
||||
static RbxDbgInfo s_instance;
|
||||
RbxDbgInfo();
|
||||
|
||||
size_t cbMaterials;
|
||||
size_t cbTextures;
|
||||
size_t cbMeshes;
|
||||
size_t cbEstFreeTextureMem;
|
||||
|
||||
size_t cCommitTotal;
|
||||
size_t cCommitLimit;
|
||||
size_t cPhysicalTotal;
|
||||
size_t cPhysicalAvailable;
|
||||
size_t cbPageSize;
|
||||
size_t cKernelPaged;
|
||||
size_t cKernelNonPaged;
|
||||
size_t cSystemCache;
|
||||
size_t HandleCount;
|
||||
size_t ProcessCount;
|
||||
size_t ThreadCount;
|
||||
|
||||
char GfxCardName [DBG_STRING_MAX];
|
||||
char GfxCardDriverVersion [DBG_STRING_MAX];
|
||||
char GfxCardVendorName[DBG_STRING_MAX];
|
||||
size_t TotalVideoMemory;
|
||||
|
||||
char CPUName[DBG_STRING_MAX];
|
||||
size_t NumCores;
|
||||
|
||||
char AudioDeviceName[DBG_STRING_MAX];
|
||||
char ServerIP[DBG_STRING_MAX];
|
||||
|
||||
// Index 0 is always the last place visited
|
||||
union{
|
||||
long PlaceIDs[PLACE_HISTORY];
|
||||
struct
|
||||
{
|
||||
long Place0, Place1, Place2, Place3;
|
||||
};
|
||||
};
|
||||
long PlaceCounter;
|
||||
long PlayerID;
|
||||
|
||||
|
||||
static void SetGfxCardName(const char* s);
|
||||
static void SetGfxCardDriverVersion(const char* s);
|
||||
static void SetGfxCardVendor(const char* s);
|
||||
static void SetCPUName(const char* s);
|
||||
static void SetServerIP(const char* s);
|
||||
|
||||
|
||||
static void AddPlace(long ID);
|
||||
static void RemovePlace(long ID);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef Rbx_strcasestr
|
||||
namespace RBX
|
||||
{
|
||||
|
||||
/* GCC often has strcasestr(); if not, you can use the following */
|
||||
|
||||
/* borrowed these definitions from Apache */
|
||||
#define ap_tolower(c) (tolower(((unsigned char)(c))))
|
||||
#define ap_toupper(c) (toupper(((unsigned char)(c))))
|
||||
|
||||
static const char *Rbx_strcasestr( const char *h, const char *n )
|
||||
{
|
||||
if( !h || !*h || !n || !*n ) { return 0; }
|
||||
char *a= (char*)h, *e=(char*)n;
|
||||
while( *a && *e ) {
|
||||
if( ap_toupper(*a) != ap_toupper(*e) ) {
|
||||
++h; a=(char*)h; e=(char*)n;
|
||||
} else {
|
||||
++a; ++e;
|
||||
}
|
||||
}
|
||||
return (const char *)(*e) ? 0 : h;
|
||||
|
||||
}
|
||||
static inline const char *Rbx_strcasestr( const char *h, char *n )
|
||||
{
|
||||
return Rbx_strcasestr( h, static_cast<const char*>(n));
|
||||
}
|
||||
|
||||
static inline const char *Rbx_strcasestr( char *h, const char *n )
|
||||
{
|
||||
return Rbx_strcasestr( static_cast<const char*>(h), n);
|
||||
}
|
||||
}
|
||||
#endif // defined Rbx_strcasestr
|
||||
|
||||
@@ -0,0 +1,843 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rbx/atomic.h"
|
||||
#include "rbx/boost.hpp"
|
||||
#include "rbx/rbxTime.h"
|
||||
#include "rbx/Debug.h"
|
||||
#include "rbx/MathUtil.h"
|
||||
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <boost/circular_buffer.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
|
||||
template<typename ValueType = double, typename AverageType = double>
|
||||
class RunningAverage
|
||||
{
|
||||
boost::shared_ptr<boost::circular_buffer<ValueType> > buffer;
|
||||
public:
|
||||
RunningAverage(double lerp = 0.05, ValueType initialValue = 0, unsigned int bufferSize = 0)
|
||||
:lerp(lerp),
|
||||
lastSampleValue(initialValue),
|
||||
averageValue(initialValue),
|
||||
averageVariance(0),
|
||||
firstTime(true)
|
||||
{
|
||||
if (bufferSize)
|
||||
buffer.reset(new boost::circular_buffer<ValueType>(bufferSize));
|
||||
}
|
||||
|
||||
void sample(ValueType value)
|
||||
{
|
||||
if (isFinite(value))
|
||||
{
|
||||
sampleValue(value);
|
||||
sampleVariance(value);
|
||||
|
||||
if (buffer)
|
||||
buffer->push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current value
|
||||
AverageType value() const { return averageValue; }
|
||||
|
||||
AverageType variance() const { return averageVariance; }
|
||||
AverageType standard_deviation() const
|
||||
{
|
||||
// TODO: Cache this value?
|
||||
return std::sqrt(averageVariance);
|
||||
}
|
||||
AverageType variance_to_mean_ratio() const { return averageVariance / (averageValue * averageValue); }
|
||||
AverageType coefficient_of_variation() const { return standard_deviation() / averageValue; }
|
||||
|
||||
// Get the last sampled value
|
||||
ValueType lastSample() const { return lastSampleValue; }
|
||||
|
||||
template<class F>
|
||||
void iter(F& f) const
|
||||
{
|
||||
if (buffer)
|
||||
{
|
||||
for(typename boost::circular_buffer<ValueType>::const_iterator it = buffer->begin(); it != buffer->end(); ++it)
|
||||
{
|
||||
f(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reset(ValueType resetValue = 0)
|
||||
{
|
||||
lastSampleValue = resetValue;
|
||||
averageValue = resetValue;
|
||||
averageVariance = 0;
|
||||
firstTime = true;
|
||||
|
||||
if (buffer)
|
||||
buffer->clear();
|
||||
}
|
||||
|
||||
bool hasSampled() {return !firstTime;}
|
||||
|
||||
const double lerp;
|
||||
private:
|
||||
ValueType lastSampleValue;
|
||||
AverageType averageValue;
|
||||
AverageType averageVariance;
|
||||
bool firstTime;
|
||||
|
||||
inline void sampleValue(ValueType value)
|
||||
{
|
||||
averageValue = firstTime ? value : (1.0 - lerp) * averageValue + lerp * (AverageType)value;
|
||||
lastSampleValue = value;
|
||||
firstTime = false;
|
||||
}
|
||||
|
||||
inline void sampleVariance(ValueType value)
|
||||
{
|
||||
const double diff = value - averageValue;
|
||||
const double variance = diff * diff;
|
||||
averageVariance = (1.0 - lerp) * averageVariance + lerp * variance;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ValueType = double, typename AverageType = double>
|
||||
class WindowAverage
|
||||
{
|
||||
protected:
|
||||
boost::circular_buffer<ValueType> buffer;
|
||||
public:
|
||||
|
||||
struct Stats
|
||||
{
|
||||
Stats(size_t samples, const AverageType& average, const AverageType& variance)
|
||||
: samples(samples), average(average), variance(variance) {};
|
||||
size_t samples;
|
||||
AverageType average;
|
||||
AverageType variance;
|
||||
};
|
||||
|
||||
WindowAverage(size_t maxSamples) : buffer(maxSamples) { }
|
||||
void setMaxSamples(size_t maxSamples) { buffer.set_capacity(maxSamples); }
|
||||
size_t getMaxSamples() const { return buffer.capacity(); }
|
||||
|
||||
void sample(ValueType value)
|
||||
{
|
||||
buffer.push_back(value);
|
||||
}
|
||||
|
||||
// calls fonbeforedrop if an item is about to be dropped
|
||||
template<class F>
|
||||
void sample(ValueType value, F& fonbeforedrop)
|
||||
{
|
||||
if(buffer.full())
|
||||
{
|
||||
fonbeforedrop(buffer.front());
|
||||
}
|
||||
|
||||
sample(value);
|
||||
}
|
||||
|
||||
Stats getSanitizedStats(Confidence conf = C90) const
|
||||
{
|
||||
Stats regularStats = getStats();
|
||||
if(regularStats.samples <= 1)
|
||||
return regularStats;
|
||||
|
||||
Stats result(0, AverageType(), AverageType());
|
||||
|
||||
AverageType std = sqrt(regularStats.variance);
|
||||
|
||||
for(typename boost::circular_buffer<ValueType>::const_iterator it = buffer.begin(); it != buffer.end(); ++it)
|
||||
{
|
||||
double value = *it;
|
||||
|
||||
if (IsValueOutlier(value, regularStats.samples, regularStats.average, std, conf))
|
||||
continue;
|
||||
|
||||
result.samples++;
|
||||
result.average += value;
|
||||
}
|
||||
|
||||
result.average /= result.samples;
|
||||
|
||||
for(typename boost::circular_buffer<ValueType>::const_iterator it = buffer.begin(); it != buffer.end(); ++it)
|
||||
{
|
||||
double value = *it;
|
||||
|
||||
if (IsValueOutlier(value, regularStats.samples, regularStats.average, std, conf))
|
||||
continue;
|
||||
|
||||
AverageType diff = (result.average - value);
|
||||
result.variance = result.variance + diff * diff;
|
||||
}
|
||||
|
||||
result.variance /= (result.samples - 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Stats getStats(size_t samples = ~0) const // get n last frames.
|
||||
{
|
||||
samples = std::min(buffer.size(), samples);
|
||||
Stats result(samples, AverageType(), AverageType());
|
||||
|
||||
typename boost::circular_buffer<ValueType>::const_reverse_iterator it;
|
||||
size_t ii;
|
||||
for(it = buffer.rbegin(), ii = 0; ii < samples; ++it, ++ii)
|
||||
{
|
||||
result.average = result.average + *it;
|
||||
}
|
||||
|
||||
if (samples != 0)
|
||||
{
|
||||
result.average /= samples;
|
||||
}
|
||||
|
||||
for(it = buffer.rbegin(), ii = 0; ii < samples; ++it, ++ii)
|
||||
{
|
||||
AverageType diff = (result.average - *it);
|
||||
result.variance = result.variance + diff * diff;
|
||||
}
|
||||
if(samples > 1)
|
||||
{
|
||||
result.variance /= (samples -1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
AverageType getLatest() const // get data from the last frame
|
||||
{
|
||||
return buffer.empty() ? 0 : *(buffer.rbegin());
|
||||
}
|
||||
|
||||
template<class F>
|
||||
void iter(F& f) const
|
||||
{
|
||||
for(typename boost::circular_buffer<ValueType>::const_iterator it = buffer.begin(); it != buffer.end(); ++it)
|
||||
{
|
||||
f(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return buffer.size();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// A class that follows the pattern of RunningAverage, but keeps track of step frequency.
|
||||
template<Time::SampleMethod sampleMethod = Time::Benchmark>
|
||||
class RunningAverageTimeInterval
|
||||
{
|
||||
public:
|
||||
RunningAverageTimeInterval(double lerp = 0.05)
|
||||
:firstTime(true),average(lerp) {}
|
||||
|
||||
void sample()
|
||||
{
|
||||
if (firstTime)
|
||||
{
|
||||
timer.reset();
|
||||
firstTime = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
average.sample(timer.reset().seconds());
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current value
|
||||
Time::Interval value() const
|
||||
{
|
||||
Time::Interval timeSinceLastSample = timer.delta();
|
||||
double v = average.value();
|
||||
if (timeSinceLastSample.seconds() > 2.0 * v)
|
||||
return timeSinceLastSample;
|
||||
else
|
||||
return Time::Interval(v);
|
||||
}
|
||||
|
||||
double rate() const
|
||||
{
|
||||
double v = value().seconds();
|
||||
return v>0.0 ? 1.0/v : 0.0;
|
||||
}
|
||||
|
||||
double variance() const { return average.variance(); }
|
||||
double standard_deviation() const { return average.standard_deviation(); }
|
||||
double variance_to_mean_ratio() const { return average.variance_to_mean_ratio(); }
|
||||
double coefficient_of_variation() const { return average.coefficient_of_variation(); }
|
||||
double getLerp() const { return average.lerp;}
|
||||
|
||||
Time::Interval lastSample() const { return Time::Interval(average.lastSample()); }
|
||||
|
||||
private:
|
||||
Timer<sampleMethod> timer;
|
||||
bool firstTime;
|
||||
RunningAverage<> average;
|
||||
};
|
||||
|
||||
struct FOnBeforeDrop
|
||||
{
|
||||
WindowAverage<>& average;
|
||||
Time::Interval& currentWindow;
|
||||
Time::Interval& maxWindow;
|
||||
FOnBeforeDrop(WindowAverage<>& average, Time::Interval& currentWindow, Time::Interval& maxWindow) : average(average), currentWindow(currentWindow), maxWindow(maxWindow) {};
|
||||
|
||||
void operator()(double sample)
|
||||
{
|
||||
if(currentWindow.seconds() < maxWindow.seconds())
|
||||
{
|
||||
// prevent dropping.
|
||||
// grow window size.
|
||||
average.setMaxSamples(average.getMaxSamples() * 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// allow drop. adjust total counter.
|
||||
currentWindow -= Time::Interval(sample);
|
||||
}
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
// A class that follows the pattern of RunningAverage, but keeps track of step frequency.
|
||||
template<Time::SampleMethod sampleMethod = Time::Benchmark>
|
||||
class WindowAverageTimeInterval
|
||||
{
|
||||
public:
|
||||
WindowAverageTimeInterval(Time::Interval maxWindow)
|
||||
:maxWindow(maxWindow), average(16) {}
|
||||
|
||||
void setMaxWindow(Time::Interval maxWindow)
|
||||
{
|
||||
this->maxWindow = maxWindow;
|
||||
if(maxWindow.seconds() == 0.0)
|
||||
{
|
||||
// special case, release memory
|
||||
average.setMaxSamples(16);
|
||||
}
|
||||
};
|
||||
Time::Interval getMaxWindow() const { return maxWindow; };
|
||||
size_t getCapacity() const { return average.getMaxSamples(); };
|
||||
|
||||
void sample()
|
||||
{
|
||||
if (firstTime)
|
||||
{
|
||||
timer.reset();
|
||||
firstTime = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
double dt = timer.reset().seconds();
|
||||
currentWindow += Time::Interval(dt);
|
||||
|
||||
// this functor will allow our ring buffer to grow geometrically
|
||||
// as long as it doesn't contain maxWindow worth of interval measurments.
|
||||
FOnBeforeDrop fonbeforedrop(average, currentWindow, maxWindow);
|
||||
average.sample(dt, fonbeforedrop);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct Stats
|
||||
{
|
||||
Stats( size_t samples,
|
||||
Time::Interval averagedt,
|
||||
Time::Interval variancedt,
|
||||
Time::Interval totalt,
|
||||
double samplespersecond)
|
||||
: samples(samples)
|
||||
, average(averagedt)
|
||||
, variance(variancedt)
|
||||
, sum(totalt)
|
||||
, samplespersecond(samplespersecond)
|
||||
{};
|
||||
size_t samples;
|
||||
Time::Interval average;
|
||||
Time::Interval variance;
|
||||
Time::Interval sum;
|
||||
double samplespersecond;
|
||||
};
|
||||
|
||||
struct FSum
|
||||
{
|
||||
FSum() : vsum(0.0) {};
|
||||
|
||||
double vsum;
|
||||
void operator() (double v) { vsum += v; };
|
||||
};
|
||||
|
||||
Stats getStats(size_t samples = ~0) const
|
||||
{
|
||||
WindowAverage<>::Stats basicstats = average.getStats(samples);
|
||||
|
||||
FSum fsum;
|
||||
|
||||
average.iter(fsum);
|
||||
|
||||
return Stats(basicstats.samples, Time::Interval(basicstats.average), Time::Interval(basicstats.variance), Time::Interval(fsum.vsum), samples / fsum.vsum);
|
||||
}
|
||||
|
||||
template<class F>
|
||||
void iter(F& f) const
|
||||
{
|
||||
average.iter(f);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
firstTime = true;
|
||||
average.clear();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return average.size();
|
||||
}
|
||||
|
||||
private:
|
||||
Time::Interval currentWindow;
|
||||
Time::Interval maxWindow;
|
||||
WindowAverage<> average;
|
||||
Timer<sampleMethod> timer;
|
||||
bool firstTime;
|
||||
};
|
||||
|
||||
template<typename ValueType = int, Time::SampleMethod sampleMethod = Time::Benchmark>
|
||||
class TotalCountTimeInterval
|
||||
{
|
||||
Timer<sampleMethod> timer;
|
||||
double interval;
|
||||
ValueType valueLastInterval;
|
||||
ValueType valueCurrentInterval;
|
||||
public:
|
||||
TotalCountTimeInterval(double interval = 1.0f) : interval(interval), valueCurrentInterval(0), valueLastInterval(0) {}
|
||||
void increment(ValueType count = 1)
|
||||
{
|
||||
if (timer.delta().seconds() >= interval)
|
||||
{
|
||||
valueLastInterval = valueCurrentInterval;
|
||||
valueCurrentInterval = 0;
|
||||
timer.reset();
|
||||
}
|
||||
valueCurrentInterval += count;
|
||||
}
|
||||
void decrement(ValueType count = 1)
|
||||
{
|
||||
if (timer.delta().seconds() >= interval)
|
||||
{
|
||||
valueLastInterval = valueCurrentInterval;
|
||||
valueCurrentInterval = 0;
|
||||
timer.reset();
|
||||
}
|
||||
valueCurrentInterval -= count;
|
||||
}
|
||||
|
||||
ValueType getCount() const
|
||||
{
|
||||
return (timer.delta().seconds() <= interval) ? valueLastInterval : 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ThrottlingHelper
|
||||
{
|
||||
int* eventsPerMinute;
|
||||
int* eventsPerObjectPerMinute;
|
||||
int requestCounter;
|
||||
int maxObjectCount;
|
||||
Time lastTimestamp;
|
||||
|
||||
public:
|
||||
ThrottlingHelper(int* eventsPerMinute, int* eventsPerObjectPerMinute = NULL) : // Designed to pass FInt
|
||||
eventsPerMinute(eventsPerMinute),
|
||||
eventsPerObjectPerMinute(eventsPerObjectPerMinute),
|
||||
requestCounter(0),
|
||||
maxObjectCount(0),
|
||||
lastTimestamp(Time::nowFast())
|
||||
{
|
||||
}
|
||||
|
||||
bool checkLimit(int objectCount = 0)
|
||||
{
|
||||
Time now = Time::nowFast();
|
||||
|
||||
if((now - lastTimestamp).seconds() > 60)
|
||||
{
|
||||
requestCounter = 0;
|
||||
lastTimestamp = now;
|
||||
maxObjectCount = 0;
|
||||
}
|
||||
|
||||
requestCounter++;
|
||||
maxObjectCount = std::max(objectCount, maxObjectCount);
|
||||
|
||||
int totalCount = *eventsPerMinute;
|
||||
if(eventsPerObjectPerMinute)
|
||||
totalCount += maxObjectCount * (*eventsPerObjectPerMinute);
|
||||
|
||||
if(requestCounter > totalCount)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class BudgetedThrottlingHelper
|
||||
{
|
||||
float currentBudget;
|
||||
|
||||
public:
|
||||
BudgetedThrottlingHelper() : currentBudget(0.0) {}
|
||||
|
||||
void addBudget(float budget, float maxBudget)
|
||||
{
|
||||
currentBudget = std::min(currentBudget + budget, maxBudget);
|
||||
}
|
||||
|
||||
bool checkAndReduceBudget()
|
||||
{
|
||||
if (currentBudget < 0)
|
||||
return false;
|
||||
|
||||
currentBudget--;
|
||||
return true;
|
||||
}
|
||||
|
||||
float getBudget() { return currentBudget; };
|
||||
};
|
||||
|
||||
// A class that follows the pattern of RunningAverage, but keeps track of time spent in a cyclical task.
|
||||
template<Time::SampleMethod sampleMethod = Time::Benchmark>
|
||||
class RunningAverageDutyCycle
|
||||
{
|
||||
public:
|
||||
RunningAverageDutyCycle(double lerp)
|
||||
:time(lerp),interval(lerp) {}
|
||||
|
||||
RunningAverageDutyCycle(double lerp, int timeBufferSize)
|
||||
:time(lerp, 0, timeBufferSize),interval(lerp) {}
|
||||
|
||||
void sample(Time::Interval elapsedTime)
|
||||
{
|
||||
interval.sample();
|
||||
time.sample(elapsedTime.seconds());
|
||||
}
|
||||
|
||||
RBX::Time startSample() const
|
||||
{
|
||||
return Time::now<sampleMethod>();
|
||||
}
|
||||
|
||||
void stopSample(RBX::Time start)
|
||||
{
|
||||
sample(Time::now<sampleMethod>() - start);
|
||||
}
|
||||
|
||||
double dutyCycle() const
|
||||
{
|
||||
double averageInterval = interval.value().seconds();
|
||||
double averageTime = time.value();
|
||||
return averageInterval!=0 ? averageTime/averageInterval : (averageTime>0 ? 1 : 0);
|
||||
}
|
||||
|
||||
double rate() const
|
||||
{
|
||||
return interval.rate();
|
||||
}
|
||||
|
||||
const RunningAverageTimeInterval<sampleMethod>& stepInterval() const
|
||||
{
|
||||
return interval;
|
||||
}
|
||||
|
||||
Time::Interval lastStepInterval()
|
||||
{
|
||||
return interval.lastSample();
|
||||
}
|
||||
|
||||
const RunningAverage<double>& stepTime() const
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
double getIntervalLerp() const
|
||||
{
|
||||
return interval.getLerp();
|
||||
}
|
||||
|
||||
private:
|
||||
RunningAverage<double> time;
|
||||
RunningAverageTimeInterval<sampleMethod> interval;
|
||||
};
|
||||
|
||||
|
||||
// A class that follows the pattern of WindowAverage, but keeps track of time spent in a cyclical task.
|
||||
template<Time::SampleMethod sampleMethod = Time::Benchmark>
|
||||
class WindowAverageDutyCycle
|
||||
{
|
||||
public:
|
||||
WindowAverageDutyCycle(Time::Interval maxWindow)
|
||||
:time(16),interval(maxWindow) {}
|
||||
|
||||
void setMaxWindow(Time::Interval maxWindow)
|
||||
{
|
||||
interval.setMaxWindow(maxWindow);
|
||||
if(maxWindow.seconds() == 0.0)
|
||||
{
|
||||
// special case, release memory
|
||||
time.setMaxSamples(16);
|
||||
}
|
||||
};
|
||||
Time::Interval getMaxWindow() const { return interval.getMaxWindow(); };
|
||||
|
||||
void sample(Time::Interval elapsedTime)
|
||||
{
|
||||
interval.sample();
|
||||
|
||||
// make sure time's buffer size tracks interval's buffer size.
|
||||
if(time.getMaxSamples() != interval.getCapacity())
|
||||
{
|
||||
time.setMaxSamples(interval.getCapacity());
|
||||
}
|
||||
|
||||
time.sample(elapsedTime.seconds());
|
||||
}
|
||||
|
||||
struct Stats
|
||||
{
|
||||
Stats( const typename WindowAverageTimeInterval<sampleMethod>::Stats& interval,
|
||||
const WindowAverage<double>::Stats& time,
|
||||
double dutyfraction )
|
||||
: interval(interval)
|
||||
, time(time)
|
||||
, dutyfraction(dutyfraction)
|
||||
{};
|
||||
typename WindowAverageTimeInterval<sampleMethod>::Stats interval;
|
||||
WindowAverage<double>::Stats time;
|
||||
double dutyfraction; // 1.0: duty time is 100% of interval time
|
||||
};
|
||||
|
||||
Stats getStats(size_t samples = ~0) const
|
||||
{
|
||||
typename WindowAverage<>::Stats timestats = time.getStats(samples);
|
||||
typename WindowAverageTimeInterval<sampleMethod>::Stats intervalstats = interval.getStats(samples);
|
||||
|
||||
double interval = intervalstats.average.seconds();
|
||||
return Stats(intervalstats, timestats, interval ? timestats.average / interval : 0);
|
||||
}
|
||||
|
||||
template<class F>
|
||||
void iterTimes(F& f) const
|
||||
{
|
||||
time.iter(f);
|
||||
}
|
||||
|
||||
template<class F>
|
||||
void iterIntervals(F& f) const
|
||||
{
|
||||
interval.iter(f);
|
||||
}
|
||||
|
||||
struct GTCounter
|
||||
{
|
||||
GTCounter(double gt) : c(0), gtValue(gt) {};
|
||||
size_t c;
|
||||
double gtValue;
|
||||
void operator()(double dt)
|
||||
{
|
||||
if(dt > gtValue)
|
||||
c++;
|
||||
}
|
||||
};
|
||||
|
||||
size_t countTimesGreaterThan(Time::Interval dt) const
|
||||
{
|
||||
GTCounter count(dt.seconds());
|
||||
iterTimes(count);
|
||||
return count.c;
|
||||
}
|
||||
|
||||
size_t countIntervalsGreaterThan(Time::Interval dt) const
|
||||
{
|
||||
GTCounter count(dt.seconds());
|
||||
iterIntervals(count);
|
||||
return count.c;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
time.clear();
|
||||
interval.clear();
|
||||
}
|
||||
|
||||
size_t timesamples() const
|
||||
{
|
||||
return time.size();
|
||||
}
|
||||
|
||||
size_t intervalsamples() const
|
||||
{
|
||||
return interval.size();
|
||||
}
|
||||
|
||||
private:
|
||||
WindowAverage<double> time;
|
||||
WindowAverageTimeInterval<sampleMethod> interval;
|
||||
};
|
||||
|
||||
|
||||
// A thread-safe, lock-free DutyCycle meter
|
||||
template<int windowSeconds>
|
||||
class ActivityMeter
|
||||
{
|
||||
static const int bucketCount = windowSeconds * 1024;
|
||||
boost::array<char, bucketCount> buckets;
|
||||
rbx::atomic<int> currentTime;
|
||||
rbx::atomic<int> currentValue;
|
||||
rbx::atomic<int> totalValue;
|
||||
RBX::Time startTime;
|
||||
RBX::Time lastSampleTime;
|
||||
|
||||
public:
|
||||
ActivityMeter()
|
||||
:currentTime(-1)
|
||||
,currentValue(0)
|
||||
,totalValue(0)
|
||||
,startTime(RBX::Time::now<Time::Fast>())
|
||||
{
|
||||
for (size_t i=0; i<buckets.size(); ++i)
|
||||
buckets[i] = 0;
|
||||
}
|
||||
|
||||
double averageValue()
|
||||
{
|
||||
updateBuckets();
|
||||
return (double)totalValue / (double)bucketCount;
|
||||
}
|
||||
|
||||
void increment()
|
||||
{
|
||||
updateBuckets();
|
||||
++currentValue;
|
||||
}
|
||||
|
||||
void decrement()
|
||||
{
|
||||
updateBuckets();
|
||||
--currentValue;
|
||||
}
|
||||
|
||||
void updateBuckets()
|
||||
{
|
||||
RBX::Time now = Time::now<Time::Fast>();
|
||||
if (lastSampleTime == now)
|
||||
return;
|
||||
|
||||
lastSampleTime = now;
|
||||
unsigned long newTime = ((unsigned long)(bucketCount * (lastSampleTime - startTime).seconds()));
|
||||
|
||||
unsigned long oldTime = currentTime.swap(newTime);
|
||||
|
||||
if (oldTime < newTime)
|
||||
{
|
||||
long newValue = currentValue;
|
||||
for (unsigned long i = oldTime + 1; i <= newTime; ++i)
|
||||
{
|
||||
int index = i % bucketCount;
|
||||
int oldBucketValue = buckets[index];
|
||||
|
||||
for (int j = 0; j < oldBucketValue; ++j)
|
||||
--totalValue;
|
||||
buckets[index] = (char)newValue;
|
||||
|
||||
for (int j = 0; j < newValue; ++j)
|
||||
++totalValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<int windowSeconds>
|
||||
class InvocationMeter
|
||||
{
|
||||
static const int bucketCount = windowSeconds * 1024;
|
||||
boost::array<char, bucketCount> buckets;
|
||||
rbx::atomic<int> currentTime;
|
||||
rbx::atomic<int> totalValue;
|
||||
RBX::Time startTime;
|
||||
RBX::Time lastSampleTime;
|
||||
|
||||
public:
|
||||
InvocationMeter()
|
||||
:currentTime(-1)
|
||||
,totalValue(0)
|
||||
,startTime(RBX::Time::now<Time::Fast>())
|
||||
{
|
||||
for (size_t i=0; i<buckets.size(); ++i)
|
||||
buckets[i] = 0;
|
||||
}
|
||||
|
||||
double getTotalValuePerSecond()
|
||||
{
|
||||
updateBuckets(false);
|
||||
return totalValue/windowSeconds;
|
||||
}
|
||||
|
||||
void increment()
|
||||
{
|
||||
updateBuckets(true);
|
||||
}
|
||||
|
||||
void updateBuckets(bool increment)
|
||||
{
|
||||
RBX::Time now = Time::now<Time::Fast>();
|
||||
if (lastSampleTime == now)
|
||||
return;
|
||||
|
||||
lastSampleTime = now;
|
||||
unsigned long newTime = ((unsigned long)(bucketCount * (lastSampleTime - startTime).seconds()));
|
||||
unsigned long oldTime = currentTime.swap(newTime);
|
||||
// RBXASSERT(oldTime <= newTime);
|
||||
// if (oldTime != newTime)
|
||||
if (oldTime < newTime) // changed per Erik 11/18/09
|
||||
|
||||
{
|
||||
for (unsigned long i = oldTime + 1; i <= newTime; ++i)
|
||||
{
|
||||
int index = i % bucketCount;
|
||||
int oldBucketValue = buckets[index];
|
||||
|
||||
for (int j = 0; j < oldBucketValue; ++j)
|
||||
--totalValue;
|
||||
buckets[index] = 0;
|
||||
}
|
||||
}
|
||||
if(increment){
|
||||
int newValue = 1;
|
||||
int newIndex = newTime % bucketCount;
|
||||
buckets[newIndex] = newValue;
|
||||
|
||||
for (int j = 0; j < newValue; ++j)
|
||||
++totalValue;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#endif
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
namespace SystemUtil
|
||||
{
|
||||
/// CPU Related
|
||||
std::string getCPUMake();
|
||||
uint64_t getCPUSpeed();
|
||||
uint64_t getCPULogicalCount();
|
||||
uint64_t getCPUCoreCount();
|
||||
uint64_t getCPUPhysicalCount();
|
||||
bool isCPU64Bit();
|
||||
|
||||
/// Memory Related
|
||||
uint64_t getMBSysRAM();
|
||||
uint64_t getMBSysAvailableRAM();
|
||||
uint64_t getVideoMemory();
|
||||
|
||||
/// OS Related
|
||||
std::string osPlatform();
|
||||
int osPlatformId();
|
||||
std::string osVer();
|
||||
std::string deviceName();
|
||||
|
||||
/// GPU Related
|
||||
std::string getGPUMake();
|
||||
|
||||
// Display Resolution
|
||||
std::string getMaxRes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rbx/Countable.h"
|
||||
#include "rbx/TaskScheduler.h"
|
||||
#include "boost/enable_shared_from_this.hpp"
|
||||
#include "boost/weak_ptr.hpp"
|
||||
|
||||
#define HANG_DETECTION 0
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
namespace Tasks
|
||||
{
|
||||
class Coordinator;
|
||||
}
|
||||
|
||||
enum CyclicExecutiveJobPriority
|
||||
{
|
||||
CyclicExecutiveJobPriority_EarlyRendering,
|
||||
CyclicExecutiveJobPriority_Network_ReceiveIncoming,
|
||||
CyclicExecutiveJobPriority_Network_ProcessIncoming,
|
||||
CyclicExecutiveJobPriority_Default,
|
||||
CyclicExecutiveJobPriority_Physics,
|
||||
CyclicExecutiveJobPriority_Heartbeat,
|
||||
CyclicExecutiveJobPriority_Network_ProcessOutgoing,
|
||||
CyclicExecutiveJobPriority_Render
|
||||
};
|
||||
|
||||
class RBXBaseClass TaskScheduler::Job
|
||||
: boost::noncopyable
|
||||
, public boost::enable_shared_from_this<Job>
|
||||
, RBX::Diagnostics::Countable<Job>
|
||||
, public TaskScheduler::SleepingHook
|
||||
, public TaskScheduler::WaitingHook
|
||||
{
|
||||
friend class TaskScheduler;
|
||||
boost::weak_ptr<Thread> lastThreadUsed; // attempt to re-use a thread for thread affinity
|
||||
|
||||
RBX::mutex coordinatorMutex;
|
||||
std::vector<boost::shared_ptr<RBX::Tasks::Coordinator> > coordinators;
|
||||
boost::shared_ptr<TaskScheduler::Arbiter> const sharedArbiter;
|
||||
boost::weak_ptr<TaskScheduler::Arbiter> const weakArbiter;
|
||||
TaskScheduler::Arbiter* const baldArbiter;
|
||||
|
||||
//stepBudget of 0 means no budget
|
||||
public:
|
||||
const std::string name;
|
||||
static double throttledSleepTime;
|
||||
const Time::Interval stepBudget;
|
||||
Time stepStartTime;
|
||||
int allotedConcurrency;
|
||||
bool cyclicExecutive;
|
||||
CyclicExecutiveJobPriority cyclicPriority;
|
||||
|
||||
#if HANG_DETECTION
|
||||
static double stepTimeThreshold; // in seconds. A count is incremented when a job's stepTime is over this value. A value of 0 means disable counting.
|
||||
Time stepTimeSampleTime;
|
||||
#endif
|
||||
|
||||
struct Stats
|
||||
{
|
||||
Stats(Job& job, Time now);
|
||||
Time timeNow;
|
||||
Time::Interval timespanSinceLastStep;
|
||||
Time::Interval timespanOfLastStep;
|
||||
};
|
||||
|
||||
struct Error
|
||||
{
|
||||
double error;
|
||||
|
||||
bool urgent; // experimental feature to prevent UI deadlocks (Essentially bumps the Job up in the priority queue)
|
||||
|
||||
Error():error(0.0),urgent(false)
|
||||
{
|
||||
}
|
||||
Error(double error):error(error),urgent(false)
|
||||
{
|
||||
}
|
||||
bool isDefault() { return error == 0.0 && !urgent; } // Generic jobs are flagged as urgent for Cyclic Executive.
|
||||
};
|
||||
|
||||
void addCoordinator(shared_ptr<Tasks::Coordinator> coordinator);
|
||||
void removeCoordinator(shared_ptr<Tasks::Coordinator> coordinator);
|
||||
|
||||
Time::Interval getSleepingTime() const; // returns > 0 if the job is asleep
|
||||
double averageDutyCycle() const;
|
||||
const RunningAverageDutyCycle<>& getStepStats() const { return dutyCycle; }
|
||||
double averageSleepRate() const;
|
||||
double averageStepsPerSecond() const;
|
||||
double averageStepTime() const;
|
||||
double averageError() const;
|
||||
bool isRunning() const { return state==Running; }
|
||||
bool isDisabled();
|
||||
|
||||
typedef enum { None, LastSample, AverageInterval } SleepAdjustMethod;
|
||||
static SleepAdjustMethod sleepAdjustMethod;
|
||||
|
||||
typedef enum { Unknown, Sleeping, Waiting, Running } State;
|
||||
State getState() const { return state; }
|
||||
Time getWakeTime() const { return wakeTime; }
|
||||
Time::Interval getWake() const { return wakeTime - Time::now<Time::Fast>(); }
|
||||
double getPriority() const { return priority; }
|
||||
|
||||
std::string getDebugName() const
|
||||
{
|
||||
shared_ptr<Arbiter> ar(getArbiter());
|
||||
if (ar)
|
||||
return RBX::format("%s:%s", ar->arbiterName().c_str(), name.c_str());
|
||||
else
|
||||
return name;
|
||||
}
|
||||
|
||||
static bool isLowerWakeTime(const TaskScheduler::Job& job1, const TaskScheduler::Job& job2)
|
||||
{
|
||||
return job1.wakeTime < job2.wakeTime;
|
||||
}
|
||||
|
||||
WindowAverageDutyCycle<>& getDutyCycleWindow() { return dutyCycleWindow; }
|
||||
const WindowAverageDutyCycle<>& getDutyCycleWindow() const { return dutyCycleWindow; }
|
||||
|
||||
protected:
|
||||
Job(const char* name, shared_ptr<TaskScheduler::Arbiter> arbiter, Time::Interval stepBudget = Time::Interval(0));
|
||||
virtual ~Job();
|
||||
|
||||
public:
|
||||
inline const shared_ptr<TaskScheduler::Arbiter>& getArbiter() const
|
||||
{
|
||||
return sharedArbiter;
|
||||
}
|
||||
inline bool hasArbiter(TaskScheduler::Arbiter* test) const
|
||||
{
|
||||
return sharedArbiter.get() == test || sharedArbiter->getSyncronizationArbiter() == test;
|
||||
};
|
||||
|
||||
inline static bool haveDifferentArbiters(const Job* job1, const Job* job2) {
|
||||
Arbiter* a1 = job1->sharedArbiter.get();
|
||||
if(a1)
|
||||
a1 = a1->getSyncronizationArbiter();
|
||||
|
||||
Arbiter* a2 = job2->sharedArbiter.get();
|
||||
if(a2)
|
||||
a2 = a2->getSyncronizationArbiter();
|
||||
|
||||
return a1 != a2;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Abstract functions
|
||||
private:
|
||||
// sleepTime>0 means the job will sleep
|
||||
virtual Time::Interval sleepTime(const Stats& stats) = 0;
|
||||
|
||||
// error==0 means the job won't be scheduled
|
||||
virtual Error error(const Stats& stats) = 0;
|
||||
|
||||
// Used in Cyclic Executive to decide if we should re-run entire TaskScheduler loop
|
||||
// This is used to let LegacyLocks clear in Studio when waiting for Render job.
|
||||
virtual bool tryJobAgain() { return false; };
|
||||
|
||||
// Used to determine which job gets priority. The priority is multiplied by this factor
|
||||
virtual double getPriorityFactor() = 0;
|
||||
|
||||
// The Job is being asked to step.
|
||||
virtual StepResult step(const Stats& stats) = 0;
|
||||
|
||||
virtual int getDesiredConcurrencyCount() const
|
||||
{
|
||||
// return >1 if the job intends to do parallel work (using multiple threads)
|
||||
// During the step function, query the number of threads alloted by checking
|
||||
// allotedConcurrency
|
||||
return 1;
|
||||
}
|
||||
protected:
|
||||
// Use this to generate the error function if you just want to try to track the desiredHz
|
||||
Error computeStandardError(const Stats& stats, double desiredHz);
|
||||
Error computeStandardErrorCyclicExecutiveSleeping(const Stats& stats, double desiredHz);
|
||||
Time::Interval computeStandardSleepTime(const Stats& stats, double desiredHz);
|
||||
|
||||
private:
|
||||
State state;
|
||||
bool isRemoveRequested;
|
||||
Time timeofLastStep;
|
||||
Time timeofLastSleep;
|
||||
Time::Interval timespanOfLastStep;
|
||||
Error currentError;
|
||||
double priority;
|
||||
Time wakeTime;
|
||||
int overStepTimeThresholdCount;
|
||||
|
||||
boost::shared_ptr<CEvent> joinEvent; // Used when joining to the event after it is removed
|
||||
RunningAverageDutyCycle<> dutyCycle;
|
||||
RunningAverage<double> sleepRate;
|
||||
RunningAverage<double> runningAverageError;
|
||||
WindowAverageDutyCycle<> dutyCycleWindow;
|
||||
|
||||
void updateError(const Time& time);
|
||||
void notifyCoordinatorsPreStep();
|
||||
void preStep();
|
||||
void postStep(StepResult result);
|
||||
void notifyCoordinatorsPostStep();
|
||||
void updatePriority();
|
||||
void updateWakeTime();
|
||||
void startSleeping();
|
||||
void startWaiting();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
#pragma once
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "rbx/RunningAverage.h"
|
||||
#include "rbx/Declarations.h"
|
||||
#include "rbx/Debug.h"
|
||||
#include "rbx/ThreadSafe.h"
|
||||
#include "rbx/boost.hpp"
|
||||
#include "rbx/CEvent.h"
|
||||
#include "rbx/atomic.h"
|
||||
|
||||
#include "boost/thread.hpp"
|
||||
#include "boost/function.hpp"
|
||||
#include "boost/shared_ptr.hpp"
|
||||
#include "boost/scoped_ptr.hpp"
|
||||
#include "boost/noncopyable.hpp"
|
||||
#include "boost/intrusive/list.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
# undef min
|
||||
# undef max
|
||||
#endif
|
||||
|
||||
|
||||
LOGGROUP(TaskSchedulerInit)
|
||||
LOGGROUP(TaskSchedulerRun)
|
||||
LOGGROUP(TaskSchedulerFindJob)
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
/// A singleton object responsible for scheduling the execution TaskScheduler.Jobs.
|
||||
class TaskScheduler
|
||||
{
|
||||
struct SleepingTag;
|
||||
typedef boost::intrusive::list_base_hook< boost::intrusive::tag<SleepingTag> > SleepingHook;
|
||||
struct WaitingTag;
|
||||
typedef boost::intrusive::list_base_hook< boost::intrusive::tag<WaitingTag> > WaitingHook;
|
||||
|
||||
public:
|
||||
class Thread;
|
||||
typedef std::vector< boost::shared_ptr<Thread> > Threads;
|
||||
class Job;
|
||||
bool DataModel30fpsThrottle;
|
||||
Time lastCyclcTimestamp;
|
||||
bool cyclicExecutiveWaitForNextFrame;
|
||||
int nonCyclicJobsToDo;
|
||||
int cyclicExecutiveLoopId;
|
||||
class RBXBaseClass Arbiter
|
||||
{
|
||||
protected:
|
||||
ActivityMeter<2> activityMeter;
|
||||
public:
|
||||
virtual std::string arbiterName() = 0;
|
||||
virtual bool areExclusive(Job* job1, Job* job2) = 0;
|
||||
virtual bool isThrottled() = 0;
|
||||
virtual void preStep(TaskScheduler::Job* job) { activityMeter.increment(); }
|
||||
virtual void postStep(TaskScheduler::Job* job) { activityMeter.decrement(); }
|
||||
double getAverageActivity() { return activityMeter.averageValue(); }
|
||||
virtual Arbiter* getSyncronizationArbiter() { return this; };
|
||||
virtual int getNumPlayers() const {return 1;}
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
Done, // The job will be removed from the TaskScheduler
|
||||
Stepped, // Another step will be scheduled
|
||||
} StepResult;
|
||||
|
||||
static TaskScheduler& singleton();
|
||||
|
||||
typedef enum { LastError, AccumulatedError, FIFO } PriorityMethod;
|
||||
static PriorityMethod priorityMethod;
|
||||
|
||||
#ifdef RBX_TEST_BUILD
|
||||
static int findJobFPS;
|
||||
static bool updateJobPriorityOnWake;
|
||||
#endif
|
||||
double threadAffinityPreference;
|
||||
typedef enum {PerCore4 = 104, PerCore3 = 103, PerCore2 = 102, PerCore1 = 101, Auto = 0, Threads1 = 1, Threads2 = 2, Threads3 = 3, Threads4 = 4, Threads8 = 8, Threads16 = 16} ThreadPoolConfig;
|
||||
|
||||
bool shouldDropThread() const;
|
||||
void dropThread(Thread* thread);
|
||||
|
||||
size_t getThreadCount() { return threadCount; }
|
||||
void setThreadCount(ThreadPoolConfig threadConfig);
|
||||
void disableThreads(int count, Threads& threads);
|
||||
void enableThreads(Threads& threads);
|
||||
|
||||
void add(boost::shared_ptr<TaskScheduler::Job> job);
|
||||
void reschedule(boost::shared_ptr<TaskScheduler::Job> job);
|
||||
|
||||
void remove(boost::shared_ptr<TaskScheduler::Job> job) { remove(job, false, NULL); }
|
||||
// This version of remove might lead to deadlocks in some cases, so try not to use it
|
||||
void removeBlocking(boost::shared_ptr<TaskScheduler::Job> job) { remove(job, true, NULL); }
|
||||
// This version of remove calls back several times a second while waiting.
|
||||
// You might use this to process events in order to avoid deadlocks.
|
||||
void removeBlocking(boost::shared_ptr<TaskScheduler::Job> job, boost::function<void()> callbackPing) { remove(job, true, callbackPing); }
|
||||
|
||||
void getJobsInfo(std::vector<boost::shared_ptr<const Job> >& result);
|
||||
void getJobsByName(const std::string& name, std::vector<boost::shared_ptr<const Job> >& result);
|
||||
|
||||
// Performance counters
|
||||
double numSleepingJobs() const { return sleepingJobCount.value(); }
|
||||
double numWaitingJobs() const { return waitingJobCount.value(); }
|
||||
double numRunningJobs() const { return averageRunningJobCount.value(); }
|
||||
double threadAffinity() const { return averageThreadAffinity.value(); }
|
||||
size_t threadPoolSize() const { return threads.size(); }
|
||||
double schedulerRate() const { return schedulerDutyCycle.rate(); }
|
||||
double getSchedulerDutyCyclePerThread() const;
|
||||
double getErrorCalculationRate() const { return errorCalculationPerSec.rate(); }
|
||||
double getSortFrequency() const { return sortFrequency.rate(); }
|
||||
rbx::atomic<int> taskCount;
|
||||
void printDiagnostics(bool aggregateJobs);
|
||||
void printJobs();
|
||||
void setJobsExtendedStatsWindow(double seconds); // set seconds to 0.0 to disable.
|
||||
void cancelCyclicExecutive();
|
||||
bool isCyclicExecutive() { return cyclicExecutiveEnabled; }
|
||||
void releaseCyclicExecutive(TaskScheduler::Job* job);
|
||||
private:
|
||||
// ** Here for thread saftey. **
|
||||
struct CyclicExecutiveJob
|
||||
{
|
||||
boost::shared_ptr<TaskScheduler::Job> job;
|
||||
bool cyclicExecutiveExecuted;
|
||||
bool isRunning;
|
||||
|
||||
CyclicExecutiveJob( const boost::shared_ptr<TaskScheduler::Job>& j )
|
||||
{
|
||||
job = j;
|
||||
cyclicExecutiveExecuted = false;
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
// Allows for using std::find.
|
||||
bool operator==( const boost::shared_ptr<TaskScheduler::Job>& j ) { return job == j; }
|
||||
bool operator==( const TaskScheduler::Job& j ) { return job.get() == &j; }
|
||||
};
|
||||
|
||||
TaskScheduler();
|
||||
~TaskScheduler();
|
||||
void endAllThreads();
|
||||
void sampleRunningJobCount();
|
||||
|
||||
static bool jobCompare(const CyclicExecutiveJob& jobA, const CyclicExecutiveJob& jobB);
|
||||
|
||||
void remove(boost::shared_ptr<TaskScheduler::Job> job, bool joinJob, boost::function<void()> callbackPing);
|
||||
void remove(const boost::shared_ptr<TaskScheduler::Job>& job, boost::shared_ptr<CEvent> joinEvent);
|
||||
|
||||
void scheduleJob(Job& job);
|
||||
|
||||
static bool areExclusive(Job* job1, Job* job2, const shared_ptr<Arbiter>& arbiterHint);
|
||||
bool conflictsWithScheduledJob(Job* item) const;
|
||||
|
||||
void incrementThreadCount();
|
||||
void decrementThreadCount();
|
||||
|
||||
RBX::mutex mutex;
|
||||
|
||||
typedef std::set< shared_ptr<Job> > AllJobs;
|
||||
AllJobs allJobs;
|
||||
typedef boost::intrusive::list< Job, boost::intrusive::base_hook<SleepingHook> > SleepingJobs;
|
||||
SleepingJobs sleepingJobs;
|
||||
bool cyclicExecutiveEnabled;
|
||||
typedef std::vector< CyclicExecutiveJob > CyclicExecutiveJobs;
|
||||
CyclicExecutiveJobs cyclicExecutiveJobs;
|
||||
typedef boost::intrusive::list< Job, boost::intrusive::base_hook<WaitingHook> > WaitingJobs;
|
||||
WaitingJobs waitingJobs;
|
||||
|
||||
shared_ptr<Job> nextScheduledJob;
|
||||
|
||||
void wakeSleepingJobs();
|
||||
void enqueueWaitingJob(Job& job);
|
||||
Time::Interval getShortestSleepTime() const;
|
||||
boost::shared_ptr<Job> findJobToRun(boost::shared_ptr<Thread> requestingThread);
|
||||
boost::shared_ptr<Job> findJobToRunNonCyclicJobs(boost::shared_ptr<Thread> requestingThread, RBX::Time now);
|
||||
int numNonCyclicJobsWithWork();
|
||||
|
||||
void checkStillWaitingNextFrame(Time now);
|
||||
|
||||
RunningAverage<int> sleepingJobCount;
|
||||
RunningAverage<int> waitingJobCount;
|
||||
RunningAverage<int> averageRunningJobCount;
|
||||
RunningAverageDutyCycle<Time::Precise> schedulerDutyCycle; // time spent scheduling jobs
|
||||
RunningAverage<double> averageThreadAffinity;
|
||||
RunningAverageTimeInterval<> errorCalculationPerSec;
|
||||
RunningAverageTimeInterval<> sortFrequency;
|
||||
|
||||
rbx::atomic<int> runningJobCount;
|
||||
|
||||
Time nextWakeTime;
|
||||
|
||||
Time lastSortTime;
|
||||
|
||||
Threads threads;
|
||||
size_t desiredThreadCount;
|
||||
|
||||
CEvent sampleRunningJobCountEvent;
|
||||
boost::scoped_ptr<boost::thread> runningJobCounterThread;
|
||||
|
||||
rbx::atomic<int> threadCount;
|
||||
|
||||
static rbx::thread_specific_reference<TaskScheduler::Job> currentJob;
|
||||
static void static_init();
|
||||
};
|
||||
|
||||
|
||||
// A simple arbiter that prevents all members of it to execute concurrently
|
||||
class ExclusiveArbiter : public TaskScheduler::Arbiter, boost::noncopyable
|
||||
{
|
||||
public:
|
||||
virtual bool areExclusive(TaskScheduler::Job* job1, TaskScheduler::Job* job2);
|
||||
virtual std::string arbiterName() { return "ExclusiveArbiter"; }
|
||||
virtual bool isThrottled() { return false; }
|
||||
static ExclusiveArbiter singleton;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class SimpleThrottlingArbiter : public TaskScheduler::Arbiter
|
||||
{
|
||||
mutable bool throttled;
|
||||
rbx::atomic<int> updatingThrottle;
|
||||
static rbx::atomic<int> arbiterCount;
|
||||
|
||||
public:
|
||||
static bool isThrottlingEnabled;
|
||||
|
||||
SimpleThrottlingArbiter()
|
||||
:throttled(false)
|
||||
,updatingThrottle(0)
|
||||
{
|
||||
++arbiterCount;
|
||||
}
|
||||
|
||||
~SimpleThrottlingArbiter()
|
||||
{
|
||||
--arbiterCount;
|
||||
}
|
||||
|
||||
virtual bool isThrottled()
|
||||
{
|
||||
if (!isThrottlingEnabled)
|
||||
return false;
|
||||
|
||||
long count = arbiterCount;
|
||||
if (count<=1)
|
||||
return false;
|
||||
if (updatingThrottle.swap(1) == 0)
|
||||
{
|
||||
double cutoff = ((double)RBX::TaskScheduler::singleton().getThreadCount()) / (double) count;
|
||||
// hysteresis
|
||||
if (throttled)
|
||||
{
|
||||
throttled = getAverageActivity() >= cutoff;
|
||||
}
|
||||
else
|
||||
{
|
||||
throttled = getAverageActivity() >= 1.1 * cutoff;
|
||||
}
|
||||
|
||||
--updatingThrottle;
|
||||
}
|
||||
|
||||
return throttled;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rbx/TaskScheduler.h"
|
||||
#include <map>
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
namespace Tasks
|
||||
{
|
||||
// Prevents jobs from running according to some coordination logic.
|
||||
// Generally a Coordinator will affect execution order but not
|
||||
// affect parallelism. It may be more efficient to enforce resource
|
||||
// locks by specializing the TaskScheduler. See the DataModel scheduler.
|
||||
class RBXBaseClass Coordinator
|
||||
{
|
||||
public:
|
||||
// These functions must be written in a thread-safe manner
|
||||
// However, no 2 threads will call a function with the same job
|
||||
|
||||
virtual bool isInhibited(TaskScheduler::Job* job) = 0;
|
||||
virtual void onPreStep(TaskScheduler::Job* job) {}
|
||||
virtual void onPostStep(TaskScheduler::Job* job) {}
|
||||
virtual void onAdded(TaskScheduler::Job* job) {}
|
||||
virtual void onRemoved(TaskScheduler::Job* job) {}
|
||||
};
|
||||
|
||||
// Prevents jobs from running in parallel
|
||||
class Exclusive : public Coordinator
|
||||
{
|
||||
volatile TaskScheduler::Job* runningJob;
|
||||
public:
|
||||
Exclusive():runningJob(NULL) {}
|
||||
virtual bool isInhibited(TaskScheduler::Job* job);
|
||||
virtual void onPreStep(TaskScheduler::Job* job);
|
||||
virtual void onPostStep(TaskScheduler::Job* job);
|
||||
virtual void onAdded(TaskScheduler::Job* job) {}
|
||||
virtual void onRemoved(TaskScheduler::Job* job) {}
|
||||
};
|
||||
|
||||
// Requires that all coordinated jobs finish stepping before any job steps again.
|
||||
// It is the task-equivalent of a thread barrier.
|
||||
class Barrier : public Coordinator
|
||||
{
|
||||
unsigned int counter;
|
||||
unsigned int remainingTasks;
|
||||
RBX::mutex mutex;
|
||||
std::map<TaskScheduler::Job*, unsigned int> jobs;
|
||||
|
||||
void releaseBarrier();
|
||||
public:
|
||||
Barrier():counter(0),remainingTasks(0) {}
|
||||
virtual bool isInhibited(TaskScheduler::Job* job);
|
||||
virtual void onPostStep(TaskScheduler::Job* job);
|
||||
virtual void onAdded(TaskScheduler::Job* job);
|
||||
virtual void onRemoved(TaskScheduler::Job* job);
|
||||
};
|
||||
|
||||
class SequenceBase : public Coordinator
|
||||
{
|
||||
private:
|
||||
unsigned int nextJobIndex;
|
||||
RBX::mutex mutex;
|
||||
std::vector<TaskScheduler::Job*> jobs;
|
||||
protected:
|
||||
void advance();
|
||||
public:
|
||||
SequenceBase():nextJobIndex(0) {}
|
||||
virtual bool isInhibited(TaskScheduler::Job* job);
|
||||
virtual void onAdded(TaskScheduler::Job* job);
|
||||
virtual void onRemoved(TaskScheduler::Job* job);
|
||||
};
|
||||
|
||||
// Requires that all coordinated jobs execute in sequence.
|
||||
// Jobs are allowed to run in parallel, but they must start
|
||||
// execution in the sequence in which they are added to the
|
||||
// coordinator
|
||||
class Sequence : public SequenceBase
|
||||
{
|
||||
public:
|
||||
virtual void onPreStep(TaskScheduler::Job* job) {
|
||||
advance();
|
||||
}
|
||||
};
|
||||
|
||||
// Requires that all coordinated jobs execute in sequence.
|
||||
// Jobs are *not* allowed to run in parallel.
|
||||
// This is equivalent to Exclusive and Sequence combined
|
||||
class ExclusiveSequence : public SequenceBase
|
||||
{
|
||||
public:
|
||||
virtual void onPostStep(TaskScheduler::Job* job) {
|
||||
advance();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
// Returns a function that executes threadfunc with a given name
|
||||
boost::function0<void> thread_wrapper(const boost::function0<void>& threadfunc, const char* name);
|
||||
|
||||
void set_thread_name(const char* name);
|
||||
|
||||
// Returns the name of a thread if it was created with one of the above functions
|
||||
const char* get_thread_name();
|
||||
|
||||
// The worker thread runs a process in a low-priority thread
|
||||
// The function you provide is called:
|
||||
// 1) Once after worker_thread is constructed
|
||||
// 2) Immediately after the function returns work_result::more
|
||||
// 3) After wake() is called
|
||||
//
|
||||
// The thread ends execution after worker_thread is deleted, but it doesn't
|
||||
// interrupt processing of work_function
|
||||
//
|
||||
// The client is responsible for ensuring that any data used by the work_function
|
||||
// is still valid. Note that the work_function might continue to execute for a
|
||||
// period of time after the owning worker_thread is destroyed.
|
||||
//
|
||||
class worker_thread : public boost::noncopyable
|
||||
{
|
||||
struct data
|
||||
{
|
||||
boost::mutex sync;
|
||||
boost::condition_variable_any wakeCondition; // TODO: condition_variable?
|
||||
bool endRequest;
|
||||
|
||||
data():endRequest(false) {}
|
||||
};
|
||||
boost::shared_ptr<data> _data;
|
||||
boost::thread t;
|
||||
public:
|
||||
enum work_result { done, more };
|
||||
worker_thread(const boost::function0<work_result>& work_function, const char* name);
|
||||
~worker_thread();
|
||||
void wake(); // causes the work_function to be called (if the thread had been sleeping)
|
||||
void join(); // asks the thread to stop and then joins it
|
||||
private:
|
||||
static void threadProc(boost::shared_ptr<data> data, const boost::function0<work_result>& work_function);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(RBX_PLATFORM_IOS) || defined(__APPLE__) || __ANDROID__
|
||||
namespace rbx
|
||||
{
|
||||
template<typename T>
|
||||
class atomic
|
||||
{
|
||||
T v;
|
||||
public:
|
||||
atomic(T value = 0) : v(value) {}
|
||||
|
||||
T operator=(T v)
|
||||
{
|
||||
this->v = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
operator T() const { return v; }
|
||||
|
||||
T compare_and_swap(T value, T comparand) {
|
||||
return __sync_val_compare_and_swap(&v, comparand, value);
|
||||
}
|
||||
|
||||
T operator++() {
|
||||
return __sync_add_and_fetch(&v, 1);
|
||||
}
|
||||
|
||||
T operator--() {
|
||||
return __sync_sub_and_fetch(&v, 1);
|
||||
}
|
||||
|
||||
T operator++(int) {
|
||||
return __sync_fetch_and_add(&v, 1);
|
||||
}
|
||||
|
||||
T operator--(int) {
|
||||
return __sync_fetch_and_sub(&v, 1);
|
||||
}
|
||||
|
||||
T swap(T value)
|
||||
{
|
||||
return __sync_lock_test_and_set(&v, value);
|
||||
}
|
||||
};
|
||||
} // namespace rbx
|
||||
|
||||
#elif defined(_WIN32) // Windows
|
||||
|
||||
#include "rbx/debug.h"
|
||||
#include "boost/detail/interlocked.hpp"
|
||||
#include "boost/static_assert.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace rbx
|
||||
{
|
||||
template <typename T>
|
||||
class atomic
|
||||
{
|
||||
BOOST_STATIC_ASSERT(sizeof(T) == sizeof(long));
|
||||
long v;
|
||||
|
||||
public:
|
||||
atomic(T value = 0) : v(value)
|
||||
{
|
||||
// http://msdn.microsoft.com/en-us/library/ms683614.aspx: The variable pointed to must be aligned on a 32-bit boundary
|
||||
RBXASSERT((((uintptr_t)(&(this->v))) & (sizeof(T) - 1)) == 0);
|
||||
}
|
||||
|
||||
T operator=(T v)
|
||||
{
|
||||
this->v = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
operator T() const { return v; }
|
||||
|
||||
T compare_and_swap(T value, T comparand) {
|
||||
return BOOST_INTERLOCKED_COMPARE_EXCHANGE(&v, value, comparand);
|
||||
}
|
||||
|
||||
T operator++() {
|
||||
return BOOST_INTERLOCKED_INCREMENT(&v);
|
||||
}
|
||||
|
||||
T operator--() {
|
||||
return BOOST_INTERLOCKED_DECREMENT(&v);
|
||||
}
|
||||
|
||||
T operator++(int) {
|
||||
return BOOST_INTERLOCKED_INCREMENT(&v)-1;
|
||||
}
|
||||
|
||||
T operator--(int) {
|
||||
return BOOST_INTERLOCKED_DECREMENT(&v)+1;
|
||||
}
|
||||
|
||||
T swap(T value) {
|
||||
return BOOST_INTERLOCKED_EXCHANGE(&v, value);
|
||||
}
|
||||
};
|
||||
} // namespace rbx
|
||||
#else // you are using the wrong atomic
|
||||
#error "not supported"
|
||||
#endif
|
||||
@@ -0,0 +1,212 @@
|
||||
|
||||
#include "boost/type_traits/function_traits.hpp"
|
||||
|
||||
namespace rbx
|
||||
{
|
||||
// icallable and callable are a lightweight wrapper similar to boost::function,
|
||||
// but with more efficient storage. Because the instances are special-cased for
|
||||
// the functor involved you can't treat callable generically - it has to be
|
||||
// used in the context of template code that knows what to do with it.
|
||||
|
||||
// See rbx::signals for an implementation that uses this in lieu of boost::function
|
||||
|
||||
template<int arity, typename Signature>
|
||||
class icallable;
|
||||
|
||||
template<typename Signature>
|
||||
class icallable<0, Signature>
|
||||
{
|
||||
public:
|
||||
virtual void call() = 0;
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class icallable<1, Signature>
|
||||
{
|
||||
public:
|
||||
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1) = 0;
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class icallable<2, Signature>
|
||||
{
|
||||
public:
|
||||
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2) = 0;
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class icallable<3, Signature>
|
||||
{
|
||||
public:
|
||||
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3) = 0;
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class icallable<4, Signature>
|
||||
{
|
||||
public:
|
||||
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4) = 0;
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class icallable<5, Signature>
|
||||
{
|
||||
public:
|
||||
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4,
|
||||
typename boost::function_traits<Signature>::arg5_type arg5) = 0;
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class icallable<6, Signature>
|
||||
{
|
||||
public:
|
||||
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4,
|
||||
typename boost::function_traits<Signature>::arg5_type arg5,
|
||||
typename boost::function_traits<Signature>::arg6_type arg6) = 0;
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class icallable<7, Signature>
|
||||
{
|
||||
public:
|
||||
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4,
|
||||
typename boost::function_traits<Signature>::arg5_type arg5,
|
||||
typename boost::function_traits<Signature>::arg6_type arg6,
|
||||
typename boost::function_traits<Signature>::arg7_type arg7) = 0;
|
||||
};
|
||||
|
||||
template<class Base, class Delegate, int arity, typename Signature>
|
||||
class callable;
|
||||
|
||||
template<class Base, class Delegate, typename Signature>
|
||||
class callable<Base, Delegate, 0, Signature> : public Base
|
||||
{
|
||||
Delegate deleg;
|
||||
public:
|
||||
template<typename Arg1>
|
||||
callable(const Delegate& d, Arg1 arg1)
|
||||
:Base(arg1)
|
||||
,deleg(d)
|
||||
{}
|
||||
virtual void call() { deleg(); }
|
||||
};
|
||||
|
||||
template<class Base, class Delegate, typename Signature>
|
||||
class callable<Base, Delegate, 1, Signature> : public Base
|
||||
{
|
||||
Delegate deleg;
|
||||
public:
|
||||
template<typename Arg1>
|
||||
callable(const Delegate& deleg, Arg1 arg1):Base(arg1),deleg(deleg) {}
|
||||
virtual void call(typename boost::function_traits<Signature>::arg1_type arg1) { deleg(arg1); }
|
||||
};
|
||||
|
||||
template<class Base, class Delegate, typename Signature>
|
||||
class callable<Base, Delegate, 2, Signature> : public Base
|
||||
{
|
||||
Delegate deleg;
|
||||
public:
|
||||
template<typename Arg1>
|
||||
callable(const Delegate& deleg, Arg1 arg1):Base(arg1),deleg(deleg) {}
|
||||
virtual void call(
|
||||
typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2
|
||||
) { deleg(arg1, arg2); }
|
||||
};
|
||||
|
||||
template<class Base, class Delegate, typename Signature>
|
||||
class callable<Base, Delegate, 3, Signature> : public Base
|
||||
{
|
||||
Delegate deleg;
|
||||
public:
|
||||
template<typename Arg1>
|
||||
callable(const Delegate& deleg, Arg1 arg1):Base(arg1),deleg(deleg) {}
|
||||
virtual void call(
|
||||
typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3
|
||||
) { deleg(arg1, arg2, arg3); }
|
||||
};
|
||||
|
||||
template<class Base, class Delegate, typename Signature>
|
||||
class callable<Base, Delegate, 4, Signature> : public Base
|
||||
{
|
||||
Delegate deleg;
|
||||
public:
|
||||
template<typename Arg1>
|
||||
callable(const Delegate& deleg, Arg1 arg1):Base(arg1),deleg(deleg) {}
|
||||
virtual void call(
|
||||
typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4
|
||||
) { deleg(arg1, arg2, arg3, arg4); }
|
||||
};
|
||||
|
||||
template<class Base, class Delegate, typename Signature>
|
||||
class callable<Base, Delegate, 5, Signature> : public Base
|
||||
{
|
||||
Delegate deleg;
|
||||
public:
|
||||
template<typename Arg1>
|
||||
callable(const Delegate& deleg, Arg1 arg1):Base(arg1),deleg(deleg) {}
|
||||
virtual void call(
|
||||
typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4,
|
||||
typename boost::function_traits<Signature>::arg5_type arg5
|
||||
) { deleg(arg1, arg2, arg3, arg4, arg5); }
|
||||
};
|
||||
|
||||
template<class Base, class Delegate, typename Signature>
|
||||
class callable<Base, Delegate, 6, Signature> : public Base
|
||||
{
|
||||
Delegate deleg;
|
||||
public:
|
||||
template<typename Arg1>
|
||||
callable(const Delegate& deleg, Arg1 arg1):Base(arg1),deleg(deleg) {}
|
||||
virtual void call(
|
||||
typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4,
|
||||
typename boost::function_traits<Signature>::arg5_type arg5,
|
||||
typename boost::function_traits<Signature>::arg6_type arg6
|
||||
) { deleg(arg1, arg2, arg3, arg4, arg5, arg6); }
|
||||
};
|
||||
|
||||
template<class Base, class Delegate, typename Signature>
|
||||
class callable<Base, Delegate, 7, Signature> : public Base
|
||||
{
|
||||
Delegate deleg;
|
||||
public:
|
||||
template<typename Arg1>
|
||||
callable(const Delegate& deleg, Arg1 arg1):Base(arg1),deleg(deleg) {}
|
||||
virtual void call(
|
||||
typename boost::function_traits<Signature>::arg1_type arg1,
|
||||
typename boost::function_traits<Signature>::arg2_type arg2,
|
||||
typename boost::function_traits<Signature>::arg3_type arg3,
|
||||
typename boost::function_traits<Signature>::arg4_type arg4,
|
||||
typename boost::function_traits<Signature>::arg5_type arg5,
|
||||
typename boost::function_traits<Signature>::arg6_type arg6,
|
||||
typename boost::function_traits<Signature>::arg7_type arg7
|
||||
) { deleg(arg1, arg2, arg3, arg4, arg5, arg6, arg7); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
#pragma once
|
||||
|
||||
#include "rbx/debug.h"
|
||||
#include "rbx/atomic.h"
|
||||
#include "rbx/declarations.h"
|
||||
#include "boost/cast.hpp"
|
||||
|
||||
/// Forward Declarations
|
||||
namespace rbx
|
||||
{
|
||||
/*
|
||||
quick_intrusive_ptr_target<> is a mix-in that implements all functions required
|
||||
to use boost::intrusive_ptr.
|
||||
maxRefs should be used if Count is a byte or short and there is the risk of an
|
||||
overflow. Some algorithms do not have this risk. To avoid writing less performant
|
||||
code, maxRefs is not strictly enforced. In a multithreaded environment you should
|
||||
pick a "reasonable" value that is less than std::numeric_limits<Count>::max. For
|
||||
example, if Count=byte, then maxRefs could be 240
|
||||
*/
|
||||
template<class T, typename Count, Count maxRefs>
|
||||
class quick_intrusive_ptr_target;
|
||||
|
||||
/*
|
||||
intrusive_ptr_target<> is a mix-in that implements all functions required
|
||||
to use boost::intrusive_ptr and rbx::intrusive_weak_ptr. If you don't need
|
||||
weak reference support, then use quick_intrusive_ptr_target
|
||||
TODO: Allow custom allocators
|
||||
*/
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
class intrusive_ptr_target;
|
||||
|
||||
}
|
||||
|
||||
/// Template Specialization for boost intrusive ptrs
|
||||
namespace boost
|
||||
{
|
||||
/// Template Specialization for boost intrusive ptrs for quick_intrusive_ptr_target
|
||||
template<class T, typename Count, Count maxRefs>
|
||||
void intrusive_ptr_add_ref(const rbx::quick_intrusive_ptr_target<T, Count, maxRefs> * p);
|
||||
template<class T, typename Count, Count maxRefs>
|
||||
void intrusive_ptr_release(const rbx::quick_intrusive_ptr_target<T, Count, maxRefs> * p);
|
||||
|
||||
/// Template Specialization for boost intrusive ptrs for intrusive_ptr_target
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
void intrusive_ptr_add_ref(const rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak> * p);
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
void intrusive_ptr_release(const rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak> * p);
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
bool intrusive_ptr_expired(const rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak> * p);
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
bool intrusive_ptr_try_lock(const rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak> * p);
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
void intrusive_ptr_add_weak_ref(const rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak> * p);
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
void intrusive_ptr_weak_release(const rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak> * p);
|
||||
}
|
||||
|
||||
|
||||
namespace rbx
|
||||
{
|
||||
class too_many_refs : public std::exception
|
||||
{
|
||||
public:
|
||||
virtual const char* what() const throw()
|
||||
{
|
||||
return "too many refs";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(8) // Packing is useful if Count is short or byte
|
||||
template<class T, typename Count = int, Count maxRefs = 0 >
|
||||
class RBXBaseClass quick_intrusive_ptr_target
|
||||
{
|
||||
private:
|
||||
rbx::atomic<Count> refs;
|
||||
public:
|
||||
inline quick_intrusive_ptr_target() { refs = 0; }
|
||||
friend void boost::intrusive_ptr_add_ref<>(const quick_intrusive_ptr_target<T, Count, maxRefs>* p);
|
||||
friend void boost::intrusive_ptr_release<>(const quick_intrusive_ptr_target<T, Count, maxRefs>* p);
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
template<class T, typename Count = int, Count maxStrong = 0, Count maxWeak = maxStrong >
|
||||
class RBXBaseClass intrusive_ptr_target
|
||||
{
|
||||
private:
|
||||
// The "counts" struct is placed in memory at the head of the object
|
||||
#pragma pack(push)
|
||||
#pragma pack(8) // Packing is useful if Count is short or byte
|
||||
struct counts
|
||||
{
|
||||
rbx::atomic<Count> strong; // #shared
|
||||
rbx::atomic<Count> weak; // #weak + (#shared != 0)
|
||||
counts()
|
||||
{
|
||||
strong = 0;
|
||||
weak = 1;
|
||||
}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static inline counts* fetch(const T* t)
|
||||
{
|
||||
return reinterpret_cast<counts*>((char*) t - sizeof(counts));
|
||||
}
|
||||
public:
|
||||
void* operator new(std::size_t t)
|
||||
{
|
||||
void* c = ::malloc(sizeof(counts) + t);
|
||||
|
||||
// placement new the counts:
|
||||
new (c) counts();
|
||||
|
||||
return (char*)c + sizeof(counts);
|
||||
}
|
||||
|
||||
void operator delete( void * p )
|
||||
{
|
||||
counts* c = fetch(reinterpret_cast<T*>(p));
|
||||
// operator delete should only be called if this object
|
||||
// never got touched by the intrusive_ptr functions
|
||||
RBXASSERT(c->strong == 0);
|
||||
RBXASSERT(c->weak == 1);
|
||||
::free(c);
|
||||
}
|
||||
|
||||
friend void boost::intrusive_ptr_add_ref<>(const intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
|
||||
friend void boost::intrusive_ptr_release<>(const intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
|
||||
friend void boost::intrusive_ptr_add_weak_ref<>(const intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
|
||||
friend bool boost::intrusive_ptr_expired<>(const intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
|
||||
friend bool boost::intrusive_ptr_try_lock<>(const intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
|
||||
friend void boost::intrusive_ptr_weak_release<>(const intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
namespace boost
|
||||
{
|
||||
/// Template specialization quick_intrusive_ptr_target
|
||||
template<class T, typename Count, Count maxRefs>
|
||||
void intrusive_ptr_add_ref(const rbx::quick_intrusive_ptr_target<T, Count, maxRefs>* p)
|
||||
{
|
||||
if (maxRefs > 0 && p->refs >= maxRefs)
|
||||
throw rbx::too_many_refs();
|
||||
|
||||
const_cast<rbx::quick_intrusive_ptr_target<T, Count, maxRefs>*>(p)->refs++;
|
||||
}
|
||||
|
||||
template<class T, typename Count, Count maxRefs>
|
||||
void intrusive_ptr_release(const rbx::quick_intrusive_ptr_target<T, Count, maxRefs>* p)
|
||||
{
|
||||
RBXASSERT(p->refs > 0);
|
||||
if (--(const_cast<rbx::quick_intrusive_ptr_target<T, Count, maxRefs>*>(p)->refs) == 0)
|
||||
delete static_cast<const T*>(p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Template specialization intrusive_ptr_target
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
void intrusive_ptr_add_ref(const rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p)
|
||||
{
|
||||
typename rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::counts* c = rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::fetch(static_cast<const T*>(p));
|
||||
|
||||
if (maxStrong > 0)
|
||||
{
|
||||
if (++(c->strong) > maxStrong)
|
||||
throw rbx::too_many_refs();
|
||||
}
|
||||
else
|
||||
{
|
||||
c->strong++;
|
||||
RBXASSERT(c->strong < std::numeric_limits<Count>::max() - 10);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
void intrusive_ptr_release(const rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>* p)
|
||||
{
|
||||
const T* t = static_cast<const T*>(p);
|
||||
typedef typename rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::counts Counts;
|
||||
Counts* c = rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::fetch(t);
|
||||
|
||||
if (--(c->strong) == 0)
|
||||
{
|
||||
// placement delete the object, but not the counts
|
||||
t->~T();
|
||||
|
||||
if (--(c->weak) == 0)
|
||||
{
|
||||
// placement delete the counts and reclaim composite object memory
|
||||
c->Counts::~counts();
|
||||
::free((void*)c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
void intrusive_ptr_add_weak_ref(const rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak> * p)
|
||||
{
|
||||
typename rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::counts* c = rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::fetch(static_cast<const T*>(p));
|
||||
|
||||
RBXASSERT(c->strong > 0);
|
||||
|
||||
if (maxWeak > 0)
|
||||
{
|
||||
if (++(c->weak) > maxWeak + 1) // weak already has a ref because of the strong refs
|
||||
throw rbx::too_many_refs();
|
||||
}
|
||||
else
|
||||
{
|
||||
++(c->weak);
|
||||
RBXASSERT(c->weak < std::numeric_limits<Count>::max() - 10);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
bool intrusive_ptr_expired(const rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak> * p)
|
||||
{
|
||||
typename rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::counts* c = rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::fetch(static_cast<const T*>(p));
|
||||
return c->strong == 0;
|
||||
}
|
||||
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
bool intrusive_ptr_try_lock(const rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak> * p)
|
||||
{
|
||||
typename rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::counts* c = rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::fetch(static_cast<const T*>(p));
|
||||
|
||||
while (true)
|
||||
{
|
||||
Count tmp = c->strong;
|
||||
if( tmp == 0 )
|
||||
return false;
|
||||
if (c->strong.compare_and_swap(tmp + 1, tmp) == tmp)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, typename Count, Count maxStrong, Count maxWeak>
|
||||
void intrusive_ptr_weak_release(const rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak> * p)
|
||||
{
|
||||
const T* t = static_cast<const T*>(p);
|
||||
typedef typename rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::counts Counts;
|
||||
Counts* c = rbx::intrusive_ptr_target<T, Count, maxStrong, maxWeak>::fetch(t);
|
||||
|
||||
if (--(c->weak) == 0)
|
||||
{
|
||||
RBXASSERT(c->strong == 0);
|
||||
// placement delete the counts and reclaim composite object memory
|
||||
c->Counts::~counts();
|
||||
|
||||
::free((void*)c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost/intrusive_ptr.hpp"
|
||||
|
||||
namespace rbx
|
||||
{
|
||||
/*
|
||||
An extension to boost::intrusive_ptr.
|
||||
To use this class, the target class needs to implement
|
||||
some functions in addition to those required by boost::instrusive_ptr:
|
||||
|
||||
bool intrusive_ptr_expired(const T* p);
|
||||
bool intrusive_ptr_try_lock(const T* p);
|
||||
void intrusive_ptr_add_weak_ref(const T* p);
|
||||
void intrusive_ptr_weak_release(const T* p);
|
||||
|
||||
Note: rbx::intrusive_ptr_target is a nice wrapper class that implements
|
||||
all required functions
|
||||
|
||||
*/
|
||||
template<class T>
|
||||
class intrusive_weak_ptr
|
||||
{
|
||||
T* p_;
|
||||
|
||||
public:
|
||||
inline intrusive_weak_ptr(): p_(0)
|
||||
{
|
||||
}
|
||||
|
||||
inline intrusive_weak_ptr(T * p): p_(p)
|
||||
{
|
||||
if(p_ != 0) boost::intrusive_ptr_add_weak_ref(p_);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
inline intrusive_weak_ptr( const intrusive_weak_ptr<U>& rhs)
|
||||
: p_( 0 )
|
||||
{
|
||||
if (!rhs.expired())
|
||||
{
|
||||
p_ = rhs.raw();
|
||||
boost::intrusive_ptr_add_weak_ref(p_);
|
||||
}
|
||||
}
|
||||
|
||||
inline intrusive_weak_ptr( const intrusive_weak_ptr& rhs)
|
||||
: p_( 0 )
|
||||
{
|
||||
if (!rhs.expired())
|
||||
{
|
||||
p_ = rhs.raw();
|
||||
boost::intrusive_ptr_add_weak_ref(p_);
|
||||
}
|
||||
}
|
||||
|
||||
template<class U>
|
||||
inline intrusive_weak_ptr( const boost::intrusive_ptr<U>& rhs)
|
||||
: p_( rhs.get() )
|
||||
{
|
||||
if( p_ != 0 ) boost::intrusive_ptr_add_weak_ref(p_);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
inline intrusive_weak_ptr& operator=(T * p)
|
||||
{
|
||||
reset(p);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class U>
|
||||
inline intrusive_weak_ptr& operator=(const boost::intrusive_ptr<U> & rhs)
|
||||
{
|
||||
reset(rhs.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline intrusive_weak_ptr& operator=(const rbx::intrusive_weak_ptr<T>& rhs)
|
||||
{
|
||||
reset(rhs.raw());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class U>
|
||||
inline intrusive_weak_ptr& operator=(const rbx::intrusive_weak_ptr<U>& rhs)
|
||||
{
|
||||
reset(rhs.raw());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ~intrusive_weak_ptr()
|
||||
{
|
||||
if( p_ != 0 ) boost::intrusive_ptr_weak_release(p_);
|
||||
}
|
||||
|
||||
inline void reset()
|
||||
{
|
||||
if( p_ != 0 )
|
||||
{
|
||||
boost::intrusive_ptr_weak_release(p_);
|
||||
p_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void reset(T* p)
|
||||
{
|
||||
if( p_ != 0 ) boost::intrusive_ptr_weak_release(p_);
|
||||
p_ = p;
|
||||
if( p_ != 0 ) boost::intrusive_ptr_add_weak_ref(p_);
|
||||
}
|
||||
|
||||
inline boost::intrusive_ptr<T> lock() const
|
||||
{
|
||||
if (p_ && boost::intrusive_ptr_try_lock(p_))
|
||||
return boost::intrusive_ptr<T>(p_, false);
|
||||
else
|
||||
return boost::intrusive_ptr<T>();
|
||||
}
|
||||
|
||||
inline bool expired() const
|
||||
{
|
||||
return (!p_ || boost::intrusive_ptr_expired(p_));
|
||||
}
|
||||
|
||||
// TODO: Can we hide this?
|
||||
inline T* raw() const
|
||||
{
|
||||
return p_;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
#pragma once
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/type_traits/type_with_alignment.hpp>
|
||||
#include <boost/type_traits/alignment_of.hpp>
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
|
||||
// This is a copy of make_shared from boost 1.42.1
|
||||
// When we upgrade we can use boost directly
|
||||
// NOTE: 1.38.1 has an undocumented make_shared.hpp, but I don't know if it is
|
||||
// safe or not to use.
|
||||
namespace rbx
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template< std::size_t N, std::size_t A > struct sp_aligned_storage
|
||||
{
|
||||
union type
|
||||
{
|
||||
char data_[ N ];
|
||||
typename boost::type_with_alignment< A >::type align_;
|
||||
};
|
||||
};
|
||||
|
||||
template< class T > class sp_ms_deleter
|
||||
{
|
||||
private:
|
||||
|
||||
typedef typename sp_aligned_storage< sizeof( T ), ::boost::alignment_of< T >::value >::type storage_type;
|
||||
|
||||
bool initialized_;
|
||||
storage_type storage_;
|
||||
|
||||
private:
|
||||
|
||||
void destroy()
|
||||
{
|
||||
if( initialized_ )
|
||||
{
|
||||
reinterpret_cast< T* >( storage_.data_ )->~T();
|
||||
initialized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
sp_ms_deleter(): initialized_( false )
|
||||
{
|
||||
}
|
||||
|
||||
// optimization: do not copy storage_
|
||||
sp_ms_deleter( sp_ms_deleter const & ): initialized_( false )
|
||||
{
|
||||
}
|
||||
|
||||
~sp_ms_deleter()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void operator()( T * )
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void * address()
|
||||
{
|
||||
return storage_.data_;
|
||||
}
|
||||
|
||||
void set_initialized()
|
||||
{
|
||||
initialized_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
#if defined( BOOST_HAS_RVALUE_REFS )
|
||||
template< class T > T&& sp_forward( T & t )
|
||||
{
|
||||
return static_cast< T&& >( t );
|
||||
}
|
||||
#endif
|
||||
} // namespace detail
|
||||
|
||||
// TODO: This implementation may not support shared_from_this properly. Upgrade to new boost, which implements this for us
|
||||
template< class T > boost::shared_ptr< T > make_shared()
|
||||
{
|
||||
boost::shared_ptr< T > pt( static_cast< T* >( 0 ), rbx::detail::sp_ms_deleter< T >() );
|
||||
|
||||
rbx::detail::sp_ms_deleter< T > * pd = boost::get_deleter< rbx::detail::sp_ms_deleter< T > >( pt );
|
||||
|
||||
void * pv = pd->address();
|
||||
|
||||
::new( pv ) T();
|
||||
pd->set_initialized();
|
||||
|
||||
T * pt2 = static_cast< T* >( pv );
|
||||
|
||||
return boost::shared_ptr< T >( pt, pt2 );
|
||||
}
|
||||
|
||||
template< class T> boost::shared_ptr< T > make_shared(std::allocator<T> a)
|
||||
{
|
||||
boost::shared_ptr< T > pt( static_cast< T* >( 0 ), rbx::detail::sp_ms_deleter< T >(), a );
|
||||
|
||||
rbx::detail::sp_ms_deleter< T > * pd = boost::get_deleter< rbx::detail::sp_ms_deleter< T > >( pt );
|
||||
|
||||
void * pv = pd->address();
|
||||
|
||||
::new( pv ) T();
|
||||
pd->set_initialized();
|
||||
|
||||
T * pt2 = static_cast< T* >( pv );
|
||||
|
||||
return boost::shared_ptr< T >( pt, pt2 );
|
||||
}
|
||||
|
||||
|
||||
template< class T, class A1 >
|
||||
boost::shared_ptr< T > make_shared( A1 const & a1)
|
||||
{
|
||||
boost::shared_ptr< T > pt( static_cast< T* >( 0 ), rbx::detail::sp_ms_deleter< T >() );
|
||||
|
||||
rbx::detail::sp_ms_deleter< T > * pd = boost::get_deleter< rbx::detail::sp_ms_deleter< T > >( pt );
|
||||
|
||||
void * pv = pd->address();
|
||||
|
||||
::new( pv ) T( a1 );
|
||||
pd->set_initialized();
|
||||
|
||||
T * pt2 = static_cast< T* >( pv );
|
||||
|
||||
return boost::shared_ptr< T >( pt, pt2 );
|
||||
}
|
||||
|
||||
template< class T, class A1, class A2 >
|
||||
boost::shared_ptr< T > make_shared( A1 const & a1, A2 const & a2 )
|
||||
{
|
||||
boost::shared_ptr< T > pt( static_cast< T* >( 0 ), rbx::detail::sp_ms_deleter< T >() );
|
||||
|
||||
rbx::detail::sp_ms_deleter< T > * pd = boost::get_deleter< rbx::detail::sp_ms_deleter< T > >( pt );
|
||||
|
||||
void * pv = pd->address();
|
||||
|
||||
::new( pv ) T( a1, a2 );
|
||||
pd->set_initialized();
|
||||
|
||||
T * pt2 = static_cast< T* >( pv );
|
||||
|
||||
return boost::shared_ptr< T >( pt, pt2 );
|
||||
}
|
||||
|
||||
template< class T, class A1, class A2, class A3 >
|
||||
boost::shared_ptr< T > make_shared( A1 const & a1, A2 const & a2, A3 const & a3 )
|
||||
{
|
||||
boost::shared_ptr< T > pt( static_cast< T* >( 0 ), rbx::detail::sp_ms_deleter< T >() );
|
||||
|
||||
rbx::detail::sp_ms_deleter< T > * pd = boost::get_deleter< rbx::detail::sp_ms_deleter< T > >( pt );
|
||||
|
||||
void * pv = pd->address();
|
||||
|
||||
::new( pv ) T( a1, a2, a3 );
|
||||
pd->set_initialized();
|
||||
|
||||
T * pt2 = static_cast< T* >( pv );
|
||||
|
||||
return boost::shared_ptr< T >( pt, pt2 );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/pool/object_pool.hpp"
|
||||
|
||||
|
||||
namespace RBX {
|
||||
|
||||
// T must have a non-throwing destructor
|
||||
template <typename T, typename UserAllocator>
|
||||
class object_pool: public boost::object_pool<T, UserAllocator>
|
||||
{
|
||||
protected:
|
||||
struct CallDestructor
|
||||
{
|
||||
void operator()(T* item)
|
||||
{
|
||||
item->~T();
|
||||
}
|
||||
} callDestructor;
|
||||
public:
|
||||
#ifndef _WIN32
|
||||
// gcc can't access typdef from base class...
|
||||
typedef typename boost::pool<UserAllocator>::size_type size_type;
|
||||
#endif
|
||||
|
||||
// This constructor parameter is an extension!
|
||||
explicit object_pool<T, UserAllocator>(const size_type next_size = 32)
|
||||
:boost::object_pool<T, UserAllocator>(next_size) { }
|
||||
|
||||
template<class F>
|
||||
void for_each(F& f)
|
||||
{
|
||||
// handle trivial case
|
||||
if (!this->list.valid())
|
||||
return;
|
||||
|
||||
boost::details::PODptr<size_type> iter = this->list;
|
||||
boost::details::PODptr<size_type> next = iter;
|
||||
|
||||
// Start 'freed_iter' at beginning of free list
|
||||
void * freed_iter = this->first;
|
||||
|
||||
const size_type partition_size = this->alloc_size();
|
||||
|
||||
do
|
||||
{
|
||||
// increment next
|
||||
next = next.next();
|
||||
|
||||
// delete all contained objects that aren't freed
|
||||
|
||||
// Iterate 'i' through all chunks in the memory block
|
||||
for (char * i = iter.begin(); i != iter.end(); i += partition_size)
|
||||
{
|
||||
// If this chunk is free
|
||||
if (i == freed_iter)
|
||||
{
|
||||
// Increment freed_iter to point to next in free list
|
||||
freed_iter = boost::simple_segregated_storage<size_type>::nextof(freed_iter);
|
||||
|
||||
// Continue searching chunks in the memory block
|
||||
continue;
|
||||
}
|
||||
|
||||
// This chunk is not free (allocated), so call f
|
||||
f(static_cast<T *>(static_cast<void *>(i)));
|
||||
// and continue searching chunks in the memory block
|
||||
}
|
||||
|
||||
// increment iter
|
||||
iter = next;
|
||||
} while (iter.valid());
|
||||
}
|
||||
|
||||
// go through all objects, calling desctructors.
|
||||
// then use store's purge operation (which doesn't call destructors)
|
||||
void clear()
|
||||
{
|
||||
for_each(callDestructor);
|
||||
|
||||
boost::pool<UserAllocator>::purge_memory();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // RBX
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <iosfwd>
|
||||
|
||||
#ifdef _WIN32
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
namespace RBX {
|
||||
|
||||
/*
|
||||
Records time in a variety of ways: CPU counters, OS Times, Multimedia Timers, etc.
|
||||
|
||||
The purpose of this class is to shield the user from OS-specific time functions. This
|
||||
is why in the original design, the "sec" field was not exposed publicly. Unfortunately,
|
||||
some functions have started to expose this, which breaks the intended encapsulation.
|
||||
|
||||
The design intent is that to get a numerical time value you subtract two Time instances
|
||||
to get an Interval.
|
||||
|
||||
For performance reasons you generally want to use the "Fast" timer. However, there
|
||||
is a "preciseOverride" if you want to have runtime control over certain time queries
|
||||
for benchmarking purposes.
|
||||
*/
|
||||
class Time {
|
||||
public:
|
||||
//! Relative time Interval.
|
||||
class Interval {
|
||||
double sec;
|
||||
public:
|
||||
inline Interval() : sec(0) {};
|
||||
|
||||
static inline Interval max() { return Interval(std::numeric_limits<double>::max()); }
|
||||
static inline Interval zero() { return Interval(0); }
|
||||
|
||||
static inline Interval from_milliseconds(double milliseconds) { return Interval(0.001 * milliseconds); }
|
||||
static inline Interval from_seconds(double seconds) { return Interval(seconds); }
|
||||
static inline Interval from_minutes(double minutes) { return Interval(60.0 * minutes); }
|
||||
static inline Interval from_hours(double hours) { return Interval(60.0 * 60.0 * hours); }
|
||||
|
||||
inline explicit Interval( double seconds ):sec(seconds) {}
|
||||
|
||||
inline double seconds() const { return sec; }
|
||||
|
||||
inline double msec() const { return sec*1000; }
|
||||
|
||||
inline bool isZero() const { return sec==0; }
|
||||
|
||||
friend class Time;
|
||||
|
||||
friend Interval operator-( const Time& t1, const Time& t0 );
|
||||
|
||||
friend Interval operator+( const Interval& i, const Interval& j ) {
|
||||
return Interval(i.sec+j.sec);
|
||||
}
|
||||
|
||||
friend Interval operator-( const Interval& i, const Interval& j ) {
|
||||
return Interval(i.sec-j.sec);
|
||||
}
|
||||
|
||||
Interval& operator+=( const Interval& i ) {sec += i.sec; return *this;}
|
||||
|
||||
Interval& operator-=( const Interval& i ) {sec -= i.sec; return *this;}
|
||||
|
||||
bool operator>( const Interval& j ) const { return sec > j.sec; }
|
||||
bool operator<( const Interval& j ) const { return sec < j.sec; }
|
||||
bool operator>=( const Interval& j ) const { return sec >= j.sec; }
|
||||
bool operator<=( const Interval& j ) const { return sec <= j.sec; }
|
||||
bool operator==( const Interval& j ) const { return sec == j.sec; }
|
||||
bool operator!=( const Interval& j ) const { return sec != j.sec; }
|
||||
|
||||
void sleep();
|
||||
|
||||
template<class charT, class traits>
|
||||
friend std::basic_ostream<charT, traits>&
|
||||
operator<< (std::basic_ostream<charT, traits> &out,
|
||||
Interval interval)
|
||||
{
|
||||
out << interval.sec;
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
//! Construct an absolute timestamp initialized to zero.
|
||||
inline Time() : sec(0) {};
|
||||
|
||||
inline static Time max() { return Time(std::numeric_limits<double>::max()); }
|
||||
|
||||
typedef enum { Fast, Benchmark, Precise, Multimedia } SampleMethod;
|
||||
// If preciseOverride==Fast then Fast and Benchmark are precise
|
||||
// If preciseOverride==Benchmark then Benchmark is precise
|
||||
static SampleMethod preciseOverride;
|
||||
|
||||
static bool isSpeedCheater();
|
||||
static bool isDebugged();
|
||||
|
||||
//! Return current time.
|
||||
template<SampleMethod sampleMethod>
|
||||
static Time now();
|
||||
|
||||
static Time now(SampleMethod sampleMethod);
|
||||
|
||||
static long long getTickCount();
|
||||
static long long getStart();
|
||||
|
||||
// Avoid using this! Instead, sample two Time::now() instances and subtract them
|
||||
static double nowFastSec();
|
||||
|
||||
static Time nowFast();
|
||||
|
||||
bool isZero() const { return sec==0; }
|
||||
|
||||
Time operator+( const Interval& j ) const {
|
||||
return Time(sec + j.sec);
|
||||
}
|
||||
Time operator-( const Interval& j ) const {
|
||||
return Time(sec - j.sec);
|
||||
}
|
||||
|
||||
Time& operator+=( const Interval& j ) {
|
||||
this->sec += j.sec;
|
||||
return *this;
|
||||
}
|
||||
Time& operator-=( const Interval& j ) {
|
||||
this->sec -= j.sec;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator>( const Time& j ) const { return sec > j.sec; }
|
||||
bool operator<( const Time& j ) const { return sec < j.sec; }
|
||||
bool operator>=( const Time& j ) const { return sec >= j.sec; }
|
||||
bool operator<=( const Time& j ) const { return sec <= j.sec; }
|
||||
bool operator==( const Time& j ) const { return sec == j.sec; }
|
||||
bool operator!=( const Time& j ) const { return sec != j.sec; }
|
||||
|
||||
template<class charT, class traits>
|
||||
friend std::basic_ostream<charT, traits>&
|
||||
operator<< (std::basic_ostream<charT, traits> &out,
|
||||
Time time)
|
||||
{
|
||||
out << time.sec;
|
||||
return out;
|
||||
}
|
||||
|
||||
//! Subtract two timestamps to get the time Interval between
|
||||
friend Interval operator-( const Time& t1, const Time& t0 );
|
||||
|
||||
// Avoid using this! Instead, sample two Time::now() instances and subtract them
|
||||
double timestampSeconds() const
|
||||
{
|
||||
return sec;
|
||||
}
|
||||
|
||||
private:
|
||||
double sec;
|
||||
|
||||
protected:
|
||||
Time(double sec) : sec(sec) {};
|
||||
};
|
||||
|
||||
template<Time::SampleMethod sampleMethod>
|
||||
class Timer
|
||||
{
|
||||
Time start;
|
||||
public:
|
||||
Timer():start(Time::now<sampleMethod>()) {}
|
||||
Time::Interval delta() const { return Time::now<sampleMethod>() - start; }
|
||||
Time::Interval reset()
|
||||
{
|
||||
Time now = Time::now<sampleMethod>();
|
||||
Time::Interval result = now - start;
|
||||
start = now;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class RemoteTime : public Time {
|
||||
public:
|
||||
inline RemoteTime() : Time() {};
|
||||
inline RemoteTime(double value) : Time(value) {};
|
||||
RemoteTime(const Time& value) : Time(value) {};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,686 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/type_traits.hpp"
|
||||
#include "boost/any.hpp"
|
||||
#include "rbx/boost.hpp"
|
||||
#include "rbx/threadsafe.h"
|
||||
#include "rbx/Debug.h"
|
||||
#include <limits>
|
||||
#include "rbx/Memory.h"
|
||||
#include "rbx/intrusive_ptr_target.h"
|
||||
#include "rbx/intrusive_weak_ptr.h"
|
||||
#include "rbx/callable.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef max
|
||||
// Did you include a windows header file without defining NOMINMAX?
|
||||
// If you can't do that, then #undef max instead
|
||||
#error
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using boost::shared_ptr;
|
||||
using boost::weak_ptr;
|
||||
|
||||
LOGGROUP(ScopedConnection);
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define RBX_SIGNALS_DEBUGGING
|
||||
#endif
|
||||
|
||||
#ifdef RBX_SIGNALS_DEBUGGING
|
||||
#define RBX_SIGNALS_ASSERT RBX_CRASH_ASSERT
|
||||
#pragma optimize( "", off )
|
||||
#else
|
||||
#define RBX_SIGNALS_ASSERT RBXASSERT
|
||||
#endif
|
||||
|
||||
namespace rbx
|
||||
{
|
||||
// The classes in this namespace mimic a small fraction of the features contained
|
||||
// in boost signals
|
||||
namespace signals
|
||||
{
|
||||
/*
|
||||
This signal class is similar to boost::signal, but with a few
|
||||
important differences. First, it doesn't implement nearly as
|
||||
much functionality as boost's. It merely implements those
|
||||
portions that Roblox uses.
|
||||
The big advantage to this implementation is its (limited) thread
|
||||
safety. Any thread is allowed to connect new slots to this
|
||||
signal and also disconnect them at any time. The firing of a signal
|
||||
is not thread safe - only one thread is allowed to fire at a time.
|
||||
*/
|
||||
|
||||
// Set this to whatever you want to handle exceptions thrown by a slot
|
||||
extern boost::function<void(std::exception&)> slot_exception_handler;
|
||||
|
||||
class connection
|
||||
{
|
||||
public:
|
||||
class islot
|
||||
: boost::noncopyable
|
||||
#if 0
|
||||
// No need to test maxStrong, since all strong references are internal
|
||||
// However, weak references are external via connection object. It is
|
||||
// expected that the total weak references to a slot are much less than 64000
|
||||
// NOTE: unsigned short is slightly slower than int on Win32. However, it
|
||||
// saves 4 bytes.
|
||||
, public rbx::intrusive_ptr_target<islot, unsigned short, 0, 0>
|
||||
#else
|
||||
, public rbx::intrusive_ptr_target<islot>
|
||||
#endif
|
||||
{
|
||||
protected:
|
||||
islot()
|
||||
{}
|
||||
public:
|
||||
virtual ~islot() {}
|
||||
virtual void disconnect() = 0;
|
||||
virtual bool connected() const = 0;
|
||||
};
|
||||
|
||||
inline connection(islot* slot):weak_slot(slot) {}
|
||||
inline connection(const connection& con):weak_slot(con.weak_slot) {}
|
||||
inline connection() {}
|
||||
connection& operator= (const connection& con);
|
||||
|
||||
void disconnect() const;
|
||||
bool connected() const;
|
||||
bool operator== (const connection& other) const;
|
||||
bool operator!= (const connection& other) const;
|
||||
|
||||
void flogPrint()
|
||||
{
|
||||
boost::intrusive_ptr<islot> s(weak_slot.lock());
|
||||
FASTLOG2(FLog::Always, "Connection %p, slot %p", this, s.get());
|
||||
}
|
||||
|
||||
private:
|
||||
// to make connections copyable, the data for a connection are shared
|
||||
rbx::intrusive_weak_ptr<islot> weak_slot; // must be weak to avoid memory leaks
|
||||
};
|
||||
|
||||
class scoped_connection : boost::noncopyable
|
||||
{
|
||||
// Has-a instead of Is-a. We do this because demoting scoped_connection reference
|
||||
// to a connection will alter the meaning of the = operator, leading to strange
|
||||
// bugs.
|
||||
connection con;
|
||||
|
||||
public:
|
||||
inline scoped_connection() {}
|
||||
inline scoped_connection(const connection& con):con(con) {}
|
||||
|
||||
inline scoped_connection& operator= (const connection& con)
|
||||
{
|
||||
if (this->con != con)
|
||||
{
|
||||
disconnect();
|
||||
this->con = con;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
inline ~scoped_connection() { disconnect(); }
|
||||
|
||||
// Accessor to underlying connection, if you really want it
|
||||
inline connection& get() { return con; }
|
||||
|
||||
// implementation of connection contract
|
||||
inline void disconnect() const {
|
||||
con.disconnect(); }
|
||||
inline bool connected() const { return con.connected(); }
|
||||
inline bool operator== (const connection& other) const { return con == other; }
|
||||
inline bool operator!= (const connection& other) const { return con != other; }
|
||||
};
|
||||
|
||||
class scoped_connection_logged : boost::noncopyable
|
||||
{
|
||||
// Has-a instead of Is-a. We do this because demoting scoped_connection reference
|
||||
// to a connection will alter the meaning of the = operator, leading to strange
|
||||
// bugs.
|
||||
connection con;
|
||||
bool logged;
|
||||
|
||||
public:
|
||||
inline scoped_connection_logged() : logged(false) {}
|
||||
inline scoped_connection_logged(bool logged) : logged(logged) {}
|
||||
|
||||
// Helper for using FastLog groups as trigger
|
||||
inline scoped_connection_logged(FLog::Channel channelId) : logged(channelId != 0) {}
|
||||
|
||||
inline scoped_connection_logged(const connection& con):con(con) {}
|
||||
|
||||
inline scoped_connection_logged& operator= (const connection& con)
|
||||
{
|
||||
if (this->con != con)
|
||||
{
|
||||
disconnect();
|
||||
this->con = con;
|
||||
if(logged)
|
||||
{
|
||||
FASTLOG2(FLog::Always, "Scoped connection %p assign: %p", this, &con);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
inline ~scoped_connection_logged() {
|
||||
if(logged)
|
||||
FASTLOG1(FLog::Always, "Scoped connection %p destructor", this);
|
||||
disconnect(); }
|
||||
|
||||
// Accessor to underlying connection, if you really want it
|
||||
inline connection& get() { return con; }
|
||||
|
||||
inline void setLogged(bool logged) { this->logged = logged; }
|
||||
|
||||
// implementation of connection contract
|
||||
inline void disconnect() const {
|
||||
if(logged)
|
||||
FASTLOG2(FLog::Always, "Scoped connection %p disconnect, previously connected: %u", this, con.connected());
|
||||
con.disconnect();
|
||||
}
|
||||
inline bool connected() const { return con.connected(); }
|
||||
inline bool operator== (const connection& other) const { return con == other; }
|
||||
inline bool operator!= (const connection& other) const { return con != other; }
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal : boost::noncopyable
|
||||
{
|
||||
protected:
|
||||
friend class slot;
|
||||
class slot :
|
||||
public connection::islot
|
||||
, public icallable<boost::function_traits<Signature>::arity, Signature>
|
||||
{
|
||||
public:
|
||||
boost::intrusive_ptr<slot> next;
|
||||
signal *sig;
|
||||
|
||||
inline slot(signal *sig)
|
||||
:sig(sig)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool connected() const
|
||||
{
|
||||
return sig != NULL;
|
||||
}
|
||||
public:
|
||||
SAFE_HEAP_STATIC(boost::mutex, mutex);
|
||||
|
||||
virtual void disconnect()
|
||||
{
|
||||
if (!sig)
|
||||
return;
|
||||
|
||||
boost::mutex::scoped_lock lock(mutex());
|
||||
|
||||
if (sig)
|
||||
{
|
||||
signal *s = sig;
|
||||
sig = NULL;
|
||||
s->remove(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class Delegate>
|
||||
class callable_slot : public callable<slot, Delegate, boost::function_traits<Signature>::arity, Signature>
|
||||
{
|
||||
public:
|
||||
inline callable_slot(const Delegate& deleg, signal *sig)
|
||||
:callable<slot, Delegate, boost::function_traits<Signature>::arity, Signature>(deleg, sig)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
// The slots are stored in a linked list, with "head" as a dummy slot used to anchor the list.
|
||||
boost::intrusive_ptr<slot> head;
|
||||
// TODO: Avoid contention by using one mutex per signal? Or an array of signals?
|
||||
// TODO: Is boost::mutex the best choice? Does it start up with a spin?
|
||||
// However, at least we have a separate mutex for each signature
|
||||
|
||||
// SAFE_HEAP_STATIC is used instead of SAFE_STATIC to work around global variables using signals (like GameSettings)
|
||||
// If you use SAFE_STATIC, mutex can be destroyed before other global variables using signals,
|
||||
// so signal destructor will fail on mutex access
|
||||
SAFE_HEAP_STATIC(boost::mutex, mutex)
|
||||
|
||||
void remove(slot* item)
|
||||
{
|
||||
// Invariant: the value of item->next does not change
|
||||
|
||||
RBXASSERT(!boost::intrusive_ptr_expired(item));
|
||||
|
||||
if (item == head)
|
||||
head = item->next;
|
||||
else
|
||||
{
|
||||
// Find "prev". This is O(n)
|
||||
slot* prev = head.get();
|
||||
// TODO: Can we just assert that prev!=NULL?
|
||||
while (prev && prev->next != item)
|
||||
prev = prev->next.get();
|
||||
|
||||
// In theory prev should never be NULL, because for it to be NULL
|
||||
// the slot would be destroyed, in which case remove() can't be
|
||||
// called. Let's play it safe and null-check anyway.
|
||||
RBX_SIGNALS_ASSERT(!prev || prev->next.get() == item);
|
||||
|
||||
if (prev)
|
||||
prev->next = item->next;
|
||||
}
|
||||
|
||||
RBXASSERT(!boost::intrusive_ptr_expired(item));
|
||||
// item is now deletable
|
||||
}
|
||||
|
||||
void insert(slot* item)
|
||||
{
|
||||
RBX_SIGNALS_ASSERT(item);
|
||||
|
||||
boost::mutex::scoped_lock lock(mutex());
|
||||
|
||||
if (!head)
|
||||
{
|
||||
head = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->next = head;
|
||||
head = item;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
inline signal()
|
||||
{
|
||||
mutex();
|
||||
}
|
||||
|
||||
inline ~signal()
|
||||
{
|
||||
disconnectAll();
|
||||
}
|
||||
|
||||
void disconnectAll()
|
||||
{
|
||||
while (head)
|
||||
{
|
||||
boost::intrusive_ptr<slot> node;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mutex());
|
||||
|
||||
// See DE131 for a justification of this "chunk" code
|
||||
const int chunkSize = 10;
|
||||
int count = chunkSize;
|
||||
for (node = head; node; node = node->next)
|
||||
{
|
||||
node->sig = NULL;
|
||||
if (count-- == 0)
|
||||
{
|
||||
// After 10 iterations we need to break out and collect
|
||||
// the slots. Otherwise we risk a stack crash
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the next line will cause nodes to be destroyed.
|
||||
// Notice that we want them to be destroyed
|
||||
// outside of the mutex lock because
|
||||
// destruction could have side-effects.
|
||||
head = node;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool empty() const
|
||||
{
|
||||
return !head;
|
||||
}
|
||||
|
||||
template<class Delegate>
|
||||
connection connect(const Delegate& function)
|
||||
{
|
||||
slot* item = new callable_slot<Delegate>(function, this);
|
||||
insert(item);
|
||||
return connection(item);
|
||||
}
|
||||
|
||||
// For debugging:
|
||||
static size_t sizeof_slot()
|
||||
{
|
||||
return sizeof(slot);
|
||||
}
|
||||
|
||||
void flogPrint()
|
||||
{
|
||||
FASTLOG1(FLog::Always, "Signal - %p", this);
|
||||
boost::intrusive_ptr<typename rbx::signals::signal<Signature>::slot> item;
|
||||
while (this->next(item))
|
||||
FASTLOG1(FLog::Always, "Signal slot = %p", item.get());
|
||||
}
|
||||
|
||||
protected:
|
||||
void on_error(std::exception& e)
|
||||
{
|
||||
if (slot_exception_handler)
|
||||
slot_exception_handler(e);
|
||||
}
|
||||
bool next(boost::intrusive_ptr<slot>& item)
|
||||
{
|
||||
if (!item)
|
||||
{
|
||||
// Start iterating; this is safe to read from
|
||||
// If another thread is in the process of prepending, we can get old head or new head
|
||||
// If we do get the new head it should already have the new next so this is race-free
|
||||
item = this->head;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Advance the iterator; we keep item alive so next is safe to read from
|
||||
// If another thread is in the process of removing the 'item->next' connection we may see
|
||||
// the next pointer either pointing to the element that's being removed or to the next one
|
||||
// Since replacing next is atomic and we can't observe any other values than these two this is
|
||||
// also race-free.
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (!item)
|
||||
{
|
||||
// Done iterating
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Iteration succeeded
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
template<int arity, typename Signature>
|
||||
class signal_with_args;
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<0, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem(typename signal<Signature>::slot* item)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call();
|
||||
}
|
||||
public:
|
||||
void operator()()
|
||||
{
|
||||
if (this->empty()) return;
|
||||
|
||||
typedef typename rbx::signals::signal<Signature>::slot slot;
|
||||
boost::intrusive_ptr<slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get());
|
||||
}
|
||||
catch (RBX::base_exception& e)
|
||||
{
|
||||
rbx::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<1, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem( typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1);
|
||||
}
|
||||
public:
|
||||
void operator()(typename boost::function_traits<Signature>::arg1_type arg1)
|
||||
{
|
||||
if (this->empty()) return;
|
||||
|
||||
boost::intrusive_ptr<typename rbx::signals::signal<Signature>::slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1);
|
||||
}
|
||||
catch (RBX::base_exception& e)
|
||||
{
|
||||
rbx::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<2, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem( typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1, arg2);
|
||||
}
|
||||
public:
|
||||
void operator ()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2)
|
||||
{
|
||||
if (this->empty()) return;
|
||||
|
||||
boost::intrusive_ptr<typename rbx::signals::signal<Signature>::slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1, arg2);
|
||||
}
|
||||
catch (RBX::base_exception& e)
|
||||
{
|
||||
rbx::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<3, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem( typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1, arg2, arg3);
|
||||
}
|
||||
public:
|
||||
void operator ()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3)
|
||||
{
|
||||
if (this->empty()) return;
|
||||
|
||||
boost::intrusive_ptr<typename rbx::signals::signal<Signature>::slot> item;
|
||||
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1, arg2, arg3);
|
||||
}
|
||||
catch (RBX::base_exception& e)
|
||||
{
|
||||
rbx::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<4, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem( typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1, arg2, arg3, arg4);
|
||||
}
|
||||
public:
|
||||
void operator ()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4)
|
||||
{
|
||||
if (this->empty()) return;
|
||||
|
||||
boost::intrusive_ptr<typename rbx::signals::signal<Signature>::slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1, arg2, arg3, arg4);
|
||||
}
|
||||
catch (RBX::base_exception& e)
|
||||
{
|
||||
rbx::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<5, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem( typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4, typename boost::function_traits<Signature>::arg5_type arg5)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
public:
|
||||
void operator ()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4, typename boost::function_traits<Signature>::arg5_type arg5)
|
||||
{
|
||||
if (this->empty()) return;
|
||||
|
||||
boost::intrusive_ptr<typename rbx::signals::signal<Signature>::slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
catch (RBX::base_exception& e)
|
||||
{
|
||||
rbx::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<6, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem( typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4, typename boost::function_traits<Signature>::arg5_type arg5, typename boost::function_traits<Signature>::arg6_type arg6)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
public:
|
||||
void operator ()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4, typename boost::function_traits<Signature>::arg5_type arg5, typename boost::function_traits<Signature>::arg6_type arg6)
|
||||
{
|
||||
if (this->empty()) return;
|
||||
|
||||
boost::intrusive_ptr<typename rbx::signals::signal<Signature>::slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
catch (RBX::base_exception& e)
|
||||
{
|
||||
rbx::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Signature>
|
||||
class signal_with_args<7, Signature> : public signal<Signature>
|
||||
{
|
||||
static inline void fireItem( typename signal<Signature>::slot* item, typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4, typename boost::function_traits<Signature>::arg5_type arg5, typename boost::function_traits<Signature>::arg6_type arg6, typename boost::function_traits<Signature>::arg7_type arg7)
|
||||
{
|
||||
if (item->sig) // Make sure this guy hasn't been disconnected
|
||||
item->call(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
}
|
||||
public:
|
||||
void operator ()(typename boost::function_traits<Signature>::arg1_type arg1, typename boost::function_traits<Signature>::arg2_type arg2, typename boost::function_traits<Signature>::arg3_type arg3, typename boost::function_traits<Signature>::arg4_type arg4, typename boost::function_traits<Signature>::arg5_type arg5, typename boost::function_traits<Signature>::arg6_type arg6, typename boost::function_traits<Signature>::arg7_type arg7)
|
||||
{
|
||||
if (this->empty()) return;
|
||||
|
||||
boost::intrusive_ptr<typename rbx::signals::signal<Signature>::slot> item;
|
||||
begin:
|
||||
try
|
||||
{
|
||||
while (this->next(item))
|
||||
fireItem(item.get(), arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
}
|
||||
catch (RBX::base_exception& e)
|
||||
{
|
||||
rbx::signals::signal<Signature>::on_error(e);
|
||||
// Note: We put this handler on the outside of the for loop
|
||||
// as an optimization. This is why we have a goto statement.
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Signature>
|
||||
class signal : public signals::signal_with_args<boost::function_traits<Signature>::arity, Signature>
|
||||
{
|
||||
};
|
||||
|
||||
//Note that remote signal is *not* virtualized against signal. This only works because the Event class is templatized, and not using polymorphism.
|
||||
// If Event becomes polymorphics, THIS CODE WILL FAIL
|
||||
template<typename Signature>
|
||||
class remote_signal : public signal<Signature>
|
||||
{
|
||||
private:
|
||||
typedef signal<Signature> Super;
|
||||
|
||||
public:
|
||||
signal<void()> connectionSignal;
|
||||
|
||||
remote_signal()
|
||||
{}
|
||||
|
||||
template<typename F>
|
||||
signals::connection connect(const F& function)
|
||||
{
|
||||
connectionSignal();
|
||||
return Super::connect(function);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#ifdef RBX_SIGNALS_DEBUGGING
|
||||
#pragma optimize( "", on )
|
||||
#endif
|
||||
@@ -0,0 +1,499 @@
|
||||
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include "rbx/boost.hpp"
|
||||
#include "rbx/Thread.hpp"
|
||||
#include "rbx/rbxtime.h"
|
||||
#include "rbx/atomic.h"
|
||||
#include "boost/shared_ptr.hpp"
|
||||
#include "boost/noncopyable.hpp"
|
||||
#include "RbxFormat.h"
|
||||
#include "FastLog.h"
|
||||
|
||||
#include "RbxPlatform.h"
|
||||
|
||||
#include "boost/thread/mutex.hpp"
|
||||
|
||||
using boost::shared_ptr;
|
||||
|
||||
LOGGROUP(MutexLifetime);
|
||||
|
||||
namespace RBX
|
||||
{
|
||||
// A lightweight mutex that uses CRITICAL_SECTION under Windows.
|
||||
// This mutex is non-recursive.
|
||||
class mutex
|
||||
{
|
||||
#ifdef _WIN32
|
||||
CRITICAL_SECTION cs;
|
||||
public:
|
||||
mutex()
|
||||
{
|
||||
::InitializeCriticalSection( &cs );
|
||||
FASTLOG1(FLog::MutexLifetime, "RBX::mutext init m = 0x%x", this);
|
||||
}
|
||||
~mutex()
|
||||
{
|
||||
::DeleteCriticalSection(&cs);
|
||||
FASTLOG1(FLog::MutexLifetime, "RBX::mutext destroy m = 0x%x", this);
|
||||
}
|
||||
class scoped_lock : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
scoped_lock(mutex& m):m(m)
|
||||
{
|
||||
::EnterCriticalSection(&m.cs);
|
||||
}
|
||||
~scoped_lock()
|
||||
{
|
||||
::LeaveCriticalSection(&m.cs);
|
||||
}
|
||||
private:
|
||||
mutex& m;
|
||||
};
|
||||
#else
|
||||
pthread_mutex_t sl;
|
||||
|
||||
public:
|
||||
mutex()
|
||||
{
|
||||
if ( pthread_mutex_init(&sl,NULL) != 0 )
|
||||
throw std::runtime_error("failed in mutex to initialize pthread_mutex_init.");
|
||||
}
|
||||
~mutex()
|
||||
{
|
||||
if(pthread_mutex_destroy(&sl) != 0){
|
||||
//printf("Error at pthread_spin_destroy()");
|
||||
}
|
||||
}
|
||||
class scoped_lock : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
scoped_lock(mutex& m0):m(m0){
|
||||
int rc = pthread_mutex_lock(&m.sl);
|
||||
if(rc != 0)
|
||||
{
|
||||
//fprintf(stderr,"Test FAILED: child failed to get spin lock,error code:%d\n" , rc);
|
||||
}
|
||||
//::EnterCriticalSection(&m.sl);
|
||||
}
|
||||
~scoped_lock(){
|
||||
if(pthread_mutex_unlock(&m.sl)!=0)
|
||||
{
|
||||
//fprintf(stderr,"child: Error at pthread_spin_unlock()\n");
|
||||
}
|
||||
//::LeaveCriticalSection(&m.sl);
|
||||
}
|
||||
private:
|
||||
mutex& m;
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
// calls RBXCRASH() on contention.
|
||||
class concurrency_catcher : boost::noncopyable
|
||||
{
|
||||
rbx::atomic<int> value;
|
||||
static const long unlocked = 0;
|
||||
static const long locked = 1;
|
||||
public:
|
||||
concurrency_catcher():value(unlocked) {}
|
||||
class scoped_lock : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
scoped_lock(concurrency_catcher& m);
|
||||
~scoped_lock();
|
||||
private:
|
||||
concurrency_catcher& m;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// calls RBXCRASH() on contention.
|
||||
struct reentrant_concurrency_catcher : boost::noncopyable
|
||||
{
|
||||
rbx::atomic<int> value;
|
||||
volatile unsigned long threadId;
|
||||
static const long unlocked = 0;
|
||||
static const long locked = 1;
|
||||
static const unsigned long noThreadId;
|
||||
public:
|
||||
reentrant_concurrency_catcher():value(unlocked),threadId(noThreadId) {}
|
||||
class scoped_lock : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
scoped_lock(reentrant_concurrency_catcher& m);
|
||||
~scoped_lock();
|
||||
private:
|
||||
bool isChild;
|
||||
reentrant_concurrency_catcher& m;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class readwrite_concurrency_catcher : boost::noncopyable
|
||||
{
|
||||
friend class scoped_write_request;
|
||||
friend class scoped_read_request;
|
||||
rbx::atomic<int> write_requested;
|
||||
rbx::atomic<int> read_requested;
|
||||
static const long unlocked = 0;
|
||||
static const long locked = 1;
|
||||
public:
|
||||
|
||||
readwrite_concurrency_catcher() : write_requested(unlocked), read_requested(0) {};
|
||||
class scoped_write_request
|
||||
{
|
||||
readwrite_concurrency_catcher& m;
|
||||
public:
|
||||
// Place this code around tasks that write to a DataModel
|
||||
scoped_write_request(readwrite_concurrency_catcher& mt);
|
||||
~scoped_write_request();
|
||||
};
|
||||
class scoped_read_request
|
||||
{
|
||||
readwrite_concurrency_catcher& m;
|
||||
public:
|
||||
// Place this code around tasks that write to a DataModel
|
||||
scoped_read_request(readwrite_concurrency_catcher& m);
|
||||
~scoped_read_request();
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace rbx
|
||||
{
|
||||
|
||||
class spin_mutex
|
||||
{
|
||||
rbx::atomic<int> sl;
|
||||
|
||||
public:
|
||||
spin_mutex()
|
||||
{
|
||||
// init
|
||||
}
|
||||
|
||||
~spin_mutex()
|
||||
{
|
||||
// destroy
|
||||
}
|
||||
|
||||
bool try_lock(){
|
||||
return sl.compare_and_swap(1,0) == 0;
|
||||
}
|
||||
|
||||
void lock(){
|
||||
while(sl.compare_and_swap(1,0)!=0){}
|
||||
}
|
||||
void unlock(){
|
||||
sl.compare_and_swap(0,1);
|
||||
}
|
||||
|
||||
class scoped_lock : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
scoped_lock(spin_mutex& m0) : m(m0)
|
||||
{
|
||||
for(;;){
|
||||
if(m.try_lock()) break;
|
||||
}
|
||||
}
|
||||
|
||||
~scoped_lock()
|
||||
{
|
||||
m.unlock();
|
||||
}
|
||||
private:
|
||||
spin_mutex& m;
|
||||
};
|
||||
};
|
||||
|
||||
// Use this queue when you want fast performance, low
|
||||
// resource usage, and the queue is not very busy.
|
||||
// For very busy queues, use tbb::concurrent_queue (correction, we dont have tbb anymore).
|
||||
template<typename T>
|
||||
class safe_queue : boost::noncopyable
|
||||
{
|
||||
protected:
|
||||
std::queue<T> queue;
|
||||
// TODO: spin_mutex is possibly a bad choice for expensive T types
|
||||
typedef spin_mutex mutex;
|
||||
mutex m;
|
||||
public:
|
||||
void clear()
|
||||
{
|
||||
mutex::scoped_lock lock(m);
|
||||
|
||||
while (!queue.empty())
|
||||
queue.pop();
|
||||
}
|
||||
void push(const T& value)
|
||||
{
|
||||
mutex::scoped_lock lock(m);
|
||||
queue.push(value);
|
||||
}
|
||||
bool pop_if_present(T& value)
|
||||
{
|
||||
mutex::scoped_lock lock(m);
|
||||
if (!queue.empty())
|
||||
{
|
||||
value = queue.front();
|
||||
queue.pop();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pop_if_present()
|
||||
{
|
||||
mutex::scoped_lock lock(m);
|
||||
if (!queue.empty())
|
||||
{
|
||||
queue.pop();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// WARNING: Peeking has side effects, if T has copy constructors and destructors
|
||||
bool peek_if_present(T& value)
|
||||
{
|
||||
mutex::scoped_lock lock(m);
|
||||
if (!queue.empty())
|
||||
{
|
||||
value = queue.front();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lock and spin-free calls:
|
||||
inline size_t size() const { return queue.size(); }
|
||||
inline bool empty() const { return queue.empty(); }
|
||||
};
|
||||
|
||||
namespace implementation
|
||||
{
|
||||
template<typename T>
|
||||
struct timestamped_safe_queue_item
|
||||
{
|
||||
T value;
|
||||
RBX::Time timestamp;
|
||||
timestamped_safe_queue_item() {}
|
||||
timestamped_safe_queue_item(const T& value)
|
||||
:timestamp(RBX::Time::now<RBX::Time::Fast>())
|
||||
,value(value)
|
||||
{}
|
||||
};
|
||||
}
|
||||
template<typename T>
|
||||
class timestamped_safe_queue : protected safe_queue< implementation::timestamped_safe_queue_item<T> >
|
||||
{
|
||||
typedef safe_queue< implementation::timestamped_safe_queue_item<T> > Super;
|
||||
double headTimestamp;
|
||||
#ifndef _WIN32
|
||||
// GCC won't inherit the mutex type defined in Super. Therefore we redeclare it here. Yuck!
|
||||
typedef spin_mutex mutex;
|
||||
#endif
|
||||
public:
|
||||
void clear()
|
||||
{
|
||||
headTimestamp = 0.f;
|
||||
Super::clear();
|
||||
}
|
||||
|
||||
void push(const T& value)
|
||||
{
|
||||
implementation::timestamped_safe_queue_item<T> item(value);
|
||||
Super::push(item);
|
||||
headTimestamp = item.timestamp.timestampSeconds();
|
||||
}
|
||||
|
||||
bool pop_if_present(T& value)
|
||||
{
|
||||
mutex::scoped_lock lock(this->m);
|
||||
if (!this->queue.empty())
|
||||
{
|
||||
value = this->queue.front().value;
|
||||
this->queue.pop();
|
||||
if (!this->queue.empty())
|
||||
{
|
||||
headTimestamp = this->queue.front().timestamp.timestampSeconds();
|
||||
}
|
||||
else
|
||||
{
|
||||
headTimestamp = 0.f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// pops the head item if it has been waiting at least waitTime
|
||||
bool pop_if_waited(RBX::Time::Interval waitTime, T& value)
|
||||
{
|
||||
mutex::scoped_lock lock(this->m);
|
||||
if (this->queue.empty())
|
||||
return false;
|
||||
if (RBX::Time::now<RBX::Time::Fast>() < this->queue.front().timestamp + waitTime)
|
||||
return false;
|
||||
value = this->queue.front().value;
|
||||
this->queue.pop();
|
||||
if (!this->queue.empty())
|
||||
{
|
||||
headTimestamp = this->queue.front().timestamp.timestampSeconds();
|
||||
}
|
||||
else
|
||||
{
|
||||
headTimestamp = 0.f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the time that the head item has been waiting or zero.
|
||||
double head_waittime_sec(const RBX::Time& timeNow) const
|
||||
{
|
||||
if (headTimestamp > 0.f)
|
||||
{
|
||||
return timeNow.timestampSeconds() - headTimestamp;
|
||||
}
|
||||
else
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
inline size_t size() const { return this->queue.size(); }
|
||||
inline bool empty() const { return this->queue.empty(); }
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
class safe_heap : boost::noncopyable
|
||||
{
|
||||
std::vector<T> vector;
|
||||
// TODO: spin_mutex is possibly a bad choice for expensive T types
|
||||
typedef spin_mutex mutex;
|
||||
mutex m;
|
||||
public:
|
||||
void clear()
|
||||
{
|
||||
mutex::scoped_lock lock(m);
|
||||
vector.clear();
|
||||
}
|
||||
void push_heap(const T& value)
|
||||
{
|
||||
mutex::scoped_lock lock(m);
|
||||
vector.push_back(value);
|
||||
std::push_heap(vector.begin(), vector.end());
|
||||
}
|
||||
bool pop_heap_if_present(T& value)
|
||||
{
|
||||
mutex::scoped_lock lock(m);
|
||||
if (!vector.empty())
|
||||
{
|
||||
std::pop_heap(vector.begin(), vector.end());
|
||||
value = vector.back();
|
||||
vector.pop_back();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pop_heap_if_present()
|
||||
{
|
||||
mutex::scoped_lock lock(m);
|
||||
if (!vector.empty())
|
||||
{
|
||||
std::pop_heap(vector.begin(), vector.end());
|
||||
vector.pop_back();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Lock and spin-free calls:
|
||||
inline size_t size() const { return vector.size(); }
|
||||
inline bool empty() const { return vector.empty(); }
|
||||
};
|
||||
|
||||
|
||||
#define SAFE_STATIC(TYPE,NAME) \
|
||||
static TYPE* safe_static_do_get_##NAME() { static TYPE value; return &value; }\
|
||||
static void safe_static_init_##NAME() { safe_static_do_get_##NAME(); }\
|
||||
static TYPE& NAME()\
|
||||
{\
|
||||
static boost::once_flag once_init_##NAME = BOOST_ONCE_INIT;\
|
||||
boost::call_once(safe_static_init_##NAME, once_init_##NAME);\
|
||||
return *safe_static_do_get_##NAME();\
|
||||
}
|
||||
|
||||
#define SAFE_HEAP_STATIC(TYPE,NAME) \
|
||||
static TYPE* safe_static_do_get_##NAME() { static TYPE* value = new TYPE; return value; }\
|
||||
static void safe_static_init_##NAME() { safe_static_do_get_##NAME(); }\
|
||||
static TYPE& NAME()\
|
||||
{\
|
||||
static boost::once_flag once_init_##NAME = BOOST_ONCE_INIT;\
|
||||
boost::call_once(safe_static_init_##NAME, once_init_##NAME);\
|
||||
return *safe_static_do_get_##NAME();\
|
||||
}
|
||||
|
||||
|
||||
// A wrapper around thread_specific_ptr that lets you
|
||||
// have a thread-specific reference to an object
|
||||
template<typename T>
|
||||
class thread_specific_reference
|
||||
{
|
||||
typedef T* TPTR;
|
||||
boost::thread_specific_ptr<TPTR> ptr;
|
||||
public:
|
||||
T* get()
|
||||
{
|
||||
TPTR* p = ptr.get();
|
||||
if (p)
|
||||
return *p;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
void reset(T* value)
|
||||
{
|
||||
TPTR* p = new TPTR(value);
|
||||
ptr.reset(p);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// A wrapper around thread_specific_ptr that lets you
|
||||
// have a thread-specific shared_ptr to an object
|
||||
template<typename T>
|
||||
class thread_specific_shared_ptr : boost::noncopyable
|
||||
{
|
||||
typedef shared_ptr<T> TPTR;
|
||||
boost::thread_specific_ptr<TPTR> ptr;
|
||||
public:
|
||||
operator shared_ptr<T>() const
|
||||
{
|
||||
TPTR* p = ptr.get();
|
||||
if (p)
|
||||
return *p;
|
||||
else
|
||||
return shared_ptr<T>();
|
||||
}
|
||||
|
||||
void reset(shared_ptr<T> value)
|
||||
{
|
||||
TPTR* p = new TPTR(value);
|
||||
ptr.reset(p);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/pool/object_pool.hpp>
|
||||
|
||||
namespace rbx
|
||||
{
|
||||
/// A fast trie, but a little expensive memory-wise
|
||||
/// Only supports ascii strings in the range 32-127
|
||||
/// operator[] is not thread safe
|
||||
template<class V, unsigned int maxDepth>
|
||||
class trie : public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
class depth_exceeded_exception : public std::exception
|
||||
{
|
||||
public:
|
||||
virtual const char* what() const throw()
|
||||
{
|
||||
return "trie depth exceeded";
|
||||
};
|
||||
};
|
||||
class bad_key : public std::exception
|
||||
{
|
||||
public:
|
||||
const char key;
|
||||
bad_key(char key):key(key) {}
|
||||
virtual const char* what() const throw()
|
||||
{
|
||||
return "key out of range";
|
||||
};
|
||||
};
|
||||
|
||||
private:
|
||||
class Node;
|
||||
typedef boost::object_pool<Node> Pool;
|
||||
typedef boost::object_pool<V> ValuePool;
|
||||
class Node : public boost::noncopyable
|
||||
{
|
||||
friend class trie;
|
||||
|
||||
std::string leaf; // used when there are no branches. Saves a lot of memory
|
||||
|
||||
static const size_t array_size = 128 - 32;
|
||||
Node* array[array_size];
|
||||
|
||||
V* value;
|
||||
const int depth;
|
||||
inline void check_key(char key)
|
||||
{
|
||||
if (key < 32)
|
||||
throw bad_key(key);
|
||||
}
|
||||
public:
|
||||
static inline unsigned char to_index(char c)
|
||||
{
|
||||
return (unsigned char)(c - 32);
|
||||
}
|
||||
static inline bool is_legal_char(char c)
|
||||
{
|
||||
return c >= 32;
|
||||
}
|
||||
Node(int depth)
|
||||
:value(0)
|
||||
,depth(depth)
|
||||
{
|
||||
if (depth > maxDepth)
|
||||
throw depth_exceeded_exception();
|
||||
memset(array, 0, sizeof(array));
|
||||
}
|
||||
void destroy(Pool& pool, ValuePool& valuePool)
|
||||
{
|
||||
for (size_t i = 0; i<array_size; ++i)
|
||||
if (array[i])
|
||||
array[i]->destroy(pool, valuePool);
|
||||
|
||||
if (value)
|
||||
valuePool.destroy(value);
|
||||
|
||||
pool.destroy(this);
|
||||
}
|
||||
template<class F>
|
||||
void each_value(const F& f) const
|
||||
{
|
||||
for (size_t i = 0; i<array_size; ++i)
|
||||
each_value(f);
|
||||
if (value)
|
||||
f(*value);
|
||||
}
|
||||
|
||||
bool empty_array() const
|
||||
{
|
||||
for (size_t i = 0; i<array_size; ++i)
|
||||
if (array[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void removeLeaf(Pool& pool, ValuePool& valuePool)
|
||||
{
|
||||
// We can't use the leaf shortcut. Need to use the array for branches
|
||||
|
||||
// Construct the path for the existing leaf
|
||||
const size_t index = to_index(leaf[0]);
|
||||
Node* next = array[index] = pool.construct(depth + 1);
|
||||
V& v = next->array_subscript(leaf.c_str() + 1, pool, valuePool);
|
||||
|
||||
// Move the value over to its new home
|
||||
v = *value;
|
||||
valuePool.destroy(value);
|
||||
value = NULL;
|
||||
|
||||
leaf = "";
|
||||
}
|
||||
|
||||
V& array_subscript(const char* key, Pool& pool, ValuePool& valuePool)
|
||||
{
|
||||
if (*key == 0)
|
||||
{
|
||||
if (!leaf.empty())
|
||||
removeLeaf(pool, valuePool);
|
||||
if (!value)
|
||||
value = valuePool.construct();
|
||||
return *value;
|
||||
}
|
||||
|
||||
check_key(*key);
|
||||
|
||||
// If the array is empty, then set the leaf
|
||||
if (empty_array()) {
|
||||
if (leaf.empty() && !value) {
|
||||
// This is the first entry, so we can use the leaf shortcut
|
||||
leaf = key;
|
||||
value = valuePool.construct();
|
||||
return *value;
|
||||
} else if (leaf == key) {
|
||||
return *value;
|
||||
} else if (!leaf.empty()) {
|
||||
removeLeaf(pool, valuePool);
|
||||
}
|
||||
}
|
||||
|
||||
size_t index = to_index(*key);
|
||||
Node* next = array[index];
|
||||
if (!next)
|
||||
array[index] = next = pool.construct(depth + 1);
|
||||
|
||||
return next->array_subscript(key + 1, pool, valuePool);
|
||||
}
|
||||
|
||||
size_t compute_size() const
|
||||
{
|
||||
size_t size = 1;
|
||||
for (size_t i = 0; i<array_size; ++i)
|
||||
if (array[i])
|
||||
size += array[i]->compute_size();
|
||||
return size;
|
||||
}
|
||||
|
||||
};
|
||||
Pool pool;
|
||||
ValuePool valuePool;
|
||||
Node* root;
|
||||
public:
|
||||
trie():pool(),root(pool.construct(1)) {}
|
||||
~trie()
|
||||
{
|
||||
root->destroy(pool, valuePool);
|
||||
}
|
||||
|
||||
static inline bool equal(const char* s, const char* k)
|
||||
{
|
||||
// For some reason this is MUCH faster than strcmp
|
||||
for (; *s == *k; ++s, ++k)
|
||||
if (*s == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool lookup(const char* key, V& value) const
|
||||
{
|
||||
const Node* node = root;
|
||||
while (true)
|
||||
{
|
||||
// If this node has a value, then see if it matches the key
|
||||
if (node->value)
|
||||
{
|
||||
// Look for a match between the key and our leaf.
|
||||
// Note that key and leaf might both be "", which would be a match
|
||||
if (equal(node->leaf.c_str(), key))
|
||||
{
|
||||
value = *node->value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Node::is_legal_char(*key)) // *key could be 0, meaning the end of the key
|
||||
return false;
|
||||
|
||||
// Advance to the next node
|
||||
node = node->array[Node::to_index(*key)];
|
||||
if (!node)
|
||||
return false;
|
||||
++key;
|
||||
}
|
||||
}
|
||||
V& operator[](const char* key)
|
||||
{
|
||||
return root->array_subscript(key, pool, valuePool);
|
||||
}
|
||||
size_t compute_size()
|
||||
{
|
||||
return root->compute_size();
|
||||
}
|
||||
size_t compute_memory_usage()
|
||||
{
|
||||
return root->compute_size() * sizeof(Node);
|
||||
}
|
||||
template<class F>
|
||||
void each_value(const F& f) const
|
||||
{
|
||||
root->each_value(f);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user