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
14namespace FASTER {
15namespace core {
16
17#define RETURN_NOT_OK(s) do { \
18 Status _s = (s); \
19 if (_s != Status::Ok) return _s; \
20 } while (0)
21
22class IAsyncContext;
23
24/// Signature of the async callback for I/Os.
25typedef void(*AsyncIOCallback)(IAsyncContext* context, Status result, size_t bytes_transferred);
26
27/// Standard interface for contexts used by async callbacks.
28class 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).
98typedef 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.
102template <class C>
103class 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