GeezerVison is showing github.com — http2,async-wrap: introduce AliasedBuffer class · nodejs/node@a32c8a5
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: node.gyp +4Lines changed: 4 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 228 | 228 | 'src/util.cc', | 229 | 229 | 'src/uv.cc', | 230 | 230 | # headers to make for a more pleasant IDE experience | | 231 | + 'src/aliased_buffer.h', | 231 | 232 | 'src/async-wrap.h', | 232 | 233 | 'src/async-wrap-inl.h', | 233 | 234 | 'src/base-object.h', | | 246 | 247 | 'src/node_constants.h', | 247 | 248 | 'src/node_debug_options.h', | 248 | 249 | 'src/node_http2.h', | | 250 | + 'src/node_http2_state.h', | 249 | 251 | 'src/node_internals.h', | 250 | 252 | 'src/node_javascript.h', | 251 | 253 | 'src/node_mutex.h', | | 650 | 652 | 'sources': [ | 651 | 653 | 'src/node_platform.cc', | 652 | 654 | 'src/node_platform.h', | | 655 | + 'test/cctest/node_test_fixture.cc', | | 656 | + 'test/cctest/test_aliased_buffer.cc', | 653 | 657 | 'test/cctest/test_base64.cc', | 654 | 658 | 'test/cctest/test_environment.cc', | 655 | 659 | 'test/cctest/test_util.cc', | |
Collapse file Copy file name to clipboard +200Lines changed: 200 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | + | | 2 | +#ifndef SRC_ALIASED_BUFFER_H_ | | 3 | +#define SRC_ALIASED_BUFFER_H_ | | 4 | + | | 5 | +#include "v8.h" | | 6 | +#include "util.h" | | 7 | +#include "util-inl.h" | | 8 | + | | 9 | +namespace node { | | 10 | + | | 11 | +/** | | 12 | + * This class encapsulates the technique of having a native buffer mapped to | | 13 | + * a JS object. Writes to the native buffer can happen efficiently without | | 14 | + * going through JS, and the data is then available to user's via the exposed | | 15 | + * JS object. | | 16 | + * | | 17 | + * While this technique is computationaly efficient, it is effectively a | | 18 | + * write to JS program state w/out going through the standard | | 19 | + * (monitored) API. Thus any VM capabilities to detect the modification are | | 20 | + * circumvented. | | 21 | + * | | 22 | + * The encapsulation herein provides a placeholder where such writes can be | | 23 | + * observed. Any notification APIs will be left as a future exercise. | | 24 | + */ | | 25 | +template <class NativeT, class V8T> | | 26 | +class AliasedBuffer { | | 27 | + public: | | 28 | + AliasedBuffer(v8::Isolate* isolate, const size_t count) | | 29 | + : isolate_(isolate), | | 30 | + count_(count), | | 31 | + byte_offset_(0), | | 32 | + free_buffer_(true) { | | 33 | + CHECK_GT(count, 0); | | 34 | + const v8::HandleScope handle_scope(isolate_); | | 35 | + | | 36 | + const size_t sizeInBytes = sizeof(NativeT) * count; | | 37 | + | | 38 | + // allocate native buffer | | 39 | + buffer_ = Calloc<NativeT>(count); | | 40 | + | | 41 | + // allocate v8 ArrayBuffer | | 42 | + v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New( | | 43 | + isolate_, buffer_, sizeInBytes); | | 44 | + | | 45 | + // allocate v8 TypedArray | | 46 | + v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count); | | 47 | + js_array_ = v8::Global<V8T>(isolate, js_array); | | 48 | + } | | 49 | + | | 50 | + /** | | 51 | + * Create an AliasedBuffer over a sub-region of another aliased buffer. | | 52 | + * The two will share a v8::ArrayBuffer instance & | | 53 | + * a native buffer, but will each read/write to different sections of the | | 54 | + * native buffer. | | 55 | + * | | 56 | + * Note that byte_offset must by aligned by sizeof(NativeT). | | 57 | + */ | | 58 | + AliasedBuffer(v8::Isolate* isolate, | | 59 | + const size_t byte_offset, | | 60 | + const size_t count, | | 61 | + const AliasedBuffer<uint8_t, | | 62 | + v8::Uint8Array>& backing_buffer) | | 63 | + : isolate_(isolate), | | 64 | + count_(count), | | 65 | + byte_offset_(byte_offset), | | 66 | + free_buffer_(false) { | | 67 | + const v8::HandleScope handle_scope(isolate_); | | 68 | + | | 69 | + v8::Local<v8::ArrayBuffer> ab = backing_buffer.GetArrayBuffer(); | | 70 | + | | 71 | + // validate that the byte_offset is aligned with sizeof(NativeT) | | 72 | + CHECK_EQ(byte_offset & (sizeof(NativeT) - 1), 0); | | 73 | + // validate this fits inside the backing buffer | | 74 | + CHECK_LE(sizeof(NativeT) * count, ab->ByteLength() - byte_offset); | | 75 | + | | 76 | + buffer_ = reinterpret_cast<NativeT*>( | | 77 | + const_cast<uint8_t*>(backing_buffer.GetNativeBuffer() + byte_offset)); | | 78 | + | | 79 | + v8::Local<V8T> js_array = V8T::New(ab, byte_offset, count); | | 80 | + js_array_ = v8::Global<V8T>(isolate, js_array); | | 81 | + } | | 82 | + | | 83 | + AliasedBuffer(const AliasedBuffer& that) | | 84 | + : isolate_(that.isolate_), | | 85 | + count_(that.count_), | | 86 | + byte_offset_(that.byte_offset_), | | 87 | + buffer_(that.buffer_), | | 88 | + free_buffer_(false) { | | 89 | + js_array_ = v8::Global<V8T>(that.isolate_, that.GetJSArray()); | | 90 | + } | | 91 | + | | 92 | + ~AliasedBuffer() { | | 93 | + if (free_buffer_ && buffer_ != nullptr) { | | 94 | + free(buffer_); | | 95 | + } | | 96 | + js_array_.Reset(); | | 97 | + } | | 98 | + | | 99 | + /** | | 100 | + * Helper class that is returned from operator[] to support assignment into | | 101 | + * a specified location. | | 102 | + */ | | 103 | + class Reference { | | 104 | + public: | | 105 | + Reference(AliasedBuffer<NativeT, V8T>* aliased_buffer, size_t index) | | 106 | + : aliased_buffer_(aliased_buffer), | | 107 | + index_(index) { | | 108 | + } | | 109 | + | | 110 | + Reference(const Reference& that) | | 111 | + : aliased_buffer_(that.aliased_buffer_), | | 112 | + index_(that.index_) { | | 113 | + } | | 114 | + | | 115 | + inline Reference& operator=(const NativeT &val) { | | 116 | + aliased_buffer_->SetValue(index_, val); | | 117 | + return *this; | | 118 | + } | | 119 | + | | 120 | + operator NativeT() const { | | 121 | + return aliased_buffer_->GetValue(index_); | | 122 | + } | | 123 | + | | 124 | + private: | | 125 | + AliasedBuffer<NativeT, V8T>* aliased_buffer_; | | 126 | + size_t index_; | | 127 | + }; | | 128 | + | | 129 | + /** | | 130 | + * Get the underlying v8 TypedArray overlayed on top of the native buffer | | 131 | + */ | | 132 | + v8::Local<V8T> GetJSArray() const { | | 133 | + return js_array_.Get(isolate_); | | 134 | + } | | 135 | + | | 136 | + /** | | 137 | + * Get the underlying v8::ArrayBuffer underlying the TypedArray and | | 138 | + * overlaying the native buffer | | 139 | + */ | | 140 | + v8::Local<v8::ArrayBuffer> GetArrayBuffer() const { | | 141 | + return GetJSArray()->Buffer(); | | 142 | + } | | 143 | + | | 144 | + /** | | 145 | + * Get the underlying native buffer. Note that all reads/writes should occur | | 146 | + * through the GetValue/SetValue/operator[] methods | | 147 | + */ | | 148 | + inline const NativeT* GetNativeBuffer() const { | | 149 | + return buffer_; | | 150 | + } | | 151 | + | | 152 | + /** | | 153 | + * Synonym for GetBuffer() | | 154 | + */ | | 155 | + inline const NativeT* operator * () const { | | 156 | + return GetNativeBuffer(); | | 157 | + } | | 158 | + | | 159 | + /** | | 160 | + * Set position index to given value. | | 161 | + */ | | 162 | + inline void SetValue(const size_t index, NativeT value) { | | 163 | +#if defined(DEBUG) && DEBUG | | 164 | + CHECK_LT(index, count_); | | 165 | +#endif | | 166 | + buffer_[index] = value; | | 167 | + } | | 168 | + | | 169 | + /** | | 170 | + * Get value at position index | | 171 | + */ | | 172 | + inline const NativeT GetValue(const size_t index) const { | | 173 | +#if defined(DEBUG) && DEBUG | | 174 | + CHECK_LT(index, count_); | | 175 | +#endif | | 176 | + return buffer_[index]; | | 177 | + } | | 178 | + | | 179 | + /** | | 180 | + * Effectively, a synonym for GetValue/SetValue | | 181 | + */ | | 182 | + Reference operator[](size_t index) { | | 183 | + return Reference(this, index); | | 184 | + } | | 185 | + | | 186 | + NativeT operator[](size_t index) const { | | 187 | + return GetValue(index); | | 188 | + } | | 189 | + | | 190 | + private: | | 191 | + v8::Isolate* const isolate_; | | 192 | + size_t count_; | | 193 | + size_t byte_offset_; | | 194 | + NativeT* buffer_; | | 195 | + v8::Global<V8T> js_array_; | | 196 | + bool free_buffer_; | | 197 | +}; | | 198 | +} // namespace node | | 199 | + | | 200 | +#endif // SRC_ALIASED_BUFFER_H_ |
Collapse file Copy file name to clipboardExpand all lines: src/async-wrap.cc +2-14Lines changed: 2 additions & 14 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 31 | 31 | #include "v8-profiler.h" | 32 | 32 |
| 33 | 33 | using v8::Array; | 34 | | -using v8::ArrayBuffer; | 35 | 34 | using v8::Context; | 36 | 35 | using v8::Float64Array; | 37 | 36 | using v8::Function; | @@ -53,7 +52,6 @@ using v8::RetainedObjectInfo; | 53 | 52 | using v8::String; | 54 | 53 | using v8::Symbol; | 55 | 54 | using v8::TryCatch; | 56 | | -using v8::Uint32Array; | 57 | 55 | using v8::Undefined; | 58 | 56 | using v8::Value; | 59 | 57 |
| @@ -476,13 +474,9 @@ void AsyncWrap::Initialize(Local<Object> target, | 476 | 474 | // callbacks waiting to be called on a particular event. It can then be | 477 | 475 | // incremented/decremented from JS quickly to communicate to C++ if there are | 478 | 476 | // any callbacks waiting to be called. | 479 | | - uint32_t* fields_ptr = env->async_hooks()->fields(); | 480 | | - int fields_count = env->async_hooks()->fields_count(); | 481 | | - Local<ArrayBuffer> fields_ab = | 482 | | - ArrayBuffer::New(isolate, fields_ptr, fields_count * sizeof(*fields_ptr)); | 483 | 477 | FORCE_SET_TARGET_FIELD(target, | 484 | 478 | "async_hook_fields", | 485 | | - Uint32Array::New(fields_ab, 0, fields_count)); | | 479 | + env->async_hooks()->fields().GetJSArray()); | 486 | 480 |
| 487 | 481 | // The following v8::Float64Array has 5 fields. These fields are shared in | 488 | 482 | // this way to allow JS and C++ to read/write each value as quickly as | @@ -493,15 +487,9 @@ void AsyncWrap::Initialize(Local<Object> target, | 493 | 487 | // kInitTriggerId: Write the id of the resource responsible for a handle's | 494 | 488 | // creation just before calling the new handle's constructor. After the new | 495 | 489 | // handle is constructed kInitTriggerId is set back to 0. | 496 | | - double* uid_fields_ptr = env->async_hooks()->uid_fields(); | 497 | | - int uid_fields_count = env->async_hooks()->uid_fields_count(); | 498 | | - Local<ArrayBuffer> uid_fields_ab = ArrayBuffer::New( | 499 | | - isolate, | 500 | | - uid_fields_ptr, | 501 | | - uid_fields_count * sizeof(*uid_fields_ptr)); | 502 | 490 | FORCE_SET_TARGET_FIELD(target, | 503 | 491 | "async_uid_fields", | 504 | | - Float64Array::New(uid_fields_ab, 0, uid_fields_count)); | | 492 | + env->async_hooks()->uid_fields().GetJSArray()); | 505 | 493 |
| 506 | 494 | Local<Object> constants = Object::New(isolate); | 507 | 495 | #define SET_HOOKS_CONSTANT(name) \ | |
Collapse file Copy file name to clipboardExpand all lines: src/env-inl.h +19-13Lines changed: 19 additions & 13 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 24 | 24 |
| 25 | 25 | #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | 26 | 26 |
| | 27 | +#include "aliased_buffer.h" | 27 | 28 | #include "env.h" | 28 | 29 | #include "node.h" | 29 | 30 | #include "util.h" | @@ -82,8 +83,8 @@ inline uint32_t* IsolateData::zero_fill_field() const { | 82 | 83 |
| 83 | 84 | inline Environment::AsyncHooks::AsyncHooks(v8::Isolate* isolate) | 84 | 85 | : isolate_(isolate), | 85 | | - fields_(), | 86 | | - uid_fields_() { | | 86 | + fields_(isolate, kFieldsCount), | | 87 | + uid_fields_(isolate, kUidFieldsCount) { | 87 | 88 | v8::HandleScope handle_scope(isolate_); | 88 | 89 |
| 89 | 90 | // kAsyncUidCntr should start at 1 because that'll be the id the execution | @@ -105,15 +106,17 @@ inline Environment::AsyncHooks::AsyncHooks(v8::Isolate* isolate) | 105 | 106 | #undef V | 106 | 107 | } | 107 | 108 |
| 108 | | -inline uint32_t* Environment::AsyncHooks::fields() { | | 109 | +inline AliasedBuffer<uint32_t, v8::Uint32Array>& | | 110 | +Environment::AsyncHooks::fields() { | 109 | 111 | return fields_; | 110 | 112 | } | 111 | 113 |
| 112 | 114 | inline int Environment::AsyncHooks::fields_count() const { | 113 | 115 | return kFieldsCount; | 114 | 116 | } | 115 | 117 |
| 116 | | -inline double* Environment::AsyncHooks::uid_fields() { | | 118 | +inline AliasedBuffer<double, v8::Float64Array>& | | 119 | +Environment::AsyncHooks::uid_fields() { | 117 | 120 | return uid_fields_; | 118 | 121 | } | 119 | 122 |
| @@ -147,7 +150,7 @@ inline bool Environment::AsyncHooks::pop_ids(double async_id) { | 147 | 150 | fprintf(stderr, | 148 | 151 | "Error: async hook stack has become corrupted (" | 149 | 152 | "actual: %.f, expected: %.f)\n", | 150 | | - uid_fields_[kCurrentAsyncId], | | 153 | + uid_fields_.GetValue(kCurrentAsyncId), | 151 | 154 | async_id); | 152 | 155 | Environment* env = Environment::GetCurrent(isolate_); | 153 | 156 | DumpBacktrace(stderr); | @@ -326,7 +329,7 @@ inline Environment::~Environment() { | 326 | 329 | delete[] heap_statistics_buffer_; | 327 | 330 | delete[] heap_space_statistics_buffer_; | 328 | 331 | delete[] http_parser_buffer_; | 329 | | - free(http2_state_buffer_); | | 332 | + delete http2_state_; | 330 | 333 | free(performance_state_); | 331 | 334 | } | 332 | 335 |
| @@ -425,7 +428,9 @@ inline std::vector<double>* Environment::destroy_ids_list() { | 425 | 428 | } | 426 | 429 |
| 427 | 430 | inline double Environment::new_async_id() { | 428 | | - return ++async_hooks()->uid_fields()[AsyncHooks::kAsyncUidCntr]; | | 431 | + async_hooks()->uid_fields()[AsyncHooks::kAsyncUidCntr] = | | 432 | + async_hooks()->uid_fields()[AsyncHooks::kAsyncUidCntr] + 1; | | 433 | + return async_hooks()->uid_fields()[AsyncHooks::kAsyncUidCntr]; | 429 | 434 | } | 430 | 435 |
| 431 | 436 | inline double Environment::current_async_id() { | @@ -437,7 +442,8 @@ inline double Environment::trigger_id() { | 437 | 442 | } | 438 | 443 |
| 439 | 444 | inline double Environment::get_init_trigger_id() { | 440 | | - double* uid_fields = async_hooks()->uid_fields(); | | 445 | + AliasedBuffer<double, v8::Float64Array>& uid_fields = | | 446 | + async_hooks()->uid_fields(); | 441 | 447 | double tid = uid_fields[AsyncHooks::kInitTriggerId]; | 442 | 448 | uid_fields[AsyncHooks::kInitTriggerId] = 0; | 443 | 449 | if (tid <= 0) tid = current_async_id(); | @@ -477,13 +483,13 @@ inline void Environment::set_http_parser_buffer(char* buffer) { | 477 | 483 | http_parser_buffer_ = buffer; | 478 | 484 | } | 479 | 485 |
| 480 | | -inline http2::http2_state* Environment::http2_state_buffer() const { | 481 | | - return http2_state_buffer_; | | 486 | +inline http2::http2_state* Environment::http2_state() const { | | 487 | + return http2_state_; | 482 | 488 | } | 483 | 489 |
| 484 | | -inline void Environment::set_http2_state_buffer(http2::http2_state* buffer) { | 485 | | - CHECK_EQ(http2_state_buffer_, nullptr); // Should be set only once. | 486 | | - http2_state_buffer_ = buffer; | | 490 | +inline void Environment::set_http2_state(http2::http2_state* buffer) { | | 491 | + CHECK_EQ(http2_state_, nullptr); // Should be set only once. | | 492 | + http2_state_ = buffer; | 487 | 493 | } | 488 | 494 |
| 489 | 495 | inline double* Environment::fs_stats_field_array() const { | |
|