Skip to content
This repository

Workaround for the default font crash (issue #59) #202

Closed
wants to merge 3 commits into from

2 participants

Christian Laurent Gomila
Christian

I've implemented a

Workaround for the default font crash (issue #59)

It basically defines a static function sf::Font::destroyDefaultFont() which the user can call to manually (usually at the end of main()) destroy the default font. After destroying the default font, it can be created again by getDefaultFont without doing anything special and calling it more than once or before the font is created does no harm. (Everything tested.)

I dont't think this is the final solution because it requires the user to call a function but it has only a small impact on the codebase and works.

First, I moved the declaration of the default font outside getDefaultFont in an anonymous namespace, wrapped in a helper struct. To allow calling the destructor manually, I also had to make the font heap allocated / a pointer. The struct is there to ensure that the font gets deleted, even when the user forgets to call the new sf::Font::destroyDefaultFont() function (so if the user forgets this, the behavior is the same as before). Then I adapted getDefaultFont to the changes (check for NULL instead of the boolean) and at last I added destroyDefaultFont() which just deletes the default font and sets the pointer to NULL.

Pros

  • (Mentioned above:) small impact on both SFML (changes to getDefaultFont() and a few additions in Font.hpp, .cpp) and user (max. 1 line) code
  • Allows the user optionally to prevent the default font crash manually
  • Does not alter behavior at all, if destroyDefaultFont() is not called (especially, if SFML is linked statically it will continue to work just fine, no matter if the function is called).
  • When a real solution to the problem is found, it is easy to make destroyDefaultFont() a no-op to keep source and even binary compatibility. Alternatively, a removal of the function only breaks one line of user code (normally ;) )

Cons

  • Theoretical possibility of getting an default-constructed (invalid?) font in case of an out-of-memory situation (i.e. when new fails), which is currently only detectable by hooking into the sf::err()-stream.
Laurent Gomila
Owner

Thanks for helping.

This issue has been discussed many times in detail, and no satisfying solution was found so far (and of course, an explicit cleanup function was one possible solution that was discussed). So I won't merge this pull request into the master branch.

Christian

Ok, i expected something like that. But maybe an extended form of a cleanup function is the only way of solving this issue, you might e.g. consider a class which does all initalizations in its constructor and cleanup in its destructor, like sfgui does.

Anyway, could you please point me to the relevant discussions? A forum search for "default font crash" (without quotes of course) only yields threads which mention e.g. the "notorious default font bug", but I found no real discussions about it.

Laurent Gomila
Owner

But maybe an extended form of a cleanup function is the only way of solving this issue,

Another interesting solution is to provide a function that creates a new default font instance, rather than one that provides the instance directly. This way, there's no instance to manage on SFML side.

Anyway, could you please point me to the relevant discussions?

Restrict the search to user "Tank", he's the one that regularly bothers me with this issue :)

Christian

Maybe, the default font should not be cleaned up at all. At least, the reference for DllMain says:

When handling DLL_PROCESS_DETACH, a DLL should free resources such as heap memory only if the DLL is being unloaded dynamically (the lpReserved parameter is NULL). If the process is terminating (the lpvReserved parameter is non-NULL), all threads in the process except the current thread either have exited already or have been explicitly terminated by a call to the ExitProcess function, which might leave some process resources such as heaps in an inconsistent state. In this case, it is not safe for the DLL to clean up the resources. Instead, the DLL should allow the operating system to reclaim the memory.

and

If your DLL is linked with the C run-time library (CRT), the entry point provided by the CRT calls the constructors and destructors for global and static C++ objects. Therefore, these restrictions for DllMain also apply to constructors and destructors and any code that is called from them.

The question is if we can trust Windows (or drivers?) to also clean up OpenGL resources properly.

EDIT:

Restrict the search to user "Tank"

Thank you for the hint :)

Laurent Gomila
Owner

Maybe, the default font should not be cleaned up at all

This is not a solution. It's not clean programming, and tools such as valgrind will complain about memory leaks.

I need a design solution, not a technical workaround ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.

Showing 2 changed files with 54 additions and 9 deletions. Show diff stats Hide diff stats

  1. +13 0 include/SFML/Graphics/Font.hpp
  2. +41 9 src/SFML/Graphics/Font.cpp
13 include/SFML/Graphics/Font.hpp
@@ -210,6 +210,19 @@ public :
210 210 ///
211 211 ////////////////////////////////////////////////////////////
212 212 static const Font& getDefaultFont();
  213 +
  214 + ////////////////////////////////////////////////////////////
  215 + /// \brief Destroy the default built-in font
  216 + ///
  217 + /// This function should be called before the main() function
  218 + /// exits if SFML is linked dynamically to prevent a crash on
  219 + /// global exit (although it does no harm even when SFML is
  220 + /// linked statically). Make sure that no sf::Text is using
  221 + /// the default font anymore (see sf::Text::setFont() for a
  222 + /// full explanation).
  223 + ///
  224 + ////////////////////////////////////////////////////////////
  225 + static void destroyDefaultFont();
213 226
214 227 private :
215 228
50 src/SFML/Graphics/Font.cpp
@@ -39,6 +39,28 @@
39 39
40 40 namespace
41 41 {
  42 + struct DefaultFontHolder {
  43 + sf::Font* defaultFont;
  44 +
  45 + void free()
  46 + {
  47 + delete defaultFont;
  48 + defaultFont = NULL;
  49 + }
  50 +
  51 + DefaultFontHolder()
  52 + {
  53 + defaultFont = NULL;
  54 + }
  55 +
  56 + ~DefaultFontHolder()
  57 + {
  58 + free();
  59 + }
  60 +
  61 +
  62 + } defaultFontHolder;
  63 +
42 64 // FreeType callbacks that operate on a sf::InputStream
43 65 unsigned long read(FT_Stream rec, unsigned long offset, unsigned char* buffer, unsigned long count)
44 66 {
@@ -329,24 +351,34 @@ Font& Font::operator =(const Font& right)
329 351 ////////////////////////////////////////////////////////////
330 352 const Font& Font::getDefaultFont()
331 353 {
332   - static Font font;
333   - static bool loaded = false;
334   -
335   - // Load the default font on first call
336   - if (!loaded)
  354 + // Load the default font on first call and after calls to destroyDefaultFont()
  355 + if (!defaultFontHolder.defaultFont)
337 356 {
338 357 static const signed char data[] =
339 358 {
340 359 #include <SFML/Graphics/Arial.hpp>
341 360 };
342   -
343   - font.loadFromMemory(data, sizeof(data));
344   - loaded = true;
  361 + try
  362 + {
  363 + defaultFontHolder.defaultFont = new sf::Font();
  364 + }
  365 + catch (std::bad_alloc& e)
  366 + {
  367 + err() << "Could not allocate default font: " << e.what() << std::endl;
  368 + static sf::Font invalid;
  369 + return invalid;
  370 + }
  371 + defaultFontHolder.defaultFont->loadFromMemory(data, sizeof(data));
345 372 }
346 373
347   - return font;
  374 + return *defaultFontHolder.defaultFont;
348 375 }
349 376
  377 +////////////////////////////////////////////////////////////
  378 +void Font::destroyDefaultFont()
  379 +{
  380 + defaultFontHolder.free();
  381 +}
350 382
351 383 ////////////////////////////////////////////////////////////
352 384 void Font::cleanup()

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.