Click here to Skip to main content
15,895,142 members
Articles / Programming Languages / C++

Cross thread calls in native C++

Rate me:
Please Sign up or sign in to vote.
4.85/5 (38 votes)
19 Jul 2007Apache20 min read 118K   1.2K   96  
An article which discusses the need for synchronization in multi-threaded applications, and features a generic framework for making calls across threads: ThreadSynch.
/************************************************************************
** Copyright 2007 Einar Otto Stangvik
** 
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
** 
**    http://www.apache.org/licenses/LICENSE-2.0
** 
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include <boost/test/test_tools.hpp>
#include <boost/test/results_reporter.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/output_test_stream.hpp>
#include <boost/test/unit_test_log.hpp>
#include <boost/test/framework.hpp>
#include <boost/test/detail/unit_test_parameters.hpp>
#include <boost/test/utils/nullstream.hpp>
#include <boost/scoped_ptr.hpp>

#include "../ThreadSynch/ThreadSynch.h"
#include "../ThreadSynch/APCPickupPolicy.h"

#ifdef _DEBUG
#pragma comment(lib, "libboost_test_exec_monitor-vc80-mt-gd.lib")
#pragma comment(lib, "libboost_unit_test_framework-vc80-mt-gd.lib")
#else
#pragma comment(lib, "libboost_test_exec_monitor-vc80-mt.lib")
#pragma comment(lib, "libboost_unit_test_framework-vc80-mt.lib")
#endif

using namespace boost::unit_test;

typedef boost::onullstream onullstream_type;

/************************************************************************
** Test helper structs and functions
*/

HANDLE g_hTestThread;
HANDLE g_hCloseEvent;
DWORD g_dwThreadId;

DWORD WINAPI testThread(PVOID)
{
	while(WaitForSingleObjectEx(g_hCloseEvent, INFINITE, TRUE) != WAIT_OBJECT_0)
	{
		Sleep(1);
	}
	return 0;
}

/************************************************************************
** Test 1: Parameters
*/

int crossThreadIntValue(int input)
{
	return input * 2;
}

int crossThreadIntPtr(int* input)
{
	return *input * 2;
}

int crossThreadIntRef(int& input)
{
	return input * 2;
}

void testParameters()
{
	ThreadSynch::CallScheduler<ThreadSynch::APCPickupPolicy>* scheduler = ThreadSynch::CallScheduler<ThreadSynch::APCPickupPolicy>::getInstance();

	int input1 = 0x42;
	boost::function<int()> callback1 = boost::bind(crossThreadIntValue, input1);
	BOOST_CHECK(callback1() == scheduler->syncCall(g_dwThreadId, callback1, INFINITE));

	boost::scoped_ptr<int> input2(new int);
	*input2 = 0x42;
	boost::function<int()> callback2 = boost::bind(crossThreadIntPtr, input2.get());
	BOOST_CHECK(callback2() == scheduler->syncCall(g_dwThreadId, callback2, INFINITE));

	int input3 = 0x42;
	boost::function<int()> callback3 = boost::bind(crossThreadIntRef, boost::ref(input3));
	BOOST_CHECK(callback3() == scheduler->syncCall(g_dwThreadId, callback3, INFINITE));
}

/************************************************************************
** Test 2: Pointer to class return value
*/

class SharedClass
{
public:
	static int refcount;
	SharedClass() { ++refcount; }
	~SharedClass() { --refcount; }
};
int SharedClass::refcount = 0;

boost::shared_ptr<SharedClass> crossThreadPtr()
{
	boost::shared_ptr<SharedClass> ptr(new SharedClass);
	BOOST_CHECK(ptr->refcount == 1);
	return ptr;
}

void testReturnValues()
{
	ThreadSynch::CallScheduler<ThreadSynch::APCPickupPolicy>* scheduler = ThreadSynch::CallScheduler<ThreadSynch::APCPickupPolicy>::getInstance();

	boost::function<boost::shared_ptr<SharedClass>()> callback1 = crossThreadPtr;
	boost::shared_ptr<SharedClass> ptr = scheduler->syncCall(g_dwThreadId, callback1, INFINITE);
	BOOST_CHECK(ptr->refcount == 1);
}

/************************************************************************
** Test 3: Exceptions
*/

class TestException
{
public:
	int magicNumber;
	TestException(int mn = 0) : magicNumber(mn) {}
};

bool isRealException(const TestException& ex)
{
	return ex.magicNumber == 42;
}

void crossThreadException()
{
	throw TestException(42);
}

void testExceptions()
{
	ThreadSynch::CallScheduler<ThreadSynch::APCPickupPolicy>* scheduler = ThreadSynch::CallScheduler<ThreadSynch::APCPickupPolicy>::getInstance();

	BOOST_CHECK_EXCEPTION(scheduler->syncCall<ExceptionTypes<TestException>>(g_dwThreadId, crossThreadException, INFINITE), TestException, isRealException);
}

/************************************************************************
** Test Setup
*/

int test_main( int argc, char* argv[] ) 
{
	g_hCloseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	g_hTestThread = CreateThread(NULL, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(testThread), NULL, 0, &g_dwThreadId);
	BOOST_REQUIRE(g_hTestThread != NULL);

	test_suite* ts = BOOST_TEST_SUITE("ThreadSynch Unit Tests");

	ts->add(BOOST_TEST_CASE(&testParameters));
	ts->add(BOOST_TEST_CASE(&testReturnValues));
	ts->add(BOOST_TEST_CASE(&testExceptions ));

	framework::run(ts);

	SetEvent(g_hCloseEvent);
	WaitForSingleObject(g_hTestThread, INFINITE);
	CloseHandle(g_hTestThread);
	CloseHandle(g_hCloseEvent);

	// Delete the singleton, for the sake of leak detection.
	// Some globals will however be detected either way, so don't be alarmed by the notification for the time beaing.
	// The key point is: The leak doesn't grow.
	delete ThreadSynch::CallScheduler<ThreadSynch::APCPickupPolicy>::getInstance();

	return 0;
}

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 Apache License, Version 2.0


Written By
Software Developer
Norway Norway
My name is Einar Otto Stangvik, and I'm a programmer based in Oslo, Norway. I mainly develop applications and software architectures targetting C++ on the Windows platform, but I have also got experience doing the same on Unix and Linux. The last few years I've used C# a lot, but native C++ is still my main focus.


As of July 2008, I'm a Microsoft MVP for Visual C++.


Follow me on Twitter: @einaros
My code blog: einaros.blogspot.com
My site: www.indev.no

Comments and Discussions