Welcome to mirror list, hosted at ThFree Co, Russian Federation.

RestrictedCallouts.cpp « Runtime « Native « src - github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 88e47d37b8f815229360d00127ecae498bf90771 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

//
// Callouts from the unmanaged portion of the runtime to C# helpers made during garbage collections. See
// RestrictedCallouts.h for more detail.
//

#include "common.h"
#include "CommonTypes.h"
#include "CommonMacros.h"
#include "daccess.h"
#include "PalRedhawkCommon.h"
#include "PalRedhawk.h"
#include "rhassert.h"
#include "slist.h"
#include "holder.h"
#include "gcrhinterface.h"
#include "shash.h"
#include "RWLock.h"
#include "module.h"
#include "rhbinder.h"
#include "Crst.h"
#include "RuntimeInstance.h"
#include "eetype.h"
#include "ObjectLayout.h"
#include "event.h"
#include "varint.h"
#include "regdisplay.h"
#include "StackFrameIterator.h"
#include "thread.h"
#include "threadstore.h"
#include "threadstore.inl"
#include "RestrictedCallouts.h"

// The head of the chains of GC callouts, one per callout type.
RestrictedCallouts::GcRestrictedCallout * RestrictedCallouts::s_rgGcRestrictedCallouts[GCRC_Count] = { 0 };

// The head of the chain of HandleTable callouts.
RestrictedCallouts::HandleTableRestrictedCallout * RestrictedCallouts::s_pHandleTableRestrictedCallouts = NULL;

// Lock protecting access to s_rgGcRestrictedCallouts and s_pHandleTableRestrictedCallouts during registration
// and unregistration (not used during actual callbacks since everything is single threaded then).
CrstStatic RestrictedCallouts::s_sLock;

// One time startup initialization.
bool RestrictedCallouts::Initialize()
{
    s_sLock.Init(CrstRestrictedCallouts, CRST_DEFAULT);

    return true;
}

// Register callback of the given type to the method with the given address. The most recently registered
// callbacks are called first. Returns true on success, false if insufficient memory was available for the
// registration.
bool RestrictedCallouts::RegisterGcCallout(GcRestrictedCalloutKind eKind, void * pCalloutMethod)
{
    // Validate callout kind.
    if (eKind >= GCRC_Count)
    {
        ASSERT_UNCONDITIONALLY("Invalid GC restricted callout kind.");
        RhFailFast();
    }

    GcRestrictedCallout * pCallout = new (nothrow) GcRestrictedCallout();
    if (pCallout == NULL)
        return false;

    pCallout->m_pCalloutMethod = pCalloutMethod;

    CrstHolder lh(&s_sLock);

    // Link new callout to head of the chain according to its type.
    pCallout->m_pNext = s_rgGcRestrictedCallouts[eKind];
    s_rgGcRestrictedCallouts[eKind] = pCallout;

    return true;
}

// Unregister a previously registered callout. Removes the first registration that matches on both callout
// kind and address. Causes a fail fast if the registration doesn't exist.
void RestrictedCallouts::UnregisterGcCallout(GcRestrictedCalloutKind eKind, void * pCalloutMethod)
{
    // Validate callout kind.
    if (eKind >= GCRC_Count)
    {
        ASSERT_UNCONDITIONALLY("Invalid GC restricted callout kind.");
        RhFailFast();
    }

    CrstHolder lh(&s_sLock);

    GcRestrictedCallout * pCurrCallout = s_rgGcRestrictedCallouts[eKind];
    GcRestrictedCallout * pPrevCallout = NULL;

    while (pCurrCallout)
    {
        if (pCurrCallout->m_pCalloutMethod == pCalloutMethod)
        {
            // Found a matching entry, remove it from the chain.
            if (pPrevCallout)
                pPrevCallout->m_pNext = pCurrCallout->m_pNext;
            else
                s_rgGcRestrictedCallouts[eKind] = pCurrCallout->m_pNext;

            delete pCurrCallout;

            return;
        }

        pPrevCallout = pCurrCallout;
        pCurrCallout = pCurrCallout->m_pNext;
    }

    // If we get here we didn't find a matching registration, indicating a bug on the part of the caller.
    ASSERT_UNCONDITIONALLY("Attempted to unregister restricted callout that wasn't registered.");
    RhFailFast();
}

