I am developing an application for Windows. I have written a class for drawing purposes, that will be accessed by several threads. The class is threadsafe by using mutex locks.
The good thing is, thread safety does not seem to be an issue, however, I seem to never get a lock when processing the WM_SIZE message (though I do get locks when processing other messages) and I do not understand why.
The class looks like this:
class DrawChildWindow {
typedef boost::mutex::scoped_lock scoped_lock;
typedef boost::mutex::scoped_try_lock scoped_try_lock;
typedef std::map<HWND, HDC> WindowMap;
public:
DrawChildWindow(HWND hWndParent);
~DrawChildWindow();
// Will move the child window and repaint it
void move(int x, int y, unsigned int cx, unsigned int cy);
// Returns the HDC stored in the WindowMap
HDC beginPaint();
// Displays the edited HDC on the client area of the child window
void endPaint();
private:
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
boost::mutex m_mutexLocal; // for work with member variables
static boost::mutex s_mutexGlobal; // for work with static variables
static WindowMap s_map; // Manages the created child windows
HWND m_hWnd; // Handle of the child window
};
I’ve shortened it to show only the methods and members that, in my opinion, might cause the trouble. This is the respective implementation:
DrawChildWindow::DrawChildWindow(HWND hWndParent) {
// I've cut the once-only registration of the window class here
scoped_lock lockG(s_mutexGlobal);
scoped_lock lockL(s_mutexLocal);
m_hWnd = CreateWindow(L"DrawChildWindow", NULL,
WS_CHILDWINDOW | WS_DLGFRAME | WS_VISIBLE,
0, 0, 0, 0,
m_hWndParent, NULL, __DCW_HINST, NULL);
s_map[m_hWnd] = NULL;
}
DrawChildWindow::~DrawChildWindow() {
scoped_lock lockG(s_mutexGlobal);
scoped_lock lockL(s_mutexLocal);
WindowMap::iterator it = s_map.find(m_hWnd);
if(it != s_map.end())
s_map.erase(it);
DestroyWindow();
}
void DrawChildWindow::move(int x, int y, unsigned int cx, unsigned int cy) {
scoped_lock lockL(s_mutexLocal);
MoveWindow(m_hWnd, x, y, cx, cy, TRUE);
}
HDC DrawChildWindow::beginPaint() {
scoped_lock lockG(s_mutexGlobal);
scoped_lock lockL(s_mutexLocal);
WindowMap::iterator it = s_map.find(m_hWnd);
if(it != s_map.end()) {
if(!it->second) {
// I create a compatible DC here
// and fit it to match the client area size
}
// I clear the background of the DC here
return it->second;
}
// This should never happen
return NULL;
}
void DrawChildWindow::endPaint() {
scoped_lock lockG(s_mutexGlobal);
scoped_lock_lockL(m_mutexLocal);
WindowMap::iterator it = s_map.find(m_hWnd);
if(it != s_map.end()) {
if(it->second) {
// I BitBlt the HDC into the client area here
}
}
}
LRESULT CALLBACK DrawChildWindow::WndProc(HWND hWnd, UINT uiMessage, WPARAM wParam, LPARAM lParam) {
switch(uiMessage) {
case WM_SIZE:
{ // This is the problematic block
scoped_try_lock lockG(s_mutexGlobal);
// This is never true
if(lockG.try_lock()) {
WindowMap::iterator it = s_map.find(hWnd);
if(it != s_map.end() && it->second) {
DeleteDC(it->second);
it->second = NULL;
}
}
}
return 0;
case WM_PAINT:
{
scoped_lock lockG(s_mutexGlobal);
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
WindowMap::iterator it = s_map.find(hWnd);
if(it != s_map.end() && it->second) {
// I BitBlt it->second to hDC here
}
else {
// Draw a notification that no data is available
}
EndPaint(hWnd, &ps);
}
return 0;
}
return DefWindowProc(hWnd, uiMessage, wParam, lParam);
}
boost::mutex DrawChildWindow::s_mutexGlobal;
DrawChildWindow::WindowMap DrawChildWindow::s_map = WindowMap();
I am aware of the fact that WM_SIZE will be called while CreateWindow() – so it will be called in my constructor, where s_mutexGlobal is indeed locked. This is why I implemented it as a scoped_try_lock.
When the parent window’s size is changed, I call move() to adjust the child window sizes, and they will be adjusted correctly, but the HDC of s_map will never be destroyed, and I fail to understand why I cannot lock s_mutexGlobal there.
The problem can be solved by using
instead of the
scoped_try_lock. I discovered this while experimenting, and would still appreciate if someone had an explanation for this behavior.