Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Testing simple concurrent containers

, 25 Oct 2009
This article illustrates simple approaches and test results when creating containers with concurrent flavor and running on a multi-core PC.
container_locks.zip
Container_locks
src
Testing_simple_concurrent_containers.pdf
VC9
container_locks.vcproj.val-PC.val.user
Debug
tbb_debug.dll
Debug64
tbb_debug.dll
Release
tbb.dll
Release64
tbb.dll
/*
Copyright (c) 2009 Valery Grebnev.
CPOL (Code Project Open License) Licensed http://www.codeproject.com/info/cpol10.aspx
*/

#pragma once

#include "interlocked.h"


class CACHE_ALIGN Spin_lock
{
public:
	void acquireLockShared()
	{
		while (0x0 != acquire_interlocked_exchange(&m_rw_status, 1))
		{
			PAUSE
		}
	}

	void acquireLockExclusive()
	{
		while (0x0 != acquire_interlocked_exchange(&m_rw_status, 1))
		{
			PAUSE
		}
	}

	void releaseLockShared()
	{
		acquire_interlocked_exchange(&m_rw_status, 0x0);
	}

	void releaseLockExclusive()
	{
		acquire_interlocked_exchange(&m_rw_status, 0x0);
	}

	Spin_lock(volatile LONG& rw_status) : m_rw_status(rw_status)
	{
	}
	
	~Spin_lock()
	{
	}

private:

	CACHE_PADDING1
	volatile LONG& m_rw_status;
	Spin_lock & operator=( const Spin_lock & ) {}
};


class CACHE_ALIGN Spin_rwlock
{
public:
	void acquireLockShared()
	{
		LONG i;
		LONG j;

		j = acquire_read(m_rw_status);
		do {
				i = j & 0xFFFF;
				j = acquire_interlocked_compare_exchange(&m_rw_status,
											i + 1,
											i);
				PAUSE
		} while (i != j);
	}

	void acquireLockExclusive()
	{
		while( acquire_interlocked_compare_exchange(&m_rw_status,0x1+0xFFFF,0x0) != 0x0)
		{
			PAUSE
		}
	}

	void releaseLockShared()
	{
		interlocked_decrement_release(&m_rw_status);
	}

	void releaseLockExclusive()
	{
		interlocked_Xor_release(&m_rw_status, 0x1+0xFFFF);
	}

	Spin_rwlock(volatile LONG& rw_status) : m_rw_status(rw_status)
	{
	}
	
	~Spin_rwlock()
	{
	}

private:

	CACHE_PADDING1
	volatile LONG& m_rw_status;
	Spin_rwlock & operator=( const Spin_rwlock & ) {}
};


class CACHE_ALIGN SHRW
{
public:
	void acquireLockShared()
	{
		if (HELPER_UNDEFINED_STATE != acquire_read(m_helper_flag))
		{
			wait_conditionally(m_helper_flag, HELPER_UNDEFINED_STATE, m_helper_slowdown_event);
		}
		else 
		{
		}

		while( !try_interlocked_increment_reader())
		{
			wait_conditionally(m_rw_status, 0xFFFF, m_writer_release_event);
		}
	}

	void acquireLockExclusive()
	{
		::EnterCriticalSection(&m_cswriters);
		int spin_count = 0;
		while( acquire_interlocked_compare_exchange(&m_rw_status,0x1+0xFFFF,0x0) != 0x0)
		{
			if (++spin_count == MAX_WRITER_SPIN)
			{
				::ResetEvent(m_helper_slowdown_event);
				write_release(m_helper_flag, HELPER_SIGNAL_SLOWDOWN);

				while( acquire_interlocked_compare_exchange(&m_rw_status,0x1+0xFFFF,0x0) != 0x0)
				{
					wait_conditionally(m_rw_status, 0x0, m_reader_release_event);
				}

				::ResetEvent(m_writer_release_event);

				write_release(m_helper_flag, HELPER_UNDEFINED_STATE);
				::SetEvent(m_helper_slowdown_event);
				return;
			}
			else
			{
				PAUSE
			}
		}

		::ResetEvent(m_writer_release_event);
	}

	void releaseLockShared()
	{
		if(interlocked_decrement_release(&m_rw_status) == 0 )
		{
			if (acquire_read(m_helper_flag) == HELPER_SIGNAL_SLOWDOWN)
			{
				::SetEvent(m_reader_release_event);
			}
			else
			{
			}
		}
		else
		{
		}
	}

	void releaseLockExclusive()
	{
		interlocked_Xor_release(&m_rw_status, 0x1+0xFFFF);
		::SetEvent(m_writer_release_event);
		::LeaveCriticalSection(&m_cswriters);
	}

	SHRW()
	{
		::InitializeCriticalSection(&m_cswriters);

		m_writer_release_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
		m_rw_status = 0; 

		m_helper_flag = HELPER_UNDEFINED_STATE;
		m_helper_slowdown_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
		m_reader_release_event = ::CreateEvent(NULL, FALSE, FALSE, NULL);
	}
	
