This commit is contained in:
watrabi
2025-09-18 17:55:52 -04:00
commit 977f1ff4b8
15030 changed files with 17324420 additions and 0 deletions
+54
View File
@@ -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
+766
View File
@@ -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;
}
}
+187
View File
@@ -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;
// }
+217
View File
@@ -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)];
};
}
+48
View File
@@ -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);
};
}
+33
View File
@@ -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
+25
View File
@@ -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);
};
}
+204
View File
@@ -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);
}
+57
View File
@@ -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
+350
View File
@@ -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();
}
};
}
+66
View File
@@ -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;
}
};
}
+249
View File
@@ -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;
};
}}
+70
View File
@@ -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
+20
View File
@@ -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);
}
+276
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
#if defined(__APPLE__)
#ifdef nil
#undef nil
#endif
#endif
+70
View File
@@ -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
+99
View File
@@ -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
+74
View File
@@ -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)
+69
View File
@@ -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);
};
}
+40
View File
@@ -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
+843
View File
@@ -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;
}
}
};
}
+38
View File
@@ -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();
}
}
+208
View File
@@ -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();
};
}
+271
View File
@@ -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;
}
};
}
+99
View File
@@ -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();
}
};
}
}
+54
View File
@@ -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);
};
}
+105
View File
@@ -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
+212
View File
@@ -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); }
};
}
+258
View File
@@ -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);
}
}
}
+131
View File
@@ -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_;
}
};
}
+171
View File
@@ -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 );
}
}
+89
View File
@@ -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
+186
View File
@@ -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) {};
};
}
+686
View File
@@ -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
+499
View File
@@ -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);
}
};
}
+227
View File
@@ -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);
}
};
}