Skip to content
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

pong widget text askew #39

Open
twiddlingbits opened this issue Sep 23, 2024 · 5 comments
Open

pong widget text askew #39

twiddlingbits opened this issue Sep 23, 2024 · 5 comments
Assignees
Labels
enhancement New feature or request

Comments

@twiddlingbits
Copy link
Owner

Windows, Chrome

If i run AI 2 player pong (regular), then the async version, sometimes i get text like this:

image

I did just change the way events work a bit. They now call the callback with less lag. I am not sure if that had an impact.

@twiddlingbits twiddlingbits added the bug Something isn't working label Sep 23, 2024
@twiddlingbits twiddlingbits added this to the 2.5.0 milestone Sep 24, 2024
@twiddlingbits twiddlingbits self-assigned this Sep 24, 2024
@twiddlingbits
Copy link
Owner Author

i started looking into this, it is probably related to my recent event changes. it appears that the animationFrame event triggers while the twrConDrawSeq is processing (or perhaps after if finished in the main thread, but before the worker thread process notification).

I'll think on it and fix.

@twiddlingbits
Copy link
Owner Author

twiddlingbits commented Sep 24, 2024

upon further investigation, it appears that what is happening is this: When (the synchronous) twrConDrawSeq is called (the twrConDrawSeq was triggered by a the flush to measure text)., it waits on an event containing the return value (an internally generated event with no callback.) This is event is queued by JS thread when twrConDrawSeq completes in the JS thread.
Then before the event is processed by the worker thread, the JS main thread issues an animationFrame which queues an event.

Then the worker thread then starts executing and starts processing the events. The way i implemented the recent event changes, the call back event are now processed before in returnvalue event. So the animationFrame callback is called before the twrConDrawSeq return event is processed (the events are processed out of order). the pong code does not handle this case.

I can fix the event behavior so that no events are processed out of order, but I am wondering if there are other issues similar to this that won't be fixed. Before these recent changes, no callback events would be processed while waiting for a return value event. They were queued, but the callbacks didn't happen until after the return value was processed. The changes i made are to process callbacks while waiting for a return value event. You can see this behaviour in the timer example i committed, where repeating timer event callbacks are processed while twr_sleep is sleeping. Its a question of which behavior is desired.

Any opinion?

i'll think on it more.

@twiddlingbits
Copy link
Owner Author

this is pretty complicated. What with nested blocking waits. Ie, a callback that is called while a a blocking wait is waiting, and the callback does a blocking wait. I restored events to the way they were before that work with pong (the events queue but don't callback if happening during a blocking wait). I am still thinking on if this is the permanent solution.

@twiddlingbits
Copy link
Owner Author

twiddlingbits commented Sep 26, 2024

reverted back to the prior method -- events queue up and are not processed during a blocking call like twr_sleep or during calls twrConDrawSeq . Queued events are not processed until the C/C++ code returns back to JavaScript. This current (reverted) implementation appears to the API user as JavaScript style event callbacks.

One could think of a blocking call like twr_sleep a little like an async function in JavaScript (which is how it is implemented), and allow callbacks to process during those style blocking calls. I could add this.

The main issue was that functions like twrConDrawSeq appear to be synchronous to the API users. But the return value is passed as an internal event. And allowing callbacks to happen while waiting for that event was unexpected (by pong, for example). And confusing.

So i will leave this open as an enhancement request -- Allow callbacks while blocking call is waiting? E.g twr_sleep.

@twiddlingbits twiddlingbits added enhancement New feature or request and removed bug Something isn't working labels Sep 26, 2024
@twiddlingbits twiddlingbits removed this from the 2.5.0 milestone Sep 26, 2024
@JohnDog3112
Copy link
Collaborator

Allow callbacks while blocking call is waiting? E.g twr_sleep.

I think allowing callbacks while a blocking calls are waiting could be fine. Though, with that implementation, it would be nice if blocking functions like twr_sleep were labeled with something like async_twr_sleep so that it's easy for the end user to figure out what calls they might need to pay extra attention to when modifying/reading shared state.

Also, I've been looking back at the pong menu code and I'm not quite sure why a second call from animationFrame while the previous one is running would cause issues like that. I'm assuming the issue comes into play where I do canvas.measureText for the text in each of the buttons. So assuming that somehow 3 animationFrame events are called before the first one returns, I think the following would happen:

  1. First animationFrame is called
    a. Button 1 is read as uninitialized
    b. Button 1 is set to initialized
    c. A call to measureText is made
  2. Second animationFrame is called:
    a. Button 1 is read as initialized
    b. Button 1 is rendered with uninitialized x, y positions since measureText has yet to return
    c. Button 2 is read as uninitialized
    d. Button 2 is set to initialized
    e. A call to measureText is made
  3. Third animationFrame is called:
    a. Button 1 and 2 are read as initialized and printed with uninitialized data
    b. Button 2 is read as initialized and set to initialized
    c. A call to measureText is made
  4. First measureText returns
    a. x, y positions for button1 are set
    b. Likely prints the button1 text incorrectly since it's now out of order
    c. Continues on to print button 2 and 3 with incorrect x, y
  5. Second and Third measureText returns
    a. Basically the same as the first button
  6. Frame after all measureText's return:
    a. Everything is rendered correctly since the button's have been fully initialized

So, I can see how animationFrame being called multiple times could cause some issues for a few frames while everything is initializing, but I don't know why that would cause issues after that unless the subsequent calls to measureText were somehow mangled. Especially since the button it set to "initialized" before making any blocking calls, so it should be impossible for a subsequent call to enter the same initialization block while the other one is still blocked.

Though, it is still possible that it can be fixed by having a simple guard variable that just returns from subsequent animationFrame calls if a previous one is still running.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants