Asynchronous Functions in C#

Similar documents
An Async Primer. By Bill Wagner August Introduction

Programming Languages Third Edition. Chapter 9 Control I Expressions and Statements

Upcoming Features in C# Mads Torgersen, MSFT

Introduce C# as Object Oriented programming language. Explain, tokens,

C# Asynchronous Programming Model

C# Programming in the.net Framework

The Various Faces of the.net Task Parallel Library

Weiss Chapter 1 terminology (parenthesized numbers are page numbers)

Chapter 4 Defining Classes I

This paper explains in more detail some of the proposed simplifications from P1342R0.

Microsoft Visual C# Step by Step. John Sharp

VS08 This One Goes to Going Parallel with PFX, PLINQ, TPL and Async Keywords

Casting -Allows a narrowing assignment by asking the Java compiler to "trust us"

Chapter 7 Control I Expressions and Statements

Programming Languages Third Edition. Chapter 7 Basic Semantics

Microsoft. Microsoft Visual C# Step by Step. John Sharp

Introduction to Coroutines. Roman Elizarov elizarov at JetBrains

Exception Handling Introduction. Error-Prevention Tip 13.1 OBJECTIVES

MCSA Universal Windows Platform. A Success Guide to Prepare- Programming in C# edusum.com

Project Compiler. CS031 TA Help Session November 28, 2011

Java How to Program, 10/e. Copyright by Pearson Education, Inc. All Rights Reserved.

Asynchronous Programming Demystified

Coroutine concepts and metafunctions

Programming C# 5.0. Ian Griffiths O'REILLY' Beijing Cambridge * Farnham Kbln Sebastopol Tokyo

(Not Quite) Minijava

Unit 20: Extensions in ActiveBPEL

Oops known as object-oriented programming language system is the main feature of C# which further support the major features of oops including:

Course Hours

Asynchronous Programming

Type Conversion. and. Statements

C# 6.0 in a nutshell / Joseph Albahari & Ben Albahari. 6th ed. Beijin [etc.], cop Spis treści

INTRODUCTION TO.NET. Domain of.net D.N.A. Architecture One Tier Two Tier Three Tier N-Tier THE COMMON LANGUAGE RUNTIME (C.L.R.)

Operational Semantics of Cool

Resumable Functions. 1. The Problem

Lecture Outline. COOL operational semantics. Operational Semantics of Cool. Motivation. Lecture 13. Notation. The rules. Evaluation Rules So Far

Exceptions, Case Study-Exception handling in C++.

DOT NET Syllabus (6 Months)

Lexical Considerations

Exceptions. Why Exception Handling?

Static Semantics. Winter /3/ Hal Perkins & UW CSE I-1

Exception Handling. Sometimes when the computer tries to execute a statement something goes wrong:

ASYNCHRONOUS PROGRAMMING IN C# 5 WITHOUT USE OF MULTIPLE THREADS

Persistent Oberon Language Specification

Exception Handling. Run-time Errors. Methods Failure. Sometimes when the computer tries to execute a statement something goes wrong:

Hierarchical inheritance: Contains one base class and multiple derived classes of the same base class.

C # Language Specification

AP COMPUTER SCIENCE JAVA CONCEPTS IV: RESERVED WORDS

Class, Variable, Constructor, Object, Method Questions

COPYRIGHTED MATERIAL. Contents. Part I: C# Fundamentals 1. Chapter 1: The.NET Framework 3. Chapter 2: Getting Started with Visual Studio

Topics Covered Thus Far CMSC 330: Organization of Programming Languages

SEMANTIC ANALYSIS TYPES AND DECLARATIONS

C# in Depth THIRD EDITION

Chapter 14. Exception Handling and Event Handling

Fresh Async With Kotlin. Presented at QCon SF, 2017 /Roman JetBrains

A Short Summary of Javali


Operational Semantics. One-Slide Summary. Lecture Outline

Lecture Outline. COOL operational semantics. Operational Semantics of Cool. Motivation. Notation. The rules. Evaluation Rules So Far.

Whidbey Enhancements to C# Jeff Vaughan MSBuild Team July 21, 2004

COS 320. Compiling Techniques

CSCE 314 Programming Languages. Type System

Simplifying Asynchronous Programming with Microsoft Visual Studio Async CTP

Typescript on LLVM Language Reference Manual

Introduction to Programming Using Java (98-388)

POINT OF FAILURES TOPICS .NET. msdn

