Rapidly adding int to vector and clearing causes access violation - c++

This is some code that is designed to be run inside a .dll. If I remove either of my calls to NumberSystem::AddZero or NumberSystem::Clear, the code executes without crashing. I have been stuck for 5 hours. I am sure that something just went right over my head. Any help is appreciated!
Code:
#include <iostream>
#include <vector>
#include <thread>
namespace NumberSystem
{
std::vector<int> Numbers = {};
void Clear()
{
Numbers.clear();
}
void AddZero()
{
NumberSystem::Numbers.push_back(0);
}
};
void NumberThread()
{
while (true)
{
NumberSystem::AddZero();
NumberSystem::Clear();
}
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID pReserved)
{
std::thread thMain(NumberThread);
thMain.detach();
return TRUE;
}
Callstack:
0xC0000005: Access violation reading location 0x000001CA87EE5000.
memcpy_repmovs() Line 114
std::_Copy_memmove<char const * const *,char const * *>(const char * const * _First=0x000001ca87ec5308, const char * const * _Last=0x000001ca87ec52f0, const char * * _Dest=0x000001ca8d9eef2c) Line 4366 C++
std::_Uninitialized_move<int *,std::allocator<int>>(int * const _First=0x000001ca87ec5308, int * const _Last=0x000001ca87ec52f0, int * _Dest=0x000001ca8d9eef2c, std::allocator<int> & _Al={...}) Line 1694 C++
std::vector<int,std::allocator<int>>::_Umove_if_noexcept1(int * _First=0x000001ca87ec5308, int * _Last=0x000001ca87ec52f0, int * _Dest=0x000001ca8d9eef2c, std::integral_constant<bool,1> __formal={...}) Line 1593 C++
std::vector<int,std::allocator<int>>::_Emplace_reallocate<int>(int * const _Whereptr=0x000001ca87ec5308, int && <_Val_0>=0x00000000) Line 757 C++
std::vector<int,std::allocator<int>>::emplace_back<int>(int && <_Val_0>=0x00000000) Line 708 C++
std::vector<int,std::allocator<int>>::push_back(int && _Val=0x00000000) Line 722 C++
NumberSystem::AddZero()
NumberThread()
Thanks again!

Your code reuses same Numbers variable from multiple threads.
I mean, DllMain callback is called multiple times with different dwReason parameter values.
You can prevent that with something like:
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID pReserved)
{
// Checks reason for being invoked.
if (dwReason == DLL_PROCESS_ATTACH) {
std::thread thMain(NumberThread);
thMain.detach();
}
return TRUE;
}
If you wonder how you could make it support multi-thread, then research about std::mutex usage for now (or ask different question).

Related

Can't read export directory properly

I'm trying to read export directory of a loaded module. The following program works as a 32-bit binary, but crashes as a 64-bit file.
All pointer is 64bit and I'm not sure the differences here, does anyone know what's wrong?
#include <windows.h>
#include <iostream>
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")
void PrintNames(HMODULE hModule)
{
DWORD dwExportsSize;
PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)ImageNtHeader(hModule);
PIMAGE_EXPORT_DIRECTORY ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToData(hModule, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &dwExportsSize);
PULONG Names = (PULONG)((DWORD)hModule + ExportDirectory->AddressOfNames);
for (ULONG cEntry = 0; cEntry < ExportDirectory->NumberOfNames; cEntry++)
{
printf("%s\n", (char*)((DWORD_PTR)hModule + Names[cEntry]));
}
}
int main()
{
PrintNames(GetModuleHandleA("ntdll"));
return 0;
}
A DWORD is 32 bit and not enough for 64 bit. Change it to DWORD_PTR if you need pointer size
PULONG Names = (PULONG)((DWORD_PTR)hModule + ExportDirectory->AddressOfNames);

Unhandled exception at the end of thread

Hello right now i'm trying to use a thread to read some info about 2 images using OpenCV 2.3.1, while I can retrieve the sizes of the 2 images without a problem, an error occurs when I reach the end of the thread.
Unhandled exception at 0x00348A56 in OpenCvDemo.exe: An invalid parameter was passed to a function that considers invalid parameters fatal.
Since I can read the size of the images just fine I know they aren't empty or anything. In another test I detached the thread and put it to Sleep for 5 seconds, the main function executed fine and displayed the image, but once the thread woke up and reached the end it crashed the application and displayed the same error as above.
Sadly I need to use OpenCV 2.3.1 since this is just a test for a bigger application.
How can I solve this problem, or at least what is the cause of it?
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <thread>
#include <opencv2/opencv.hpp>
void Loop(cv::Mat &image1, cv::Mat &image2) {
std::vector<uchar> buff1;
std::vector<uchar> buff2;
cv::imencode(".png", image1, buff1);
cv::imencode(".png", image2, buff2);
int imageSize1 = buff1.size();
int imageSize2 = buff2.size();
std::cout << "Size " << imageSize1 << "\n";
std::cout << "Size " << imageSize2 << "\n";
}
int main()
{
cv::Mat image1 = cv::imread("C:\\Users\\Cesar\\Pictures\\happyface.png", 1);
cv::Mat image2 = cv::imread("C:\\Users\\Cesar\\Pictures\\happyface.png", 1);
std::thread mythread(Loop, std::ref(image1), std::ref(image2));
mythread.join();
cv::imshow("name", image1);
cv::waitKey(0);
return 0;
}
The callstack
OpenCvDemo.exe!_invoke_watson(const wchar_t * expression, const wchar_t * function_name, const wchar_t * file_name, unsigned int line_number, unsigned int reserved) Line 224 C++
OpenCvDemo.exe!_invalid_parameter(const wchar_t * expression, const wchar_t * function_name, const wchar_t * file_name, unsigned int line_number, unsigned int reserved) Line 113 C++
[External Code]
OpenCvDemo.exe!Loop(cv::Mat & image1, cv::Mat & image2) Line 23 C++
[External Code]
OpenCvDemo.exe!invoke_thread_procedure(unsigned int(__stdcall*)(void *) procedure, void * const context) Line 92 C++
OpenCvDemo.exe!thread_start<unsigned int (__stdcall*)(void *)>(void * const parameter) Line 115 C++
[External Code]
[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]

Finding memory leaks in MFC C++ app by overloading new operators

I have an MFC application. I would like to trace every dynamic memory allocations (on heap) to be able to find out the source of the memory leaks in that app. The IDE is Visual Studio 2010.
I did the following:
Introduced a preprocessor directive called 'MEMORY_LEAK_FINDER'.
Added a class called 'CMemLeakHunter', you find the exact content of
these files below.
The idea was to overload every new operators (all
3 of them: new, new[] and CObject::new) and use them to trace the
location where the memory was allocated (file, line). At the end of
the execution I wanted to bring the memory leaks' location to the
output using 'CMemoryState' class, so I could finally compare the allocations' trace with the CMemoryState's compare (difference) trace.
The problem is, that the application compiles (in VS 2010 debug mode), but the following linker errors are occurred:
Error 4 error LNK2005: "void * __cdecl operator new[](unsigned int,char const *,int)" (??_U#YAPAXIPBDH#Z) already defined in CMemLeakHunter.obj E:\Software\Nafxcwd.lib(afxmem.obj)
Error 3 error LNK2005: "void * __cdecl operator new(unsigned int,char const *,int)" (??2#YAPAXIPBDH#Z) already defined in CMemLeakHunter.obj E:\Software\Nafxcwd.lib(afxmem.obj)
Error 5 error LNK2005: "public: static void * __stdcall CObject::operator new(unsigned int,char const *,int)" (??2CObject##SGPAXIPBDH#Z) already defined in CMemLeakHunter.obj E:\Software\Nafxcwd.lib(afxmem.obj)
Error 6 error LNK1169: one or more multiply defined symbols found E:\Software\Module1.exe 1
I googled and found out, that ignoring the library Nafxcwd.lib may solve the problem. In my application not, I tried out, but ignoring that library, another 17000 linker error (unresolved externals).
Additional dependencies are: Nafxcwd.lib;Ws2_32.lib;Version.lib
Ignore specific default libraries are: msvcprtd.lib;libcimtd.lib;libcmt.lib
I can't split the software so easily, therefore I ask for help: how could I trace the memory allocations done by my own app if I am using MFC and I need to use the .lib files mentioned above? What could be the solution? Please help me to resolve this matter to be able to trace the memory allocations to find out the possible sources of the leaks. I'm also open-minded to use another MFC built-in routines if they are able to do this. However, I did not find any useful by myself.
The header file CMemLeakHunter.hpp is written as follows:
#ifndef _MEM_LEAK_HUNTER_
#define _MEM_LEAK_HUNTER_
#ifdef MEMORY_LEAK_FINDER
#pragma message("Macro MEMORY_LEAK_FINDER is active, overloading new...")
#include "stdafx.h"
#include <map>
using std::map;
#undef new
void* operator new(size_t size, LPCSTR file, int line);
void* operator new[](size_t size, LPCSTR file, int line);
#define new new(__FILE__, __LINE__)
namespace
{
static const size_t LOG_BUFFER_SIZE = 2;
static const size_t DEFAULT_BUFFER_LINE_SIZE = 512;
}
class CMemLeakHunter
{
public:
static CMemLeakHunter& singleton();
void startMemoryTrace(const char* leakPath, const char* allocPath);
void endMemoryTrace();
void addMemory(void* area, size_t size, LPCSTR file, int line);
void deleteMemory(void* area, LPCSTR file, int line);
private:
void flushAllocLog();
void filterTrace();
map<DWORD, size_t> mMemChunks;
CString sLogBuffer[LOG_BUFFER_SIZE];
size_t nLogBufferLines;
CMemoryState oldMemState;
CMemoryState newMemState;
CMemoryState diffMemState;
CString sMemLeakTracePath;
CString sMemAllocTracePath;
static CMutex s_oObjMutex;
};
#endif // MEMORY_LEAK_FINDER
#endif // _MEM_LEAK_HUNTER_
The source file CMemLeakHunter.cpp is written as follows:
#include "CMemLeakHunter.hpp"
#ifdef MEMORY_LEAK_FINDER
#pragma message("Memory-Leak finder is activated, building functions for the class...")
#undef new
void* operator new(size_t size, LPCSTR file, int line)
{
void* pArea = ::operator new(size);
CMemLeakHunter::singleton().addMemory(pArea, size, file, line);
return pArea;
}
void* operator new[](size_t size, LPCSTR file, int line)
{
void* pArea = ::operator new[](size);
CMemLeakHunter::singleton().addMemory(pArea, size, file, line);
return pArea;
}
void* PASCAL CObject::operator new(size_t size, LPCSTR file, int line)
{
void* pArea = CObject::operator new(size);
CMemLeakHunter::singleton().addMemory(pArea, size, file, line);
return pArea;
}
CMutex CMemLeakHunter::s_oObjMutex;
CMemLeakHunter& CMemLeakHunter::singleton()
{
static CMemLeakHunter theSingleObject;
return theSingleObject;
}
void CMemLeakHunter::startMemoryTrace(const char* leakPath, const char* allocPath)
{
sMemLeakTracePath = leakPath;
sMemAllocTracePath = allocPath;
oldMemState.Checkpoint();
for(size_t i=0; i<LOG_BUFFER_SIZE; i++)
{
sLogBuffer[i] = CString('\0', DEFAULT_BUFFER_LINE_SIZE);
}
}
void CMemLeakHunter::endMemoryTrace()
{
newMemState.Checkpoint();
if(FALSE != diffMemState.Difference(oldMemState, newMemState))
{
CFile oDumpFile;
if(TRUE == oDumpFile.Open(sMemLeakTracePath, CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyWrite))
{
CFile* oldContext = afxDump.m_pFile;
afxDump.m_pFile = &oDumpFile;
diffMemState.DumpStatistics();
oDumpFile.Write("\n", 1);
diffMemState.DumpAllObjectsSince();
oDumpFile.Close();
afxDump.m_pFile = oldContext;
}
else
{
// TODO: log that file cannot be created!!!
}
}
flushAllocLog();
filterTrace();
}
void CMemLeakHunter::addMemory(void* area, size_t size, LPCSTR file, int line)
{
CSingleLock oMemHunterTraceLock(&s_oObjMutex, TRUE);
DWORD nAreaAddr = reinterpret_cast<DWORD>(area);
mMemChunks[nAreaAddr] = size;
if(nLogBufferLines >= LOG_BUFFER_SIZE)
{
flushAllocLog();
}
sLogBuffer[nLogBufferLines++].Format("### Memory allocation: Address 0x%08X, Size: %u, File: '%s', Line: %d\n", nAreaAddr, size, file, line);
}
void CMemLeakHunter::deleteMemory(void* area, LPCSTR file, int line)
{
CSingleLock oMemHunterTraceLock(&s_oObjMutex, TRUE);
DWORD nAreaAddr = reinterpret_cast<DWORD>(area);
mMemChunks.erase(nAreaAddr);
if(nLogBufferLines >= LOG_BUFFER_SIZE)
{
flushAllocLog();
}
sLogBuffer[nLogBufferLines++].Format("!!! Memory release: Address 0x%08X, File: '%s', Line: %d\n", nAreaAddr, file, line);
}
void CMemLeakHunter::flushAllocLog()
{
CStdioFile oAllocFile;
if(FALSE == PathFileExists(sMemAllocTracePath))
{
oAllocFile.Open(sMemAllocTracePath, CFile::modeCreate);
oAllocFile.Close();
}
if(TRUE == oAllocFile.Open(sMemAllocTracePath, CFile::modeReadWrite | CFile::shareDenyWrite))
{
oAllocFile.SeekToEnd();
for(size_t i=0; i<min(nLogBufferLines, LOG_BUFFER_SIZE); i++)
{
oAllocFile.WriteString(sLogBuffer[i]);
}
oAllocFile.Close();
}
else
{
// TODO: log that file cannot be accessed!!!
}
nLogBufferLines = 0;
}
void CMemLeakHunter::filterTrace()
{
CStdioFile oAllocFile;
if(TRUE == oAllocFile.Open(sMemAllocTracePath, CFile::modeRead | CFile::shareDenyWrite))
{
CString sFilterTraceFile;
sFilterTraceFile.Format("filter_%s", sMemAllocTracePath);
CStdioFile oFilterAllocFile;
if(TRUE == oFilterAllocFile.Open(sFilterTraceFile, CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyWrite))
{
for (std::map<DWORD, size_t>::iterator it=mMemChunks.begin(); it!=mMemChunks.end(); it++)
{
CString addrHex;
addrHex.Format("0x%08X", it->first);
CString sLine;
while(FALSE != oAllocFile.ReadString(sLine))
{
if(sLine.Find(addrHex) > -1)
{
CString sLineWithNewline;
sLineWithNewline.Format("%s\n", sLine);
oFilterAllocFile.WriteString(sLineWithNewline);
}
}
}
oFilterAllocFile.Close();
}
else
{
// TODO: log that file cannot be created!!!
}
oAllocFile.Close();
}
else
{
// TODO: log that file cannot be accessed!!!
}
}
#endif // MEMORY_LEAK_FINDER
No need to do this yourself.
In your main.cpp include:
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
and at the top of your main function call:
#ifdef _DEBUG
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
For every file where you want to detect leaks place this at the top:
#ifdef _DEBUG
#ifndef DBG_NEW
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#define new DBG_NEW
#endif
#endif // _DEBUG
Then any detected leaks are output to the console on application exit.
See here.

boost test case from dll access violation

I want to start Boost test case from dll under Windows RT. I built test case as dll via the Visual Studio command prompt using the following comandline:
cl.exe /EHsc /D_USRDLL /D_WINDLL /LDd ~location\testcase.cpp ~library location\libboost_unit_test_framework-vc110-mt-sgd-1_53.lib /link /DLL /OUT:~output directory\testcase.dll
placed it into my application’s folder and set property "Content" to "true". After launching of my application I have the following error:
Unhadled exception at the 0x00B9AF16 in TestApp.exe: 0xC0000005: Access violation reading location 0x00000000
Top of the call stack is below:
> TestApp.exe!boost::unit_test::framework::get(unsigned long id, boost::unit_test::test_unit_type t) Line 388 C++
TestApp.exe!boost::unit_test::framework::get(unsigned long id) Line 73 C++
TestApp.exe!boost::unit_test::traverse_test_tree(unsigned long id, boost::unit_test::test_tree_visitor & V) Line 232 C++
TestApp.exe!boost::unit_test::traverse_test_tree(const boost::unit_test::test_suite & suite, boost::unit_test::test_tree_visitor & V) Line 207 C++
TestApp.exe!boost::unit_test::traverse_test_tree(unsigned long id, boost::unit_test::test_tree_visitor & V) Line 234 C++
TestApp.exe!boost::unit_test::framework::run(unsigned long id, bool continue_test) Line 403 C++
TestApp.exe!boost::unit_test::unit_test_main(boost::unit_test::test_suite * (int, char * *) * init_func, int argc, char * * argv) Line 185 C++
Here is the dll code (NOTE: If I place the same code directly into my source, it works fine):
void test_stat()
{
//some code there
}
extern "C" {
__declspec (dllexport) test_suite* init_unit_test_suite( int argc, char* argv[] )
{
test_suite *test = BOOST_TEST_SUITE("test name");
test->add(BOOST_TEST_CASE(&test_stat));
return test;
}
}
Code of the application for launching of the test case:
boost::unit_test::test_suite* main_global_test_suite;
test_suite* init_unit_test_suite( int argc, char* argv[] ) {
return NULL; }
test_suite* run_global_test_suite (int, char* []) {
return main_global_test_suite;
}
HINSTANCE hMyDll;
typedef test_suite* (*PFN_MyFunction)(int,const char*);
PFN_MyFunction pfnMyFunction;
test_suite* rPtr;
if((hMyDll=::LoadPackagedLibrary(L"testcase", 0))==NULL)
{
return;
}
pfnMyFunction=(PFN_MyFunction)GetProcAddress(hMyDll,"init_unit_test_suite");
if (pfnMyFunction != NULL)
{
//just create fake arguments for the boost::unit_test::unit_test_main function call
char* argv[1024];
argv[0] = "Text";
rPtr = pfnMyFunction(1, NULL);
main_global_test_suite = rPtr;
const int error =
boost::unit_test::unit_test_main(&run_global_test_suite, 1, argv );
}
else
{
//handling code
}
FreeLibrary(hMyDll);
Is there any ideas how to solve the problem?
Check what console_test_runner is doing. This is command line application (part of Boost.Test), which intended to do just that - load and execute test units implemented in shared library. Also please make sure you tell UTF that you want to build dll: define BOOST_TEST_DYN_LINK.

C++11 static local variables and threads

Is static local variables safe for a class that creates/uses std::threads?
Because when I use something like this:
logger& logger::get_instance(void)
{
static logger lg;
return lg;
}
And try to exit (force close) the executable, it crashes/exits improperly (somethings the Visual Studio 2012 debugger even crashes).
When I don't do that, the program exits gracefully when i force close.
Here's the stack call when it crashes
ntdll.dll!77c10dbd() Unknown
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
ntdll.dll!77b7bfdc() Unknown
kernel32.dll!75b55bab() Unknown
> msvcr110d.dll!__crtCreateThreadpoolWait(void (_TP_CALLBACK_INSTANCE *, void *, _TP_WAIT *, unsigned long) * pfnwa, void * pv, _TP_CALLBACK_ENVIRON_V1 * pcbe) Line 569 C
msvcr110d.dll!Concurrency::details::RegisterAsyncWaitAndLoadLibrary(void * waitingEvent, void (_TP_CALLBACK_INSTANCE *, void *, _TP_WAIT *, unsigned long) * callback, void * data) Line 675 C++
msvcr110d.dll!Concurrency::details::ExternalContextBase::PrepareForUse(bool explicitAttach) Line 120 C++
msvcr110d.dll!Concurrency::details::ExternalContextBase::ExternalContextBase(Concurrency::details::SchedulerBase * pScheduler, bool explicitAttach) Line 52 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::GetExternalContext(bool explicitAttach) Line 1579 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::AttachExternalContext(bool explicitAttach) Line 1527 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::CreateContextFromDefaultScheduler() Line 569 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::CurrentContext() Line 402 C++
msvcr110d.dll!Concurrency::details::LockQueueNode::LockQueueNode(unsigned int timeout) Line 616 C++
msvcr110d.dll!Concurrency::critical_section::lock() Line 1017 C++
msvcp110d.dll!mtx_do_lock(_Mtx_internal_imp_t * * mtx, const xtime * target) Line 65 C++
msvcp110d.dll!_Mtx_lock(_Mtx_internal_imp_t * * mtx) Line 144 C++
escobar.exe!std::_Mtx_lockX(_Mtx_internal_imp_t * * _Mtx) Line 68 C++
escobar.exe!std::_Mutex_base::lock() Line 43 C++
escobar.exe!std::unique_lock<std::mutex>::unique_lock<std::mutex>(std::mutex & _Mtx) Line 228 C++
escobar.exe!escobar::utilities::blocking_queue<escobar::logging::log_message *>::interrupt() Line 71 C++
escobar.exe!escobar::logging::log_worker::~log_worker() Line 17 C++
escobar.exe!escobar::logging::log_worker::`scalar deleting destructor'(unsigned int) C++
escobar.exe!escobar::logging::logger::close() Line 72 C++
escobar.exe!escobar::logging::logger::~logger() Line 27 C++
escobar.exe!`escobar::logging::logger::get_instance'::`2'::`dynamic atexit destructor for 'lg''() C++
msvcr110d.dll!doexit(int code, int quick, int retcaller) Line 585 C
msvcr110d.dll!_cexit() Line 410 C
msvcr110d.dll!__CRTDLL_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 296 C
msvcr110d.dll!_CRTDLL_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 210 C
ntdll.dll!77bb2846() Unknown
ntdll.dll!77bb2893() Unknown
ntdll.dll!77bc09c8() Unknown
ntdll.dll!77bc08ad() Unknown
KernelBase.dll!75525bbb() Unknown
KernelBase.dll!75525c51() Unknown
kernel32.dll!75b58543() Unknown
ntdll.dll!77bbac69() Unknown
ntdll.dll!77bbac3c() Unknown
Here are a couple of the functoin
log_worker::~log_worker(void)
{
this->queue.interrupt();
service.join();
}
void log_worker::run(void)
{
while (true)
{
log_message* msg;
if (this->queue.dequeue(msg) == false)
break;
this->lg->print_log_message(msg);
delete msg;
}
}
bool dequeue(T& item)
{
std::unique_lock<std::mutex> lock(m);
// handles spurious wakeups
while (!this->data_available && !this->interrupted)
cv.wait(lock);
if (this->interrupted)
return false;
item = std::move(this->front());
this->pop();
if (this->empty())
this->data_available = false;
return true;
}
void interrupt(void)
{
std::unique_lock<std::mutex> lock(m);
this->interrupted = true;
cv.notify_all();
printf("notified threads...\n");
}
Looks like you have a detached thread SchedulerBase(could be any scheduled thread which still uses logger) which is still running while your application is stopped and logger is destroyed which caused the crash.
And log_worker is dynamically allocated in the logger class.
You need to make sure all threads who use logger instance are shutdown properly before logger is destroyed
delete log_worker in logger destructor
I simply had to stop deleting things on shutdown. When you close the console using the 'X' button, it's not a proper shutdown, so It's pointless trying to shutdown threads.