Calling a Platform Function asynchronously
After a PlatformFunction has been created, it can be called using one of these asynchronous protocols:
Standard asynchronous call
asyncCall, asyncCallWith:, asyncCallWith:with:, and so on
Resource future call
futureCall, futureCallWith:, futureCallWith:with:, and so on
Static future call
staticFutureCall:, staticFutureCall:with:, staticFutureCall:with:with:, and so on
The number of arguments in the call must match the number of arguments in the PlatformFunction. The following sections describe how to make asynchronous calls using each of the protocols.
Standard asynchronous calls
Standard asynchronous calls are created using the asyncCall... API. Use standard asynchronous calls when the value is required by a single process, and the process must wait until the call has completed. Standard asynchronous calls cause the current Smalltalk process to be suspended until the value is returned. Other Smalltalk processes continue to execute.
When an asynchronous call is made, an operating system thread is acquired, the current Smalltalk process suspends, and the platform function is executed in that thread. When the call completes, the result is posted back to Smalltalk, and the process that was waiting for the result resumes.
For example, given the following platform function on Windows:
isUppercase := PlatformFunction
callingConvention: 'C'
function: 'IsCharUpperA'
library: 'user32.dll'
parameterTypes: #(char8)
returnType: #bool.
the function would be called asynchronously as follows:
param1 := $a.
rc := isUppercase asyncCallWith: param1
param1 is the argument to the call. The
rc is the return value of the platform function (true/false), or an
AcoError if an error occurred during the execution of the call. For a discussion of errors, see
ACO errors and error cases.
When a standard asynchronous call is made during a system callback (that is when the operating system has called back into Smalltalk), the call is performed synchronously. Standard asynchronous calls are the fastest form of asynchronous call.
Resource future calls
Resource future calls should be used if more than one process needs to wait on the return value or if the current process must continue executing until the return value is needed.
When a resource future call is made, a future is returned. This future can be queried for the state of the call while the platform function executes in a separate thread. If a process asks for the value of the future before the value is ready, the process is suspended. More than one process can wait on the return value of the future. When the call completes, the result is posted back to Smalltalk and the processes that are waiting on the result are resumed.
For example, to call a platform function using a resource future call:
param1 := $a.
aFuture := isUppercase futureCallWith: param1.
12 fibonnacci. "May or may not execute before the call completes."
rc := aFuture value.
12 fibonnacci. "Will not execute until the call completes."
param1 is the arguments to the call. The rc is the return value of the platform function (true/false), or an AcoError if an error occurred. The aFuture is a future that contains the result of the platform function. You must explicitly ask the future for its value. If the platform function has not yet completed, the process asking for the value of the platform function call blocks until the value is ready.
To check the status of a blocking call, the messages checkForValue or isReady can be sent to the future. The message checkForValue answers the return value if it is ready or an AcoError if it is not yet ready. The message isReady answers true if the return value is ready; otherwise, it answers false. When a future is asked for its value during a system callback (that is when the operating system has called back into Smalltalk), if the return value has not been calculated, then an AcoError is answered. Resource future calls are slower than standard asynchronous calls.
Static future calls
Like resource future calls, static future calls allow the current Smalltalk process to continue running until the return value is needed.
In addition, static future calls allow you to use the same operating system thread in more than one asynchronous call. The static future call does this by reserving a thread. This is useful when data is stored in the local memory for a thread. As a result, you must manage the resources by explicitly creating and destroying static futures and their resources.
Allocating resources for static futures discusses the allocation and deallocation of resources.
To begin a static future call, the developer creates a static future and makes the call. When the call completes, the result is posted back to Smalltalk and processes waiting on the result are resumed. At this point, the developer may either reuse the static future or return its resources to the AcoResourceManager.
For example, to call a platform function using a static future call:
aFuture := AcoResourceManager default createStaticFuture.
param1 := $a.
rc := isUppercase staticFutureCallWith: param1.
rc := aFuture value.
"A second call can be made using the same thread."
c := isUppercase staticFutureCallWith: param1.
rc := aFuture value.
AcoResourceManager default returnStaticFuture: aFuture.
param1 is the parameters to the call. The rc is the return value of the platform function (true/false). The aFuture is a static future that contains the result of the platform function. You must explicitly ask the future explicitly for its value. If the platform function has not completed, the process asking for the value of a future blocks until the value is ready.
The static future can be reused for other platform function calls. These calls will be made in the same operating system thread. Once the developer no longer requires the static future, it must be returned to the AcoResourceManager.
To check the status of a blocking call, the messages checkForValue or isReady can be sent to the future. The message checkForValue answers the return value if it is ready or an AcoError if it is not ready. The message isReady answers true if the return value is ready; otherwise, it answers false.
Before a static future can be used in a subsequent call, the previous call must have completed. There are two protocols for determining this. Sending value to the future guarantees that the previous call has completed before the next starts because value waits until the call completes. When a static future is sent isAvailable, it answers true if the future is not involved in another asynchronous call and false if it is.
When a future is asked for its value during a system callback (that is when the operating system has called back into Smalltalk), if the return value has not been calculated, then an AcoError is answered.
Static future calls are slower than standard asynchronous calls, but faster than resource future calls.
Allocating resources for static futures
The AcoResourceManager allocates and deallocates resources for a static future. Resources include the thread used to make the call and some fixed-space memory. To get the default resource manager, the message default is sent to the AcoResourceManager class. The default resource manager can then be sent the following messages to allocate and deallocate resources:
createStaticFuture
Allocates a static future and the required resources. Answers the static future created.
returnStaticFuture:
Returns to the resource manager the resources used by the static future.
reinitializeStaticFuture:
Reacquires resources for the static future that were previously lost when the image was saved.
createStaticFutureWithStackSize:
Creates a static future with a thread having a stack size equal to anInteger. Answers the static future created.