Skip to content

[rcore] InitWindow() can cause a crash when Window and/or OpenGL context failed to initialize / IsWindowReady() always returns true, meaning there is no way to recover #4801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sleeptightAnsiC opened this issue Feb 28, 2025 · 12 comments · Fixed by #4803 or #4804

Comments

@sleeptightAnsiC
Copy link
Contributor

sleeptightAnsiC commented Feb 28, 2025

First mentioned here #4798 (comment) :

Currently, there is a problem with raylib's initialization where, if a platform cannot provide Window and/or OpenGL context, the whole application will crash deep inside of rcore backend code, without any possibility to recover.

Given following code:

#include "raylib.h"

int main(void)
{
	InitWindow(800, 600, "sample text");

	// exit if Window/OpenGL context creation failed
	if (!IsWindowReady()) {
		return 1;
	}

	// main loop
	while (!WindowShouldClose()) {
		BeginDrawing();
		ClearBackground(GRAY);
		EndDrawing();
	}
	CloseWindow();

	return 0;
}

and compiled as follows:

$ git clone https://github.com/raysan5/raylib.git
$ cd raylib/src
$ make RAYLIB_BUILD_MODE=DEBUG
$ cc main.c -o  main -O0 -g -I. -L. -lraylib -lm -lGL
$ ./main

I expect that this program will exit with error code 1, if InitWindow() failed to initialize Window/OpenGL context. This is exactly what note about IsWindowReady() suggests.

However, this is not what is happening right now. Application simply crashes, either somewhere inside of InitWindow() or somewhere later during main loop. There are multiple cases where this can happen (please, click at them in order to see the details):

[ Case 1: old GPU trying to load newer OpenGL context ]

