1 | // Copyright (c) Microsoft Corporation. All rights reserved. |
2 | // Licensed under the MIT license. |
3 | |
4 | #pragma once |
5 | |
6 | #include <atomic> |
7 | #include <mutex> |
8 | #include <thread> |
9 | #include <condition_variable> |
10 | |
11 | #include "auto_ptr.h" |
12 | #include "status.h" |
13 | |
14 | namespace FASTER { |
15 | namespace core { |
16 | |
17 | #define RETURN_NOT_OK(s) do { \ |
18 | Status _s = (s); \ |
19 | if (_s != Status::Ok) return _s; \ |
20 | } while (0) |
21 | |
22 | class IAsyncContext; |
23 | |
24 | /// Signature of the async callback for I/Os. |
25 | typedef void(*AsyncIOCallback)(IAsyncContext* context, Status result, size_t bytes_transferred); |
26 | |
27 | /// Standard interface for contexts used by async callbacks. |
28 | class IAsyncContext { |
29 | public: |
30 | IAsyncContext() |
31 | : from_deep_copy_{ false } { |
32 | } |
33 | |
34 | virtual ~IAsyncContext() { } |
35 | |
36 | /// Contexts are initially allocated (as local variables) on the stack. When an operation goes |
37 | /// async, it deep copies its context to a new heap allocation; this context must also deep copy |
38 | /// its parent context, if any. Once a context has been deep copied, subsequent DeepCopy() calls |
39 | /// just return the original, heap-allocated copy. |
40 | Status DeepCopy(IAsyncContext*& context_copy) { |
41 | if(from_deep_copy_) { |
42 | // Already on the heap: nothing to do. |
43 | context_copy = this; |
44 | return Status::Ok; |
45 | } else { |
46 | RETURN_NOT_OK(DeepCopy_Internal(context_copy)); |
47 | context_copy->from_deep_copy_ = true; |
48 | return Status::Ok; |
49 | } |
50 | } |
51 | |
52 | /// Whether the internal state for the async context has been copied to a heap-allocated memory |
53 | /// block. |
54 | bool from_deep_copy() const { |
55 | return from_deep_copy_; |
56 | } |
57 | |
58 | protected: |
59 | /// Override this method to make a deep, persistent copy of your context. A context should: |
60 | /// 1. Allocate memory for its copy. If the allocation fails, return Status::OutOfMemory. |
61 | /// 2. If it has a parent/caller context, call DeepCopy() on that context. If the call fails, |
62 | /// free the memory it just allocated and return the call's error code. |
63 | /// 3. Initialize its copy and return Status::Ok.. |
64 | virtual Status DeepCopy_Internal(IAsyncContext*& context_copy) = 0; |
65 | |
66 | /// A common pattern: deep copy, when context has no parent/caller context. |
67 | template <class C> |
68 | inline static Status DeepCopy_Internal(C& context, IAsyncContext*& context_copy) { |
69 | context_copy = nullptr; |
70 | auto ctxt = alloc_context<C>(sizeof(C)); |
71 | if(!ctxt.get()) return Status::OutOfMemory; |
72 | new(ctxt.get()) C{ context }; |
73 | context_copy = ctxt.release(); |
74 | return Status::Ok; |
75 | } |
76 | /// Another common pattern: deep copy, when context has a parent/caller context. |
77 | template <class C> |
78 | inline static Status DeepCopy_Internal(C& context, IAsyncContext* caller_context, |
79 | IAsyncContext*& context_copy) { |
80 | context_copy = nullptr; |
81 | auto ctxt = alloc_context<C>(sizeof(C)); |
82 | if(!ctxt.get()) return Status::OutOfMemory; |
83 | IAsyncContext* caller_context_copy; |
84 | RETURN_NOT_OK(caller_context->DeepCopy(caller_context_copy)); |
85 | new(ctxt.get()) C{ context, caller_context_copy }; |
86 | context_copy = ctxt.release(); |
87 | return Status::Ok; |
88 | } |
89 | |
90 | private: |
91 | /// Whether the internal state for the async context has been copied to a heap-allocated memory |
92 | /// block. |
93 | bool from_deep_copy_; |
94 | }; |
95 | |
96 | /// User-defined callbacks for async FASTER operations. Async callback equivalent of: |
97 | /// Status some_function(context* arg). |
98 | typedef void(*AsyncCallback)(IAsyncContext* ctxt, Status result); |
99 | |
100 | /// Helper class, for use inside a continuation callback, that ensures the context will be freed |
101 | /// when the callback exits. |
102 | template <class C> |
103 | class CallbackContext { |
104 | public: |
105 | CallbackContext(IAsyncContext* context) |
106 | : async{ false } { |
107 | context_ = make_context_unique_ptr(static_cast<C*>(context)); |
108 | } |
109 | |
110 | ~CallbackContext() { |
111 | if(async || !context_->from_deep_copy()) { |
112 | // The callback went async again, or it never went async. The next callback or the caller is |
113 | // responsible for freeing the context. |
114 | context_.release(); |
115 | } |
116 | } |
117 | |
118 | C* get() const { |
119 | return context_.get(); |
120 | } |
121 | C* operator->() const { |
122 | return context_.get(); |
123 | } |
124 | |
125 | public: |
126 | bool async; |
127 | protected: |
128 | context_unique_ptr_t<C> context_; |
129 | }; |
130 | |
131 | } |
132 | } // namespace FASTER::core |