Skip to content

Commit b7ebc00

Browse files
smessmerfacebook-github-bot
authored andcommitted
Move Blob to ATen/core (pytorch#11924)
Summary: Pull Request resolved: pytorch#11924 Previous diffs removed Blob -> caffe2 dependencies, now we can move it to ATen/core. This is pre-work for allowing storing Blob in IValue. Reviewed By: ezyang Differential Revision: D9980641 fbshipit-source-id: 32082a673ec94c42c20b2298adced8bb7ca94d07
1 parent 8ff435c commit b7ebc00

File tree

3 files changed

+237
-212
lines changed

3 files changed

+237
-212
lines changed

aten/src/ATen/core/blob.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include <ATen/core/blob.h>

aten/src/ATen/core/blob.h

+235
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
#pragma once
2+
3+
#include <cstddef>
4+
#include <sstream>
5+
#include <type_traits>
6+
#include <typeinfo>
7+
#include <vector>
8+
9+
#include <ATen/core/typeid.h>
10+
#include <c10/macros/Macros.h>
11+
12+
namespace caffe2 {
13+
14+
class Tensor;
15+
16+
/**
17+
* @brief Blob is a general container that hosts a typed pointer.
18+
*
19+
* A Blob hosts a pointer as well as its type, and takes charge of deleting it
20+
* properly when the blob is deallocated or re-allocated with a new type. A blob
21+
* could contain anything, although the most common case is to contain a Tensor.
22+
*/
23+
class CAFFE2_API Blob final {
24+
public:
25+
using DestroyCall = void(void*);
26+
27+
/**
28+
* Initializes an empty Blob.
29+
*/
30+
Blob() noexcept : meta_(), pointer_(nullptr), destroy_(nullptr) {}
31+
~Blob() {
32+
Reset();
33+
}
34+
35+
Blob(Blob&& other) noexcept : Blob() {
36+
swap(other);
37+
}
38+
39+
Blob& operator=(Blob&& other) noexcept {
40+
Blob(std::move(other)).swap(*this);
41+
return *this;
42+
}
43+
44+
/**
45+
* Checks if the content stored in the blob is of type T.
46+
*/
47+
template <class T>
48+
bool IsType() const noexcept {
49+
return meta_.Match<T>();
50+
}
51+
52+
/**
53+
* Returns the meta info of the blob.
54+
*/
55+
inline const TypeMeta& meta() const noexcept {
56+
return meta_;
57+
}
58+
59+
/**
60+
* Returns a printable typename of the blob.
61+
*/
62+
inline const char* TypeName() const noexcept {
63+
return meta_.name();
64+
}
65+
66+
/**
67+
* @brief Gets the const reference of the stored object. The code checks if
68+
* the stored object is of the desired type.
69+
*/
70+
// TODO(jerryzh): add a Get(DeviceType) function?
71+
template <class T>
72+
const T& Get() const {
73+
AT_ASSERTM(
74+
IsType<T>(),
75+
"wrong type for the Blob instance. Blob contains ",
76+
meta_.name(),
77+
" while caller expects ",
78+
TypeMeta::TypeName<T>());
79+
// TODO: after we add Get<Tensor>(DeviceType)
80+
// and changed all the callsites, we can add
81+
// a static assert here to enforce T != Tensor
82+
return *static_cast<const T*>(pointer_);
83+
}
84+
85+
const void* GetRaw() const noexcept {
86+
return pointer_;
87+
}
88+
void* GetRaw() noexcept {
89+
return pointer_;
90+
}
91+
92+
/**
93+
* @brief Gets a mutable pointer to the stored object.
94+
*
95+
* If the current object is not of the right type, a new object is created
96+
* and the old object is freed. Note that type T should have a default
97+
* constructor. Otherwise, create the object yourself first, and use
98+
* Reset().
99+
*/
100+
template <class T>
101+
T* GetMutable() {
102+
static_assert(
103+
std::is_default_constructible<T>::value,
104+
"GetMutable can't be called with non-default-constructible types. "
105+
"Try using specialized methods");
106+
if (IsType<T>()) {
107+
return static_cast<T*>(pointer_);
108+
} else {
109+
// TODO Re-enable logging
110+
// VLOG(1) << "Create new mutable object " << TypeMeta::TypeName<T>();
111+
return Reset<T>(new T());
112+
}
113+
}
114+
115+
template <class T>
116+
T* GetMutableOrNull() {
117+
if (IsType<T>()) {
118+
return static_cast<T*>(pointer_);
119+
} else {
120+
return nullptr;
121+
}
122+
}
123+
124+
/**
125+
* Sets the underlying object to the allocated one. The Blob then takes over
126+
* the ownership of the passed in pointer. If there is already an object in
127+
* the Blob, the old object is freed.
128+
*
129+
* This is used when the underlying class T does not have a default ctor, or
130+
* complex initializations needs to be done outside the blob.
131+
*/
132+
template <class T>
133+
T* Reset(T* allocated) {
134+
if (pointer_ && destroy_) {
135+
destroy_(pointer_);
136+
}
137+
meta_ = TypeMeta::Make<T>();
138+
pointer_ = static_cast<void*>(allocated);
139+
destroy_ = &Destroy<T>;
140+
return allocated;
141+
}
142+
143+
inline void* Reset(
144+
void* allocated,
145+
const TypeMeta& meta,
146+
DestroyCall* destroy) {
147+
if (pointer_ && destroy_) {
148+
destroy_(pointer_);
149+
}
150+
meta_ = meta;
151+
pointer_ = static_cast<void*>(allocated);
152+
destroy_ = destroy;
153+
return allocated;
154+
}
155+
156+
/**
157+
* Releases the ownership, if any, this Blob has on the underlying pointer.
158+
* The user is then responsible for freeing the data if needed
159+
*/
160+
inline DestroyCall* Release() {
161+
DestroyCall* d = destroy_;
162+
destroy_ = nullptr;
163+
return d;
164+
}
165+
166+
/**
167+
* Sets the underlying object to the allocated one, but does not take over
168+
* the ownership of the passed in pointer. If there is already an object in
169+
* the Blob, the old object is freed.
170+
*
171+
* Unlike Reset, this does not take over the ownership of the pointer and the
172+
* caller is responsible for making sure that the lifetime of the allocated
173+
* blob outlasts the lifetime of any access to this blob, until another Reset
174+
* call is made or the blob is destructed.
175+
*/
176+
template <class T>
177+
typename std::remove_const<T>::type* ShareExternal(
178+
typename std::remove_const<T>::type* allocated) {
179+
return static_cast<T*>(ShareExternal(
180+
static_cast<void*>(allocated),
181+
TypeMeta::Make<typename std::remove_const<T>::type>()));
182+
}
183+
184+
void* ShareExternal(void* allocated, const TypeMeta& meta) {
185+
if (pointer_ && destroy_) {
186+
destroy_(pointer_);
187+
}
188+
meta_ = meta;
189+
pointer_ = static_cast<void*>(allocated);
190+
destroy_ = nullptr;
191+
return allocated;
192+
}
193+
194+
/**
195+
* Resets the Blob to an empty one.
196+
*/
197+
inline void Reset() {
198+
if (pointer_ && destroy_) {
199+
destroy_(pointer_);
200+
}
201+
pointer_ = nullptr;
202+
meta_ = TypeMeta();
203+
destroy_ = nullptr;
204+
}
205+
206+
/**
207+
* @brief Swaps the underlying storage of two blobs.
208+
*/
209+
void swap(Blob& rhs) {
210+
using std::swap;
211+
swap(meta_, rhs.meta_);
212+
swap(pointer_, rhs.pointer_);
213+
swap(destroy_, rhs.destroy_);
214+
}
215+
216+
private:
217+
/**
218+
* @brief A destroy call that is used to properly deconstruct objects.
219+
*/
220+
template <class T>
221+
static void Destroy(void* pointer) {
222+
delete static_cast<T*>(pointer);
223+
}
224+
TypeMeta meta_;
225+
void* pointer_ = nullptr;
226+
DestroyCall* destroy_ = nullptr;
227+
228+
C10_DISABLE_COPY_AND_ASSIGN(Blob);
229+
};
230+
231+
inline void swap(Blob& lhs, Blob& rhs) {
232+
lhs.swap(rhs);
233+
}
234+
235+
} // namespace caffe2

0 commit comments

Comments
 (0)