blob: 1423d7f1a2adb72fcb7ca74b9099080e020db155 [file] [log] [blame]
// Copyright (C) 2001-2003
// William E. Kempf
// Copyright (C) 2007 Anthony Williams
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/thread/detail/config.hpp>
#include <boost/thread/tss.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <boost/test/unit_test.hpp>
#include <libs/thread/test/util.inl>
#include <iostream>
#if defined(BOOST_HAS_WINTHREADS)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
boost::mutex check_mutex;
boost::mutex tss_mutex;
int tss_instances = 0;
int tss_total = 0;
struct tss_value_t
{
tss_value_t()
{
boost::mutex::scoped_lock lock(tss_mutex);
++tss_instances;
++tss_total;
value = 0;
}
~tss_value_t()
{
boost::mutex::scoped_lock lock(tss_mutex);
--tss_instances;
}
int value;
};
boost::thread_specific_ptr<tss_value_t> tss_value;
void test_tss_thread()
{
tss_value.reset(new tss_value_t());
for (int i=0; i<1000; ++i)
{
int& n = tss_value->value;
// Don't call BOOST_CHECK_EQUAL directly, as it doesn't appear to
// be thread safe. Must evaluate further.
if (n != i)
{
boost::mutex::scoped_lock lock(check_mutex);
BOOST_CHECK_EQUAL(n, i);
}
++n;
}
}
#if defined(BOOST_THREAD_PLATFORM_WIN32)
typedef HANDLE native_thread_t;
DWORD WINAPI test_tss_thread_native(LPVOID /*lpParameter*/)
{
test_tss_thread();
return 0;
}
native_thread_t create_native_thread(void)
{
native_thread_t const res=CreateThread(
0, //security attributes (0 = not inheritable)
0, //stack size (0 = default)
&test_tss_thread_native, //function to execute
0, //parameter to pass to function
0, //creation flags (0 = run immediately)
0 //thread id (0 = thread id not returned)
);
BOOST_CHECK(res!=0);
return res;
}
void join_native_thread(native_thread_t thread)
{
DWORD res = WaitForSingleObject(thread, INFINITE);
BOOST_CHECK(res == WAIT_OBJECT_0);
res = CloseHandle(thread);
BOOST_CHECK(SUCCEEDED(res));
}
#elif defined(BOOST_THREAD_PLATFORM_PTHREAD)
typedef pthread_t native_thread_t;
extern "C"
{
void* test_tss_thread_native(void* lpParameter)
{
test_tss_thread();
return 0;
}
}
native_thread_t create_native_thread()
{
native_thread_t thread_handle;
int const res = pthread_create(&thread_handle, 0, &test_tss_thread_native, 0);
BOOST_CHECK(!res);
return thread_handle;
}
void join_native_thread(native_thread_t thread)
{
void* result=0;
int const res=pthread_join(thread,&result);
BOOST_CHECK(!res);
}
#endif
void do_test_tss()
{
tss_instances = 0;
tss_total = 0;
const int NUMTHREADS=5;
boost::thread_group threads;
try
{
for (int i=0; i<NUMTHREADS; ++i)
threads.create_thread(&test_tss_thread);
threads.join_all();
}
catch(...)
{
threads.interrupt_all();
threads.join_all();
throw;
}
std::cout
<< "tss_instances = " << tss_instances
<< "; tss_total = " << tss_total
<< "\n";
std::cout.flush();
BOOST_CHECK_EQUAL(tss_instances, 0);
BOOST_CHECK_EQUAL(tss_total, 5);
tss_instances = 0;
tss_total = 0;
native_thread_t thread1 = create_native_thread();
native_thread_t thread2 = create_native_thread();
native_thread_t thread3 = create_native_thread();
native_thread_t thread4 = create_native_thread();
native_thread_t thread5 = create_native_thread();
join_native_thread(thread5);
join_native_thread(thread4);
join_native_thread(thread3);
join_native_thread(thread2);
join_native_thread(thread1);
std::cout
<< "tss_instances = " << tss_instances
<< "; tss_total = " << tss_total
<< "\n";
std::cout.flush();
// The following is not really an error. TSS cleanup support still is available for boost threads.
// Also this usually will be triggered only when bound to the static version of thread lib.
// 2006-10-02 Roland Schwarz
//BOOST_CHECK_EQUAL(tss_instances, 0);
BOOST_CHECK_MESSAGE(tss_instances == 0, "Support of automatic tss cleanup for native threading API not available");
BOOST_CHECK_EQUAL(tss_total, 5);
}
void test_tss()
{
timed_test(&do_test_tss, 2);
}
bool tss_cleanup_called=false;
struct Dummy
{};
void tss_custom_cleanup(Dummy* d)
{
delete d;
tss_cleanup_called=true;
}
boost::thread_specific_ptr<Dummy> tss_with_cleanup(tss_custom_cleanup);
void tss_thread_with_custom_cleanup()
{
tss_with_cleanup.reset(new Dummy);
}
void do_test_tss_with_custom_cleanup()
{
boost::thread t(tss_thread_with_custom_cleanup);
try
{
t.join();
}
catch(...)
{
t.interrupt();
t.join();
throw;
}
BOOST_CHECK(tss_cleanup_called);
}
void test_tss_with_custom_cleanup()
{
timed_test(&do_test_tss_with_custom_cleanup, 2);
}
Dummy* tss_object=new Dummy;
void tss_thread_with_custom_cleanup_and_release()
{
tss_with_cleanup.reset(tss_object);
tss_with_cleanup.release();
}
void do_test_tss_does_no_cleanup_after_release()
{
tss_cleanup_called=false;
boost::thread t(tss_thread_with_custom_cleanup_and_release);
try
{
t.join();
}
catch(...)
{
t.interrupt();
t.join();
throw;
}
BOOST_CHECK(!tss_cleanup_called);
if(!tss_cleanup_called)
{
delete tss_object;
}
}
struct dummy_class_tracks_deletions
{
static unsigned deletions;
~dummy_class_tracks_deletions()
{
++deletions;
}
};
unsigned dummy_class_tracks_deletions::deletions=0;
boost::thread_specific_ptr<dummy_class_tracks_deletions> tss_with_null_cleanup(NULL);
void tss_thread_with_null_cleanup(dummy_class_tracks_deletions* delete_tracker)
{
tss_with_null_cleanup.reset(delete_tracker);
}
void do_test_tss_does_no_cleanup_with_null_cleanup_function()
{
dummy_class_tracks_deletions* delete_tracker=new dummy_class_tracks_deletions;
boost::thread t(tss_thread_with_null_cleanup,delete_tracker);
try
{
t.join();
}
catch(...)
{
t.interrupt();
t.join();
throw;
}
BOOST_CHECK(!dummy_class_tracks_deletions::deletions);
if(!dummy_class_tracks_deletions::deletions)
{
delete delete_tracker;
}
}
void test_tss_does_no_cleanup_after_release()
{
timed_test(&do_test_tss_does_no_cleanup_after_release, 2);
}
void test_tss_does_no_cleanup_with_null_cleanup_function()
{
timed_test(&do_test_tss_does_no_cleanup_with_null_cleanup_function, 2);
}
void thread_with_local_tss_ptr()
{
{
boost::thread_specific_ptr<Dummy> local_tss(tss_custom_cleanup);
local_tss.reset(new Dummy);
}
BOOST_CHECK(tss_cleanup_called);
tss_cleanup_called=false;
}
void test_tss_does_not_call_cleanup_after_ptr_destroyed()
{
boost::thread t(thread_with_local_tss_ptr);
t.join();
BOOST_CHECK(!tss_cleanup_called);
}
void test_tss_cleanup_not_called_for_null_pointer()
{
boost::thread_specific_ptr<Dummy> local_tss(tss_custom_cleanup);
local_tss.reset(new Dummy);
tss_cleanup_called=false;
local_tss.reset(0);
BOOST_CHECK(tss_cleanup_called);
tss_cleanup_called=false;
local_tss.reset(new Dummy);
BOOST_CHECK(!tss_cleanup_called);
}
boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[])
{
boost::unit_test_framework::test_suite* test =
BOOST_TEST_SUITE("Boost.Threads: tss test suite");
test->add(BOOST_TEST_CASE(test_tss));
test->add(BOOST_TEST_CASE(test_tss_with_custom_cleanup));
test->add(BOOST_TEST_CASE(test_tss_does_no_cleanup_after_release));
test->add(BOOST_TEST_CASE(test_tss_does_no_cleanup_with_null_cleanup_function));
test->add(BOOST_TEST_CASE(test_tss_does_not_call_cleanup_after_ptr_destroyed));
test->add(BOOST_TEST_CASE(test_tss_cleanup_not_called_for_null_pointer));
return test;
}