You may want to check useri. I’m no longer sure if it fully solved the problem in the way you want but it was designed to work both with cooperative concurency and event callbacks (e.g. the browser via jsoo), see here for an example.
You have to be careful with these things, as the underlying OS libraries may have strong requirements about which thread is allowed to get user events (IIRC on macos this had to be done on the main thread). As far as SDL is concerned you have to do this in the same thread that initialized the video subsystem.
I had a Tsdl + Lwt application that worked fine when I was using software renderers, but then I swapped an Nvidia GPU into the computer and the application stopped updating the window altogether.
Although the SDL docs say you need to make sure you limit stuff like SDL_WaitEvent to the thread that initialized SDL video, the underlying Nvidia library appears to further require (without logging anything?) that this thread is the first application thread. I had tried to coordinate all SDL stuff through a detached thread, which didn’t work.
The solution was to do all of the SDL stuff in my main thread and adopt the polling approach mentioned here to get multi-threading to work. Thanks for posting this!