Skip to content

[API Proposal]: NoGC callback #66039

Description

@cshung

Background and motivation

When people use the TryStartNoGCRegion method, in general, they are trying to prevent a GC from happening. However, this can be done as much as the allocation stays within the totalSize specified upfront.

Ensuring the application does not allocate more than totalSize is difficult, if not impossible. Right now, the GC will perform a GC when the totalSize is exceeded and the GC will have to garbage collect. This is okay from some applications, but for some others, it might not.

API Proposal

Customers are asking for a callback when that happens and let the customer decide if we should

  1. Simply terminate the process as OutOfMemory.
  2. Allocate more memory and let the process continue, or
  3. Perform a GC and let the process continue.

We had some discussions (see below) to explore the actual use case and implementation constraints, and we discovered a key conflict between them.

From the requirements perspective, we would like to have a callback when the memory is exhausted so that we can gracefully terminate the game without a GC, but

From the implementation perspective, we cannot serve a callback that could potentially allocate more memory without a GC while we have already exhausted our pre-allocated memory.

To resolve this conflict, we notice that exactly exhausting the memory is not necessary from the requirement side. As long as we have a callback issued so that the game can gracefully terminate, this is good enough.

We also notice that nobody need the process to be terminated right away.

Therefore, I revised the proposal as follow:

API Usage

voidOnBeforeEndNoGCRegion(){/* * This callback will be invoked on the finalizer thread. * At this point, you have used 256M memory since you started the  * NoGCRegion, you have 4M to go before the NoGCRegion really ends *  * Since this is invoked on the finalizer thread, we expect minimal work here * to notify the game to terminate. The game loop thread will probably read * a flag and start showing goodbye. */}GC.TryStartNoGCRegion(260*1024*1024);GC.RegisterAllocationCallback(256*1024*1024,OnBeforeEndNoGCRegion);/* * Ideally, the work here should not use more than 256M, if that's the case, the code will  * perform a GC when EndNoGCRegion is invoked and that's the best case. *  * However, if the usage exceeds 256M, the callback will be invoked. *  * If the usage exceeds 260M, a GC will be automatically invoked and the NoGCRegion * will be terminated automatically. */GC.EndNoGCRegion();// And the callback will be detached automatically.

Alternative Designs

One alternative design is to expose a enum what to do when the NoGCRegion is exhausted. The options are to terminate the NoGCRegion, to commit more memory, or to fail fast. This is decided to be not good enough because what customer really wanted to customizable logic to show some screen and end the game gracefully, none of those options allow them to do that safely.

Another alternative design is to add parameters to the TryStartNoGCRegion. That will work, but there are two reasons why we wanted to have separate APIs for starting to NoGCRegion and registering the callback.

  1. There are already a few overloads on GC.TryStartNoGCRegion, and adding more parameters to it makes it looks really complicated, and
  2. We are envisioning extending this API to support scenarios other than just NoGCRegions, as of the time of writing, there is no plan to implement that yet, but having this API shape allows us to do that when we wanted.

Risks

By asking the caller to provide two limits, the caller must estimate how much memory is necessary for serving the callback, which circles back to the initial question that it is not easy to estimate memory usage. The key idea here is that the callback is supposed to be some simple thing, as @WenceyWang suggested below, it will simply be switching a flag and using some preallocated resources. That hopefully ease the problem.