// Register callback for the "is alive" property of ref counted handles with objects of the given type (the
// type match must be exact). The most recently registered callbacks are called first. Returns true on
// success, false if insufficient memory was available for the registration.
bool RestrictedCallouts::RegisterRefCountedHandleCallback(void * pCalloutMethod, EEType * pTypeFilter)
{
    HandleTableRestrictedCallout * pCallout = new (nothrow) HandleTableRestrictedCallout();
    if (pCallout == NULL)
        return false;

    pCallout->m_pCalloutMethod = pCalloutMethod;
    pCallout->m_pTypeFilter = pTypeFilter;

    CrstHolder lh(&s_sLock);

    // Link new callout to head of the chain.
    pCallout->m_pNext = s_pHandleTableRestrictedCallouts;
    s_pHandleTableRestrictedCallouts = pCallout;

    return true;
}

// Unregister a previously registered callout. Removes the first registration that matches on both callout
// address and filter type. Causes a fail fast if the registration doesn't exist.
void RestrictedCallouts::UnregisterRefCountedHandleCallback(void * pCalloutMethod, EEType * pTypeFilter)
{
    CrstHolder lh(&s_sLock);

    HandleTableRestrictedCallout * pCurrCallout = s_pHandleTableRestrictedCallouts;
    HandleTableRestrictedCallout * pPrevCallout = NULL;

    while (pCurrCallout)
    {
        if ((pCurrCallout->m_pCalloutMethod == pCalloutMethod) &&
            (pCurrCallout->m_pTypeFilter == pTypeFilter))
        {
            // Found a matching entry, remove it from the chain.
            if (pPrevCallout)
                pPrevCallout->m_pNext = pCurrCallout->m_pNext;
            else
                s_pHandleTableRestrictedCallouts = pCurrCallout->m_pNext;

            delete pCurrCallout;

            return;
        }

        pPrevCallout = pCurrCallout;
        pCurrCallout = pCurrCallout->m_pNext;
    }

    // If we get here we didn't find a matching registration, indicating a bug on the part of the caller.
    ASSERT_UNCONDITIONALLY("Attempted to unregister restricted callout that wasn't registered.");
    RhFailFast();
}

// Invoke all the registered GC callouts of the given kind. The condemned generation of the current collection
// is passed along to the callouts.
void RestrictedCallouts::InvokeGcCallouts(GcRestrictedCalloutKind eKind, UInt32 uiCondemnedGeneration)
{
    ASSERT(eKind < GCRC_Count);

    // It is illegal for any of the callouts to trigger a GC.
    Thread * pThread = ThreadStore::GetCurrentThread();
    pThread->SetDoNotTriggerGc();

    // Due to the above we have better suppress GC stress.
    bool fGcStressWasSuppressed = pThread->IsSuppressGcStressSet();
    if (!fGcStressWasSuppressed)
        pThread->SetSuppressGcStress();

    GcRestrictedCallout * pCurrCallout = s_rgGcRestrictedCallouts[eKind];
    while (pCurrCallout)
    {
        // Make the callout.
        ((GcRestrictedCallbackFunction)pCurrCallout->m_pCalloutMethod)(uiCondemnedGeneration);

        pCurrCallout = pCurrCallout->m_pNext;
    }

    // Revert GC stress mode if we changed it.
    if (!fGcStressWasSuppressed)
        pThread->ClearSuppressGcStress();

    pThread->ClearDoNotTriggerGc();
}

// Invoke all the registered ref counted handle callouts for the given object extracted from the handle. The
// result is the union of the results for all the handlers that matched the object type (i.e. if one of them
// returned true the overall result is true otherwise false is returned (which includes the case where no
// handlers matched)). Since there should be no other side-effects of the callout, the invocations cease as
// soon as a handler returns true.
bool RestrictedCallouts::InvokeRefCountedHandleCallbacks(Object * pObject)
{
    bool fResult = false;

    // It is illegal for any of the callouts to trigger a GC.
    Thread * pThread = ThreadStore::GetCurrentThread();
    pThread->SetDoNotTriggerGc();

    // Due to the above we have better suppress GC stress.
    bool fGcStressWasSuppressed = pThread->IsSuppressGcStressSet();
    if (!fGcStressWasSuppressed)
        pThread->SetSuppressGcStress();

    HandleTableRestrictedCallout * pCurrCallout = s_pHandleTableRestrictedCallouts;
    while (pCurrCallout)
    {
        if (pObject->get_SafeEEType() == pCurrCallout->m_pTypeFilter)
        {
            // Make the callout. Return true to our caller as soon as we see a true result here.
            if (((HandleTableRestrictedCallbackFunction)pCurrCallout->m_pCalloutMethod)(pObject))
            {
                fResult = true;
                goto Done;
            }
        }

        pCurrCallout = pCurrCallout->m_pNext;
    }

  Done:
    // Revert GC stress mode if we changed it.
    if (!fGcStressWasSuppressed)
        pThread->ClearSuppressGcStress();

    pThread->ClearDoNotTriggerGc();

    return fResult;
}