|
Boost : |
Subject: [boost] [ASIO] Safely shut down timer thread in boost::asio::io_service in Windows DLL
From: Bo Peng (elapsing_at_[hidden])
Date: 2013-10-01 04:22:31
Hi folks,
As you might already know, Windows DLL has quite some limitation about what
you can do during its loading and unloading. For example, you can't 'join'
a thread when unloading a DLL. The consequence of doing that, from my
opinion, is quite serious: it will hang the process in a dead lock.
I have a Windows DLL, which uses boost::asio::deadline_timer. I found that
deadline_timer::async_wait creates a timer thread as a private member of
io_service. This thread won't be shut down even if all the timers are
cancelled. In the destructor of io_service, 'join' will be called on the
thread. In some exceptional situation, users might exit their main function
before destructs the io_service inside my DLL, so the destructor is called
during the unloading of the DLL, which hangs their process.
The following code demonstrates this issue. action.h and action.cpp are the
code for the dll, main.cpp are the code for the application which uses the
dll.
action.h:
#pragma once
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
extern "C" __declspec(dllexport) void start();
extern "C" __declspec(dllexport) void stop();
action.cpp:
#include "action.h"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
class Action
{
public:
Action() :
ios_(),
timer_(ios_, boost::posix_time::seconds(1)),
worker_()
{
}
void start()
{
worker_.reset(new boost::thread(boost::bind(&Action::startEntry,
this)));
}
void stop()
{
if (worker_)
{
ios_.post(boost::bind(&Action::stopEntry, this));
worker_->join();
worker_.reset();
}
}
private:
void startEntry()
{
timer_.async_wait(boost::bind(&Action::timerHandler, this, _1));
ios_.run();
}
void stopEntry()
{
timer_.cancel();
}
void timerHandler(const boost::system::error_code& e)
{
if(e == boost::asio::error::operation_aborted)
{
std::cout << "Finished." << std::endl;
}
else
{
std::cout << "Triggered." << std::endl;
timer_.expires_from_now(boost::posix_time::seconds(1));
timer_.async_wait(boost::bind(&Action::timerHandler, this, _1));
}
}
boost::asio::io_service ios_;
boost::asio::deadline_timer timer_;
boost::shared_ptr<boost::thread> worker_;
};
boost::shared_ptr<Action> action;
void start()
{
action.reset(new Action);
action->start();
}
void stop()
{
if (action)
{
action->stop();
action.reset();
}
}
main.cpp:
#include <iostream>
#include <windows.h>
int main()
{
typedef void (__cdecl *PROC)();
HINSTANCE hInstLib = LoadLibrary(TEXT("dll.dll"));
if (hInstLib == NULL)
{
std::cout << "Failed to load dll" << std::endl;
return 1;
}
PROC startProc = reinterpret_cast<PROC>(GetProcAddress(hInstLib,
"start"));
if (startProc == NULL)
{
std::cout << "Failed to load start function" << std::endl;
return 1;
}
PROC stopProc = reinterpret_cast<PROC>(GetProcAddress(hInstLib,
"stop"));
if (startProc == NULL)
{
std::cout << "Failed to load stop function" << std::endl;
return 1;
}
startProc();
std::cout << "Press any key to exit..." << std::endl;
getchar();
//stopProc();
FreeLibrary(hInstLib);
return 0;
}
As you can see, stopProc() is commented out to simulate that the user
doesn't have the chance to call the clean up function of my DLL.
What I am wondering is, is it possible to shut down the thread whenever
there isn't any timer outstanding. Or if I had access to the handle of this
thread, I could call TerminateThread, which is allowed even during dll
unloading.
Regards,
Bo
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk