Patent Description:
In either case, the asynchronous function returns a return data structure of a particular type. As an example, in C# code, asynchronous functions can be declared (using "async") causing the asynchronous function to be executed using a C# generated async state machine. Such state machines return a data structure called a "Task" that has a particular schema. Task<T> is a task object that is used to return a generic type. In any case, the return of such an object causes allocation of a Task<T> object in the heap of the computing system.

To avoid heap allocation of a Task object, some runtimes have a predetermined set of Task objects pre-allocated in the heap, each corresponding to a common returned value. For instance, there might be an allocation of a Task object populated with a "True" Boolean value, a Task object populated with a "False" Boolean value, a Task populated with a Null value, Task objects for each of the integers <NUM> through <NUM>, and so forth. In this case, if a returned value is one of these common values, then the pre-allocated Task object for that value can be found and returned, avoiding allocation of a new Task object in the heap. If the returned value is not one of the common values, a new Task object would be allocated, populated with the value, and then returned.

Rather, this background is only provided to illustrate one exemplary technology area where some embodiments describe herein may be practiced. <CIT> relates to techniques for providing network services and testing software and/or hardware modules. A technique of application program execution within a computer system comprising an operating system provides an interface to external events. The operating system is essentially unmodifiable by the application programmer. The application program has at least one high-level language application program module which is modifiable by the programmer. The operating system controls the computer most of the time and only intermittently dispatches control to the high-level module, whereby the execution of the application program module is not sequential. The technique comprises creating a thread for execution; detecting a need for an asynchronous operation; in response to a detected need, suspending the thread's execution; detecting completion of the asynchronous operation; and in response to a detected completion of the asynchronous operation, resuming the thread's execution. <CIT> relates to establishing a conditional branch frame barrier. A conditional branch in a function epilogue is used to provide frame-specific control. The conditional branch evaluates a return condition to determine whether to return from a callee function to a calling function, or to execute a slow path instead. The return condition is evaluated based on a thread local value. The thread local value is set such that returns to potentially unsafe frames in a call stack are prohibited. The prohibition to return to a potentially unsafe frame may be referred to as a "frame barrier". Anonymous: "Task asynchronous programming model", retrievable from the Internet: URL:https://docs. com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model explains that performance bottlenecks can be avoided and the overall responsiveness of an application can be enhanced by using asynchronous programming. Anonymous: "await operator - C# reference", retrievable from the Internet: URL:https://docs. com/en-us/dotnet/csharp/language-reference/operators/await explains that the await operator suspends evaluation of an enclosing async method until the asynchronous operation represented by its operand completes.

The principles described herein relate to reuse of a thread-local return data structure in order to prevent a return data structure from being allocated every time asynchronous functions return synchronously. The principles described herein operate in a computing environment in which one or more caller functions place one or more function calls to one or more asynchronous functions.

As an example, as part of a thread, a particular caller function places a function call to an asynchronous function. In accordance with some embodiments described herein, a computing system synchronously returns thread operation from the asynchronous function back to the caller function using a return data structure. However, the thread operation is returned in a manner that the return data structure can be reused for future asynchronous function returns within that same thread.

To do so, the computing system (e.g., a runtime) first accesses data that was generated by the asynchronous function in response to the caller function placing the function call to the asynchronous function. The computing system determines that the data is to be returned within a return data structure of a particular type to the caller function. The computing system then determines that the asynchronous function is returning synchronously, which means that the asynchronous function has completed its operation and thus does not need to be rescheduled to resume. The computing system then determines that the caller function will use the return data structure as populated only once.

In response, the computing system allocates a thread-local return data structure within the heap of the computing system, populates the thread-local return data structure with the accessed data, and returns the populated thread-local return data structure to the caller function. The caller function will use the return data structure only once, but the computing system will still refrain from removing the thread-local return data structure from the heap. Instead, the thread-local return data structure remains in the heap to be reused upon future returns from asynchronous functions within the thread.

In accordance with some embodiments described herein, regardless of how or when the reusable thread-local return data structure is first allocated in the heap, that thread-local return data structure is reused. As an example, when an asynchronous function later is to synchronously return within a thread, the computing system determines that the data is to be returned within a return data structure of a particular type to the caller function. Then, the computing system determines that the asynchronous function is returning synchronously, and that the caller function will use the return data structure only once. In response, the computing system finds the pre-allocated thread-local return data structure of that particular type within the heap of the computing system, populates that return data structure with the accessed data, and returns the return data structure to the caller function. Again, once the caller function has used the return data structure, the reusable thread-local return data structure remains on the heap.

In this way, the computing system avoids having to allocate a return data structure on the heap every time an asynchronous function synchronously returns, thereby making more efficient use of computing resource when synchronously returning from asynchronous functions. This may be particularly advantageous if that data is to be returned down multiple levels in a stack in data structures of the same type, as that would typically involve reallocating a return data structure in the heap each time the data is passed down the stack. Instead, the same return data structure could be reused each time the data is passed down the stack.

Because the principles described herein are performed in the context of a computing system, some introductory discussion of a computing system will be described with respect to <FIG>.

As illustrated in <FIG>, in its most basic configuration, a computing system <NUM> includes at least one hardware processing unit <NUM> and memory <NUM>. The processing unit <NUM> includes a general-purpose processor. Although not required, the processing unit <NUM> may also include a field programmable gate array (FPGA), an application specific integrated circuit (ASIC), or any other specialized circuit. In one embodiment, the memory <NUM> includes a physical system memory. That physical system memory may be volatile, non-volatile, or some combination of the two. In a second embodiment, the memory is non-volatile mass storage such as physical storage media. If the computing system is distributed, the processing, memory and/or storage capability may be distributed as well.

The computing system <NUM> also has thereon multiple structures often referred to as an "executable component". For instance, the memory <NUM> of the computing system <NUM> is illustrated as including executable component <NUM>. The term "executable component" is the name for a structure that is well understood to one of ordinary skill in the art in the field of computing as being a structure that can be software, hardware, or a combination thereof. For instance, when implemented in software, one of ordinary skill in the art would understand that the structure of an executable component may include software objects, routines, methods (and so forth) that may be executed on the computing system. Such an executable component exists in the heap of a computing system, in computer-readable storage media, or a combination.

One of ordinary skill in the art will recognize that the structure of the executable component exists on a computer-readable medium such that, when interpreted by one or more processors of a computing system (e.g., by a processor thread), the computing system is caused to perform a function. Such structure may be computer readable directly by the processors (as is the case if the executable component were binary). Alternatively, the structure may be structured to be interpretable and/or compiled (whether in a single stage or in multiple stages) so as to generate such binary that is directly interpretable by the processors. Such an understanding of example structures of an executable component is well within the understanding of one of ordinary skill in the art of computing when using the term "executable component".

While not all computing systems require a user interface, in some embodiments, the computing system <NUM> includes a user interface system <NUM> for use in interfacing with a user. The user interface system <NUM> may include output mechanisms 112A as well as input mechanisms 112B. The principles described herein are not limited to the precise output mechanisms 112A or input mechanisms 112B as such will depend on the nature of the device. However, output mechanisms 112A might include, for instance, speakers, displays, tactile output, virtual or augmented reality, holograms and so forth. Examples of input mechanisms 112B might include, for instance, microphones, touchscreens, virtual or augmented reality, holograms, cameras, keyboards, mouse or other pointer input, sensors of any type, and so forth.

For the processes and methods disclosed herein, the operations performed in the processes and methods may be implemented in differing order. Furthermore, the outlined operations are only provided as examples, and some of the operations may be optional, combined into fewer steps and operations, supplemented with further operations, or expanded into additional operations without detracting from the essence of the disclosed embodiments.

<FIG> illustrates an operation environment <NUM> that may operate within the computing system <NUM> described above with respect to <FIG>. The operation environment <NUM> includes a management component <NUM>, a scheduler component <NUM>, thread operation environments <NUM>, and a heap <NUM>. Each of the management component <NUM> and the scheduler component <NUM> may be structured as described above from the executable component <NUM> of <FIG>. The operation of the management component <NUM> will be described further below.

A thread represents a sequence of execution that operates within a particular thread environment. Multi-threaded systems allow for multiple sequences of execution, each having their own environment. For instance, the thread operation environments <NUM> are illustrated as including a first thread operation environment <NUM> for a first thread <NUM>, and a second thread operation environment <NUM> for a second thread <NUM>, with the ellipsis <NUM> representing that there may be any number of threads operating on the computing system limited only by the capabilities of the computing system, its processing units, and its operating system. The number of threads operating within a computing system can vary dynamically over time as new threads start execution, and old threads complete execution. The scheduler component <NUM> assigns the different threads to a processor cores and/or times.

Each thread operation environment can have its own dedicated variables called "thread-local" variables that only the corresponding thread can access in accordance with access permissions of the thread-local variables. For instance, the first thread operation environment <NUM> has visibility on the thread-local variables of the first thread <NUM>, but not the thread-local variables of the second thread <NUM>. Likewise, the second thread operation environment <NUM> has visibility on the thread-local variables of the second thread <NUM>, but not the thread-local variables of the first thread <NUM>. However, multiple thread operation environments can have visibility on non-thread-local variables. As an example, both the first thread <NUM> and the second thread <NUM> have visibility and operate upon non-thread-local variables.

Whether thread-local or not, the data structures representing variables are allocated on the heap <NUM>. The heap <NUM> may be a portion of the memory of the computing system. For instance, when the operation environment <NUM> is on the computing system <NUM> of <FIG>, the heap <NUM> is within the memory <NUM> of computing system <NUM>.

<FIG> illustrates a thread operation environment <NUM> that includes functions and data structures that are visible to a particular thread <NUM>. As an example, if the thread <NUM> is the first thread <NUM> of <FIG>, the thread operation environment <NUM> is an example of the first thread operation environment <NUM> of <FIG>. If the thread <NUM> is the second thread <NUM> of <FIG>, the thread operation environment <NUM> is an example of the second thread operation environment <NUM> of <FIG>.

The thread operation environment <NUM> includes a plurality of functions <NUM> and data structures <NUM> that are each visible to the corresponding thread <NUM>. For instance, by way of example only, the functions <NUM> are illustrated as including five functions <NUM> through <NUM>, though the ellipsis <NUM> represents that a thread operation environment may include any number of functions, and the number of functions within a thread operation environment may change dynamically over time as the thread creates and deletes function instances.

The data structures <NUM> represent data structures present within the heap <NUM> and that are visible to the corresponding thread <NUM>. The data structures <NUM> are illustrated as including three data structures <NUM> through <NUM>, although the ellipsis <NUM> represents that the thread operation environment <NUM> may have visible any number of data structures, and the number of data structures may change dynamically over time as data structures visible to the thread <NUM> are created and deleted. The data structures <NUM> may be of different types as represented by the data structures <NUM> through <NUM> having different shapes. As an example, the data structures <NUM> and <NUM> are of the same type as symbolized by them being illustrated as circles. However, the data structure <NUM> is of a different type as symbolized by it being illustrated as a triangle.

Within a given thread operation environment, one function may place a function call to another function, and potentially also receive resulting data. For example, <FIG> illustrates two functions <NUM> and <NUM>. Function <NUM> places a function call <NUM> to the function <NUM>. The function <NUM> will be referred to herein as a "caller function" as it is the function that places the function call <NUM>. The function <NUM> will be referred to as the "callee function" as it is the function that receives the function call <NUM>. With respect to the function call <NUM>, the function <NUM> is the caller, and the function <NUM> is the callee. However, with respect to other function calls not shown, the function <NUM> may be a callee. And with respect to other function calls not shown, the function <NUM> may be a caller. In accordance with the principles described herein, the callee function is an asynchronous function.

The asynchronous function <NUM> thereafter synchronously returns (as represented by arrow <NUM>) a call return to the caller function <NUM>. In the illustrated case, the asynchronous function <NUM> returns a return data structure <NUM> of a particular type. This particular type is represented by the data structure <NUM> having a particular triangular shape. As an example, the return data structure <NUM> may be the data structure <NUM> of <FIG>. For instance, asynchronous functions running in the common language runtime returns an object of type "Task". The asynchronous function <NUM> populated the return data structure <NUM> with data <NUM> that the caller function <NUM> can then use. Because the function return <NUM> is a synchronous function return, the data <NUM> is the final result that is produced by the asynchronous function <NUM>. In other words, the data <NUM> is not some intermediate result necessitating that the asynchronous function be scheduled to resume upon the occurrence of some future event in order to later generate the final result.

