[Proposal] Sync-async method binding #8911
Unanswered
ShadedBlink
asked this question in
Language Ideas
Replies: 1 comment 4 replies
-
Having the presence of an attribute be used to generator code that is based off of user code is exactly the realm of a source generator. You should be able to do this today without needing a language change or any additional features from the compiler :-) |
Beta Was this translation helpful? Give feedback.
4 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
For the long time since the introduction of
async
methods we have to write synchronous overloads for asynchronous methods to support both workflows. However it means that we have to write double amount of the code which differs only byawait
keyword.Example:
These methods almost always have to be almost same except awaiting method(blocking for synchronous, await for asynchronous). This proposal's purpose is to eradicate this code duplication by enforcing method binding. Forementioned methods are always logically bound, i.e. when you use
stream.Read()
you expect thatstream.ReadAsync()
should do exactly the same, just in non-blocking way. This assumption allows us to make compilation of synchronous methods dependent on their asynchronous counterpart.Proposal source code example, it compiles in exactly same code as in example before:
At the moment proposal is expected to perform at MSIL compile-time so it's mostly C#-limited feature. Later it can be moved to JIT compile-time, so we can reduce our binary size as well since only one instance of the bound methods would be present in a binary.
When project is compiled and compiler encounters method
Foo.WriteAsync()
, it checks the existence ofSync
attribute. Since it is present, compiler emits a synchronous counterpart ofWriteAsync()
, where name is replaced bySyncAttribute.NameOverride
or initial method's name withoutAsync
suffix.Return type is decided by the return type of initial method: if task-like type is typed(
Task<T>
,ValueTask<T>
and etc), then return type isT
, otherwise(Task
,ValueTask
and etc) - void.Any parameter marked with
AsyncOnly
(we can introduce theSyncOnly
counterpart as well, but I don't see any reason for it now) is omitted from synchronous method. The order of parameters is saved as is.For the all method's code compiler should apply asynchronous method's rules, i.e. no locks with awaits, no byref variables and etc. Even though it is considered that we will have a synchronous counterpart, rules should be the same since methods are expected to have same logical behaviour.
When compiler encounters an
await
in the code, it uses it as is inasync
case, but for thesync
case it should check if called method(stream.WriteAsync()
in forementioned case) is marked withSync
attribute. If the attribute is present, then compiler automatically replaces this call with synchronous counterpart, all parameters are mapped from initial async call. In our casecancellationToken
would be omitted from synchronous call since it would be markedAsyncOnly
in called method. If an expession was used as a parameter forcancellationToken
, i.e.stream.WriteAsync(arr, LogAndReturn(cancellationToken))
, thenLogAndReturn()
is not called in synchronous counterpart, i.e. behaviour is alike withConditionalAttribute
.If awaited method is not marked with
Sync
, then compiler throws an error: "Method is not bound with a synchronous counterpart".Compiler should ensure that no
AsyncOnly
parameters are used in synchronous context.In case when we have to use different logic for sync and async cases, we can use next approach:
async
andsync
keywords placed in method body are just a syntax sugar that can be used to mark scope with corresponding rules, i.e. likeunsafe
. When compiler compilesWriteAsync()
method, for theasync
counterpart it will ignore allsync
blocks and vice versa. When inside of async
orasync
block, compiler ignores any need forSync
attribute mapping of awaited methods and just compiles this code as is.Beta Was this translation helpful? Give feedback.
All reactions