-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Allow handling PlayerChatEvent asynchronously #5056
Conversation
Seems to me like this violates the MONITOR priority's expected conditions (that the event won't be modified on or after MONITOR). |
what if we use sofe's mutex for this? |
something like mutex to be right |
or.. nah im not sure im just async fanboy |
@Gewinum the problem is that you can't guarantee that handlers use the mutex |
what if we call playerchatevent and check every listener? after calling every playerchatevent we wait for the waitgroup and when its done we continue calling chatevent same way. if code in playerchatevent is entirely sync and doesnt use waitgroup it would be not a problem anymore. if waitgroup is empty we just dont wait for it and continue calling these playerchatevents, so nothing would be ruined, right? |
yeah, my solution could have some type of latency but does it matter with playerchatevent by the way? |
it has nothing to do with latency. the problem is correctness, because this PR cannot ensure that MONITOR handlers will use the mutex, so it would result in incorrect behavior. |
Two simple code snippets for TLDR:
await-generator users:
Introduction
PlayerChatEvent is a simple event that can be handled asynchronously. As an early prototype, this PR implements a minimal, non-intrusive, backwards-compatible approach to enable asynchronous capabilities without introducing any changes to the event API in general (and does not even impact existing handlers of PlayerChatEvent, unless they have some unfortunate logic that expects the message to be sent immediately, in the same tick).
Relevant issues
There are multiple prior attempts to implement asynchronous events, most of which have failed due to excessive complexity.
The initial attempt was proposed in #1881 and implemented in #2096. It adopted a more sophisticated approach that adds a generic
pause
function to all events and requirescallAsyncEvent
instead ofcallEvent
to be used. It stops the whole handler chain to wait for a single function.The more recent attempt was #4120, which enforced concurrency isolation at each priority.
In contrast to the previous attempts, this PR lacks the following features:
done()
, user will never be able to figure out what's wrong. This can be improved in the future when we have better async framework in PocketMine that allows easily scheduling a task to check that a called WaitGroup event is not complete after an expected duration and generate warnings.sync.WaitGroup
, myWaitGroup::wait()
does not allow calling multiple times in favor of more explicit state control.Changes
API changes
Adds a
WaitGroup
class, and initializes it as a field inPlayerChatEvent
. Users can defer execution of the event with the following code:It is tempting to call
done
before setting the message in the example above. Calling an event setter after event completion, just as it currently is to modify a normal synchronous event in an async callback, is a bug, and should lead to an error, which should be addressed in a separate issue.Since there is no async runtime involved, forgetting to call
done()
on a WaitGroup will lead to the event getting silently dropped. Although this is not desired (because no handler got notified of the cancellation and is most likely a bug anyway), this does not lead to memory leak because the WaitGroup and the executor are only referenced from the context that holds the WaitGroup. The most common case of forgetting to calldone()
is the error case, in which I would recommend using await-generator instead:Another advantage of using await-generator is that the developer would not accidentally call
done
before usingsetMessage
in the case above.Behavioural changes
If no plugins handle the event asynchronously, there are no behavioral changes.
For plugins that handle the event asynchronously but resolve correctly, the only change is slightly delayed message broadcast, which is expected and is barely noticeable for users.
For plugins that do not resolve correctly, see the analysis above.
Backwards compatibility
PlayerChatEvent cannot be invoked multiple times. Plugins have never been expected to leak the PlayerchatEvent and call the same instance anyway, as there is practically no advantage to this.
Follow-up
Tests
I would add tests after the API design has been reviewed.