如何处理与约束的执行区分配? [英] How to deal with allocations in constrained execution regions?

查看:178
本文介绍了如何处理与约束的执行区分配?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

约束执行区域为C#/ .NET,允许开发人员试图葫芦三巨头的异常出code关键区域的功能 - 内存不足,计算器和ThreadAbort。

Constrained Execution Regions are a feature of C# / .Net that allow the developer to attempt to hoist the 'big three' exceptions out of critical regions of code - OutOfMemory, StackOverflow and ThreadAbort.

的CER通过推迟ThreadAborts,preparing在调用图中的所有方法(因此没有JIT有发生,这会导致分配),并通过确保足够的堆栈空间实现这一点可以适应随后的调用堆栈。

CERs achieve this by postponing ThreadAborts, preparing all methods in the call graph (so no JIT has to occur, which can cause allocations), and by ensuring enough stack space is available to fit the ensuing call stack.

一个典型的不间断区域可能是这样的:

A typical uninterruptable region might look like:

public static void GetNativeFlag()
{
    IntPtr nativeResource = new IntPtr();
    int flag;

    // Remember, only the finally block is constrained; try is normal.
    RuntimeHelpers.PrepareConstrainedRegions();
    try
    { }
    finally
    {
        NativeMethods.GetPackageFlags( ref nativeResource );

        if ( nativeResource != IntPtr.Zero ) {
            flag = Marshal.ReadInt32( nativeResource );
            NativeMethods.FreeBuffer( nativeResource );
        }
    }
}

以上是大部分都很好,因为没有一个规则的CER内打破 - 所有的.NET拨款的核证减排量之外, Marshal.ReadInt32()有兼容 ReliabilityContract ,和我们presuming我NativeMethods类似标记,使虚拟机能够正确地考虑他们的时候preparing的CER。

The above is mostly all well and good because none of the rules are broken inside the CER - all .Net allocations are outside the CER, Marshal.ReadInt32() has a compatible ReliabilityContract, and we're presuming my NativeMethods are similarly tagged so that the VM can properly account for them when preparing the CER.

所以,所有这一切挡道,你如何处理的情况下的分配已发生一个CER内?分配打破规则,因为它很可能得到一个OutOfMemoryException。

So with all of that out of the way, how do you handle situations where an allocation has to happen inside of a CER? Allocations break the rules, since it's very possible to get an OutOfMemoryException.

我查询本机API(SSPI的的 QuerySecurityPackageInfo ),迫使我打破这些规则。本机API不执行自己(本地)的分配,但如果失败,我只是得到一个空的结果,所以没有什么大不了的存在。然而,在它分配的结构,它存储几个C字符串未知大小的

I've run into this problem when querying a native API (SSPI's QuerySecurityPackageInfo) that forces me to break these rules. The native API does perform its own (native) allocation, but if that fails I just get an empty result, so no big deal there. However, in the structure that it allocates, it stores a few C-strings of unknown size.

当它给我回一个指向它分配的结构,我必须复制整个事情,并分配空间来存储这些C-字符串作为净字符串对象。毕竟这一点,我应该告诉它释放的分配。

When it gives me back a pointer to the structure it allocated, I have to copy the entire thing, and allocate room to store those c-strings as .Net string objects. After all of that, I'm supposed to tell it to free the allocation.

不过,因为我表演净拨款的CER,我打破了规则,并可能泄露的句柄。

However, since I'm performing .Net allocations in the CER, I'm breaking the rules and possibly leaking a handle.

什么是正确的方式来处理呢?

What is the right way to deal with this?

有关它的价值,这是我的幼稚的做法:

For what it's worth, this is my naive approach:

