diff options
Diffstat (limited to 'node_modules/node-sass/src/libsass/docs/dev-ast-memory.md')
-rw-r--r-- | node_modules/node-sass/src/libsass/docs/dev-ast-memory.md | 223 |
1 files changed, 0 insertions, 223 deletions
diff --git a/node_modules/node-sass/src/libsass/docs/dev-ast-memory.md b/node_modules/node-sass/src/libsass/docs/dev-ast-memory.md deleted file mode 100644 index 31004bc..0000000 --- a/node_modules/node-sass/src/libsass/docs/dev-ast-memory.md +++ /dev/null @@ -1,223 +0,0 @@ -# LibSass smart pointer implementation - -LibSass uses smart pointers very similar to `shared_ptr` known -by Boost or C++11. Implementation is a bit less modular since -it was not needed. Various compile time debug options are -available if you need to debug memory life-cycles. - - -## Memory Classes - -### SharedObj - -Base class for the actual node implementations. This ensures -that every object has a reference counter and other values. - -```c++ -class AST_Node : public SharedObj { ... }; -``` - -### SharedPtr (base class for SharedImpl) - -Base class that holds on to the pointer. The reference counter -is stored inside the pointer object directly (`SharedObj`). - -### SharedImpl (inherits from SharedPtr) - -This is the main base class for objects you use in your code. It -will make sure that the memory it points at will be deleted once -all copies to the same object/memory go out of scope. - -```c++ -Class* pointer = new Class(...); -SharedImpl<Class> obj(pointer); -``` - -To spare the developer of typing the templated class every time, -we created typedefs for each available AST Node specialization. - -```c++ -typedef SharedImpl<Number> Number_Obj; -Number_Obj number = SASS_MEMORY_NEW(...); -``` - - -## Memory life-cycles - -### Pointer pickups - -I often use the terminology of "pickup". This means the moment when -a raw pointer not under any control is assigned to a reference counted -object (`XYZ_Obj = XYZ_Ptr`). From that point on memory will be -automatically released once the object goes out of scope (but only -if the reference counter reaches zero). Main point beeing, you don't -have to worry about memory management yourself. - -### Object detach - -Sometimes we can't return reference counted objects directly (see -invalid covariant return types problems below). But we often still -need to use reference objects inside a function to avoid leaks when -something throws. For this you can use `detach`, which basically -detaches the pointer memory from the reference counted object. So -when the reference counted object goes out of scope, it will not -free the attached memory. You are now again in charge of freeing -the memory (just assign it to a reference counted object again). - - -## Circular references - -Reference counted memory implementations are prone to circular references. -This can be addressed by using a multi generation garbage collector. But -for our use-case that seems overkill. There is no way so far for users -(sass code) to create circular references. Therefore we can code around -this possible issue. But developers should be aware of this limitation. - -There are AFAIR two places where circular references could happen. One is -the `sources` member on every `Selector`. The other one can happen in the -extend code (Node handling). The easy way to avoid this is to only assign -complete object clones to these members. If you know the objects lifetime -is longer than the reference you create, you can also just store the raw -pointer. Once needed this could be solved with weak pointers. - - -## Addressing the invalid covariant return types problems - -If you are not familiar with the mentioned problem, you may want -to read up on covariant return types and virtual functions, i.e. - -- http://stackoverflow.com/questions/6924754/return-type-covariance-with-smart-pointers -- http://stackoverflow.com/questions/196733/how-can-i-use-covariant-return-types-with-smart-pointers -- http://stackoverflow.com/questions/2687790/how-to-accomplish-covariant-return-types-when-returning-a-shared-ptr - -We hit this issue at least with the CRTP visitor pattern (eval, expand, -listize and so forth). This means we cannot return reference counted -objects directly. We are forced to return raw pointers or we would need -to have a lot of explicit and expensive upcasts by callers/consumers. - -### Simple functions that allocate new AST Nodes - -In the parser step we often create new objects and can just return a -unique pointer (meaning ownership clearly shifts back to the caller). -The caller/consumer is responsible that the memory is freed. - -```c++ -typedef Number* Number_Ptr; -int parse_integer() { - ... // do the parsing - return 42; -} -Number_Ptr parse_number() { - Number_Ptr p_nr = SASS_MEMORY_NEW(...); - p_nr->value(parse_integer()); - return p_nr; -} -Number_Obj nr = parse_number(); -``` - -The above would be the encouraged pattern for such simple cases. - -### Allocate new AST Nodes in functions that can throw - -There is a major caveat with the previous example, considering this -more real-life implementation that throws an error. The throw may -happen deep down in another function. Holding raw pointers that -we need to free would leak in this case. - -```c++ -int parse_integer() { - ... // do the parsing - if (error) throw(error); - return 42; -} -``` - -With this `parse_integer` function the previous example would leak memory. -I guess it is pretty obvious, as the allocated memory will not be freed, -as it was never assigned to a SharedObj value. Therefore the above code -would better be written as: - -```c++ -typedef Number* Number_Ptr; -int parse_integer() { - ... // do the parsing - if (error) throw(error); - return 42; -} -// this leaks due to pointer return -// should return Number_Obj instead -// though not possible for virtuals! -Number_Ptr parse_number() { - Number_Obj nr = SASS_MEMORY_NEW(...); - nr->value(parse_integer()); // throws - return &nr; // Ptr from Obj -} -Number_Obj nr = parse_number(); -// will now be freed automatically -``` - -The example above unfortunately will not work as is, since we return a -`Number_Ptr` from that function. Therefore the object allocated inside -the function is already gone when it is picked up again by the caller. -The easy fix for the given simplified use case would be to change the -return type of `parse_number` to `Number_Obj`. Indeed we do it exactly -this way in the parser. But as stated above, this will not work for -virtual functions due to invalid covariant return types! - -### Return managed objects from virtual functions - -The easy fix would be to just create a new copy on the heap and return -that. But this seems like a very inelegant solution to this problem. I -mean why can't we just tell the object to treat it like a newly allocated -object? And indeed we can. I've added a `detach` method that will tell -the object to survive deallocation until the next pickup. This means -that it will leak if it is not picked up by consumer. - -```c++ -typedef Number* Number_Ptr; -int parse_integer() { - ... // do the parsing - if (error) throw(error); - return 42; -} -Number_Ptr parse_number() { - Number_Obj nr = SASS_MEMORY_NEW(...); - nr->value(parse_integer()); // throws - return nr.detach(); -} -Number_Obj nr = parse_number(); -// will now be freed automatically -``` - - -## Compile time debug options - -To enable memory debugging you need to define `DEBUG_SHARED_PTR`. -This can i.e. be done in `include/sass/base.h` - -```c++ -define DEBUG_SHARED_PTR -``` - -This will print lost memory on exit to stderr. You can also use -`setDbg(true)` on sepecific variables to emit reference counter -increase, decrease and other events. - - -## Why reinvent the wheel when there is `shared_ptr` from C++11 - -First, implementing a smart pointer class is not really that hard. It -was indeed also a learning experience for myself. But there are more -profound advantages: - -- Better GCC 4.4 compatibility (which most code still has OOTB) -- Not thread safe (give us some free performance on some compiler) -- Beeing able to track memory allocations for debugging purposes -- Adding additional features if needed (as seen in `detach`) -- Optional: optimized weak pointer implementation possible - -### Thread Safety - -As said above, this is not thread safe currently. But we don't need -this ATM anyway. And I guess we probably never will share AST Nodes -across different threads.
\ No newline at end of file |