The D standard library does not include asynchronous I/O functions, but we can still use them by calling C functions or with third-party libraries.
We will execute the following steps to use asynchronous I/O:
libuv
or libevent
, bindings to which can be found at http://code.dlang.org/.Here's an example of how to get an asynchronous input from a text file on Windows using the Win32 API:
import core.sys.windows.windows; // basic Windows headers import std.conv; // Not all necessary functions are defined in core.sys.windows.windows // but that's never a dealbreaker: we can just define the prototypes ourselves // ReadFileEx is specialized for asynchronous reading extern(Windows) BOOL ReadFileEx(HANDLE, LPVOID, DWORD, OVERLAPPED*, void*); // SleepEx will pause the program, allowing our async handler to be called when the data is ready extern(Windows) DWORD SleepEx(DWORD, BOOL); // This function will be called when the operation is complete extern(Windows) void readCallback(DWORD errorCode, DWORD numberOfBytes, OVERLAPPED* overlapped) { // hEvent carries user-defined data. We load it with a pointer to the buffer below. auto data = (cast(ubyte*) overlapped.hEvent)[0 .. numberOfBytes]; import std.stdio; writeln("Received: ", cast(string) data); // we may issue another read here to get more data } void main() { // We need to open the file with the Windows API too, so we can set the // required FILE_FLAG_OVERLAPPED for asynchronous access auto handle = CreateFileA("test.txt", GENERIC_READ, 0, null, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, null); if(handle is null) throw new Exception("Couldn't open file."); scope(exit) CloseHandle(handle); // always cleanup C resources with scope guards OVERLAPPED overlapped; ubyte[1024] buffer; overlapped.hEvent = buffer.ptr; // This is allowed to be user-defined data, we'll fill it with a pointer to the buffer for use in the callback // issue the async read request if(ReadFileEx(handle, buffer.ptr, buffer.length, &overlapped, &readCallback) == 0) throw new Exception("ReadFileEx failed " ~ to!string(GetLastError())); /* Do other things while the operating system loads the data for us…. */ SleepEx(500, true); // wait for the operation to complete }
If you create a file called test.txt
and then run the program, it will display its contents.
Since D has full access to C libraries and operating system APIs, we can use functionality that is not yet available in Phobos when we need it. Asynchronous I/O is an example where this is necessary. The example is virtually identical to what you might see in an example in the C language.
On Windows, a flag must be set when the handle is created to enable asynchronous operations (called overlapped operations in the Microsoft documentation). On Posix, a flag may be set with fnctl
after the file is opened, or you may use an event loop that triggers on file readiness to avoid blocking operations.
These operations require full access to the file handle, which limits the amount of Phobos calls you can use. For example, std.process.pipeProcess
conveniently creates the pipes for you, but that precludes setting the overlapped flag. If you want to use asynchronous I/O on a pipe to communicate with another process, it will be necessary to perform all the operations yourself at the operating system API level. (Indeed, on Windows, you cannot even use the CreatePipe
function that the std.process
implementation uses to create the pipes because it does not allow you to set the overlapped flag either; you must use the lower-level CreateNamedPipe
function instead.)
In some cases, you can create a Phobos object from an operating system handle. For example, you can create std.socket.Socket
from an existing low-level handle, but in most cases, you are on your own to write the code as you would in C. When in doubt, you can search the Internet for C asynchronous I/O documentation and examples and apply that same knowledge to D.
18.117.231.15