Chris@16: #ifndef BOOST_THREAD_PTHREAD_SHARED_MUTEX_HPP Chris@16: #define BOOST_THREAD_PTHREAD_SHARED_MUTEX_HPP Chris@16: Chris@16: // (C) Copyright 2006-8 Anthony Williams Chris@16: // (C) Copyright 2012 Vicente J. Botet Escriba Chris@16: // Chris@16: // Distributed under the Boost Software License, Version 1.0. (See Chris@16: // accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: #include Chris@16: #endif Chris@16: #ifdef BOOST_THREAD_USES_CHRONO Chris@16: #include Chris@16: #include Chris@16: #endif Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@16: namespace boost Chris@16: { Chris@16: class shared_mutex Chris@16: { Chris@16: private: Chris@16: class state_data Chris@16: { Chris@16: public: Chris@16: state_data () : Chris@16: shared_count(0), Chris@16: exclusive(false), Chris@16: upgrade(false), Chris@16: exclusive_waiting_blocked(false) Chris@16: {} Chris@16: Chris@16: void assert_free() const Chris@16: { Chris@16: BOOST_ASSERT( ! exclusive ); Chris@16: BOOST_ASSERT( ! upgrade ); Chris@16: BOOST_ASSERT( shared_count==0 ); Chris@16: } Chris@16: Chris@16: void assert_locked() const Chris@16: { Chris@16: BOOST_ASSERT( exclusive ); Chris@16: BOOST_ASSERT( shared_count==0 ); Chris@16: BOOST_ASSERT( ! upgrade ); Chris@16: } Chris@16: Chris@16: void assert_lock_shared () const Chris@16: { Chris@16: BOOST_ASSERT( ! exclusive ); Chris@16: BOOST_ASSERT( shared_count>0 ); Chris@16: //BOOST_ASSERT( (! upgrade) || (shared_count>1)); Chris@16: // if upgraded there are at least 2 threads sharing the mutex, Chris@16: // except when unlock_upgrade_and_lock has decreased the number of readers but has not taken yet exclusive ownership. Chris@16: } Chris@16: Chris@16: void assert_lock_upgraded () const Chris@16: { Chris@16: BOOST_ASSERT( ! exclusive ); Chris@16: BOOST_ASSERT( upgrade ); Chris@16: BOOST_ASSERT( shared_count>0 ); Chris@16: } Chris@16: Chris@16: void assert_lock_not_upgraded () const Chris@16: { Chris@16: BOOST_ASSERT( ! upgrade ); Chris@16: } Chris@16: Chris@16: bool can_lock () const Chris@16: { Chris@16: return ! (shared_count || exclusive); Chris@16: } Chris@16: Chris@16: void exclusive_blocked (bool blocked) Chris@16: { Chris@16: exclusive_waiting_blocked = blocked; Chris@16: } Chris@16: Chris@16: void lock () Chris@16: { Chris@16: exclusive = true; Chris@16: } Chris@16: Chris@16: void unlock () Chris@16: { Chris@16: exclusive = false; Chris@16: exclusive_waiting_blocked = false; Chris@16: } Chris@16: Chris@16: bool can_lock_shared () const Chris@16: { Chris@16: return ! (exclusive || exclusive_waiting_blocked); Chris@16: } Chris@16: Chris@16: bool more_shared () const Chris@16: { Chris@16: return shared_count > 0 ; Chris@16: } Chris@16: unsigned get_shared_count () const Chris@16: { Chris@16: return shared_count ; Chris@16: } Chris@16: unsigned lock_shared () Chris@16: { Chris@16: return ++shared_count; Chris@16: } Chris@16: Chris@16: Chris@16: void unlock_shared () Chris@16: { Chris@16: --shared_count; Chris@16: } Chris@16: Chris@16: bool unlock_shared_downgrades() Chris@16: { Chris@16: if (upgrade) { Chris@16: upgrade=false; Chris@16: exclusive=true; Chris@16: return true; Chris@16: } else { Chris@16: exclusive_waiting_blocked=false; Chris@16: return false; Chris@16: } Chris@16: } Chris@16: Chris@16: void lock_upgrade () Chris@16: { Chris@16: ++shared_count; Chris@16: upgrade=true; Chris@16: } Chris@16: bool can_lock_upgrade () const Chris@16: { Chris@16: return ! (exclusive || exclusive_waiting_blocked || upgrade); Chris@16: } Chris@16: Chris@16: void unlock_upgrade () Chris@16: { Chris@16: upgrade=false; Chris@16: --shared_count; Chris@16: } Chris@16: Chris@16: //private: Chris@16: unsigned shared_count; Chris@16: bool exclusive; Chris@16: bool upgrade; Chris@16: bool exclusive_waiting_blocked; Chris@16: }; Chris@16: Chris@16: Chris@16: Chris@16: state_data state; Chris@16: boost::mutex state_change; Chris@16: boost::condition_variable shared_cond; Chris@16: boost::condition_variable exclusive_cond; Chris@16: boost::condition_variable upgrade_cond; Chris@16: Chris@16: void release_waiters() Chris@16: { Chris@16: exclusive_cond.notify_one(); Chris@16: shared_cond.notify_all(); Chris@16: } Chris@16: Chris@16: public: Chris@16: Chris@16: BOOST_THREAD_NO_COPYABLE(shared_mutex) Chris@16: Chris@16: shared_mutex() Chris@16: { Chris@16: } Chris@16: Chris@16: ~shared_mutex() Chris@16: { Chris@16: } Chris@16: Chris@16: void lock_shared() Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: while(!state.can_lock_shared()) Chris@16: { Chris@16: shared_cond.wait(lk); Chris@16: } Chris@16: state.lock_shared(); Chris@16: } Chris@16: Chris@16: bool try_lock_shared() Chris@16: { Chris@16: boost::unique_lock lk(state_change); Chris@16: Chris@16: if(!state.can_lock_shared()) Chris@16: { Chris@16: return false; Chris@16: } Chris@16: state.lock_shared(); Chris@16: return true; Chris@16: } Chris@16: Chris@16: #if defined BOOST_THREAD_USES_DATETIME Chris@16: bool timed_lock_shared(system_time const& timeout) Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: Chris@16: while(!state.can_lock_shared()) Chris@16: { Chris@16: if(!shared_cond.timed_wait(lk,timeout)) Chris@16: { Chris@16: return false; Chris@16: } Chris@16: } Chris@16: state.lock_shared(); Chris@16: return true; Chris@16: } Chris@16: Chris@16: template Chris@16: bool timed_lock_shared(TimeDuration const & relative_time) Chris@16: { Chris@16: return timed_lock_shared(get_system_time()+relative_time); Chris@16: } Chris@16: #endif Chris@16: #ifdef BOOST_THREAD_USES_CHRONO Chris@16: template Chris@16: bool try_lock_shared_for(const chrono::duration& rel_time) Chris@16: { Chris@16: return try_lock_shared_until(chrono::steady_clock::now() + rel_time); Chris@16: } Chris@16: template Chris@16: bool try_lock_shared_until(const chrono::time_point& abs_time) Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: Chris@16: while(!state.can_lock_shared()) Chris@16: //while(state.exclusive || state.exclusive_waiting_blocked) Chris@16: { Chris@16: if(cv_status::timeout==shared_cond.wait_until(lk,abs_time)) Chris@16: { Chris@16: return false; Chris@16: } Chris@16: } Chris@16: state.lock_shared(); Chris@16: return true; Chris@16: } Chris@16: #endif Chris@16: void unlock_shared() Chris@16: { Chris@16: boost::unique_lock lk(state_change); Chris@16: state.assert_lock_shared(); Chris@16: state.unlock_shared(); Chris@16: if (! state.more_shared()) Chris@16: { Chris@16: if (state.upgrade) Chris@16: { Chris@16: // As there is a thread doing a unlock_upgrade_and_lock that is waiting for ! state.more_shared() Chris@16: // avoid other threads to lock, lock_upgrade or lock_shared, so only this thread is notified. Chris@16: state.upgrade=false; Chris@16: state.exclusive=true; Chris@16: lk.unlock(); Chris@16: upgrade_cond.notify_one(); Chris@16: } Chris@16: else Chris@16: { Chris@16: state.exclusive_waiting_blocked=false; Chris@16: lk.unlock(); Chris@16: } Chris@16: release_waiters(); Chris@16: } Chris@16: } Chris@16: Chris@16: void lock() Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: Chris@16: while (state.shared_count || state.exclusive) Chris@16: { Chris@16: state.exclusive_waiting_blocked=true; Chris@16: exclusive_cond.wait(lk); Chris@16: } Chris@16: state.exclusive=true; Chris@16: } Chris@16: Chris@16: #if defined BOOST_THREAD_USES_DATETIME Chris@16: bool timed_lock(system_time const& timeout) Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: Chris@16: while(state.shared_count || state.exclusive) Chris@16: { Chris@16: state.exclusive_waiting_blocked=true; Chris@16: if(!exclusive_cond.timed_wait(lk,timeout)) Chris@16: { Chris@16: if(state.shared_count || state.exclusive) Chris@16: { Chris@16: state.exclusive_waiting_blocked=false; Chris@16: release_waiters(); Chris@16: return false; Chris@16: } Chris@16: break; Chris@16: } Chris@16: } Chris@16: state.exclusive=true; Chris@16: return true; Chris@16: } Chris@16: Chris@16: template Chris@16: bool timed_lock(TimeDuration const & relative_time) Chris@16: { Chris@16: return timed_lock(get_system_time()+relative_time); Chris@16: } Chris@16: #endif Chris@16: #ifdef BOOST_THREAD_USES_CHRONO Chris@16: template Chris@16: bool try_lock_for(const chrono::duration& rel_time) Chris@16: { Chris@16: return try_lock_until(chrono::steady_clock::now() + rel_time); Chris@16: } Chris@16: template Chris@16: bool try_lock_until(const chrono::time_point& abs_time) Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: Chris@16: while(state.shared_count || state.exclusive) Chris@16: { Chris@16: state.exclusive_waiting_blocked=true; Chris@16: if(cv_status::timeout == exclusive_cond.wait_until(lk,abs_time)) Chris@16: { Chris@16: if(state.shared_count || state.exclusive) Chris@16: { Chris@16: state.exclusive_waiting_blocked=false; Chris@16: release_waiters(); Chris@16: return false; Chris@16: } Chris@16: break; Chris@16: } Chris@16: } Chris@16: state.exclusive=true; Chris@16: return true; Chris@16: } Chris@16: #endif Chris@16: Chris@16: bool try_lock() Chris@16: { Chris@16: boost::unique_lock lk(state_change); Chris@16: Chris@16: if(state.shared_count || state.exclusive) Chris@16: { Chris@16: return false; Chris@16: } Chris@16: else Chris@16: { Chris@16: state.exclusive=true; Chris@16: return true; Chris@16: } Chris@16: Chris@16: } Chris@16: Chris@16: void unlock() Chris@16: { Chris@16: boost::unique_lock lk(state_change); Chris@16: state.assert_locked(); Chris@16: state.exclusive=false; Chris@16: state.exclusive_waiting_blocked=false; Chris@16: state.assert_free(); Chris@16: release_waiters(); Chris@16: } Chris@16: Chris@16: void lock_upgrade() Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) Chris@16: { Chris@16: shared_cond.wait(lk); Chris@16: } Chris@16: state.lock_shared(); Chris@16: state.upgrade=true; Chris@16: } Chris@16: Chris@16: #if defined BOOST_THREAD_USES_DATETIME Chris@16: bool timed_lock_upgrade(system_time const& timeout) Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) Chris@16: { Chris@16: if(!shared_cond.timed_wait(lk,timeout)) Chris@16: { Chris@16: if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) Chris@16: { Chris@16: return false; Chris@16: } Chris@16: break; Chris@16: } Chris@16: } Chris@16: state.lock_shared(); Chris@16: state.upgrade=true; Chris@16: return true; Chris@16: } Chris@16: Chris@16: template Chris@16: bool timed_lock_upgrade(TimeDuration const & relative_time) Chris@16: { Chris@16: return timed_lock_upgrade(get_system_time()+relative_time); Chris@16: } Chris@16: #endif Chris@16: #ifdef BOOST_THREAD_USES_CHRONO Chris@16: template Chris@16: bool try_lock_upgrade_for(const chrono::duration& rel_time) Chris@16: { Chris@16: return try_lock_upgrade_until(chrono::steady_clock::now() + rel_time); Chris@16: } Chris@16: template Chris@16: bool try_lock_upgrade_until(const chrono::time_point& abs_time) Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) Chris@16: { Chris@16: if(cv_status::timeout == shared_cond.wait_until(lk,abs_time)) Chris@16: { Chris@16: if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) Chris@16: { Chris@16: return false; Chris@16: } Chris@16: break; Chris@16: } Chris@16: } Chris@16: state.lock_shared(); Chris@16: state.upgrade=true; Chris@16: return true; Chris@16: } Chris@16: #endif Chris@16: bool try_lock_upgrade() Chris@16: { Chris@16: boost::unique_lock lk(state_change); Chris@16: if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) Chris@16: { Chris@16: return false; Chris@16: } Chris@16: else Chris@16: { Chris@16: state.lock_shared(); Chris@16: state.upgrade=true; Chris@16: state.assert_lock_upgraded(); Chris@16: return true; Chris@16: } Chris@16: } Chris@16: Chris@16: void unlock_upgrade() Chris@16: { Chris@16: boost::unique_lock lk(state_change); Chris@16: //state.upgrade=false; Chris@16: state.unlock_upgrade(); Chris@16: if(! state.more_shared() ) Chris@16: { Chris@16: state.exclusive_waiting_blocked=false; Chris@16: release_waiters(); Chris@16: } else { Chris@16: shared_cond.notify_all(); Chris@16: } Chris@16: } Chris@16: Chris@16: // Upgrade <-> Exclusive Chris@16: void unlock_upgrade_and_lock() Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: state.assert_lock_upgraded(); Chris@16: state.unlock_shared(); Chris@16: while (state.more_shared()) Chris@16: { Chris@16: upgrade_cond.wait(lk); Chris@16: } Chris@16: state.upgrade=false; Chris@16: state.exclusive=true; Chris@16: state.assert_locked(); Chris@16: } Chris@16: Chris@16: void unlock_and_lock_upgrade() Chris@16: { Chris@16: boost::unique_lock lk(state_change); Chris@16: state.assert_locked(); Chris@16: state.exclusive=false; Chris@16: state.upgrade=true; Chris@16: state.lock_shared(); Chris@16: state.exclusive_waiting_blocked=false; Chris@16: state.assert_lock_upgraded(); Chris@16: release_waiters(); Chris@16: } Chris@16: Chris@16: bool try_unlock_upgrade_and_lock() Chris@16: { Chris@16: boost::unique_lock lk(state_change); Chris@16: state.assert_lock_upgraded(); Chris@16: if( !state.exclusive Chris@16: && !state.exclusive_waiting_blocked Chris@16: && state.upgrade Chris@16: && state.shared_count==1) Chris@16: { Chris@16: state.shared_count=0; Chris@16: state.exclusive=true; Chris@16: state.upgrade=false; Chris@16: state.assert_locked(); Chris@16: return true; Chris@16: } Chris@16: return false; Chris@16: } Chris@16: #ifdef BOOST_THREAD_USES_CHRONO Chris@16: template Chris@16: bool Chris@16: try_unlock_upgrade_and_lock_for( Chris@16: const chrono::duration& rel_time) Chris@16: { Chris@16: return try_unlock_upgrade_and_lock_until( Chris@16: chrono::steady_clock::now() + rel_time); Chris@16: } Chris@16: template Chris@16: bool Chris@16: try_unlock_upgrade_and_lock_until( Chris@16: const chrono::time_point& abs_time) Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: state.assert_lock_upgraded(); Chris@16: if (state.shared_count != 1) Chris@16: { Chris@16: for (;;) Chris@16: { Chris@16: cv_status status = shared_cond.wait_until(lk,abs_time); Chris@16: if (state.shared_count == 1) Chris@16: break; Chris@16: if(status == cv_status::timeout) Chris@16: return false; Chris@16: } Chris@16: } Chris@16: state.upgrade=false; Chris@16: state.exclusive=true; Chris@16: state.exclusive_waiting_blocked=false; Chris@16: state.shared_count=0; Chris@16: return true; Chris@16: } Chris@16: #endif Chris@16: Chris@16: // Shared <-> Exclusive Chris@16: void unlock_and_lock_shared() Chris@16: { Chris@16: boost::unique_lock lk(state_change); Chris@16: state.assert_locked(); Chris@16: state.exclusive=false; Chris@16: state.lock_shared(); Chris@16: state.exclusive_waiting_blocked=false; Chris@16: release_waiters(); Chris@16: } Chris@16: Chris@16: #ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS Chris@16: bool try_unlock_shared_and_lock() Chris@16: { Chris@16: boost::unique_lock lk(state_change); Chris@16: state.assert_lock_shared(); Chris@16: if( !state.exclusive Chris@16: && !state.exclusive_waiting_blocked Chris@16: && !state.upgrade Chris@16: && state.shared_count==1) Chris@16: { Chris@16: state.shared_count=0; Chris@16: state.exclusive=true; Chris@16: return true; Chris@16: } Chris@16: return false; Chris@16: } Chris@16: #ifdef BOOST_THREAD_USES_CHRONO Chris@16: template Chris@16: bool Chris@16: try_unlock_shared_and_lock_for( Chris@16: const chrono::duration& rel_time) Chris@16: { Chris@16: return try_unlock_shared_and_lock_until( Chris@16: chrono::steady_clock::now() + rel_time); Chris@16: } Chris@16: template Chris@16: bool Chris@16: try_unlock_shared_and_lock_until( Chris@16: const chrono::time_point& abs_time) Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: state.assert_lock_shared(); Chris@16: if (state.shared_count != 1) Chris@16: { Chris@16: for (;;) Chris@16: { Chris@16: cv_status status = shared_cond.wait_until(lk,abs_time); Chris@16: if (state.shared_count == 1) Chris@16: break; Chris@16: if(status == cv_status::timeout) Chris@16: return false; Chris@16: } Chris@16: } Chris@16: state.upgrade=false; Chris@16: state.exclusive=true; Chris@16: state.exclusive_waiting_blocked=false; Chris@16: state.shared_count=0; Chris@16: return true; Chris@16: } Chris@16: #endif Chris@16: #endif Chris@16: Chris@16: // Shared <-> Upgrade Chris@16: void unlock_upgrade_and_lock_shared() Chris@16: { Chris@16: boost::unique_lock lk(state_change); Chris@16: state.assert_lock_upgraded(); Chris@16: state.upgrade=false; Chris@16: state.exclusive_waiting_blocked=false; Chris@16: release_waiters(); Chris@16: } Chris@16: Chris@16: #ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS Chris@16: bool try_unlock_shared_and_lock_upgrade() Chris@16: { Chris@16: boost::unique_lock lk(state_change); Chris@16: state.assert_lock_shared(); Chris@16: if( !state.exclusive Chris@16: && !state.exclusive_waiting_blocked Chris@16: && !state.upgrade Chris@16: ) Chris@16: { Chris@16: state.upgrade=true; Chris@16: return true; Chris@16: } Chris@16: return false; Chris@16: } Chris@16: #ifdef BOOST_THREAD_USES_CHRONO Chris@16: template Chris@16: bool Chris@16: try_unlock_shared_and_lock_upgrade_for( Chris@16: const chrono::duration& rel_time) Chris@16: { Chris@16: return try_unlock_shared_and_lock_upgrade_until( Chris@16: chrono::steady_clock::now() + rel_time); Chris@16: } Chris@16: template Chris@16: bool Chris@16: try_unlock_shared_and_lock_upgrade_until( Chris@16: const chrono::time_point& abs_time) Chris@16: { Chris@16: #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS Chris@16: boost::this_thread::disable_interruption do_not_disturb; Chris@16: #endif Chris@16: boost::unique_lock lk(state_change); Chris@16: state.assert_lock_shared(); Chris@16: if( state.exclusive Chris@16: || state.exclusive_waiting_blocked Chris@16: || state.upgrade Chris@16: ) Chris@16: { Chris@16: for (;;) Chris@16: { Chris@16: cv_status status = exclusive_cond.wait_until(lk,abs_time); Chris@16: if( ! state.exclusive Chris@16: && ! state.exclusive_waiting_blocked Chris@16: && ! state.upgrade Chris@16: ) Chris@16: break; Chris@16: if(status == cv_status::timeout) Chris@16: return false; Chris@16: } Chris@16: } Chris@16: state.upgrade=true; Chris@16: return true; Chris@16: } Chris@16: #endif Chris@16: #endif Chris@16: }; Chris@16: Chris@16: typedef shared_mutex upgrade_mutex; Chris@16: } Chris@16: Chris@16: #include Chris@16: Chris@16: #endif