509 lines
12 KiB
C++
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);
|
|
// }
|
|
//}
|
|
|