Review of the C Programming Language for Principles of Operating Systems

Software Exception Flow Analysis for WPF C# Asynchronous Programs Using Microsoft Visual Studio

School of Informatics, University of Edinburgh

Table of Contents Preface Bare Necessities... 17

Chapter 12: How to Create and Use Classes

Asynchronous Programming in Javascript, Part 2. CSCI 5828: Foundations of Software Engineering Lecture 19 10/25/2016

CMSC 330: Organization of Programming Languages

Previous C# Releases. C# 3.0 Language Features. C# 3.0 Features. C# 3.0 Orcas. Local Variables. Language Integrated Query 3/23/2007

Appendix. Grammar. A.1 Introduction. A.2 Keywords. There is no worse danger for a teacher than to teach words instead of things.

Language Reference Manual simplicity

Topics Covered Thus Far. CMSC 330: Organization of Programming Languages. Language Features Covered Thus Far. Programming Languages Revisited

14. Exception Handling

BEAAquaLogic. Service Bus. Interoperability With EJB Transport

CS558 Programming Languages

1 Lexical Considerations

CS 3 Introduction to Software Engineering. 3: Exceptions

The Object Model Overview. Contents. Section Title

The Compiler So Far. CSC 4181 Compiler Construction. Semantic Analysis. Beyond Syntax. Goals of a Semantic Analyzer.

Introducing C# and the.net Framework

OBJECT ORIENTED PROGRAMMING USING C++ CSCI Object Oriented Analysis and Design By Manali Torpe

Intermediate Code Generation

Weeks 6&7: Procedures and Parameter Passing

! How is a thread different from a process? ! Why are threads useful? ! How can POSIX threads be useful?

CMSC 330: Organization of Programming Languages

Declarations and Access Control SCJP tips

Distribution and Integration Technologies. C# Language

Goal. Generic Programming and Inner classes. Minor rewrite of linear search. Obvious linear search code. Intuitive idea of generic linear search

!! How is a thread different from a process? !! Why are threads useful? !! How can POSIX threads be useful?

C# Java. C# Types Naming Conventions. Distribution and Integration Technologies. C# C++.NET A C# program is a collection of: C C++ C# Language

IPCoreL. Phillip Duane Douglas, Jr. 11/3/2010

Structure of Programming Languages Lecture 10

The Myx Architectural Style

CS313D: ADVANCED PROGRAMMING LANGUAGE

JAVASCRIPT AND JQUERY: AN INTRODUCTION (WEB PROGRAMMING, X452.1)

Review of the C Programming Language

Transcription:

Asynchronous Functions in C# Asynchronous operations are methods and other function members that may have most of their execution take place after they return. In.NET the recommended pattern for asynchronous operations is for them to return a task which represents the ongoing operation and allows waiting for its eventual outcome. Asynchronous operations are useful when multiple flows of control need to share the threads they run on. For instance they can be used to share a single user interface thread between multiple ongoing operations, or to service thousands of simultaneous ongoing requests on a server with a much smaller pool of threads. Traditionally, writing and composing asynchronous operations is difficult and error prone. It involves installing callbacks sometimes called continuations on other asynchronous operations to express all the logic that needs to happen after those operations completes. This alters, and usually significantly complicates, the structure of asynchronous code as compared to a corresponding synchronous program. Asynchronous functions is a new feature in C# which provides an easy means for expressing asynchronous operations. Inside asynchronous functions await expressions can await ongoing tasks, which causes the rest of the execution of the asynchronous function to be transparently signed up as a continuation of the awaited task. In other words, it becomes the job of the programming language, not the programmer, to express and sign up continuations. As a result, asynchronous code can retain its logical structure. An asynchronous function in C# must either return void or one of the types Task or Task<T>. C# does not dictate a specific type for the representation of tasks that are await ed, as long as they satisfy a certain pattern. The types Task and Task<T> satisfy this pattern, and will be used in subsequent examples. The reliance on the pattern allows other representations of asynchronous operations to be awaited, for interoperability purposes and to allow APIs to produce tasks with special semantics different from those of Task and Task<T>. The following example demonstrates a few simple asynchronous operations: Task<Movie> GetMovieAsync(string title); Task PlayMovieAsync(Movie movie); async void GetAndPlayMoviesAsync(string[] titles) foreach (var title in titles) var movie = await GetMovieAsync(title); await PlayMovieAsync(movie);

By convention asynchronous operations use the postfix Async to show that part of their execution may take place after the method call has returned. Both GetMovieAsync and PlayMovieAsync return tasks, which means that their completion can be subsequently await ed. By contrast, GetAndPlayMoviesAsync returns void, so its completion cannot be await ed. Such asynchronous operations are often referred to as fire and forget, and are useful e.g. for implementing event handlers asynchronously. GetAndPlayMoviesAsync is marked by the async modifier as an asynchronous function, containing two await expressions. This fact is an implementation detail to its callers, but fundamentally changes the way the method is executed: As soon as an unfinished Task is awaited, it will return to its caller. When the awaited Task completes, it will resume execution of the GetAndPlayMoviesAsync method until the next unfinished Task is awaited, and so on. In between, while awaiting unfinished Tasks, no thread is occupied with the execution of this method: it borrows time on threads only when it is active. Asynchronous functions An asynchronous function is a method or anonymous function which is marked with the async modifier. A function without the async modifier is called synchronous. Asynchronous functions are evaluated differently from other functions in that their evaluation is discontinuous: Their evaluation may be suspended at any await-expression until the awaited task determines that it is time to resume their execution. Syntax The grammar of C# is extended as follows: method-modifier: async opt lambda-expression: async opt anonymous-function-signature => anonymous-function-body anonymous-method-expression: async opt delegate explicit-anonymous-function-signature opt block The async modifier is not allowed on method-declarations where the method-body is a ;. Note that async is a contextual keyword. In all syntactic contexts other than the ones above it is considered an identifier. Thus, the following is allowed (though strongly discouraged!): using async = System.Threading.Tasks.Task; async async async(async async)

Return types of asynchronous functions The return type of an asynchronous function must be either void, Task or Task<T> for some T. The return type of an anonymous function is the return type if any of the delegate or expression type that it is being converted to. In an asynchronous function with the return type void or Task, return statements must not have an expression. In an asynchronous function with the return type Task<T> for some T, return statements must have an expression that is implicitly convertible to T, and the endpoint of the body must be unreachable. Evaluation of Task-returning asynchronous functions Invocation of a Task-returning asynchronous function initially is no different from a synchronous function. However, when reaching an await expression, the asynchronous function may return to its caller, even though its execution is not yet finished. A Task or Task<T> will be returned to the caller. It is guaranteed not to be null. If execution of the asynchronous method completes successfully, the returned task will be marked as succeeded, and any value produced through a return statement with an expression will become the result of the returned Task<T>. If an exception occurs and is not caught within the asynchronous method, the task is marked as faulted or cancelled and the exception stored within it. The task is marked cancelled only in the case of certain implementation dependent exceptions designating cancellation. When an asynchronous function is invoked it can be seen as being in one of three states: running, suspended at an await expression, or completed. Evaluation of void-returning asynchronous functions If the return type of the asynchronous function is void, evaluation differs from the above in various ways. Because no Task is returned, the function instead communicates completion and exceptions to the current thread s context. The exact definition of context is implementation defined, but is a representation of where the current thread is running. The context is notified when evaluation of a void returning asynchronous function commences, completes successfully, or causes an unhandled exception to be thrown. This allows the context to keep track of how many void-returning asynchronous functions are running under it, and to decide how to propagate exceptions coming out of them. Await expressions Await expressions are used to suspend the execution of an asynchronous function until the awaited task completes.

Syntax The grammar of C# is extended as follows: unary-expression: await-expression await-expression: await unary-expression statement-expression: await-expression An await expression is only allowed when occurring in the body of an asynchronous function. Inside of the innermost enclosing asynchronous function it may furthermore not occur inside of the body of a synchronous function, in a catch or finally block of a try-statement, inside the block of a lockstatement, or in an unsafe context. Note that this means that an await expression cannot occur in most places within a query-expression, because those get rewritten to use synchronous lambda expressions. Since an await expression can only occur in an asynchronous function, it can never be confused with uses of await as an identifier already occurring in code written in older versions of C#. The await pattern The expression t of an await-expression await t is called the task of the await expression. The task t is required to be awaitable, which means that one of the following must hold: The task t is of type dynamic, or An accessible instance or extension method GetAwaiter with no parameters and return type A is available on t, where A has the following accessible instance members: o bool IsCompleted get; with a setter being also permitted. o void OnCompleted(Action); o One of: void GetResult(); T GetResult(); where T is any type. A is called the awaiter type for the await expression. The GetAwaiter method is used to obtain an awaiter for the task. The IsCompleted property is used to determine if the task is already complete. If so, there is no need to sign up a continuation and suspend evaluation. The OnCompleted method is used to sign up a continuation on the awaited task. The GetResult method is used to obtain the outcome of the task once it is complete.

All of GetAwaiter, IsCompleted, OnCompleted and GetResult are intended to be non-blocking ; that is, not cause the calling thread to wait for a significant amount of time, e.g. for an operation to complete. Classification If an await-expression await t is valid, it is classified the same way as the expression (a).getresult(). That is: If the expression (a).getresult() invokes a method that returns void, the result is nothing. Otherwise, the result is a value of the type returned by (a).getresult(). Evaluation of await expressions During the execution of the body of the asynchronous function, await t is evaluated as follows: An awaiter a is obtained by evaluating the expression (t).getawaiter(). The expression (a).iscompleted is evaluated. If the result is false then o The expression (a).oncompleted(r) is evaluated, where r is a delegate whose invocation will cause execution of the enclosing asynchronous function to be resumed after the current await expression. o Evaluation is suspended, and control is returned to the function that invoked or resumed the asynchronous function. Then (either immediately following or upon resumption) the expression (a).getresult() is evaluated. If it returns a value, that value is the result of the await-expression. Otherwise the result is nothing. If the type of t is dynamic, this may lead to binding errors at runtime, if corresponding members cannot be found. But beyond that, the requirements that non-dynamic awaitables are subject to at compiletime, are not enforced at runtime on dynamic awaitables. The IsCompleted property allows the await ing of already-completed tasks to avoid suspending execution only to have it immediately resumed. The implementation of OnCompleted should make sure that the delegate r is invoked at most once. If r is invoked beyond these restrictions, its behavior is undefined. Conversions In order to account for asynchronous anonymous functions, the rules for conversion of anonymous functions at the beginning of section 6.5 are modified as follows: An anonymous-method-expression or lambda-expression is classified as an anonymous function ( Error! Reference source not found.). The expression does not have a type but can be implicitly converted to a

compatible delegate type or expression tree type. Specifically, a delegate type D is compatible with an anonymous function F provided: If F contains an anonymous-function-signature, then D and F have the same number of parameters. If F does not contain an anonymous-function-signature, then o D may have zero or more parameters of any type. o No parameter of D may have the out parameter modifier. o If F is asynchronous, no parameter of D may have the ref parameter modifier. If F has an explicitly typed parameter list, each parameter in D has the same type and modifiers as the corresponding parameter in F. If F has an implicitly typed parameter list, D has no ref or out parameters. If the body of F is an expression and either D has a void return type, or F is asynchronous and the return type of D is Task, then when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid expression (wrt 7) that would be permitted as a statement-expression ( 8.6). If the body of F is a statement block and either D has a void return type, or F is asynchronous and the return type of D is Task, then when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid statement block (wrt 8.2) in which no return statement specifies an expression. If the body of F is an expression, and either F is synchronous and D has a non-void return type T, or F is asynchronous and D has a return type Task<T>, then when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid expression (wrt 7) that is implicitly convertible to T. If the body of F is a statement block, and either F is synchronous and D has a non-void return type T, or F is asynchronous and D has a return type Task<T>, then when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid statement block (wrt 8.2) with a non-reachable end point in which each return statement specifies an expression that is implicitly convertible to T. An expression tree type Expression<D> is compatible with an anonymous function F if the delegate type D is compatible with F. Certain anonymous functions cannot be converted to expression tree types: Even though the conversion exists, it fails at compile-time. This is the case if the anonymous expression: contains simple or compound assignment operators contains dynamically bound expressions is asynchronous

Type inference and overload resolution Where anonymous functions are treated specially in type inference and overload resolution contexts, special care is also given to anonymous asynchronous functions. Inferred return type To express the notion of inferred return types for asynchronous anonymous functions, a helper notion of inferred result type is introduced. It is used only in the definition of inferred return type. The inferred return type of an anonymous function F is used during type inference and overload resolution. The inferred return type can only be determined for an anonymous function where all parameter types are known, either because they are explicitly given, provided through an anonymous function conversion or inferred during type inference on an enclosing generic method invocation. The inferred result type of F is determined as follows: If the body of F is an expression that has a type, then the inferred result type of F is the type of that expression. If the body of F is a block and the set of expressions in the block s return statements has a best common type T ( 7.5.2.14), then the inferred result type of F is T. Otherwise, a result type cannot be inferred for E. The inferred return type of F is determined as follows: If F is asynchronous and the body of F is either an expression classified as nothing, or a statement block where no return statements have expressions, the inferred return type is Task If F has an inferred result type T: o If F is synchronous the inferred return type is T. o If F is asynchronous the inferred return type is Task<T>. Otherwise a return type cannot be inferred for F Better conversion from expression During overload resolution, the inferred return type is used to determine which conversion is better from an anonymous function to a delegate or expression type. This mechanism is extended to better select between overloads when called with asynchronous anonymous functions, as follows: Given an implicit conversion C 1 that converts from an expression E to a type T 1, and an implicit conversion C 2 that converts from an expression E to a type T 2, C 1 is a better conversion than C 2 if at least one of the following holds: E has a type S and an identity conversion exists from S to T 1 but not from S to T 2 E is not an anonymous function and T 1 is a better conversion target than T 2 ( 7.5.3.5) E is an anonymous function, T 1 is either a delegate type D 1 or an expression tree type Expression<D 1 >, T 2 is either a delegate type D 2 or an expression tree type Expression<D 2 > and one of the following holds:

o D 1 is a better conversion target than D 2 o D 1 and D 2 have identical parameter lists, and one of the following holds: D 1 has a return type Y 1, and D 2 has a return type Y 2, an inferred return type X exists for E in the context of that parameter list ( 7.5.2.12), and the conversion from X to Y 1 is better than the conversion from X to Y 2 E is an asynchronous function, D 1 has a return type Task<Y 1 >, and D 2 has a return type Task<Y 2 >, an inferred return type Task<X> exists for E in the context of that parameter list ( 7.5.2.12), and the conversion from X to Y 1 is better than the conversion from X to Y 2 D 1 has a return type Y, and D 2 is void returning Implementation example The following shows an approach to implementing asynchronous functions via syntactic expansion. Asynchronous functions returning Task<T> User code async Task<string> Fred(int p) var x = await e; await s; Syntactic expansion Task<string> Fred(int p) var $builder = AsyncTaskMethodBuilder<string>.Create(); var $state = 0; TaskAwaiter< > $a1; TaskAwaiter $a2; Action $resume = null; $resume = delegate try... jump table based on $state $a1 = (e).getawaiter(); $state=1; if ($a1.iscompleted) $a1.oncompleted($resume)) JUMP_LABEL_1: var x = $a1.getresult(); $a2 = (s).getawaiter(); $state=2; if ($a2.iscompleted) $a2.oncompleted($resume)) exactly the same signature get a builder for Task<T> one declaration for each await point if the user had nested try blocks, then we use nested jump tables; and there s additional logic so that the bodies of finally blocks are skipped where appropriate these calls are just syntactic expansions they follow the usual rules for overload resolution &c. It s a compileerror to await something if the calls can t be resolved. In the statement form of await, the GetResult method is allowed to return

throw new Exception(); JUMP_LABEL_2: $a2.getresult(); throw new Exception(); void. return r; ; $builder.setresult(r); catch (Exception $ex) $builder.setexception($ex); $resume(); return $builder.task; Asynchronous functions returning Task User code async Task Fred(int p) await s; Syntactic expansion Task Fred(int p) var $builder = AsyncTaskMethodBuilder.Create(); var $state = 0; TaskAwaiter $a1; Action $resume = null; $resume = delegate try... jump table based on $state $a1 = (s).getawaiter; $state=1; if ($a1.iscompleted) $a1.oncompleted($resume)) JUMP_LABEL_1: $a1.endawait(); $builder.getresult(); catch (Exception $ex) $builder.setexception($ex); $builder.setresult() is now parameterless

; $builder.setresult(); Automatically inserted at the end of the delegate to allow omission of explicit return statement $resume(); return $builder.task; Asynchronous functions returning void User code async void Fred(int p) await s; Syntactic expansion Task Fred(int p) var $builder = AsyncVoidMethodBuilder.Create(); var $state = 0; TaskAwaiter $a1; Action $resume = null; $resume = delegate try... jump table based on $state $a1 = (s).getawaiter; $state=1; if ($a1.iscompleted) $a1.oncompleted($resume)) JUMP_LABEL_1: $a1.getresult(); ; $builder.setresult(); catch (Exception $ex) $builder.setexception($ex); $builder.setresult(); $resume(); different builder for void async methods no Task returned