GDI: Use thread data for font HDC

Use the newly-introduced thread data structure to store
the per-thread font HDC. As an added bonus, we can now
avoid leaking HDCs on module unlaods.
This commit is contained in:
Luca Bacci 2026-03-04 15:10:13 +01:00
parent a9229136ea
commit 1f5230dde0
4 changed files with 44 additions and 18 deletions

View file

@ -143,19 +143,13 @@ _cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font
static HDC
_get_global_font_dc (void)
{
static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT;
static DWORD hdc_tls_index;
HDC hdc;
cairo_win32_thread_data_t *data = cairo_win32_thread_data_get ();
if (_cairo_atomic_init_once_enter (&once)) {
hdc_tls_index = TlsAlloc ();
assert (hdc_tls_index != TLS_OUT_OF_INDEXES);
_cairo_atomic_init_once_leave (&once);
}
if (!data->hdc) {
HDC hdc_screen = GetDC (NULL);
HDC hdc;
hdc = TlsGetValue (hdc_tls_index);
if (!hdc) {
hdc = CreateCompatibleDC (NULL);
hdc = CreateCompatibleDC (hdc_screen);
if (!hdc) {
fprintf (stderr, "%s:%s\n", __FUNCTION__, "CreateCompatibleDC");
return NULL;
@ -167,13 +161,19 @@ _get_global_font_dc (void)
return NULL;
}
if (!TlsSetValue (hdc_tls_index, hdc)) {
DeleteDC (hdc);
return NULL;
}
data->hdc = hdc;
/* From MSDN docs for CreateCompatibleDC:
*
* If [the reference] hdc is NULL, the thread that calls CreateCompatibleDC
* owns the HDC that is created. When this thread is destroyed, the HDC is
* no longer valid.
*/
data->free_hdc = (hdc_screen != NULL);
ReleaseDC (NULL, hdc_screen);
}
return hdc;
return data->hdc;
}
static cairo_status_t

View file

@ -36,14 +36,16 @@
#ifndef CAIRO_WIN32_PRIVATE_H
#define CAIRO_WIN32_PRIVATE_H
#include "cairo-win32.h"
#include "cairoint.h"
#include "cairo-device-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-surface-private.h"
#include "cairo-win32.h"
#include <windows.h>
#define WIN32_FONT_LOGICAL_SCALE 32
CAIRO_BEGIN_DECLS
@ -236,7 +238,15 @@ cairo_win32_get_system_text_quality (void);
HMODULE
_cairo_win32_load_library_from_system32 (const wchar_t *name);
typedef DWORD (__stdcall *stdcall_free_func_t) (void *);
void
cairo_win32_async_stdcall_free (stdcall_free_func_t func, void *data);
typedef struct {
HDC hdc;
cairo_bool_t free_hdc;
cairo_bool_t added_to_list;
} cairo_win32_thread_data_t;

View file

@ -109,6 +109,12 @@ _cairo_win32_load_library_from_system32 (const wchar_t *name)
return module_handle;
}
void
cairo_win32_async_stdcall_free (stdcall_free_func_t func, void *data)
{
QueueUserWorkItem (func, data, WT_EXECUTEDEFAULT);
}
static void
cairo_win32_initialize (void)
{

View file

@ -97,6 +97,16 @@ thread_data_free (cairo_win32_thread_data_t *data)
{
/* Loader-lock-safe */
if (data->free_hdc) {
/* Delete the HDC explicitly only if it was created via a non-NULL
* reference HDC. Otherwise the system deletes it automatically on
* thread-exit and our asynchronous delete would be racy. For more
* informations, refer to the MSDN docs for CreateCompatibleDC.
*/
void *free_func = DeleteDC;
cairo_win32_async_stdcall_free (free_func, data->hdc);
}
#ifdef USE_EXPLICIT_TLS
thread_data_allocation_free (data);
#endif