thread/test/test_tss.cpp

509 lines
12 KiB
C++

// 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)
#define BOOST_THREAD_VERSION 2
#define BOOST_THREAD_PROVIDES_INTERRUPTIONS
#define BOOST_TEST_MODULE Boost.Threads: tss test suite
#include <boost/thread/detail/config.hpp>
#include <boost/predef/platform.h>
#include <boost/thread/tss.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <boost/test/unit_test.hpp>
#include "./util.inl"
#include <iostream>
#if defined(BOOST_THREAD_PLATFORM_WIN32)
#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::unique_lock<boost::mutex> lock(tss_mutex);
++tss_instances;
++tss_total;
value = 0;
}
~tss_value_t()
{
boost::unique_lock<boost::mutex> 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::unique_lock<boost::mutex> lock(check_mutex);
BOOST_CHECK_EQUAL(n, i);
}
++n;
}
}
#if defined(BOOST_THREAD_PLATFORM_WIN32)
#if BOOST_PLAT_WINDOWS_RUNTIME
typedef std::shared_ptr<std::thread> native_thread_t;
BOOST_AUTO_TEST_CASE(test_tss_thread_native)
{
test_tss_thread();
}
native_thread_t create_native_thread()
{
return std::make_shared<std::thread>(test_tss_thread_native);
}
void join_native_thread(native_thread_t thread)
{
thread->join();
}
#else
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));
}
#endif
#elif defined(BOOST_THREAD_PLATFORM_PTHREAD)
typedef pthread_t native_thread_t;
extern "C"
{
void* test_tss_thread_native(void* )
{
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);
}
BOOST_AUTO_TEST_CASE(test_tss)
{
timed_test(&do_test_tss, 2);
}
bool tss_void_cleanup_called=false;
void tss_void_custom_cleanup(void* d)
{
std::cout << d << std::endl;
delete reinterpret_cast<tss_value_t*>(d);
tss_void_cleanup_called=true;
}
boost::thread_specific_ptr<void> tss_void(tss_void_custom_cleanup);
void test_tss_void_thread()
{
tss_void.reset(new tss_value_t());
for (int i=0; i<10; ++i)
{
int& n = static_cast<tss_value_t*>(tss_value.get())->value;
*tss_value;
// Don't call BOOST_CHECK_EQUAL directly, as it doesn't appear to
// be thread safe. Must evaluate further.
if (n != i)
{
boost::unique_lock<boost::mutex> lock(check_mutex);
BOOST_CHECK_EQUAL(n, i);
}
++n;
}
}
void do_test_tss_void()
{
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_void_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);
}
//BOOST_AUTO_TEST_CASE(test_tss_void)
//{
// timed_test(&do_test_tss_void, 2);
//}
boost::thread_specific_ptr<void> tss_void_with_cleanup(tss_void_custom_cleanup);
void tss_void_thread_with_custom_cleanup()
{
tss_void_with_cleanup.reset(new tss_value_t);
}
void do_test_tss_void_with_custom_cleanup()
{
boost::thread t(tss_void_thread_with_custom_cleanup);
try
{
t.join();
}
catch(...)
{
t.interrupt();
t.join();
throw;
}
BOOST_CHECK(tss_void_cleanup_called);
}
BOOST_AUTO_TEST_CASE(test_tss_void_with_custom_cleanup)
{
timed_test(&do_test_tss_void_with_custom_cleanup, 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);
}
BOOST_AUTO_TEST_CASE(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;
}
}
BOOST_AUTO_TEST_CASE(test_tss_does_no_cleanup_after_release)
{
timed_test(&do_test_tss_does_no_cleanup_after_release, 2);
}
BOOST_AUTO_TEST_CASE(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;
}
BOOST_AUTO_TEST_CASE(test_tss_does_not_call_cleanup_after_ptr_destroyed)
{
boost::thread t(thread_with_local_tss_ptr);
t.join();
BOOST_CHECK(!tss_cleanup_called);
}
BOOST_AUTO_TEST_CASE(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_AUTO_TEST_CASE(test_tss_at_the_same_adress)
//{
// for(int i=0; i<2; i++)
// {
// boost::thread_specific_ptr<Dummy> local_tss(tss_custom_cleanup);
// local_tss.reset(new Dummy);
// tss_cleanup_called=false;
// BOOST_CHECK(tss_cleanup_called);
// tss_cleanup_called=false;
// BOOST_CHECK(!tss_cleanup_called);
// }
//}