nodeguy/c1411b4d.fa8e11f2.js
2022-06-06 10:37:37 +00:00

1 line
7.1 KiB
JavaScript

(window.webpackJsonp=window.webpackJsonp||[]).push([[308],{367:function(e,t,r){"use strict";r.r(t),r.d(t,"frontMatter",(function(){return i})),r.d(t,"metadata",(function(){return o})),r.d(t,"rightToc",(function(){return p})),r.d(t,"default",(function(){return l}));var n=r(2),a=r(6),c=(r(0),r(468)),i={},o={unversionedId:"development/wrapper_caching",id:"development/wrapper_caching",isDocsHomePage:!1,title:"wrapper_caching",description:"Wrapper caching and memory management",source:"@site/docs/development/wrapper_caching.md",slug:"/development/wrapper_caching",permalink:"/docs/development/wrapper_caching",editUrl:"https://github.com/nodegui/nodegui/edit/master/website/docs/development/wrapper_caching.md",version:"current"},p=[{value:"&quot;Wrapper Keep Alive&quot; case",id:"wrapper-keep-alive-case",children:[]},{value:"&quot;Wrapper Recycle&quot; case",id:"wrapper-recycle-case",children:[]}],s={rightToc:p};function l(e){var t=e.components,i=Object(a.a)(e,["components"]);return Object(c.b)("wrapper",Object(n.a)({},s,i,{components:t,mdxType:"MDXLayout"}),Object(c.b)("h1",{id:"wrapper-caching-and-memory-management"},"Wrapper caching and memory management"),Object(c.b)("h2",{id:"wrapper-keep-alive-case"},'"Wrapper Keep Alive" case'),Object(c.b)("p",null,'The goal of the "Wrapper Keep Alive" case is to maintain and "keep alive" wrappers for the life-time of a ',Object(c.b)("inlineCode",{parentName:"p"},"QObject")," whose life-cycle is under complete control by Qt itself. i.e. Qt may expose the object, but it creates the instance itself and also destroys it later on. This situation applies to ",Object(c.b)("inlineCode",{parentName:"p"},"QScreen")," and ",Object(c.b)("inlineCode",{parentName:"p"},"QClipboard")," for example. A common use case for keeping a wrapper alive is to receive and relay signals from a ",Object(c.b)("inlineCode",{parentName:"p"},"QObject")," back to JS during the object's life-time."),Object(c.b)("p",null,Object(c.b)("img",{alt:"Wrapper cache diagram",src:r(535).default})),Object(c.b)("p",null,'The WrapperCache (C++) holds strong JS references to the Napi wrapper instances. This keeps them alive. When the core QObject is destroyed the "destroyed" signal is emitted and received by WrapperCache (C++). It then removes the Napi wrapper from its cache and uses a callback function to inform the WrapperCache (JS) about the destruction. WrapperCache (JS) can also perform clean up and null out references to the Napi wrappers. If someone then tries to use a JS side wrapper, then will get a JS side null pointer exception with stacktrace.'),Object(c.b)("p",null,Object(c.b)("strong",{parentName:"p"},"Life-cycle Sequence")),Object(c.b)("p",null,"This sequence diagram shows the events when the application calls ",Object(c.b)("inlineCode",{parentName:"p"},"QWindow.screen()")," to fetch the ",Object(c.b)("inlineCode",{parentName:"p"},"QScreen")," for the window. The ",Object(c.b)("inlineCode",{parentName:"p"},"QScreen")," instance is fully created and managed by Qt. Here you can see how the wrapper creation interacts with the JS and C++ side cache classes. You can also see how the wrappers are gracefully shutdown when the core Qt object is destroyed. Any JS side use of the destroyed wrapper / Qt object results in a neat JS side null pointer exception. This is much better than null pointer segfault on the C++ side."),Object(c.b)("p",null,Object(c.b)("img",{alt:"Wrapper keep alive sequence diagram",src:r(536).default})),Object(c.b)("h2",{id:"wrapper-recycle-case"},'"Wrapper Recycle" case'),Object(c.b)("p",null,'A related use case is where we want to "recycle" wrappers and ensure that for a QObject we only have one coresponding JS wrapper active at the same time. For example, repeated calls to ',Object(c.b)("inlineCode",{parentName:"p"},"QObject.parent()")," should return the same value/object."),Object(c.b)("p",null,'Another goal of this use case is to ensure that the unexpected destruction of the underlying QObject is handled in a more graceful and helpful way than just segfaulting the whole application. This requires the tracking of the QObject via its "destroy" signal and using that to communicate back to JS what has happened.'),Object(c.b)("p",null,'The object creation sequence runs quite differently than in the "Wrapper Keep Alive" case because the application initiates the creation of the object and NodeGui then creates the different wrappers on the JS and C++ sides.'),Object(c.b)("p",null,Object(c.b)("img",{alt:"Wrapper recycle sequence diagram",src:r(537).default})),Object(c.b)("p",null,'The destruction sequence in the case of the C++ object being destroyed is basically the same as the "Wrapper Keep Alive" case.'))}l.isMDXComponent=!0},468:function(e,t,r){"use strict";r.d(t,"a",(function(){return u})),r.d(t,"b",(function(){return b}));var n=r(0),a=r.n(n);function c(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?i(Object(r),!0).forEach((function(t){c(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):i(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function p(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r,n,a={},c=Object.keys(e);for(n=0;n<c.length;n++)r=c[n],t.indexOf(r)>=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(e);for(n=0;n<c.length;n++)r=c[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var s=a.a.createContext({}),l=function(e){var t=a.a.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},u=function(e){var t=l(e.components);return a.a.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.a.createElement(a.a.Fragment,{},t)}},h=a.a.forwardRef((function(e,t){var r=e.components,n=e.mdxType,c=e.originalType,i=e.parentName,s=p(e,["components","mdxType","originalType","parentName"]),u=l(r),h=n,b=u["".concat(i,".").concat(h)]||u[h]||d[h]||c;return r?a.a.createElement(b,o(o({ref:t},s),{},{components:r})):a.a.createElement(b,o({ref:t},s))}));function b(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var c=r.length,i=new Array(c);i[0]=h;var o={};for(var p in t)hasOwnProperty.call(t,p)&&(o[p]=t[p]);o.originalType=e,o.mdxType="string"==typeof e?e:n,i[1]=o;for(var s=2;s<c;s++)i[s]=r[s];return a.a.createElement.apply(null,i)}return a.a.createElement.apply(null,r)}h.displayName="MDXCreateElement"},535:function(e,t,r){"use strict";r.r(t),t.default=r.p+"assets/images/wrapper_cache.drawio-46451808d90ee4e514947480ab75f185.svg"},536:function(e,t,r){"use strict";r.r(t),t.default=r.p+"assets/images/wrapper_keep_alive_seq-1b49fcf3124585ae8900b6d67037c76e.png"},537:function(e,t,r){"use strict";r.r(t),t.default=r.p+"assets/images/wrapper_recycle_seq-fff36b6a9f35e317fc1f0a3fe2d31542.png"}}]);