	~SHRW()
	{
		::DeleteCriticalSection(&m_cswriters);
		::CloseHandle(m_writer_release_event);

		::CloseHandle(m_helper_slowdown_event);
		::CloseHandle(m_reader_release_event);
	}

private:

	bool try_interlocked_increment_reader(void)
	{
		LONG i;
		LONG j;

		j = acquire_read(m_rw_status);
		int spin_count = 0;

		do {
			if (spin_count == MAX_READER_SPIN)
			{
				return false;
			}
			else
			{
				i = j & 0xFFFF;
				j = acquire_interlocked_compare_exchange(&m_rw_status,
											i + 1,
											i);
				spin_count ++;
			}
			
		} while (i != j);
		return true;
	}
	
	inline void wait_conditionally(volatile LONG& cond_variable, int cond_min, HANDLE wait_event)
	{
		while( acquire_read(cond_variable) > cond_min)
		{
			::WaitForSingleObject(wait_event, INFINITE);
		}
	}

	enum { HELPER_UNDEFINED_STATE = 0, HELPER_SIGNAL_SLOWDOWN = 1 };
	
	// should be set to 1 on a Uniprocessor PC
	enum { MAX_WRITER_SPIN = 1000, MAX_READER_SPIN = 1000 };

	CRITICAL_SECTION m_cswriters;
	HANDLE m_writer_release_event;
	HANDLE m_reader_release_event;
	HANDLE m_helper_slowdown_event;

	CACHE_PADDING(1) CACHE_ALIGN

	volatile LONG m_rw_status;

	CACHE_PADDING(2) CACHE_ALIGN

	volatile LONG m_helper_flag;

};


class CACHE_ALIGN SHRW2
{
public:
	void acquireLockShared()
	{
		while( !try_interlocked_increment_reader())
		{
			wait_conditionally(m_rw_status, 0xFFFF, m_writer_release_event);
		}
	}

	void acquireLockExclusive()
	{
		::EnterCriticalSection(&m_cswriters);
		int spin_count = 0;
		while( acquire_interlocked_compare_exchange(&m_rw_status,0x1+0xFFFF,0x0) != 0x0)
		{
			if (++spin_count == MAX_WRITER_SPIN)
			{
				::ResetEvent(m_writer_release_event);

				interlocked_Or_release(&m_rw_status,0x1+0xFFFF);

				wait_conditionally(m_rw_status, 0x1+0xFFFF, m_reader_release_event);

				return;
			}
			else
			{
				PAUSE
			}
		}
		::ResetEvent(m_writer_release_event);
	}

	void releaseLockShared()
	{
		if(interlocked_decrement_release(&m_rw_status) == 0x1+0xFFFF )
		{
			::SetEvent(m_reader_release_event);
		}
		else
		{
		}
	}

	void releaseLockExclusive()
	{
		interlocked_Xor_release(&m_rw_status, 0x1+0xFFFF);
		::SetEvent(m_writer_release_event);
		::LeaveCriticalSection(&m_cswriters);
	}

	SHRW2()
	{
		::InitializeCriticalSection(&m_cswriters);

		m_writer_release_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
		m_reader_release_event = ::CreateEvent(NULL, FALSE, FALSE, NULL);
		m_rw_status = 0; 
	}
	
	~SHRW2()
	{
		::DeleteCriticalSection(&m_cswriters);
		::CloseHandle(m_writer_release_event);
		::CloseHandle(m_reader_release_event);
	}

private:

	bool try_interlocked_increment_reader(void)
	{
		LONG i;
		LONG j;

		j = acquire_read(m_rw_status);
		int spin_count = 0;

		do {
			if (spin_count == MAX_READER_SPIN)
			{
				return false;
			}
			else
			{
				i = j & 0xFFFF;
				j = acquire_interlocked_compare_exchange(&m_rw_status,
											i + 1,
											i);
				spin_count ++;
			}
			
		} while (i != j);
		return true;
	}
	
	inline void wait_conditionally(volatile LONG& cond_variable, int cond_min, HANDLE wait_event)
	{
		while( acquire_read(cond_variable) > cond_min)
		{
			::WaitForSingleObject(wait_event, INFINITE);
		}
	}

	// should be set to 1 on a Uniprocessor PC
	enum { MAX_WRITER_SPIN = 1000 ,MAX_READER_SPIN = 1000 };

	CACHE_PADDING(1)

	volatile LONG m_rw_status;

	CACHE_PADDING(2) CACHE_ALIGN

	CRITICAL_SECTION m_cswriters;
	HANDLE m_writer_release_event;
	HANDLE m_reader_release_event;

};

#pragma warning(default:4324)

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Valery Grebnev
Software Developer
Canada Canada
No Biography provided

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 25 Oct 2009
Article Copyright 2009 by Valery Grebnev
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid