Click here to Skip to main content
12,626,297 members (37,234 online)
Click here to Skip to main content

Tagged as

Stats

27.7K views
345 downloads
39 bookmarked
Posted

Testing simple concurrent containers

, 25 Oct 2009 CPOL
This article illustrates simple approaches and test results when creating containers with concurrent flavor and running on a multi-core PC.
/*
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

You may also be interested in...

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