@ 180612.2 · fetching

160122.1 · 9faa2317114a515d656599da389616fc40d804bb

001: initial C++ Streams proposal

reading declarations

Changes to 001-public-stream-base.md. All files

001-public-stream-base.md

new file mode 100644@@ -0,0 +1,166 @@+| Title  | Make C++ Stream APIs public |+|--------|-----------------------------|+| Author | @indutny                    |+| Status | REJECTED                    |+| Date   | 2015-12-17 19:39:46         |++## Description++Currently we use so called `StreamBase` APIs through the core to implement+various performance-critical networking protocols with maximum efficiency.+It is evident that this performance improvement could be beneficial for+user-land C++ addons too.++I propose to make this APIs public, introducing C++ interface similar to the+following one (simplified):++## Interface++```C+++class Stream {+ public:+  //+  // Low-level part+  //++  // Perform protocol-specific shutdown (EOF)+  virtual int DoShutdown(ShutdownWrap* req_wrap) = 0;++  // Write data asynchronously+  virtual int DoWrite(WriteWrap* w,+                      uv_buf_t* bufs,+                      size_t count,+                      uv_stream_t* send_handle) = 0;++  // **(optional)**+  // Try to write provided data immediately without blocking, if not+  // possible to do - should return `0` if no data was written, should decrement+  // `count` and change pointers/length in `bufs` if some data was written.+  virtual int DoTryWrite(uv_buf_t** bufs, size_t* count);++  //+  // High-level part+  //++  // Cast to the deepest class. Just a stub, probably does not need to be+  // here.+  virtual void* Cast() = 0;++  // Return `true` or `false` depending on liveness of the underlying+  // resource. If Stream is not alive - all operations on it will result in+  // `EINVAL`+  virtual bool IsAlive() = 0;++  // Return `true` if stream is currently closing. Closed streams should return+  // `false` when calling `IsAlive`+  virtual bool IsClosing() = 0;++  // **optional**, if `true` - `send_handle` may be not `nullptr` in `DoWrite`+  virtual bool IsIPCPipe();++  // **optional**, return fd+  virtual int GetFD();++  // Start reading and emitting data+  virtual int ReadStart() = 0;++  // Stop reading and emitting data (immediately)+  virtual int ReadStop() = 0;++ protected:+  // One of these must be implemented+  virtual AsyncWrap* GetAsyncWrap();+  virtual v8::Local<v8::Object> GetObject();+};+```++## Public APIs++Public facing APIs have two sides: JS and C++.++### JS++```js+stream.fd; // stream's fd or -1+stream._externalStream;  // External pointer to the deepest C++ class+stream.readStart();+stream.readStop();+stream.shutdown(req);+stream.writev(req, [ ..., chunk, size, ... ]);+stream.writeBuffer(req, buffer);+stream.writeAsciiString(req, string);+stream.writeUtf8String(req, string);+stream.writeUcs2String(req, string);+stream.writeBinaryString(req, string);+```++### C++++All of the C++ interface methods may be called. Additionally following methods+are available:++```C+++class Stream {+ public:+  //+  // Low-level APIs+  //++  // Invoke `after_write_cb`+  inline void OnAfterWrite(WriteWrap* w);++  // Invoke `alloc_cb`+  inline void OnAlloc(size_t size, uv_buf_t* buf);++  // Invoke `read_cb`+  inline void OnRead(size_t nread,+                     const uv_buf_t* buf,+                     uv_handle_type pending = UV_UNKNOWN_HANDLE);++  // Override and get callbacks+  inline void set_after_write_cb(Callback<AfterWriteCb> c);+  inline void set_alloc_cb(Callback<AllocCb> c);+  inline void set_read_cb(Callback<ReadCb> c);+  inline Callback<AfterWriteCb> after_write_cb();+  inline Callback<AllocCb> alloc_cb();+  inline Callback<ReadCb> read_cb();++  //+  // High-level APIs+  //++  // Add JS API methods to the target `FunctionTemplate`+  // NOTE: `flags` control some optional methods like: `shutdown` and `writev`+  static inline void AddMethods(Environment* env,+                                v8::Local<v8::FunctionTemplate> target,+                                int flags = kFlagNone);++  // Emit data to the JavaScript listeners+  void EmitData(ssize_t nread,+                v8::Local<v8::Object> buf,+                v8::Local<v8::Object> handle);++  // Mark stream as consumed+  inline void Consume();++  // TODO(indutny): add this to node.js core+  inline void Unconsume();+};+```++## Putting things together++This APIs could be used for implementing high-performance protocols in a+following way:++Each TCP socket has a `_handle` property, which is actually a C++ `Stream`+instance. One may wrap this `_handle` into a custom C++ `Stream` instance, and+replace the `_handle` property with it. This stream could override `read`,+`alloc`, `after_write` callbacks to hook up the incoming data from the wrapped+stream.++Calling `DoWrite` and friends on the wrapped stream will result in performing+actual TCP writes.++It is easy to see that it could be wrapped this way several times, creating+levels of abstraction without leaving C++!