少年修仙传客户端基础资源
hch
2024-04-01 d01413b00ef631ac20347716b23818b0b811f65f
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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#include "il2cpp-config.h"
#include "il2cpp-object-internals.h"
#include "il2cpp-vm-support.h"
#include "GarbageCollector.h"
#include "os/Event.h"
#include "os/Mutex.h"
#include "os/Semaphore.h"
#include "os/Thread.h"
#include "utils/Il2CppHashMap.h"
#include "utils/HashUtils.h"
 
#if !IL2CPP_TINY_WITHOUT_DEBUGGER
#include "vm/CCW.h"
#include "vm/Class.h"
#include "vm/Domain.h"
#include "vm/Exception.h"
#include "vm/RCW.h"
#include "vm/Runtime.h"
#include "vm/Thread.h"
#endif
 
using namespace il2cpp::os;
using namespace il2cpp::vm;
 
namespace il2cpp
{
namespace gc
{
#if !IL2CPP_TINY_WITHOUT_DEBUGGER
// So COM Callable Wrapper can be created for any kind of managed object,
// whether it has finalizer or not. If it doesn't then it's an easy case:
// when creating the CCW, we just register our cleanup method to be the
// finalizer method. In case it does, then we need to be able to invoke
// both our CCW cleanup method, and the finalizer in question.
// We could chain them by registering CCW cleanup method over the finalizer
// method and storing the previous finalizer in CCW cache, but it would
// screw us over if for example the object is IDisposable and then it
// calls `GC.SuppressFinalize` from C# - instead of the finalizer getting
// unregistered, our CCW cleanup method gets unregistered, and we now have
// a dangling pointer to the managed heap in the CCW cache. Not pretty.
// Instead, we made GarbageCollector::RegisterFinalizer and GarbageCollector::SuppressFinalizer
// to be CCW cache aware. Now, any managed object goes into 1 of these categories:
//
// 1. Object has no finalizer and it is not in CCW cache. It gets garbage
//    collected and no cleanup is needed.
// 2. Object has a finalizer and it is not in CCW cache. GarbageCollector::RunFinalizer
//    gets registered with the GC for such object.
// 3. Object has no finalizer and it is in CCW cache. CleanupCCW is
//    registered with the GC for such object. Once it is called, it removes
//    the object from the CCW cache.
// 4. Object has a finalizer and it is in CCW cache. CleanupCCW is
//    registered with the GC for such object. Once it is called, it removes
//    the object from the CCW cache and then calls its finalizer.
//
// To know whether we have case 3 or 4, we have the "hasFinalizer" field in
// the CachedCCW object.
//
// When GarbageCollector::RegisterFinalizer or GarbageCollector::SuppressFinalizer
// is called, we have managed objects fitting in two buckets:
//
// 1. Those that do not exist in CCW cache. Finalizer is normally registered with
//    the GC.
// 2. Those that are in the CCW cache. In such case, GC won't know about the call,
//    but instead we'll find the object in the CCW cache and RegisterFinalizer will
//    set "hasFinalizer" field to true, while SuppressFinalizer will set it to false
//
// Any methods that interact with s_CCWCache have to lock s_CCWCacheMutex.
    struct CachedCCW
    {
        Il2CppIManagedObjectHolder* managedObjectHolder;
        bool hasFinalizer;
    };
 
    typedef Il2CppHashMap<Il2CppObject*, CachedCCW, utils::PointerHash<Il2CppObject> > CCWCache;
 
    static FastMutex s_CCWCacheMutex;
    static CCWCache s_CCWCache;
 
#if IL2CPP_SUPPORT_THREADS
 
    static bool s_StopFinalizer = false;
    static il2cpp::os::Thread* s_FinalizerThread;
    static Il2CppThread* s_FinalizerThreadObject;
    static Semaphore s_FinalizerSemaphore(0, 32767);
    static Event s_FinalizersThreadStartedEvent;
    static Event s_FinalizersCompletedEvent(true, false);
 
    static void FinalizerThread(void* arg)
    {
        s_FinalizerThreadObject = il2cpp::vm::Thread::Attach(Domain::GetCurrent());
        s_FinalizerThread->SetName("GC Finalizer");
 
        s_FinalizersThreadStartedEvent.Set();
 
        while (!s_StopFinalizer)
        {
            s_FinalizerSemaphore.Wait();
 
            GarbageCollector::InvokeFinalizers();
 
            s_FinalizersCompletedEvent.Set();
        }
 
        il2cpp::vm::Thread::Detach(s_FinalizerThreadObject);
    }
 
    bool GarbageCollector::IsFinalizerThread(Il2CppThread *thread)
    {
        return s_FinalizerThreadObject == thread;
    }
 
#else
 
    bool GarbageCollector::IsFinalizerThread(Il2CppThread *thread)
    {
        return false;
    }
 
#endif
 
    void GarbageCollector::InitializeFinalizer()
    {
        GarbageCollector::InvokeFinalizers();
#if IL2CPP_SUPPORT_THREADS
        s_FinalizerThread = new il2cpp::os::Thread;
        s_FinalizerThread->Run(&FinalizerThread, NULL);
        s_FinalizersThreadStartedEvent.Wait();
#endif
    }
 
    void GarbageCollector::UninitializeFinalizers()
    {
#if IL2CPP_SUPPORT_THREADS
        s_StopFinalizer = true;
        NotifyFinalizers();
        s_FinalizerThread->Join();
        delete s_FinalizerThread;
        s_FinalizerThread = NULL;
#endif
    }
 
    void GarbageCollector::NotifyFinalizers()
    {
#if IL2CPP_SUPPORT_THREADS
        s_FinalizerSemaphore.Post(1, NULL);
#endif
    }
 
    void GarbageCollector::RunFinalizer(void *obj, void *data)
    {
        IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(GarbageCollector::RunFinalizer, "Compare to mono implementation special cases");
 
        Il2CppException *exc = NULL;
        Il2CppObject *o;
        const MethodInfo* finalizer = NULL;
 
        o = (Il2CppObject*)obj;
 
        finalizer = Class::GetFinalizer(o->klass);
 
        Runtime::Invoke(finalizer, o, NULL, &exc);
 
        if (exc)
            Runtime::UnhandledException(exc);
    }
 
    void GarbageCollector::RegisterFinalizerForNewObject(Il2CppObject* obj)
    {
        // Fast path
        // No need to check CCW cache since it's guaranteed to not be in it for a new object
        RegisterFinalizerWithCallback(obj, &GarbageCollector::RunFinalizer);
    }
 
    void GarbageCollector::RegisterFinalizer(Il2CppObject* obj)
    {
        // Slow path
        // Check in CCW cache first
        os::FastAutoLock lock(&s_CCWCacheMutex);
        CCWCache::iterator it = s_CCWCache.find(obj);
 
        if (it != s_CCWCache.end())
        {
            it->second.hasFinalizer = true;
        }
        else
        {
            RegisterFinalizerWithCallback(obj, &GarbageCollector::RunFinalizer);
        }
    }
 
    void GarbageCollector::SuppressFinalizer(Il2CppObject* obj)
    {
        // Slow path
        // Check in CCW cache first
        os::FastAutoLock lock(&s_CCWCacheMutex);
        CCWCache::iterator it = s_CCWCache.find(obj);
 
        if (it != s_CCWCache.end())
        {
            it->second.hasFinalizer = false;
        }
        else
        {
            RegisterFinalizerWithCallback(obj, NULL);
        }
    }
 
    void GarbageCollector::WaitForPendingFinalizers()
    {
        if (!GarbageCollector::HasPendingFinalizers())
            return;
 
#if IL2CPP_SUPPORT_THREADS
        /* Avoid deadlocks */
        if (vm::Thread::Current() == s_FinalizerThreadObject)
            return;
 
        s_FinalizersCompletedEvent.Reset();
        NotifyFinalizers();
        s_FinalizersCompletedEvent.Wait();
#else
        GarbageCollector::InvokeFinalizers();
#endif
    }
 
    static void CleanupCCW(void* obj, void* data)
    {
        bool hasFinalizer;
 
        // We have to destroy CCW before invoking the finalizer, because we cannot know whether the finalizer will revive the object
        // In cases it does revive it, it's also possible for it to hit CCW cache, and in that case we'd want to create a new CCW object
        // rather than returning the one that we're about to destroy here
        {
            os::FastAutoLock lock(&s_CCWCacheMutex);
            CCWCache::iterator it = s_CCWCache.find(static_cast<Il2CppObject*>(obj));
            IL2CPP_ASSERT(it != s_CCWCache.end());
 
            Il2CppIManagedObjectHolder* managedObjectHolder = it->second.managedObjectHolder;
            hasFinalizer = it->second.hasFinalizer;
            s_CCWCache.erase(it);
 
            managedObjectHolder->Destroy();
        }
 
        if (hasFinalizer)
            GarbageCollector::RunFinalizer(obj, data);
    }
 
    // When creating COM Callable Wrappers for classes that project to other Windows Runtime classes
    // for instance, System.Uri to Windows.Foundation.Uri, we cannot actually create a wrapper against managed object
    // as the native side actually wants an instance of a real Windows.Foundation.Uri class. So do that, our createCCW
    // instead of creating a COM Callable Wrapper, will create an instance of that windows runtime class. In that case,
    // we do not (AND CANNOT!) put it into a CCW cache because it's not a CCW.
    static bool ShouldInsertIntoCCWCache(Il2CppObject* obj)
    {
        if (obj->klass == il2cpp_defaults.system_uri_class && il2cpp_defaults.windows_foundation_uri_class != NULL)
            return false;
 
        /* TODO - Add the rest of the class projections:
        System.Collections.Specialized.NotifyCollectionChangedEventArgs <-> Windows.UI.Xaml.Interop.NotifyCollectionChangedEventArgs
        System.ComponentModel.PropertyChangedEventArgs <-> Windows.UI.Xaml.Data.PropertyChangedEventArgs
        */
 
        return true;
    }
 
    Il2CppIUnknown* GarbageCollector::GetOrCreateCCW(Il2CppObject* obj, const Il2CppGuid& iid)
    {
        if (obj == NULL)
            return NULL;
 
        // check for rcw object. COM interface can be extracted from it and there's no need to create ccw
        if (obj->klass->is_import_or_windows_runtime)
        {
            Il2CppIUnknown* result = RCW::QueryInterfaceNoAddRef<true>(static_cast<Il2CppComObject*>(obj), iid);
            result->AddRef();
            return result;
        }
 
        os::FastAutoLock lock(&s_CCWCacheMutex);
        CCWCache::iterator it = s_CCWCache.find(obj);
        Il2CppIUnknown* comCallableWrapper;
 
        if (it == s_CCWCache.end())
        {
            comCallableWrapper = CCW::CreateCCW(obj);
 
            if (ShouldInsertIntoCCWCache(obj))
            {
#if IL2CPP_DEBUG
                // Assert that CCW::CreateCCW actually returns upcasted Il2CppIManagedObjectHolder
                Il2CppIManagedObjectHolder* managedObjectHolder = NULL;
                comCallableWrapper->QueryInterface(Il2CppIManagedObjectHolder::IID, reinterpret_cast<void**>(&managedObjectHolder));
                IL2CPP_ASSERT(static_cast<void*>(comCallableWrapper) == static_cast<void*>(managedObjectHolder));
                managedObjectHolder->Release();
#endif
                CachedCCW ccw =
                {
                    static_cast<Il2CppIManagedObjectHolder*>(comCallableWrapper),
                    RegisterFinalizerWithCallback(obj, &CleanupCCW) != NULL
                };
 
                s_CCWCache.insert(std::make_pair(obj, ccw));
            }
        }
        else
        {
            comCallableWrapper = it->second.managedObjectHolder;
        }
 
        Il2CppIUnknown* result;
        il2cpp_hresult_t hr = comCallableWrapper->QueryInterface(iid, reinterpret_cast<void**>(&result));
        IL2CPP_VM_RAISE_IF_FAILED(hr, true);
        return result;
    }
 
#endif // !IL2CPP_TINY_WITHOUT_DEBUGGER
 
    int32_t GarbageCollector::GetGeneration(void* addr)
    {
        return 0;
    }
 
    void GarbageCollector::AddMemoryPressure(int64_t value)
    {
    }
 
#if IL2CPP_ENABLE_WRITE_BARRIERS
    void il2cpp::gc::GarbageCollector::SetWriteBarrier(void **ptr, size_t size)
    {
#if IL2CPP_ENABLE_STRICT_WRITE_BARRIERS
        for (size_t i = 0; i < size / sizeof(void**); i++)
            SetWriteBarrier(ptr + i);
#else
        SetWriteBarrier(ptr);
#endif
    }
 
#endif
 
    void il2cpp::gc::GarbageCollector::SetSkipThread(bool skip)
    {
    }
} // namespace gc
} // namespace il2cpp