<FIG> illustrates a flowchart of a method <NUM> performed by a computing system for returning thread operation from an asynchronous function to a caller function using a return data structure so that the return data structure can be reused for future asynchronous function returns within the thread. As an example, in an example described hereinafter, the computing system performs the method <NUM> for causing the call return <NUM> to be provided from the asynchronous function <NUM> to the caller function <NUM>. The method <NUM> may be performed by a computing system, such as the computing system <NUM> of <FIG>. As a specific example, the method <NUM> may be performed by the management component <NUM> of <FIG>, which may be a runtime environment or a component of a runtime environment of the computing system <NUM>.

The method <NUM> includes accessing data generated by an asynchronous function (act <NUM>). For example, referring to <FIG>, the computing system accesses the data <NUM> that was generated by the asynchronous function <NUM> as a final result in response to the caller function <NUM> placing the function call <NUM>.

In addition, the computing system determines that the data is to be returned within a return data structure of a particular type to the caller function (act <NUM>). Referring to the example of <FIG>, the computing system determines that the data <NUM> is to be populated within a return data structure that is of a particular type (represented by the triangular shape). As an example, functions that are described as "async" in C# code are returned in the form of what is referred to as a "Task" object in C# code.

The computing system also determines that the function return is a synchronous function return (act <NUM>). As an example, the "Task" object in C# is marked as complete when the Task object is synchronously returned from the asynchronous function. The computing system further dynamically determines that the caller function will use the return data structure as populated by the data only once (act <NUM>). For instance, referring to <FIG>, the computing system determines that the caller function <NUM> will use the return data structure <NUM> only once while populated with the data <NUM>.

In response to determining that the caller function will use the return data structure only once as populated by the data, the computing system attempts to find a thread-local return data structure that has already been allocated within the heap (act <NUM>). If the computing system successfully finds such a reusable return data structure ("Yes" in decision block <NUM>), the computing system populates the thread-local return data structure with the accessed data (act <NUM>), and returns that populated data structure (act <NUM>). Otherwise, if the computing system does not find such a reusable return data structure ("No" in decision block <NUM>), the computing system allocates the thread-local return data structure within the heap of the computing system (act <NUM>), and only then populates the allocated thread-local return data structure with the accessed data (act <NUM>), prior to returning the reusable return data structure to the caller function (act <NUM>). In either case, as represented in <FIG>, the caller function <NUM> receives the reusable thread-local return data structure <NUM> as populated with the return data <NUM>.

Once the caller function <NUM> uses the return data structure <NUM>, the caller function <NUM> thereafter has no more ability to use the return data structure <NUM>, and may not even have visibility on the return data structure <NUM> at all. However, the thread-local return data structure <NUM> remains within the heap of the computing system. Accordingly, when the same or a different caller functions makes a function call to the same or a different asynchronous function, that same pre-allocated thread-local return data structure can be reused, with the computing system populating the new accessed data into the same thread-local return data structure.

Because the return data structure is thread-local, the computing system can verify with certainty whether the return data structure as populated by any given return data will be used only once. Once that is confirmed, and the computing system verifies that the caller function did use the return data structure that once, it is certain that no other function will use the return data structure until the method <NUM> is once again performed, causing new data from a new asynchronous function to be populated into the return data structure to a new caller function. Thus, the appearance of immutability is preserved from the perspective of any function within the thread operation environment. Yet, instead of having to allocate a return data structure every time an asynchronous function synchronously returns, the computing system only had to allocate the return data structure once. Furthermore, this capability exists regardless of the type of data that is populated into the return data structure. For example, in the language of C#, the particular type of the return data structure can be Task<T>, which allows for the return of any generic type of data.

The above description operates on the assumption that the computing system is capable of determining whether a return data structure is to be used only once. This description will now focus on ways that the computing system can make this determination. In one embodiment, the determination is made by a runtime compiler by finding a function that instructs the runtime environment to wait for the occurrence of a particular event. This may be accomplished if the intermediate code expresses such a wait intrinsic.

As described below, the language compiler may be structured such that the runtime compiler has visibility on such an await function. In that case, the runtime compiler may compile the await intrinsic to include a particular no-operation instructions in machine code. Such an instruction might include, for example, copying a processor register onto itself, or adding zero to the current value of a processor register. Such operations accomplish nothing and take very little processing power, thus they do not change the behavior of the machine code. However, they are used as a signal to the runtime environment that the return data structure will indeed be used only once as populated by the data. A way in which visibility of the await function can be provided to the runtime compiler will now be described.

<FIG> illustrates a process flow <NUM> associated with an example compilation and execution of software. In the case of the process flow <NUM>, compilation occurs in two distinct stages at two distinct times. Namely, compilation occurs first in a source language compilation stage at a source language compilation time, and thereafter compilation occurs in a platform compilation stage at a platform compilation time. In the example process flow <NUM> of <FIG>, at source language compilation time, a language compiler <NUM> compiles source code <NUM> into intermediate code <NUM>. At platform compilation time, a runtime compiler <NUM> compiles the intermediate code <NUM> into machine code <NUM>.

After compilation, at execution time, the compiled code is executed by a runtime environment. As depicted in <FIG>, at execution time, the runtime environment <NUM> executes the machine code <NUM> to accomplish within the runtime environment <NUM> the functionality original described in the source code <NUM>. Each of the language compiler <NUM>, the runtime compiler <NUM> and the runtime environment <NUM> may be structured as described above for the computing system <NUM> of <FIG>. The runtime environment <NUM> may also include components <NUM> that are used to assist the runtime environment <NUM> in running asynchronous functions. As an example, the components <NUM> include components <NUM> and <NUM>, an example of which will be described further below. If the runtime compiler <NUM> is a Just-in-Time (JIT) compiler, then runtime compilation occurs in preparation for immediate execution of the resulting machine code. However, the runtime compiler <NUM> may alternatively be an Ahead-of-Time compiler, in which case the resulting machine code can be executed well after platform compilation time.

The source code <NUM> is authored in a source code language and thus conforms to semantic rules defined by the source code language <NUM>. Examples of source code languages include Java, C#, Pascal, Python, JavaScript, amongst many others. In general, source code language uses textual structures and semantic rules that are more intuitive to a human programmer to express software functionality. The language compiler <NUM> is configured to compile source code of a particular source code language. As examples, there are Java compilers, C# compilers, Pascal compilers, Python compilers, Javascript compilers, and compilers for all other source code languages. The language compiler <NUM> is considered a front-end compiler and may perform lexical, syntactic and semantic analysis to generate the intermediate code <NUM>.

The intermediate code <NUM> is code format that can be executed across a broad spectrum of different platforms after compilation by an appropriate runtime compiler. That is, the runtime compiler for a particular platform will take into consideration that platform-specific runtime environment, and compile the intermediate code <NUM> into machine code that is targeted to, and optimized for, the corresponding platform. The intermediate code <NUM> may also be independent of the source code language that it was compiled from. However, intermediate code can be either source code language specific or source code language independent.

Examples of intermediate code include Byte Code which is specific to Java. There is also three-address code, which is source language independent. As another example, there is an intermediate language called "Common Intermediate Language" (or CIL) that is designed for runtime compilers used by the. NET framework. There can be different levels of intermediate language code - such as high level intermediate code which is closer to the source code, and low level intermediate code which is close to the machine code, and all levels in between. The principles described herein are not limited to a particular type of intermediate language, or whether such intermediate languages now exist or are to be developed in the future.

The use of intermediate code is helpful as it keeps the analysis portion of the compiler the same regardless of the nature of the platform in which the intermediate code will execute. Thus, a full compiler is not required for each unique system in which the code will operate. The runtime compiler can thus focus on optimization to a particular environment. In practice, source language compilation time often occurs prior to delivery of software for execution in a particular platform, whereas platform compilation time occurs after delivery of the software, once the characteristics of the platform in which the software will execute is known. In some embodiments, platform compilation time occurs at the same time as execution time in a just-in-time model. In other embodiments, platform compilation time can occur well in advance of the execution of the software within the runtime environment.

<FIG> illustrates a flowchart of a method <NUM> for a language compiler to compile source code into intermediate code, in accordance with the principles described herein. The method <NUM> may be performed by the language compiler <NUM> of <FIG>. As an example, if the language compiler <NUM> is structured as described above for the computing system <NUM> of <FIG>, there are computer-executable instructions stored in the memory <NUM> of the computing system <NUM> such that, if the computer-executable instructions are executed by the one or more processing units <NUM>, the computing system <NUM> performs the method <NUM>.

The method <NUM> includes parsing source code that conforms with a language of the language compiler (act <NUM>). Referring to <FIG> as an example, the language compiler <NUM> parses the source code <NUM> using lexicological, syntactic and semantic rules of the source code language corresponding to the language compiler <NUM>. This allows the language compiler to build an abstract syntax tree of the source code <NUM>.

In this process, suppose that the language compiler detects a source code representation of an asynchronous function that was represented within the source code (act <NUM>). <FIG> schematically illustrates source code <NUM>, which includes a source code representation of an asynchronous function <NUM>. An "asynchronous function" is any function, method, object, or component that can be paused at one or more execution points, and then resume upon the occurrence of a specified event. The following is an example of source code drafted in C# that includes an asynchronous function called "DoStuff", and which has line numbering added for clarity and for future reference.

This example will be referred to as the "DoStuff" example herein. As shown in line <NUM>, the DoStuff function receives an object called "oasync" which is of type "ObjectThatHasAsync", which is an object type that includes one or more asynchronous methods. The "DoStuff" function operates as described between the opening bracket on line <NUM> and the closing bracket on line <NUM>. Specifically, the object includes a method called "DoOtherStuff" as well as a method called "DateToString", any one of which perhaps having to pause at some point waiting for an event to occur.

As shown in line <NUM>, "DateTime date = await oasync. DoOtherStuff();", which declares a parameter called "date" and being of type "DateTime". The term "await" identifies the method DoOtherStuff as being capable of returning either synchronously or asynchronously. Here, returning asynchronously means that the method was able to perform all of its processing and return an actual value (in this case "date" of type DateTime). Returning asynchronous means that the method paused and returned without a complete value, and can resume upon the occurrence of an event. As shown in line <NUM>, "string str = await oasync. DateToString(date)" declares a parameter called "str" of type string. The term "await" identifies the method DateToString as also being capable of returning either synchronously or asynchronously. As shown in line <NUM>, "return Int32. Parse(str);" causes the DoStuff method to return in Int32 representation of the value str of type string.

Returning to <FIG>, in response to detecting that the source code includes a source code representation of an asynchronous function (act <NUM>), the language compiler generates an intermediate language representation of an asynchronous state machine that corresponds to the asynchronous function (act <NUM>). This declaration is structured to be interpreted by a runtime compiler as an instruction to declare an asynchronous state machine. <FIG> illustrates an example of an intermediate code <NUM> that includes an intermediate code representation <NUM> of the asynchronous state machine that describes the same functionality as the source code representation <NUM> of the asynchronous state machine.

The intermediate code <NUM> is an example of the intermediate code <NUM> of <FIG>. Thus, the asynchronous function is visible to the runtime compiler (e.g., runtime compiler <NUM>), and thus the runtime compiler can optimize performance of the asynchronous function. The intermediate code representation <NUM> of the asynchronous function includes three data structures <NUM>, <NUM> and <NUM>, which will now be described. The data structure <NUM> is a marker <NUM> that is structured to be interpretable by the runtime compiler as indicating that the asynchronous function can return asynchronously. Stated differently, the marker <NUM> is interpretable by the runtime compiler as an instruction to make available to a runtime of the asynchronous function one or more components that assist the asynchronous function to return asynchronously. Referring to <FIG>, the components <NUM> may be made available to the runtime environment <NUM> in response to a marker <NUM> being present within the intermediate code representation <NUM> of the asynchronous function.

The data structure <NUM> is a location identifier <NUM> interpretable by the runtime compiler as identifying one or more portions at which the asynchronous function can pause. There may a location identifier <NUM> for each location at which the asynchronous function can pause. The data structure <NUM> is an instruction <NUM> that is structured to be interpretable by the runtime environment <NUM> as instructing how to return from the asynchronous function.

Returning to the DoStuff example, the source code for the DoStuff function could be compiled into the following intermediate code representation (with line numbers added for clarity and ease of reference).

Line <NUM> is an example of marker <NUM> of <FIG>, and is an intermediate code representation of an instruction to the runtime compiler to make available to the runtime components that can be used to perform the asynchronous function at runtime. For example, line <NUM> will cause components to be available that are used by the runtime to generate an asynchronous state machine. Such components include a pause component called "RuntimeHelpers. Await", which is later invoked in lines <NUM> and <NUM>, and are each examples of the location identification <NUM> in <FIG>. Such components also include a return component called "RuntimeHelpers. RuntimeGeneratedTaskTReturn", which is later invoked in line <NUM>, which is an example of the return instruction <NUM> of <FIG>. Referring to <FIG>, the function <NUM> represents a pause component that instructs the runtime <NUM> on where the asynchronous function represented in the machine code <NUM> can pause. The function <NUM> represents a return component that instructs the runtime <NUM> on how to return from an asynchronous function "Task<int> DoStuff(ObjectThatHasAsync oasync)".

Line <NUM> identifies that source code function that the intermediate code corresponds to. In this case, the intermediate code is an intermediate code representation of the source code ". Lines <NUM> and <NUM> declare the local variables of type DateTime and String. The opening bracket on line <NUM> and the closing bracket on line <NUM> and define the extend of the intermediate code that defines the function identified in line <NUM>. Lines <NUM> to <NUM> of the intermediate code is the intermediate code representation of the source code from line <NUM> of the source code. Lines <NUM> to <NUM> of the intermediate code is the intermediate code representation of the source code from line <NUM> of the source code. Lines <NUM> to <NUM> of the intermediate code is the intermediate code representation of the source code from line <NUM> of the source code. This patent application will hereinafter refer to this intermediate code of the DoStuff example.

<FIG> illustrates a flowchart of a method <NUM> for compiling intermediate code into binary code that is executable by a runtime environment, in accordance with the principles described herein. The method <NUM> may be performed by the runtime compiler <NUM> of <FIG>. As an example, if the runtime compiler <NUM> is structured as described above for the computing system <NUM> of <FIG>, there are computer-executable instructions stored in the memory <NUM> of the computing system <NUM> such that, if the computer-executable instructions are executed by the one or more processing units <NUM>, the computing system <NUM> performs the method <NUM>.

The method <NUM> includes parsing intermediate code that is structured to be interpreted by a runtime compiler (act <NUM>). As an example, the runtime compiler may be the runtime compiler <NUM> of <FIG>, which may be part of the runtime environment <NUM> of a computing system. While parsing the intermediate language code (act <NUM>), the runtime compiling detects an intermediate language representation of an asynchronous state machine from the parsed intermediate code (act <NUM>). For instance, when parsing the intermediate code of the DoStuff example, there are two times that the runtime compiler would detect, once at line <NUM> when encountering "Task<DateTime> ObjectThatHasAsync. DoOtherStuff()" and once at line <NUM> when encountering "Task<string> ObjectThatHasAsync. DateToString(DateTime)". The runtime compiler then generates machine language code that, when executed by the runtime environment, formulates an asynchronous state machine in the memory of the computing system (act <NUM>). It is at this point that the runtime compiler <NUM> can also insert the no-operation instruction in association with the other machine code associated with each await instruction (at lines <NUM> and <NUM>).

<FIG> illustrates a flowchart of a method <NUM> for executing the asynchronous function by executing machine language code, in accordance with the principles described herein. Here, the method <NUM> may be performed by the runtime environment <NUM> of <FIG>. As an example, if the runtime environment <NUM> is structured as described above for the computing system <NUM> of <FIG>, there are computer-executable instructions stored in the memory <NUM> of the computing system <NUM> such that, if the computer-executable instructions are executed by the one or more processing units <NUM>, the computing system <NUM> performs the method <NUM>. If the runtime compiler is a Just-in-Time compiler, then the method <NUM> would be performed right after the machine code is generated in the method <NUM> of <FIG>. However, in Ahead-of-Time compilation, the machine code may not be executed for some time.

In accordance with the method <NUM>, the runtime environment executes a binary representation of the intermediate code. For example, referring to <FIG>, the runtime environment <NUM> executes the machine code <NUM> (act <NUM>). Because the machine code is structured to cause the runtime environment to formulate a state machine representation of the asynchronous function (e.g., in act <NUM> of method <NUM>), the execution of the machine code (act <NUM>) causes the runtime environment to formulate the state machine. More details about how this could be accomplished will now be described with respect to the intermediate code of the DoStuff example.

Upon executing the machine code that the runtime compiler compiled from line <NUM> of the intermediate code, the runtime environment imports the functions that are within the System. CompilerServices library corresponding to the. RuntimeGeneratedAsyncStateMachineAttribute attribute. These include at least the functions identified as "RuntimeHelpers. Await" and "RuntimeHelpers. RuntimeGeneratedTaskTReturn".

Upon executing the machine code compiled from line <NUM> "Task<DateTime> ObjectThatHasAsync. DoOtherStuff()" of the intermediate code, the runtime environment will cause the DoOtherStuff method to be called, and such will return with a task that is called in the intermediate code "Task<DateTime>". Upon executing the machine code compiled from line <NUM> "RuntimeHelpers. Await<DateTime>(Task<!!<NUM>>)", the runtime environment determines whether the method has returned a completed value (e.g., an actual DateTime value), and if it has, it allows execution to proceed. In this case, the Task<DateTime> returns an actual DateTime value.

However, if the runtime determines that the method has returned to await an event, the execution of the binary corresponding to line <NUM> causes the DoStuff function to pause, constructs a state machine that will allow the DoStuff function to resume upon the occurrence of a specified event, causes the DoStuff function to return with the state machine, and schedules the DoStuff function to resume (by causing the state machine to continue) upon the occurrence of the event. As part of the construction of the state machine, state may be saved to the runtime environment that may later be loaded in order to resume operation of the DoStuff method at the point at which it was paused.

Similarly, upon executing the machine code compiled from line <NUM> "Task<string> ObjectThatHasAsync. DateToString(DateTime)" of the intermediate code, the runtime environment will cause the DateToString method to be called with a completed value from the DoOtherStuff method (which is the DateTime value). Upon executing the machine code compiled from line <NUM> "RuntimeHelpers. Await<string>(Task<! !<NUM>>)", the runtime environment determines whether the method has returned a completed value (e.g., an actual String value), and if it has, it allows execution to proceed (e.g., to return from the method DoStuff in line <NUM>).

However, if the runtime environment determines that the method has returned to await an event, the execution of the binary corresponding to line <NUM> causes the DoStuff function to pause, constructs a state machine that will allow the DoStuff function to resume upon the occurrence of a specified event, causes the DoStuff function to return with the state machine, and schedules the DoStuff function to resume (by causing the state machine to continue) upon the occurrence of the event.

However, suppose the execution of line <NUM> of the intermediate code has already caused a state machine to be constructed because the DoOtherStuff method also returned asynchronously. In that case, there would be no need to reconstruct the state machine. Instead, the state of the DoStuff method would be recorded back into the previously constructed state machine. Accordingly, the runtime environment would then record the state and just schedule the resumption of DoStuff method to occur upon the occurrence of the event.

Suppose that the DoStuff function is returning a value synchronously. This would correspond to cases where during the execution of the method at lines <NUM> and <NUM>, the called functions returned synchronously. In that case, at the execution of line <NUM>, the function DoStuff is about to synchronously return a completed Task object, which is an example of the return data structure <NUM>. At this point, since this return is a synchronous return (act <NUM>), the runtime would determine that the return data structure <NUM> would be used only once (act <NUM>), and thus could allocate or reuse a thread-local return data structure, and populate that thread-local data structure with the final data (act <NUM>).

Accordingly, the principles described herein allow for an effective way for an asynchronous function to return without having to re-allocate a return data structure every time that asynchronous functions return. Furthermore, the principles described herein may be employed to achieve this end regardless of whether the values returned are common values, or any generic value.

Claim 1:
A method performed by a computing system for synchronously returning thread operation from an asynchronous function to a caller function using a return data structure so that the return data structure can be reused for future asynchronous function returns within the thread, the method comprising:
accessing (<NUM>) data generated by an asynchronous function in response to a caller function placing a function call to the asynchronous function;
determining (<NUM>) that the data is to be synchronously returned within a return data structure of a particular type to the caller function;
determining (<NUM>) that the caller function will use the return data structure as populated by the data only once, wherein the determining that the caller function will use the return data structure only once as populated by the data comprises determining that there is a no-operation instruction in a predetermined location within machine code of the calling function, the no-operation being used as a signal that the return data structure will be used only once as populated by the data; and
in response to determining that the caller function will use the return data structure only once as populated by the data,
allocating (<NUM>) a thread-local return data structure within the heap of the computing system;
populating (<NUM>) the thread-local return data structure with the accessed data; and
returning (<NUM>) the populated thread-local return data structure to the caller function, wherein the thread-local return data structure remains in the heap to be reused upon future returns from asynchronous functions within the thread.