internal static SecPkgInfo GetPackageCapabilities_Bad( string packageName )
{
    SecPkgInfo info;

    IntPtr rawInfoPtr;

    rawInfoPtr = new IntPtr();
    info = new SecPkgInfo();

    RuntimeHelpers.PrepareConstrainedRegions();
    try
    { }
    finally
    {
        NativeMethods.QuerySecurityPackageInfo( packageName, ref rawInfoPtr );

        if ( rawInfoPtr != IntPtr.Zero )
        {
            // This performs allocations as it makes room for the strings contained in the result.
            Marshal.PtrToStructure( rawInfoPtr, info );

            NativeMethods.FreeContextBuffer( rawInfoPtr );
        }
    }

    return info;
}

修改

我要指出,成功对我来说在这种情况下,我从来没有泄漏手柄;这样很好,如果我进行分配而发生故障,并释放手柄,然后返回一个错误给我来电,表示分配失败。只是不能泄漏句柄。

I should mention that 'success' for me in this case is that I never leak the handle; its alright if I perform an allocation which fails, and release the handle, and then return an error to my caller indicating that an allocation failed. Just can't leak handles.

编辑弗兰克Hileman

我们没有太多的控制权,当我们进行互操作调用所需的内存分配。

We don't have much control over the memory allocations required when we perform interop calls.

要看你是什么意思? - 可能被分配到执行该调用呼叫建立的呼叫调用,或存储内存

Depends on what you mean - memory that might be allocated to perform the call invocation, or the memory created by the invoked call?

我们有完美地控制分配给执行该调用的存储器 - 这是存储器由JIT创建汇编所涉及的方法,以及执行该调用所需的堆栈的存储器。在JIT编译内存在CER的preparation分配;如果失败,永远不会执行整个CER。在CER preparation还计算了多少堆栈空间需要由CER进行静态调用图,并中止CER preparation如果没有足够的堆栈。

We have perfect control over the memory allocated to perform the invocation - that's memory created by the JIT to compile the involved methods, and the memory needed by the stack to perform the invocation. The JIT compile memory is allocated during the preparation of the CER; if that fails, the whole CER is never executed. The CER preparation also calculates how much stack space is needed in the static call graph performed by the CER, and aborts the CER preparation if there's not enough stack.

巧合的是,这涉及到堆栈空间preparation任何的try-catch-finally程序框架,甚至嵌套的try-catch-finally程序框架,这事发生在定义和分享的CER。嵌套的try-catch-终于里面一个CER是完全合理的,因为JIT可以计算,记录在try-catch-终于方面需要堆栈存储器的数量和中止CER preparation都是一样的,如果太多的需要。

Coincidentally, this involves stack space preparation for any try-catch-finally frames, even nested try-catch-finally frames, that happen to define and partake in the CER. Nesting try-catch-finally's inside a CER is perfectly reasonable, because the JIT can calculate the amount of stack memory needed to record the try-catch-finally context and abort the CER preparation all the same if too much is needed.

该调用本身可以做.NET堆以外的一些内存分配;我很惊讶本地调用允许一个CER内都没有。

The call itself may do some memory allocations outside the .net heap; I am surprised native calls are allowed inside a CER at all.

如果你的意思是通过调用调用执行本机内存分配,那也没有核证减排量的问题。本机内存分配成功或返回一个状态code。没有本机内存分配产生奥姆斯。如果本机分配失败,presumably的原生API,我调用通过返回一个状态code,或一个空指针处理它。呼叫仍然是确定的。唯一的副作用是,它可能会导致后续的管理分配失败因内存pressure。 然而的,如果我们要么从未进行分配,或者可以确定性地处理失败的管理分配,那么它仍然不是一个问题。

If you meant native memory allocations performed by the invoked call, then that's also not a problem for CERs. Native memory allocations either succeed or return a status code. OOMs are not generated by native memory allocations. If a native allocation fails, presumably the native API I'm invoking handles it by returning a status code, or a null pointer. The call is still deterministic. The only side effect is that it may cause subsequent managed allocations to fail due to increased memory pressure. However, if we either never perform allocations, or can deterministically handle failed managed allocations, then it's still not a problem.

所以,唯一的一种分配是坏的CER是有管理的分配,因为这可能会导致异步OOM异常。所以,现在的问题是,我怎么确定性处理CER的内部故障管理的分配。

So the only kind of allocation that is bad in a CER is a managed allocation, since it may cause the 'asynchronous' OOM exception. So now the question is how do I deterministically handle a failed managed allocation inside of a CER..

但是,这是完全有可能的。核证减排量可以有嵌套的try-catch-finally块。的所有的呼叫在一个CER,并且需要时通过CER所有堆栈空间,即使用于记录嵌套的try-终于内的上下文中,CER的最后,可在的所述preparation被确定性地计算整个CER,我的任何code实际执行之前。

But that's completely possible. A CER can have nested try-catch-finally blocks. All calls in a CER, and all stack space needed by a CER, even for recording the context of a nested try-finally inside a CER's finally, can be deterministically calculated during the preparation of the entire CER, before any of my code actually executes.

推荐答案

这是可能的一个CER内进行管理分配,只要CER是ppared处理分配失败$ P $。

It is possible to perform managed allocations inside of a CER, so long as the CER is prepared to handle the failed allocation.

首先,这是破code:

First, this is the broken code:

SecPkgInfo info;
SecurityStatus status = SecurityStatus.InternalError;
SecurityStatus freeStatus;

IntPtr rawInfoPtr;

rawInfoPtr = new IntPtr();
info = new SecPkgInfo();

RuntimeHelpers.PrepareConstrainedRegions();
try
{ }
finally
{
    status = NativeMethods.QuerySecurityPackageInfo( packageName, ref rawInfoPtr );

    if ( rawInfoPtr != IntPtr.Zero  )
    {
        if ( status == SecurityStatus.OK )
        {
            // *** BWOOOP **** BWOOOP ***
            // This performs allocations as it makes room for the strings contained 
            // in the SecPkgInfo class. That means that we're performing managed 
            // allocation inside of a CER. This CER is broken and may cause a leak because
            // it never calls FreeContextBuffer if an OOM is caused by the Marshal.
            Marshal.PtrToStructure( rawInfoPtr, info );
        }

        freeStatus = NativeMethods.FreeContextBuffer( rawInfoPtr );
    }
}

由于的try-catch-终于真实可以嵌套,并在需要的嵌套的try-catch-finally程序是在CER的prepration pcalculated $ P $,我们可以用一个try-finally里面我们的核证减排量的额外堆栈空间主要的最后保证了我们的 FreeContextBuffer 绝不泄露:

SecPkgInfo info;
SecurityStatus status = SecurityStatus.InternalError;
SecurityStatus freeStatus;

IntPtr rawInfoPtr;

rawInfoPtr = new IntPtr();
info = new SecPkgInfo();

RuntimeHelpers.PrepareConstrainedRegions();
try
{ }
finally
{
    status = NativeMethods.QuerySecurityPackageInfo( packageName, ref rawInfoPtr );

    if ( rawInfoPtr != IntPtr.Zero  )
    {
        try
        {
            if ( status == SecurityStatus.OK )
            {
                // This may fail but the finally will make sure we always free the native pointer.
                Marshal.PtrToStructure( rawInfoPtr, info );
            }
        }
        finally
        {
            freeStatus = NativeMethods.FreeContextBuffer( rawInfoPtr );
        }
    }
}

我也把一个演示程序,可在 HTTP://www.antiduh。 COM /测试/ LeakTest.zip 。它调用该DLL有点定制机DLL,它跟踪分配和管理应用程序。它显示了一个CER,使用嵌套的try-终于是仍然可以确定性地释放非托管资源,甚至在CER的一部分,导致OOM异常。

I've also put together a demo program, available at http://www.antiduh.com/tests/LeakTest.zip. It has a little custom native DLL that keeps track of allocations, and a managed app that invokes that DLL. It shows how a CER, using nested try-finally's can still deterministically release unmanaged resources even when part of the CER causes an OOM exception.

这篇关于如何处理与约束的执行区分配?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