$ ./main
INFO: Initializing raylib 5.6-dev
INFO: Platform backend: DESKTOP (GLFW)
INFO: Supported raylib modules:
INFO:     > rcore:..... loaded (mandatory)
INFO:     > rlgl:...... loaded (mandatory)
INFO:     > rshapes:... loaded (optional)
INFO:     > rtextures:. loaded (optional)
INFO:     > rtext:..... loaded (optional)
INFO:     > rmodels:... loaded (optional)
INFO:     > raudio:.... loaded (optional)
WARNING: GLFW: Error: 65543 Description: GLX: Failed to create context: GLXBadFBConfig
main: external/glfw/src/window.c:581: glfwGetWindowPos: Assertion `window != NULL' failed.
Aborted (core dumped)

Thread 1 "main" received signal SIGABRT, Aborted.
0x00007ffff7cce624 in ?? () from /usr/lib/libc.so.6
#0  0x00007ffff7cce624 in ?? () from /usr/lib/libc.so.6
#1  0x00007ffff7c74ba0 in raise () from /usr/lib/libc.so.6
#2  0x00007ffff7c5c582 in abort () from /usr/lib/libc.so.6
#3  0x00007ffff7c5c4eb in ?? () from /usr/lib/libc.so.6
#4  0x00005555556584c7 in glfwGetWindowPos (handle=0x0, xpos=0x7fffffffe2a4, ypos=0x7fffffffe2a8) at external/glfw/src/window.c:581
#5  0x0000555555588eac in GetCurrentMonitor () at platforms/rcore_desktop_glfw.c:769
#6  0x000055555558a90b in InitPlatform () at platforms/rcore_desktop_glfw.c:1519
#7  0x000055555558be2b in InitWindow (width=800, height=600, title=0x555555675004 "sample text") at rcore.c:683
#8  0x000055555555c8cb in main () at main.c:5

[ Case 2: trying to run inside of headless environment ]

$ ./main 
INFO: Initializing raylib 5.6-dev
INFO: Platform backend: DESKTOP (GLFW)
INFO: Supported raylib modules:
INFO:     > rcore:..... loaded (mandatory)
INFO:     > rlgl:...... loaded (mandatory)
INFO:     > rshapes:... loaded (optional)
INFO:     > rtextures:. loaded (optional)
INFO:     > rtext:..... loaded (optional)
INFO:     > rmodels:... loaded (optional)
INFO:     > raudio:.... loaded (optional)
WARNING: GLFW: Error: 65550 Description: X11: The DISPLAY environment variable is missing
WARNING: GLFW: Failed to initialize GLFW
Segmentation fault (core dumped)

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
#0  0x0000000000000000 in ?? ()
#1  0x000055555556ea88 in rlLoadTexture (data=0x7fffffffe414, width=1, height=1, format=7, mipmapCount=1) at /home/korn/raylib/src/rlgl.h:3182
#2  0x000055555556be9e in rlglInit (width=0, height=0) at /home/korn/raylib/src/rlgl.h:2258
#3  0x000055555558be4a in InitWindow (width=800, height=600, title=0x555555675004 "sample text") at rcore.c:688
#4  0x000055555555c8cb in main () at main.c:5

[ Case 3: doing something freaky, like trying to run raylib in NodeJS (similar to Case 2) ]

$ make clean
$ make TARGET_PLATFORM=PLATFORM_WEB
$ emcc main.c -s USE_GLFW=3 -g -Os -o main.js -I. -L. -l:libraylib.web.a

$ node ./main.js
INFO: Initializing raylib 5.6-dev
INFO: Platform backend: WEB (HTML5)
INFO: Supported raylib modules:
INFO:     > rcore:..... loaded (mandatory)
INFO:     > rlgl:...... loaded (mandatory)
INFO:     > rshapes:... loaded (optional)
INFO:     > rtextures:. loaded (optional)
INFO:     > rtext:..... loaded (optional)
INFO:     > rmodels:... loaded (optional)
INFO:     > raudio:.... loaded (optional)
/home/korn/raylib/src/main.js:8449
      window.addEventListener('gamepadconnected', GLFW.onGamepadConnected, true);
      ^

ReferenceError: window is not defined
    at _glfwInit (/home/korn/raylib/src/main.js:8449:7)
    at main.wasm.InitPlatform (wasm://wasm/main.wasm-001b2736:wasm-function[318]:0x743b)
    at main.wasm.InitWindow (wasm://wasm/main.wasm-001b2736:wasm-function[339]:0x87de)
    at main.wasm.__original_main (wasm://wasm/main.wasm-001b2736:wasm-function[279]:0x272d)
    at main.wasm.main (wasm://wasm/main.wasm-001b2736:wasm-function[280]:0x277f)
    at Module._main (/home/korn/raylib/src/main.js:9185:90)
    at callMain (/home/korn/raylib/src/main.js:9208:15)
    at doRun (/home/korn/raylib/src/main.js:9247:24)
    at run (/home/korn/raylib/src/main.js:9260:5)
    at removeRunDependency (/home/korn/raylib/src/main.js:360:7)

Tested mainly on Linux with Xorg, but also spotted in the past on Windows11 without GPU acceleration, and on MacOSX running inside of VM.

Related to: #4164, #4751, #3332, #2873

@sleeptightAnsiC sleeptightAnsiC changed the title [rcore] IsWindowReady() does not return false when Window and/or OpenGL context failed to initialize, which can cause crashes without possibility to recover [rcore] InitWindow() can cause a crash when Window and/or OpenGL context failed to initialize / IsWindowReady() always returns true, meaning there is no way to recover Feb 28, 2025
@raysan5
Copy link
Owner

raysan5 commented Feb 28, 2025

Agree that avoiding the crash would be a better alternative.

Could the process (window+context creation) be validated to avoid the crash and just show a TraceLog() message with the issue (like most raylib functions approach) and then just set CORE.Window.ready to false for further user check?

@sleeptightAnsiC
Copy link
Contributor Author

sleeptightAnsiC commented Feb 28, 2025

@raysan5

Could the process (window+context creation) be validated to avoid the crash and just show a TraceLog() message with the issue (like most raylib functions approach) and then just set CORE.Window.ready to false for further user check?

AFAIK: rcore_desktop_glfw InitPlatform() already does a lot of heavy lifting and validates some of window+context creation (e.g. here) but InitWindow() simply ignores any return values from InitPlatform (here). So doing what you have proposed shouldn't be a problem. We just need to make sure that all cases are handled and that other back-ends (SDL, RGFW, etc) do the same.

I'll try to mess with this when I have some spare time (maybe even today).

@raysan5
Copy link
Owner

raysan5 commented Feb 28, 2025

@sleeptightAnsiC Yeah, probably InitWindow() can check int result = InitPlatform() in that regards.

@sleeptightAnsiC

This comment has been minimized.

@sleeptightAnsiC
Copy link
Contributor Author

sleeptightAnsiC commented Feb 28, 2025

Case 3 is impossible to fix on Raylib's side (I think). It seems like an internal issue in Emscripten's GLFW port. Crash happens inside of java script when calling glfwInit().

sleeptightAnsiC added a commit to sleeptightAnsiC/raylib that referenced this issue Feb 28, 2025
Check the result of InitPlatform(), if it isn't 0, report the Error
and return. This prevent crashes and allows for gracefully aborting
or recovering by checking IsWindowReady().

Partially-fixes: raysan5#4801
sleeptightAnsiC added a commit to sleeptightAnsiC/raylib that referenced this issue Feb 28, 2025
...returning NULL. This was causing a crash few lines later.

Refs: raysan5#4801 (comment)
Partially-fixes: raysan5#4801
sleeptightAnsiC added a commit to sleeptightAnsiC/raylib that referenced this issue Feb 28, 2025
...returning NULL. This was causing a crash few lines later.

Refs: raysan5#4801 (comment)
Partially-fixes: raysan5#4801
@sleeptightAnsiC

This comment has been minimized.

raysan5 pushed a commit that referenced this issue Mar 1, 2025
…4804)

...returning NULL. This was causing a crash few lines later.

Refs: #4801 (comment)
Partially-fixes: #4801
@sleeptightAnsiC

This comment has been minimized.

@raysan5 raysan5 reopened this Mar 1, 2025
@raysan5
Copy link
Owner

raysan5 commented Mar 1, 2025

@sleeptightAnsiC oh, it was closed on PR merge..

@orcmid
Copy link
Contributor

orcmid commented Mar 2, 2025

I think I am being a bit off-topic here, but one question nags at me.

Who is the log message intended to benefit in a distributed raylib app? And if so, how are they expected to know about and find it?

raysan5 pushed a commit that referenced this issue Mar 2, 2025
…orm (#4803)

* [rcore] fix crash in InitWindow, due to unchecked result of InitPlatform

Check the result of InitPlatform(), if it isn't 0, report the Error
and return. This prevent crashes and allows for gracefully aborting
or recovering by checking IsWindowReady().

Partially-fixes: #4801

* [rcore] style: store the result of InitPlatform() before checking it

Small style change that doesn't impact how the code behaves here.
Variable 'result' is not used anywhere else in this block,
it's just for compliance with Raylib's coding conventions.

Requested in PR: #4803 (comment)

* [rcore] use LOG_WARNING when InitPlatform() fails

...as this is preferred over LOG_ERROR.

Requested-by: #4803 (comment)
@sleeptightAnsiC
Copy link
Contributor Author

@raysan5 another reopen, pretty please 🙏
PR merge closed it again. There is still some work to do on other backends.


@orcmid

Who is the log message intended to benefit in a distributed raylib app? And if so, how are they expected to know about and find it?

This issue is not about how to provide logging/errors to the end-user of the application. It's about recovering from the state, in which, developer MAY provide some information to the end-user (and abort/recover gracefully), but this is up to the developer to decide how to do it.

There are many ways. You can predefined custom TRACELOG() macro and store messages in internal buffer. Then, if an issue like this occurs (failed to initialize Window/GPU) you can display very simple popup, for example with tinyfiledialogs (these dialogs do not require GPU and should work even in headless environment)

Before those patches I made, application would just crash, you would not be able to detect that something is wrong with IsWindowReady(), and thus there was no possibility to inform end-user about what is going on.

I hope this answers your question? If not, let me know. Maybe I'm misunderstanding something here.

@orcmid
Copy link
Contributor

orcmid commented Mar 2, 2025

@raysan5 another reopen, pretty please 🙏 PR merge closed it again. There is still some work to do on other backends.

@orcmid

Who is the log message intended to benefit in a distributed raylib app? And if so, how are they expected to know about and find it?

This issue is not about how to provide logging/errors to the end-user of the application. It's about recovering from the state, in which, developer MAY provide some information to the end-user (and abort/recover gracefully), but this is up to the developer to decide how to do it.

I appreciate that the idea is not to crash and to find an alternative way to gracefully handle the particular situation. However, the code does rely on a raylib-established (?) macro and basically punts, without any indication that this is not helpful in an end-user situation. I can't imagine the above-proposed contortions being evident to those training on raylib. The preference for "warning" is also puzzling.

@sleeptightAnsiC
Copy link
Contributor Author

sleeptightAnsiC commented Mar 2, 2025

@orcmid I don't think I understand the problem. I think, we may have a different poin-view on what is being discussed here.

this is not helpful in an end-user situation. I can't imagine the above-proposed contortions being evident to those training on raylib

I'm open to any suggestions. I agree the message in the warning might be too generic. I mentioned this in PR that failures in InitPlatform/InitWindow are very generic. Also, if InitPlatform ever fails, some other warnings should lead to it, coming from backend (see details for Case1 and Case2 in the report) and any trainee should be able to google them.

The preference for "warning" is also puzzling.

I wanted this to be an LOG_ERROR when I submitted PR #4803 (comment) but I honestly don't mind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants