Re: Serious GDI Multithreading bug while printing to metafiles



There's nothing thread safe about GDI calls.

Is there a thread synchronization issue there?

Mark

--
Mark Salsbery
Microsoft MVP - Visual C++



"Christian Kaiser" <bchk@xxxxxx> wrote in message news:eWIGx4wmIHA.4504@xxxxxxxxxxxxxxxxxxxxxxx
Customers of ours reported a bug in our software that causes metafiles
to be incorrect.

We now reduced the bug using the appended code, which is run by
multiple threads simultaneously. The code opens a printer DC, uses
that as reference DC for a new metafile DC, creates/selects a font,
draws a text, deselects and destroys the font, then destroys the
metafile DC and the printer DC. Trivial code in my eyes.

This should result in repeated calls like (decompiled to C code using
EMF decoder, special thanks to Feng Yuan who makes that very easy):

...
hObj[1]=CreateFont(-10,0,0,0,0,0,0,0,0,0,0,0,0,"Arial");
SelectObject(hDC, hObj[1]);
const int Dx_5[]={ 5 };
ExtTextOutW(hDC, 4,4,0,NULL,L"x",1,Dx_5);
SelectObject(hDC, GetStockObject(DEVICE_DEFAULT_FONT));
DeleteObject(hObj[1]);
hObj[1]=CreateFont(-10,0,0,0,0,0,0,0,0,0,0,0,0,"Arial");
SelectObject(hDC, hObj[1]);
const int Dx_6[]={ 5 };
ExtTextOutW(hDC, 5,5,0,NULL,L"x",1,Dx_6);
SelectObject(hDC, GetStockObject(DEVICE_DEFAULT_FONT));
DeleteObject(hObj[1]);
hObj[1]=CreateFont(-10,0,0,0,0,0,0,0,0,0,0,0,0,"Arial");
SelectObject(hDC, hObj[1]);
const int Dx_7[]={ 5 };
ExtTextOutW(hDC, 6,6,0,NULL,L"x",1,Dx_7);
SelectObject(hDC, GetStockObject(DEVICE_DEFAULT_FONT));
DeleteObject(hObj[1]);
...

and most of the times it does.

When run in multiple concurrent threads, however, some of the
repetitions miss some GDI calls:

...
hObj[1]=CreateFont(-10,0,0,0,0,0,0,0,0,0,0,0,0,"Arial");
SelectObject(hDC, hObj[1]);
const int Dx_62[]={ 5 };
ExtTextOutW(hDC, 61,61,0,NULL,L"x",1,Dx_62);
SelectObject(hDC, GetStockObject(DEVICE_DEFAULT_FONT));
DeleteObject(hObj[1]);
const int Dx_63[]={ 5 };
ExtTextOutW(hDC, 62,62,0,NULL,L"x",1,Dx_63);
const int Dx_64[]={ 5 };
ExtTextOutW(hDC, 63,63,0,NULL,L"x",1,Dx_64);
SelectObject(hDC, GetStockObject(DEVICE_DEFAULT_FONT));
const int Dx_65[]={ 5 };
ExtTextOutW(hDC, 64,64,0,NULL,L"x",1,Dx_65);
const int Dx_66[]={ 5 };
ExtTextOutW(hDC, 65,65,0,NULL,L"x",1,Dx_66);
const int Dx_67[]={ 5 };
ExtTextOutW(hDC, 66,66,0,NULL,L"x",1,Dx_67);
const int Dx_68[]={ 5 };
ExtTextOutW(hDC, 67,67,0,NULL,L"x",1,Dx_68);
const int Dx_69[]={ 5 };
ExtTextOutW(hDC, 68,68,0,NULL,L"x",1,Dx_69);
const int Dx_70[]={ 5 };
ExtTextOutW(hDC, 69,69,0,NULL,L"x",1,Dx_70);
const int Dx_71[]={ 5 };
ExtTextOutW(hDC, 70,70,0,NULL,L"x",1,Dx_71);
const int Dx_72[]={ 5 };
ExtTextOutW(hDC, 71,71,0,NULL,L"x",1,Dx_72);
const int Dx_73[]={ 5 };
ExtTextOutW(hDC, 72,72,0,NULL,L"x",1,Dx_73);
...

This happens at least in Windows XP and in Vista.

Strange is, the CreateFont(), SelectFont(), ..., DeleteFont() is being
executed in the DC correctly, and the DC state reflects this (we can
see this in our application, which uses the selected font for some
layouting - these calculations are correct). The GDI calls are just
not stored in the metafile. There is no failure in CreateFont or
SelectFont (I left out of the checking code in the demonstration code
below, for clarity reasons).

Next astonishing thing is that setting the ProcessAffinityMask to one
processor does not help. It still happens - this, for me, was really
unexpected.

When the code below is executed multiple times, it should create
similar EMF files - but they all vary in size as indication there's
something going wrong. Looking into them, it can be seen that there
are a lot of missing records.

Serializing the whole sequence is a possible solution (a part, for
example, only CreateFont/SelectFont does not help), but scales
extremely bad with multiple processors ;-(. Especially when the GDI
calls are not so easily locateable, but all over thousands of code
lines.

Is a workaround known? This is a serious problem in multithreaded
printing (using metafiles).

Christian

------------------------------
PRINTDLG pd = {0};
TCHAR szFile[MAX_PATH];

_stprintf(szFile,"test_%08x.emf",::GetCurrentThreadId());

pd.lStructSize = sizeof(pd);
pd.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
::PrintDlg(&pd);

RECT rcPage = {0,0,10000,10000};
HDC hMetaDC = ::CreateEnhMetaFile(pd.hDC,szFile,&rcPage,"Hallo");

for (int i = 0; i < 2000; ++i)
{
HFONT hFont = ::CreateFont(
-10,
0,
0,
0,
0,
0,
0,
0,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
0,
"Arial");
HFONT hOldFont = SelectFont(hMetaDC,hFont);

::ExtTextOut(hMetaDC,
i,
i,
0,
NULL,
"x",
1,
NULL);

SelectFont(hMetaDC,hOldFont);
DeleteFont(hFont);
}
HENHMETAFILE hEMF = ::CloseEnhMetaFile(hMetaDC);
::DeleteObject(hEMF);

::DeleteDC(pd.hDC);




.



Relevant Pages

  • Re: Serious GDI Multithreading bug while printing to metafiles
    ... GDI calls end up in the EMF. ... metafile DC and the printer DC. ... Strange is, the CreateFont(), SelectFont, ..., DeleteFontis ...
    (microsoft.public.win32.programmer.gdi)
  • Re: Serious GDI Multithreading bug while printing to metafiles
    ... The problem appears only when recording into metafiles in parallel. ... course it's a threading issue - inside GDI. ... When run in multiple concurrent threads, however, some of the ... Strange is, the CreateFont(), SelectFont, ..., DeleteFontis ...
    (microsoft.public.win32.programmer.gdi)
  • Serious GDI Multithreading bug while printing to metafiles
    ... Customers of ours reported a bug in our software that causes metafiles ... metafile DC and the printer DC. ... When run in multiple concurrent threads, however, some of the ... Strange is, the CreateFont(), SelectFont, ..., DeleteFontis being ...
    (microsoft.public.win32.programmer.gdi)
  • Re: Serious GDI Multithreading bug while printing to metafiles
    ... I may not be fully understanding the problem here, but what I am getting is that the GDI calls are made, and then the DC are deleted, but not all GDI calls end up in the EMF. ... Have you tried calling GdiFlush to make sure that the GDI queue is flushed before calling the DeleteDC? ... metafile DC and the printer DC. ... Strange is, the CreateFont(), SelectFont, ..., DeleteFontis being ...
    (microsoft.public.win32.programmer.gdi)
  • Re: Serious GDI Multithreading bug while printing to metafiles
    ... I would expect that GDI calls done to DIFFERENT DCs are thread-safe. ... The problem appears only when recording into metafiles in parallel. ... When run in multiple concurrent threads, however, some of the ... Strange is, the CreateFont(), SelectFont, ..., DeleteFontis ...
    (microsoft.public.win32.programmer.gdi)