{"id":2201,"date":"2014-02-24T13:00:13","date_gmt":"2014-02-24T13:00:13","guid":{"rendered":"http:\/\/writeasync.net\/?p=2201"},"modified":"2014-02-23T23:59:00","modified_gmt":"2014-02-23T23:59:00","slug":"overlapped-io","status":"publish","type":"post","link":"http:\/\/writeasync.net\/?p=2201","title":{"rendered":"Introducing overlapped I\/O"},"content":{"rendered":"<p>In Windows, I\/O operations such as <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/aa365467(v=vs.85).aspx\"><code>ReadFile<\/code><\/a> can be performed synchronously or asynchronously. Asynchronous I\/O is generally referred to as <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/aa365683(v=vs.85).aspx\">overlapped I\/O<\/a> since multiple operations can be issued at once and &#8220;overlap&#8221; in their request lifetimes. There are a few techniques for dealing with overlapped I\/O, but they basically boil down to using either <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/aa363772(v=vs.85).aspx\">alertable I\/O<\/a> or <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/aa365198(v=vs.85).aspx\">I\/O completion ports<\/a>.<\/p>\n<p>Alertable I\/O relies on <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms681951(v=vs.85).aspx\">asynchronous procedure calls<\/a> and is more limited &#8212; in particular, the completion callback always runs on the same thread as the original request and cannot be dispatched until the thread is in &#8220;alertable state&#8221; (which generally involves calling a blocking wait function like <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms687036(v=vs.85).aspx\"><code>WaitForSingleObjectEx<\/code><\/a>). This really isn&#8217;t compatible with the <a href=\"http:\/\/en.wikipedia.org\/wiki\/Continuation-passing_style\">continuation-passing style<\/a> of asynchronous programming which is implemented by the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd492418.aspx\">Parallel Patterns Library<\/a> in C++ and the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd460717(v=vs.110).aspx\">Task Parallel Library<\/a> in .NET.<\/p>\n<p>So that leaves I\/O completion ports as the mechanism of choice for native async, and indeed this is the mechanism used under the hood by .NET APIs like <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.io.filestream.readasync(v=vs.110).aspx\"><code>FileStream.ReadAsync<\/code><\/a> (assuming of course that <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.io.filestream.isasync(v=vs.110).aspx\"><code>FileStream.IsAsync<\/code><\/a> is <code>true<\/code>). Unfortunately, direct manipulation of completion ports is rather complicated (although <a href=\"http:\/\/kennykerr.ca\">Kenny Kerr<\/a>&#8216;s illuminating blog post <a href=\"http:\/\/weblogs.asp.net\/kennykerr\/archive\/2008\/01\/03\/parallel-programming-with-c-part-4-i-o-completion-ports.aspx\">&#8220;I\/O Completion Ports&#8221;<\/a> helps a bit). Luckily there is a (relatively) simpler way with the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms686766(v=vs.85).aspx\">new thread pool API<\/a> available since Windows Vista. Using <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms682464(v=vs.85).aspx\"><code>CreateThreadPoolIo<\/code><\/a>, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms686326(v=vs.85).aspx\"><code>StartThreadPoolIo<\/code><\/a>, etc. an application can use worker threads in the thread pool to efficiently handle I\/O completion callbacks.<\/p>\n<p>Let&#8217;s walk through an example set of C++ classes for handling asynchronous file reading. (I should note that in the middle of writing this example I saw Artur Laksberg&#8217;s blog post <a href=\"http:\/\/blogs.msdn.com\/b\/nativeconcurrency\/archive\/2012\/07\/21\/simplifying-overlapped-i-o-with-ppl.aspx\">&#8220;Simplifying Overlapped I\/O With PPL&#8221;<\/a> which covers very similar ground, though there is hopefully enough &#8220;value-add&#8221; in this post to justify its existence.) First, a few core includes, namespaces, and classes:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;Windows.h&gt;\r\n#include &lt;string&gt;\r\n#include &lt;iostream&gt;\r\n#include &lt;sstream&gt;\r\n#include &lt;vector&gt;\r\n#include &lt;stdexcept&gt;\r\n#include &lt;memory&gt;\r\n#include &lt;functional&gt;\r\n#include &lt;ppltasks.h&gt;\r\n#include &quot;ppltasks_extra.h&quot;\r\n\r\nusing namespace concurrency;\r\nusing namespace concurrency::extras;\r\nusing namespace std;\r\n\r\n\/\/ Utility function to trace a message with a thread ID\r\nvoid TraceThread(wstring const &amp; text)\r\n{\r\n    DWORD id = GetCurrentThreadId();\r\n    wstringstream wss;\r\n    wss &lt;&lt; L&quot;&#x5B;T=&quot; &lt;&lt; id &lt;&lt; L&quot;] &quot; &lt;&lt; text &lt;&lt; endl;\r\n    wcout &lt;&lt; wss.str();\r\n}\r\n\r\n\/\/ Base class to mark a class as non-copyable\r\nstruct DenyCopy\r\n{\r\n    DenyCopy() { }\r\n    ~DenyCopy() { }\r\n    DenyCopy &amp; operator =(DenyCopy const &amp;) = delete;\r\n    DenyCopy(DenyCopy const &amp;) = delete;\r\n};\r\n\r\n\/\/ Exception created from generic Win32 error code.\r\nclass Win32Error : public runtime_error\r\n{\r\npublic:\r\n    Win32Error(string const &amp; message, DWORD error)\r\n        : runtime_error(message),\r\n        error_(error)\r\n    {\r\n    }\r\n\r\n    ~Win32Error() { }\r\n\r\n    DWORD get_Error() const { return error_; }\r\n\r\nprivate:\r\n    DWORD error_;\r\n};\r\n\r\n\/\/ IO-specific exception associated with a given file.\r\nclass IOError : public Win32Error\r\n{\r\npublic:\r\n    IOError(string const &amp; message, DWORD error, wstring const &amp; fileName)\r\n        : Win32Error(message, error),\r\n        fileName_(fileName)\r\n    {\r\n    }\r\n\r\n    ~IOError() { }\r\n\r\n    wstring const &amp; get_FileName() const { return fileName_; }\r\n\r\nprivate:\r\n    wstring fileName_;\r\n};\r\n<\/pre>\n<p>Now, we need a file handle wrapper that will open a file for reading and prepare it for overlapped I\/O. We&#8217;ll need use <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/aa363858(v=vs.85).aspx\"><code>CreateFile<\/code><\/a> (with <code>OPEN_EXISTING<\/code>) and pass <a href=\"\"><code>FILE_FLAG_OVERLAPPED<\/code><\/a> otherwise all async I\/O attempts will fail:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nclass FileReadHandle : private DenyCopy\r\n{\r\npublic:\r\n    FileReadHandle(wstring const &amp; fileName)\r\n        : fileName_(fileName),\r\n        handle_(CreateFile(fileName.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, nullptr))\r\n    {\r\n        if (handle_ == INVALID_HANDLE_VALUE)\r\n        {\r\n            DWORD error = GetLastError();\r\n            throw IOError(&quot;Open failed.&quot;, error, fileName);\r\n        }\r\n    }\r\n\r\n    ~FileReadHandle()\r\n    {\r\n        CloseHandle(handle_);\r\n    }\r\n\r\n    wstring const &amp; get_FileName() const { return fileName_; }\r\n\r\n    HANDLE get_Value() { return handle_; }\r\n\r\nprivate:\r\n    wstring fileName_;\r\n    HANDLE handle_;\r\n};\r\n<\/pre>\n<p>Now we need to add some code for manipulating I\/O worker threads. The expected workflow involves multiple steps as follows:<\/p>\n<ul>\n<li>Before issuing <em>any<\/em> I\/O requests, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms682464(v=vs.85).aspx\"><code>CreateThreadPoolIo<\/code><\/a> must be called to associate a callback for the specified handle.<\/li>\n<li>Before <em>each<\/em> I\/O request, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms686326(v=vs.85).aspx\"><code>StartThreadpoolIo<\/code><\/a> must be called.<\/li>\n<li>If the I\/O request fails, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms681983(v=vs.85).aspx\"><code>CancelThreadPoolIo<\/code><\/a> must be called to explicitly clean up resources associated with the request.<\/li>\n<li>After the <em>final<\/em> I\/O request, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms682038(v=vs.85).aspx\"><code>CloseThreadPoolIo<\/code><\/a> must be called to clean up thread pool resources. To ensure there are no pending requests still active, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms687038(v=vs.85).aspx\"><code>WaitForThreadpoolIoCallbacks<\/code><\/a> can be used.<\/li>\n<\/ul>\n<p>These steps are easy to get wrong, so it is safest to build an RAII pattern with cleanup happening in destructors. There are two classes that work together to manage this, <code>ThreadPoolIO<\/code> which represents the overall thread pool resources with an associated callback and <code>PendingIO<\/code> which manages a single pending call. Calling <code>Start<\/code> signals the start of an async request and returns a <code>PendingIO<\/code> object. The <code>OnStarted<\/code> method should be called if the async request is started successfully, otherwise, the I\/O operation is safely canceled in the destructor:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nclass PendingIO : private DenyCopy\r\n{\r\npublic:\r\n    PendingIO(PTP_IO io)\r\n        : io_(io)\r\n    {\r\n    }\r\n\r\n    ~PendingIO()\r\n    {\r\n        if (io_)\r\n        {\r\n            CancelThreadpoolIo(io_);\r\n        }\r\n    }\r\n\r\n    void OnStarted()\r\n    {\r\n        io_ = nullptr;\r\n    }\r\n\r\nprivate:\r\n    PTP_IO io_;\r\n};\r\n\r\ntemplate &lt;typename TCallback&gt;\r\nclass ThreadPoolIO : private DenyCopy\r\n{\r\npublic:\r\n    ThreadPoolIO(HANDLE handle, TCallback callback)\r\n        : io_(),\r\n        callback_(callback)\r\n    {\r\n        io_ = CreateThreadpoolIo(handle, IOCompletedAdapter, this, nullptr);\r\n        if (!io_)\r\n        {\r\n            DWORD error = GetLastError();\r\n            throw Win32Error(&quot;CreateThreadPoolIo failed.&quot;, error);\r\n        }\r\n    }\r\n\r\n    ~ThreadPoolIO()\r\n    {\r\n        WaitForThreadpoolIoCallbacks(io_, FALSE);\r\n        CloseThreadpoolIo(io_);\r\n    }\r\n\r\n    PendingIO Start()\r\n    {\r\n        StartThreadpoolIo(io_);\r\n        return PendingIO(io_);\r\n    }\r\n\r\nprivate:\r\n    PTP_IO io_;\r\n    TCallback callback_;\r\n\r\n    static void WINAPI IOCompletedAdapter(\r\n        PTP_CALLBACK_INSTANCE instance,\r\n        PVOID context,\r\n        PVOID overlapped,\r\n        ULONG result,\r\n        ULONG_PTR bytesTransferred,\r\n        PTP_IO io)\r\n    {\r\n        static_cast&lt;ThreadPoolIO *&gt;(context)-&gt;OnIOCompleted(static_cast&lt;OVERLAPPED *&gt;(overlapped), result, bytesTransferred);\r\n    }\r\n\r\n    void OnIOCompleted(OVERLAPPED * overlapped, ULONG result, ULONG_PTR bytesTransferred)\r\n    {\r\n        callback_(overlapped, result, bytesTransferred);\r\n    }\r\n};\r\n<\/pre>\n<p>Note the use of an <a href=\"http:\/\/en.wikipedia.org\/wiki\/Adapter_pattern\">adapter method<\/a> (I&#8217;ve sometimes seen this referred to as a <a href=\"http:\/\/en.wikipedia.org\/wiki\/Thunk\">&#8220;thunk&#8221;<\/a>) for the I\/O completion callback. In here we cast the generic context pointer to our <code>this<\/code> pointer we originally passed and then use it to invoke the member function which in turn executes the real callback we want.<\/p>\n<p>Now for the final piece, the <code>FileReader<\/code> which does all the actual async I\/O work. The interface is very simple &#8212; just a <code>ReadAsync<\/code> method which accepts a buffer (C++-style, using a <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/aa383751(v=vs.85).aspx\"><code>BYTE<\/code><\/a> <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/9xd04bzs.aspx\"><code>vector<\/code><\/a>) and a query method <code>EndOfFile<\/code> to tell when we&#8217;ve reached the end. To construct the reader, we need to pass a <code>FileReadHandle<\/code>. Internally, this is used to create an <code>ThreadPoolIO<\/code> instance to which we also pass another callback adapter. The real work happens in the inner <code>ReadRequest<\/code> class which is an <a href=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/12\/17\/10106259.aspx\">enhanced\/augmented OVERLAPPED structure<\/a>. This enables our callback adapter (which receives the original OVERLAPPED pointer) to unpack the original <code>ReadRequest<\/code> and call an appropriate member function.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nclass FileReader : private DenyCopy\r\n{\r\npublic:\r\n    FileReader(FileReadHandle &amp; handle)\r\n        : handle_(handle),\r\n        io_(handle.get_Value(), IOCompletedAdapter),\r\n        offset_(0),\r\n        endOfFile_(false)\r\n    {\r\n    }\r\n\r\n    ~FileReader()\r\n    {\r\n    }\r\n\r\n    bool EndOfFile() const { return endOfFile_; }\r\n\r\n    task&lt;int&gt; ReadAsync(vector&lt;BYTE&gt; &amp; buffer)\r\n    {\r\n        unique_ptr&lt;ReadRequest&gt; request = make_unique&lt;ReadRequest&gt;(*this);\r\n        BOOL result = ReadFile(handle_.get_Value(), &amp;buffer&#x5B;0], static_cast&lt;DWORD&gt;(buffer.size()), nullptr, request.get());\r\n        if (!result)\r\n        {\r\n            request-&gt;OnStartError();\r\n        }\r\n\r\n        return request.release()-&gt;OnStarted();\r\n    }\r\n\r\nprivate:\r\n    class ReadRequest;\r\n\r\n    FileReadHandle &amp; handle_;\r\n    ThreadPoolIO&lt;function&lt;void(OVERLAPPED *, ULONG, ULONG_PTR)&gt;&gt; io_;\r\n    unsigned long long offset_;\r\n    bool endOfFile_;\r\n\r\n    static void WINAPI IOCompletedAdapter(OVERLAPPED * overlapped, ULONG result, ULONG_PTR bytesTransferred)\r\n    {\r\n        unique_ptr&lt;ReadRequest&gt; request(static_cast&lt;ReadRequest *&gt;(overlapped));\r\n        request-&gt;OnCompleted(static_cast&lt;DWORD&gt;(result), static_cast&lt;int&gt;(bytesTransferred));\r\n    }\r\n\r\n    class ReadRequest : public OVERLAPPED\r\n    {\r\n    public:\r\n        ReadRequest(FileReader &amp; parent)\r\n            : OVERLAPPED({ 0 }),\r\n            parent_(parent),\r\n            pendingIO_(parent.io_.Start()),\r\n            taskEvent_()\r\n        {\r\n            Offset = parent_.offset_ &amp; 0xFFFFFFFF;\r\n            OffsetHigh = parent_.offset_ &gt;&gt; 32;\r\n        }\r\n\r\n        ~ReadRequest()\r\n        {\r\n        }\r\n\r\n        void OnStartError()\r\n        {\r\n            DWORD error = GetLastError();\r\n            if (error != ERROR_IO_PENDING)\r\n            {\r\n                throw IOError(&quot;Read failed.&quot;, error, parent_.handle_.get_FileName());\r\n            }\r\n        }\r\n\r\n        task&lt;int&gt; OnStarted()\r\n        {\r\n            pendingIO_.OnStarted();\r\n            return task&lt;int&gt;(taskEvent_);\r\n        }\r\n\r\n        void OnCompleted(DWORD error, int bytesRead)\r\n        {\r\n            if (error == ERROR_HANDLE_EOF)\r\n            {\r\n                parent_.endOfFile_ = true;\r\n                error = ERROR_SUCCESS;\r\n            }\r\n\r\n            if (error == ERROR_SUCCESS)\r\n            {\r\n                parent_.offset_ += bytesRead;\r\n                taskEvent_.set(bytesRead);\r\n            }\r\n            else\r\n            {\r\n                taskEvent_.set_exception(make_exception_ptr(IOError(&quot;Read failed.&quot;, error, parent_.handle_.get_FileName())));\r\n            }\r\n        }\r\n\r\n    private:\r\n        FileReader &amp; parent_;\r\n        PendingIO pendingIO_;\r\n        task_completion_event&lt;int&gt; taskEvent_;\r\n    };\r\n};\r\n<\/pre>\n<p>Note the memory management here, achieved by careful use of <code>unique_ptr<\/code>. The <code>ReadAsync<\/code> constructs the <code>ReadRequest<\/code> in the free store, calls <code>ReadFile<\/code> and then checks the result. If it is not success, we ask the request to process the error. Note that for async requests, we almost always expect an &#8220;error&#8221; with <code>ERROR_IO_PENDING<\/code>, so the request code handles this case by simply returning &#8212; otherwise, we throw.<\/p>\n<p>In the exception case, the request is safely cleaned up by the <code>unique_ptr<\/code> destructor. However, in the success case, we <code>release()<\/code> our interest in the object as we call <code>OnStarted<\/code>; this is critical to avoid either early or double deletion, since the async request is now in flight and will complete on (and should thus be cleaned up by) another thread. If <code>OnStarted<\/code> is ultimately called, it will call <code>OnStarted<\/code> on the <code>PendingIO<\/code> object (otherwise, on destruction it will be canceled).<\/p>\n<p>When the completion callback arrives (<code>IOCompletedAdapter<\/code>), we immediately wrap the pointer back into a <code>unique_ptr<\/code> and jump back into the request. This ensures that we will always clean up the resource at the end of the call. In the <code>OnCompleted<\/code> method, we simply detect success or failure from the return value. On success, we can advance the read offset for the next call and mark the task as completed (note that we also consider EOF as success). On failure we throw an <code>IOError<\/code> back to the caller via the task.<\/p>\n<p>Sample code using <a href=\"http:\/\/blogs.msdn.com\/b\/nativeconcurrency\/archive\/2012\/05\/09\/how-to-put-a-ppltasks-continuation-chain-into-a-loop.aspx\"><code>create_iterative_task<\/code><\/a> to implement an async read loop:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\ntemplate&lt;typename TCallback&gt;\r\ntask&lt;void&gt; ReadLoopAsync(FileReader &amp; reader, int bufferSize, TCallback callback)\r\n{\r\n    TraceThread(L&quot;starting read loop&quot;);\r\n    return create_iterative_task(&#x5B;&amp;reader, bufferSize, callback]\r\n    {\r\n        shared_ptr&lt;vector&lt;BYTE&gt;&gt; buffer = make_shared&lt;vector&lt;BYTE&gt;&gt;(bufferSize);\r\n        TraceThread(L&quot;starting read&quot;);\r\n        task&lt;int&gt; readTask = reader.ReadAsync(*buffer);\r\n        return readTask.then(&#x5B;buffer, &amp;reader, callback](task&lt;int&gt; t)\r\n        {\r\n            int bytesRead = t.get();\r\n            TraceThread(L&quot;completing read request&quot;);\r\n            callback(*buffer, bytesRead);\r\n            bool shouldContinue = (bytesRead &gt; 0) &amp;&amp; !reader.EndOfFile();\r\n            return task_from_result(shouldContinue);\r\n        });\r\n    });\r\n}\r\n<\/pre>\n<p>And finally, an app that reads a file asynchronously in 64-byte chunks, printing the contents to the screen as ASCII characters:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nvoid TraceBufferRead(vector&lt;BYTE&gt; &amp; buffer, int bytesRead)\r\n{\r\n    for (int i = 0; i &lt; bytesRead; ++i)\r\n    {\r\n        cout &lt;&lt; static_cast&lt;char&gt;(buffer&#x5B;i]);\r\n    }\r\n\r\n    cout &lt;&lt; endl;\r\n}\r\n\r\nvoid ReadSample(wstring const &amp; fileName)\r\n{\r\n    FileReadHandle handle(fileName);\r\n    FileReader reader(handle);\r\n    task&lt;void&gt; task = ReadLoopAsync(reader, 64, TraceBufferRead);\r\n    task.wait();\r\n}\r\n<\/pre>\n<p>A snippet of the output using a sample file with contents of &#8216;1234567890&#8230;&#8217; (and some newlines) repeated a bunch of times:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n&#x5B;T=12676] starting read loop\r\n&#x5B;T=18444] starting read\r\n&#x5B;T=18444] completing read request\r\n1234567890123456789012345678901234567890123456789012345678901234\r\n&#x5B;T=22240] starting read\r\n&#x5B;T=18444] completing read request\r\n5678901234567890\r\n1234567890123456789012345678901234567890123456\r\n&#x5B;T=22240] starting read\r\n&#x5B;T=18444] completing read request\r\n7890123456789012345678901234567890\r\n1234567890123456789012345678\r\n&#x5B;T=22240] starting read\r\n&#x5B;T=18444] completing read request\r\n9012345678901234567890123456789012345678901234567890\r\n1234567890\r\n&#x5B;T=22240] starting read\r\n&#x5B;T=18444] completing read request\r\n1234567890123456789012345678901234567890123456789012345678901234\r\n . . . \r\n<\/pre>\n<p>Hopefully this gives you a better idea of how async works at the core. The rules for overlapped I\/O are rather complex but modern C++ design can help ease some of the burden and create safe, reusable patterns.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Windows, I\/O operations such as ReadFile can be performed synchronously or asynchronously. Asynchronous I\/O is generally referred to as overlapped I\/O since multiple operations can be issued at once and &#8220;overlap&#8221; in their request lifetimes. There are a few&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21,101],"tags":[],"class_list":["post-2201","post","type-post","status-publish","format-standard","hentry","category-async","category-native"],"_links":{"self":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/2201","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2201"}],"version-history":[{"count":0,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/2201\/revisions"}],"wp:attachment":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2201"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2201"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2201"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}