Implementation-wise, we will reserve up to the larger limit to begin the NoGCRegion. Therefore, there is no risk to issue the callback without GC. When we reach the larger limit, we will terminate the NoGCRegion regardless. That way we can maintain our implementation reliability.

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-approvedAPI was approved in API review, it can be implementedapi-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-GC-coreclr

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
       blocks
      (function() {
      function addCopyButtons() {
      document.querySelectorAll('pre code').forEach(function(codeBlock) {
      if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
      codeBlock.parentElement.setAttribute('data-copy-added', 'true');
      var btn = document.createElement('button');
      btn.textContent = 'Copy';
      btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
      btn.onmouseover = function() { this.style.opacity = '1'; };
      btn.onmouseout = function() { this.style.opacity = '0.7'; };
      btn.onclick = function() {
      navigator.clipboard.writeText(codeBlock.textContent).then(function() {
      btn.textContent = 'Copied!';
      setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
      });
      };
      codeBlock.parentElement.style.position = 'relative';
      codeBlock.parentElement.appendChild(btn);
      });
      }
      addCopyButtons();
      // Re-run on dynamic content
      var observer = new MutationObserver(addCopyButtons);
      observer.observe(document.body, { childList: true, subtree: true });
      })();
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      [API Proposal]: NoGC callback · Issue #66039 · dotnet/runtime · GitHub
      Skip to content

      [API Proposal]: NoGC callback #66039

      Description

      @cshung

      Background and motivation

      When people use the TryStartNoGCRegion method, in general, they are trying to prevent a GC from happening. However, this can be done as much as the allocation stays within the totalSize specified upfront.

      Ensuring the application does not allocate more than totalSize is difficult, if not impossible. Right now, the GC will perform a GC when the totalSize is exceeded and the GC will have to garbage collect. This is okay from some applications, but for some others, it might not.

      API Proposal

      Customers are asking for a callback when that happens and let the customer decide if we should

      1. Simply terminate the process as OutOfMemory.
      2. Allocate more memory and let the process continue, or
      3. Perform a GC and let the process continue.

      We had some discussions (see below) to explore the actual use case and implementation constraints, and we discovered a key conflict between them.

      From the requirements perspective, we would like to have a callback when the memory is exhausted so that we can gracefully terminate the game without a GC, but

      From the implementation perspective, we cannot serve a callback that could potentially allocate more memory without a GC while we have already exhausted our pre-allocated memory.

      To resolve this conflict, we notice that exactly exhausting the memory is not necessary from the requirement side. As long as we have a callback issued so that the game can gracefully terminate, this is good enough.

      We also notice that nobody need the process to be terminated right away.

      Therefore, I revised the proposal as follow:

      API Usage

      voidOnBeforeEndNoGCRegion(){/* * This callback will be invoked on the finalizer thread. * At this point, you have used 256M memory since you started the  * NoGCRegion, you have 4M to go before the NoGCRegion really ends *  * Since this is invoked on the finalizer thread, we expect minimal work here * to notify the game to terminate. The game loop thread will probably read * a flag and start showing goodbye. */}GC.TryStartNoGCRegion(260*1024*1024);GC.RegisterAllocationCallback(256*1024*1024,OnBeforeEndNoGCRegion);/* * Ideally, the work here should not use more than 256M, if that's the case, the code will  * perform a GC when EndNoGCRegion is invoked and that's the best case. *  * However, if the usage exceeds 256M, the callback will be invoked. *  * If the usage exceeds 260M, a GC will be automatically invoked and the NoGCRegion * will be terminated automatically. */GC.EndNoGCRegion();// And the callback will be detached automatically.

      Alternative Designs

      One alternative design is to expose a enum what to do when the NoGCRegion is exhausted. The options are to terminate the NoGCRegion, to commit more memory, or to fail fast. This is decided to be not good enough because what customer really wanted to customizable logic to show some screen and end the game gracefully, none of those options allow them to do that safely.

      Another alternative design is to add parameters to the TryStartNoGCRegion. That will work, but there are two reasons why we wanted to have separate APIs for starting to NoGCRegion and registering the callback.

      1. There are already a few overloads on GC.TryStartNoGCRegion, and adding more parameters to it makes it looks really complicated, and
      2. We are envisioning extending this API to support scenarios other than just NoGCRegions, as of the time of writing, there is no plan to implement that yet, but having this API shape allows us to do that when we wanted.

      Risks

      By asking the caller to provide two limits, the caller must estimate how much memory is necessary for serving the callback, which circles back to the initial question that it is not easy to estimate memory usage. The key idea here is that the callback is supposed to be some simple thing, as @WenceyWang suggested below, it will simply be switching a flag and using some preallocated resources. That hopefully ease the problem.

      Implementation-wise, we will reserve up to the larger limit to begin the NoGCRegion. Therefore, there is no risk to issue the callback without GC. When we reach the larger limit, we will terminate the NoGCRegion regardless. That way we can maintain our implementation reliability.

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        api-approvedAPI was approved in API review, it can be implementedapi-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-GC-coreclr

        Type

        No type

        Projects

        No projects

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [API Proposal]: NoGC callback · Issue #66039 · dotnet/runtime · GitHub
          Skip to content

          [API Proposal]: NoGC callback #66039

          Description

          @cshung

          Background and motivation

          When people use the TryStartNoGCRegion method, in general, they are trying to prevent a GC from happening. However, this can be done as much as the allocation stays within the totalSize specified upfront.

          Ensuring the application does not allocate more than totalSize is difficult, if not impossible. Right now, the GC will perform a GC when the totalSize is exceeded and the GC will have to garbage collect. This is okay from some applications, but for some others, it might not.

          API Proposal

          Customers are asking for a callback when that happens and let the customer decide if we should

          1. Simply terminate the process as OutOfMemory.
          2. Allocate more memory and let the process continue, or
          3. Perform a GC and let the process continue.

          We had some discussions (see below) to explore the actual use case and implementation constraints, and we discovered a key conflict between them.

          From the requirements perspective, we would like to have a callback when the memory is exhausted so that we can gracefully terminate the game without a GC, but

          From the implementation perspective, we cannot serve a callback that could potentially allocate more memory without a GC while we have already exhausted our pre-allocated memory.

          To resolve this conflict, we notice that exactly exhausting the memory is not necessary from the requirement side. As long as we have a callback issued so that the game can gracefully terminate, this is good enough.

          We also notice that nobody need the process to be terminated right away.

          Therefore, I revised the proposal as follow:

          API Usage

          voidOnBeforeEndNoGCRegion(){/* * This callback will be invoked on the finalizer thread. * At this point, you have used 256M memory since you started the  * NoGCRegion, you have 4M to go before the NoGCRegion really ends *  * Since this is invoked on the finalizer thread, we expect minimal work here * to notify the game to terminate. The game loop thread will probably read * a flag and start showing goodbye. */}GC.TryStartNoGCRegion(260*1024*1024);GC.RegisterAllocationCallback(256*1024*1024,OnBeforeEndNoGCRegion);/* * Ideally, the work here should not use more than 256M, if that's the case, the code will  * perform a GC when EndNoGCRegion is invoked and that's the best case. *  * However, if the usage exceeds 256M, the callback will be invoked. *  * If the usage exceeds 260M, a GC will be automatically invoked and the NoGCRegion * will be terminated automatically. */GC.EndNoGCRegion();// And the callback will be detached automatically.

          Alternative Designs

          One alternative design is to expose a enum what to do when the NoGCRegion is exhausted. The options are to terminate the NoGCRegion, to commit more memory, or to fail fast. This is decided to be not good enough because what customer really wanted to customizable logic to show some screen and end the game gracefully, none of those options allow them to do that safely.

          Another alternative design is to add parameters to the TryStartNoGCRegion. That will work, but there are two reasons why we wanted to have separate APIs for starting to NoGCRegion and registering the callback.

          1. There are already a few overloads on GC.TryStartNoGCRegion, and adding more parameters to it makes it looks really complicated, and
          2. We are envisioning extending this API to support scenarios other than just NoGCRegions, as of the time of writing, there is no plan to implement that yet, but having this API shape allows us to do that when we wanted.

          Risks

          By asking the caller to provide two limits, the caller must estimate how much memory is necessary for serving the callback, which circles back to the initial question that it is not easy to estimate memory usage. The key idea here is that the callback is supposed to be some simple thing, as @WenceyWang suggested below, it will simply be switching a flag and using some preallocated resources. That hopefully ease the problem.

          Implementation-wise, we will reserve up to the larger limit to begin the NoGCRegion. Therefore, there is no risk to issue the callback without GC. When we reach the larger limit, we will terminate the NoGCRegion regardless. That way we can maintain our implementation reliability.

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            api-approvedAPI was approved in API review, it can be implementedapi-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-GC-coreclr

            Type

            No type

            Projects

            No projects

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [API Proposal]: NoGC callback · Issue #66039 · dotnet/runtime · GitHub
              Skip to content

              [API Proposal]: NoGC callback #66039

              Description

              @cshung

              Background and motivation

              When people use the TryStartNoGCRegion method, in general, they are trying to prevent a GC from happening. However, this can be done as much as the allocation stays within the totalSize specified upfront.

              Ensuring the application does not allocate more than totalSize is difficult, if not impossible. Right now, the GC will perform a GC when the totalSize is exceeded and the GC will have to garbage collect. This is okay from some applications, but for some others, it might not.

              API Proposal

              Customers are asking for a callback when that happens and let the customer decide if we should

              1. Simply terminate the process as OutOfMemory.
              2. Allocate more memory and let the process continue, or
              3. Perform a GC and let the process continue.

              We had some discussions (see below) to explore the actual use case and implementation constraints, and we discovered a key conflict between them.

              From the requirements perspective, we would like to have a callback when the memory is exhausted so that we can gracefully terminate the game without a GC, but

              From the implementation perspective, we cannot serve a callback that could potentially allocate more memory without a GC while we have already exhausted our pre-allocated memory.

              To resolve this conflict, we notice that exactly exhausting the memory is not necessary from the requirement side. As long as we have a callback issued so that the game can gracefully terminate, this is good enough.

              We also notice that nobody need the process to be terminated right away.

              Therefore, I revised the proposal as follow:

              API Usage

              voidOnBeforeEndNoGCRegion(){/* * This callback will be invoked on the finalizer thread. * At this point, you have used 256M memory since you started the  * NoGCRegion, you have 4M to go before the NoGCRegion really ends *  * Since this is invoked on the finalizer thread, we expect minimal work here * to notify the game to terminate. The game loop thread will probably read * a flag and start showing goodbye. */}GC.TryStartNoGCRegion(260*1024*1024);GC.RegisterAllocationCallback(256*1024*1024,OnBeforeEndNoGCRegion);/* * Ideally, the work here should not use more than 256M, if that's the case, the code will  * perform a GC when EndNoGCRegion is invoked and that's the best case. *  * However, if the usage exceeds 256M, the callback will be invoked. *  * If the usage exceeds 260M, a GC will be automatically invoked and the NoGCRegion * will be terminated automatically. */GC.EndNoGCRegion();// And the callback will be detached automatically.

              Alternative Designs

              One alternative design is to expose a enum what to do when the NoGCRegion is exhausted. The options are to terminate the NoGCRegion, to commit more memory, or to fail fast. This is decided to be not good enough because what customer really wanted to customizable logic to show some screen and end the game gracefully, none of those options allow them to do that safely.

              Another alternative design is to add parameters to the TryStartNoGCRegion. That will work, but there are two reasons why we wanted to have separate APIs for starting to NoGCRegion and registering the callback.

              1. There are already a few overloads on GC.TryStartNoGCRegion, and adding more parameters to it makes it looks really complicated, and
              2. We are envisioning extending this API to support scenarios other than just NoGCRegions, as of the time of writing, there is no plan to implement that yet, but having this API shape allows us to do that when we wanted.

              Risks

              By asking the caller to provide two limits, the caller must estimate how much memory is necessary for serving the callback, which circles back to the initial question that it is not easy to estimate memory usage. The key idea here is that the callback is supposed to be some simple thing, as @WenceyWang suggested below, it will simply be switching a flag and using some preallocated resources. That hopefully ease the problem.

              Implementation-wise, we will reserve up to the larger limit to begin the NoGCRegion. Therefore, there is no risk to issue the callback without GC. When we reach the larger limit, we will terminate the NoGCRegion regardless. That way we can maintain our implementation reliability.

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                api-approvedAPI was approved in API review, it can be implementedapi-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-GC-coreclr

                Type

                No type

                Projects

                No projects

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [API Proposal]: NoGC callback · Issue #66039 · dotnet/runtime · GitHub
                  Skip to content

                  [API Proposal]: NoGC callback #66039

                  Description

                  @cshung

                  Background and motivation

                  When people use the TryStartNoGCRegion method, in general, they are trying to prevent a GC from happening. However, this can be done as much as the allocation stays within the totalSize specified upfront.

                  Ensuring the application does not allocate more than totalSize is difficult, if not impossible. Right now, the GC will perform a GC when the totalSize is exceeded and the GC will have to garbage collect. This is okay from some applications, but for some others, it might not.

                  API Proposal

                  Customers are asking for a callback when that happens and let the customer decide if we should

                  1. Simply terminate the process as OutOfMemory.
                  2. Allocate more memory and let the process continue, or
                  3. Perform a GC and let the process continue.

                  We had some discussions (see below) to explore the actual use case and implementation constraints, and we discovered a key conflict between them.

                  From the requirements perspective, we would like to have a callback when the memory is exhausted so that we can gracefully terminate the game without a GC, but

                  From the implementation perspective, we cannot serve a callback that could potentially allocate more memory without a GC while we have already exhausted our pre-allocated memory.

                  To resolve this conflict, we notice that exactly exhausting the memory is not necessary from the requirement side. As long as we have a callback issued so that the game can gracefully terminate, this is good enough.

                  We also notice that nobody need the process to be terminated right away.

                  Therefore, I revised the proposal as follow:

                  API Usage

                  voidOnBeforeEndNoGCRegion(){/* * This callback will be invoked on the finalizer thread. * At this point, you have used 256M memory since you started the  * NoGCRegion, you have 4M to go before the NoGCRegion really ends *  * Since this is invoked on the finalizer thread, we expect minimal work here * to notify the game to terminate. The game loop thread will probably read * a flag and start showing goodbye. */}GC.TryStartNoGCRegion(260*1024*1024);GC.RegisterAllocationCallback(256*1024*1024,OnBeforeEndNoGCRegion);/* * Ideally, the work here should not use more than 256M, if that's the case, the code will  * perform a GC when EndNoGCRegion is invoked and that's the best case. *  * However, if the usage exceeds 256M, the callback will be invoked. *  * If the usage exceeds 260M, a GC will be automatically invoked and the NoGCRegion * will be terminated automatically. */GC.EndNoGCRegion();// And the callback will be detached automatically.

                  Alternative Designs

                  One alternative design is to expose a enum what to do when the NoGCRegion is exhausted. The options are to terminate the NoGCRegion, to commit more memory, or to fail fast. This is decided to be not good enough because what customer really wanted to customizable logic to show some screen and end the game gracefully, none of those options allow them to do that safely.

                  Another alternative design is to add parameters to the TryStartNoGCRegion. That will work, but there are two reasons why we wanted to have separate APIs for starting to NoGCRegion and registering the callback.

                  1. There are already a few overloads on GC.TryStartNoGCRegion, and adding more parameters to it makes it looks really complicated, and
                  2. We are envisioning extending this API to support scenarios other than just NoGCRegions, as of the time of writing, there is no plan to implement that yet, but having this API shape allows us to do that when we wanted.

                  Risks

                  By asking the caller to provide two limits, the caller must estimate how much memory is necessary for serving the callback, which circles back to the initial question that it is not easy to estimate memory usage. The key idea here is that the callback is supposed to be some simple thing, as @WenceyWang suggested below, it will simply be switching a flag and using some preallocated resources. That hopefully ease the problem.

                  Implementation-wise, we will reserve up to the larger limit to begin the NoGCRegion. Therefore, there is no risk to issue the callback without GC. When we reach the larger limit, we will terminate the NoGCRegion regardless. That way we can maintain our implementation reliability.

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    api-approvedAPI was approved in API review, it can be implementedapi-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-GC-coreclr

                    Type

                    No type

                    Projects

                    No projects

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [API Proposal]: NoGC callback · Issue #66039 · dotnet/runtime · GitHub
                      Skip to content

                      [API Proposal]: NoGC callback #66039

                      Description

                      @cshung

                      Background and motivation

                      When people use the TryStartNoGCRegion method, in general, they are trying to prevent a GC from happening. However, this can be done as much as the allocation stays within the totalSize specified upfront.

                      Ensuring the application does not allocate more than totalSize is difficult, if not impossible. Right now, the GC will perform a GC when the totalSize is exceeded and the GC will have to garbage collect. This is okay from some applications, but for some others, it might not.

                      API Proposal

                      Customers are asking for a callback when that happens and let the customer decide if we should

                      1. Simply terminate the process as OutOfMemory.
                      2. Allocate more memory and let the process continue, or
                      3. Perform a GC and let the process continue.

                      We had some discussions (see below) to explore the actual use case and implementation constraints, and we discovered a key conflict between them.

                      From the requirements perspective, we would like to have a callback when the memory is exhausted so that we can gracefully terminate the game without a GC, but

                      From the implementation perspective, we cannot serve a callback that could potentially allocate more memory without a GC while we have already exhausted our pre-allocated memory.

                      To resolve this conflict, we notice that exactly exhausting the memory is not necessary from the requirement side. As long as we have a callback issued so that the game can gracefully terminate, this is good enough.

                      We also notice that nobody need the process to be terminated right away.

                      Therefore, I revised the proposal as follow:

                      API Usage

                      voidOnBeforeEndNoGCRegion(){/* * This callback will be invoked on the finalizer thread. * At this point, you have used 256M memory since you started the  * NoGCRegion, you have 4M to go before the NoGCRegion really ends *  * Since this is invoked on the finalizer thread, we expect minimal work here * to notify the game to terminate. The game loop thread will probably read * a flag and start showing goodbye. */}GC.TryStartNoGCRegion(260*1024*1024);GC.RegisterAllocationCallback(256*1024*1024,OnBeforeEndNoGCRegion);/* * Ideally, the work here should not use more than 256M, if that's the case, the code will  * perform a GC when EndNoGCRegion is invoked and that's the best case. *  * However, if the usage exceeds 256M, the callback will be invoked. *  * If the usage exceeds 260M, a GC will be automatically invoked and the NoGCRegion * will be terminated automatically. */GC.EndNoGCRegion();// And the callback will be detached automatically.

                      Alternative Designs

                      One alternative design is to expose a enum what to do when the NoGCRegion is exhausted. The options are to terminate the NoGCRegion, to commit more memory, or to fail fast. This is decided to be not good enough because what customer really wanted to customizable logic to show some screen and end the game gracefully, none of those options allow them to do that safely.

                      Another alternative design is to add parameters to the TryStartNoGCRegion. That will work, but there are two reasons why we wanted to have separate APIs for starting to NoGCRegion and registering the callback.

                      1. There are already a few overloads on GC.TryStartNoGCRegion, and adding more parameters to it makes it looks really complicated, and
                      2. We are envisioning extending this API to support scenarios other than just NoGCRegions, as of the time of writing, there is no plan to implement that yet, but having this API shape allows us to do that when we wanted.

                      Risks

                      By asking the caller to provide two limits, the caller must estimate how much memory is necessary for serving the callback, which circles back to the initial question that it is not easy to estimate memory usage. The key idea here is that the callback is supposed to be some simple thing, as @WenceyWang suggested below, it will simply be switching a flag and using some preallocated resources. That hopefully ease the problem.

                      Implementation-wise, we will reserve up to the larger limit to begin the NoGCRegion. Therefore, there is no risk to issue the callback without GC. When we reach the larger limit, we will terminate the NoGCRegion regardless. That way we can maintain our implementation reliability.

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        api-approvedAPI was approved in API review, it can be implementedapi-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-GC-coreclr

                        Type

                        No type

                        Projects

                        No projects

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [API Proposal]: NoGC callback · Issue #66039 · dotnet/runtime · GitHub
                          Skip to content

                          [API Proposal]: NoGC callback #66039

                          Description

                          @cshung

                          Background and motivation

                          When people use the TryStartNoGCRegion method, in general, they are trying to prevent a GC from happening. However, this can be done as much as the allocation stays within the totalSize specified upfront.

                          Ensuring the application does not allocate more than totalSize is difficult, if not impossible. Right now, the GC will perform a GC when the totalSize is exceeded and the GC will have to garbage collect. This is okay from some applications, but for some others, it might not.

                          API Proposal

                          Customers are asking for a callback when that happens and let the customer decide if we should

                          1. Simply terminate the process as OutOfMemory.
                          2. Allocate more memory and let the process continue, or
                          3. Perform a GC and let the process continue.

                          We had some discussions (see below) to explore the actual use case and implementation constraints, and we discovered a key conflict between them.

                          From the requirements perspective, we would like to have a callback when the memory is exhausted so that we can gracefully terminate the game without a GC, but

                          From the implementation perspective, we cannot serve a callback that could potentially allocate more memory without a GC while we have already exhausted our pre-allocated memory.

                          To resolve this conflict, we notice that exactly exhausting the memory is not necessary from the requirement side. As long as we have a callback issued so that the game can gracefully terminate, this is good enough.

                          We also notice that nobody need the process to be terminated right away.

                          Therefore, I revised the proposal as follow:

                          API Usage

                          voidOnBeforeEndNoGCRegion(){/* * This callback will be invoked on the finalizer thread. * At this point, you have used 256M memory since you started the  * NoGCRegion, you have 4M to go before the NoGCRegion really ends *  * Since this is invoked on the finalizer thread, we expect minimal work here * to notify the game to terminate. The game loop thread will probably read * a flag and start showing goodbye. */}GC.TryStartNoGCRegion(260*1024*1024);GC.RegisterAllocationCallback(256*1024*1024,OnBeforeEndNoGCRegion);/* * Ideally, the work here should not use more than 256M, if that's the case, the code will  * perform a GC when EndNoGCRegion is invoked and that's the best case. *  * However, if the usage exceeds 256M, the callback will be invoked. *  * If the usage exceeds 260M, a GC will be automatically invoked and the NoGCRegion * will be terminated automatically. */GC.EndNoGCRegion();// And the callback will be detached automatically.

                          Alternative Designs

                          One alternative design is to expose a enum what to do when the NoGCRegion is exhausted. The options are to terminate the NoGCRegion, to commit more memory, or to fail fast. This is decided to be not good enough because what customer really wanted to customizable logic to show some screen and end the game gracefully, none of those options allow them to do that safely.

                          Another alternative design is to add parameters to the TryStartNoGCRegion. That will work, but there are two reasons why we wanted to have separate APIs for starting to NoGCRegion and registering the callback.

                          1. There are already a few overloads on GC.TryStartNoGCRegion, and adding more parameters to it makes it looks really complicated, and
                          2. We are envisioning extending this API to support scenarios other than just NoGCRegions, as of the time of writing, there is no plan to implement that yet, but having this API shape allows us to do that when we wanted.

                          Risks

                          By asking the caller to provide two limits, the caller must estimate how much memory is necessary for serving the callback, which circles back to the initial question that it is not easy to estimate memory usage. The key idea here is that the callback is supposed to be some simple thing, as @WenceyWang suggested below, it will simply be switching a flag and using some preallocated resources. That hopefully ease the problem.

                          Implementation-wise, we will reserve up to the larger limit to begin the NoGCRegion. Therefore, there is no risk to issue the callback without GC. When we reach the larger limit, we will terminate the NoGCRegion regardless. That way we can maintain our implementation reliability.

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            api-approvedAPI was approved in API review, it can be implementedapi-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-GC-coreclr

                            Type

                            No type

                            Projects

                            No projects

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [API Proposal]: NoGC callback · Issue #66039 · dotnet/runtime · GitHub
                              Skip to content

                              [API Proposal]: NoGC callback #66039

                              Description

                              @cshung

                              Background and motivation

                              When people use the TryStartNoGCRegion method, in general, they are trying to prevent a GC from happening. However, this can be done as much as the allocation stays within the totalSize specified upfront.

                              Ensuring the application does not allocate more than totalSize is difficult, if not impossible. Right now, the GC will perform a GC when the totalSize is exceeded and the GC will have to garbage collect. This is okay from some applications, but for some others, it might not.

                              API Proposal

                              Customers are asking for a callback when that happens and let the customer decide if we should

                              1. Simply terminate the process as OutOfMemory.
                              2. Allocate more memory and let the process continue, or
                              3. Perform a GC and let the process continue.

                              We had some discussions (see below) to explore the actual use case and implementation constraints, and we discovered a key conflict between them.

                              From the requirements perspective, we would like to have a callback when the memory is exhausted so that we can gracefully terminate the game without a GC, but

                              From the implementation perspective, we cannot serve a callback that could potentially allocate more memory without a GC while we have already exhausted our pre-allocated memory.

                              To resolve this conflict, we notice that exactly exhausting the memory is not necessary from the requirement side. As long as we have a callback issued so that the game can gracefully terminate, this is good enough.

                              We also notice that nobody need the process to be terminated right away.

                              Therefore, I revised the proposal as follow:

                              API Usage

                              voidOnBeforeEndNoGCRegion(){/* * This callback will be invoked on the finalizer thread. * At this point, you have used 256M memory since you started the  * NoGCRegion, you have 4M to go before the NoGCRegion really ends *  * Since this is invoked on the finalizer thread, we expect minimal work here * to notify the game to terminate. The game loop thread will probably read * a flag and start showing goodbye. */}GC.TryStartNoGCRegion(260*1024*1024);GC.RegisterAllocationCallback(256*1024*1024,OnBeforeEndNoGCRegion);/* * Ideally, the work here should not use more than 256M, if that's the case, the code will  * perform a GC when EndNoGCRegion is invoked and that's the best case. *  * However, if the usage exceeds 256M, the callback will be invoked. *  * If the usage exceeds 260M, a GC will be automatically invoked and the NoGCRegion * will be terminated automatically. */GC.EndNoGCRegion();// And the callback will be detached automatically.

                              Alternative Designs

                              One alternative design is to expose a enum what to do when the NoGCRegion is exhausted. The options are to terminate the NoGCRegion, to commit more memory, or to fail fast. This is decided to be not good enough because what customer really wanted to customizable logic to show some screen and end the game gracefully, none of those options allow them to do that safely.

                              Another alternative design is to add parameters to the TryStartNoGCRegion. That will work, but there are two reasons why we wanted to have separate APIs for starting to NoGCRegion and registering the callback.

                              1. There are already a few overloads on GC.TryStartNoGCRegion, and adding more parameters to it makes it looks really complicated, and
                              2. We are envisioning extending this API to support scenarios other than just NoGCRegions, as of the time of writing, there is no plan to implement that yet, but having this API shape allows us to do that when we wanted.

                              Risks

                              By asking the caller to provide two limits, the caller must estimate how much memory is necessary for serving the callback, which circles back to the initial question that it is not easy to estimate memory usage. The key idea here is that the callback is supposed to be some simple thing, as @WenceyWang suggested below, it will simply be switching a flag and using some preallocated resources. That hopefully ease the problem.

                              Implementation-wise, we will reserve up to the larger limit to begin the NoGCRegion. Therefore, there is no risk to issue the callback without GC. When we reach the larger limit, we will terminate the NoGCRegion regardless. That way we can maintain our implementation reliability.

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                api-approvedAPI was approved in API review, it can be implementedapi-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-GC-coreclr

                                Type

                                No type

                                Projects

                                No projects

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions