少年修仙传客户端基础资源
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
/**
* \file
* Win32 OS wait wrappers and interrupt/abort APC handling.
*
* Author:
*   Johan Lorensson (lateralusx.github@gmail.com)
*
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
 
#include <mono/utils/mono-os-wait.h>
#include <mono/utils/mono-threads.h>
#include <mono/utils/mono-threads-debug.h>
 
enum ThreadWaitInfo {
    THREAD_WAIT_INFO_CLEARED = 0,
    THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT = 1 << 0,
    THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT = 1 << 1,
    THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT = 1 << 2
};
 
static inline void
request_interrupt (gpointer thread_info, HANDLE native_thread_handle, gint32 pending_apc_slot, PAPCFUNC apc_callback, DWORD tid)
{
    /*
    * On Windows platforms, an async interrupt/abort request queues an APC
    * that needs to be processed by target thread before it can return from an
    * alertable OS wait call and complete the mono interrupt/abort request.
    * Uncontrolled queuing of APC's could flood the APC queue preventing the target thread
    * to return from its alertable OS wait call, blocking the interrupt/abort requests to complete
    * This check makes sure that only one APC per type gets queued, preventing potential flooding
    * of the APC queue. NOTE, this code will execute regardless if targeted thread is currently in
    * an alertable wait or not. This is done to prevent races between interrupt/abort requests and
    * alertable wait calls. Threads already in an alertable wait should handle WAIT_IO_COMPLETION
    * return scenarios and restart the alertable wait operation if needed or take other actions
    * (like service the interrupt/abort request).
    */
    MonoThreadInfo *info = (MonoThreadInfo *)thread_info;
    gint32 old_wait_info, new_wait_info;
 
    do {
        old_wait_info = mono_atomic_load_i32 (&info->thread_wait_info);
        if (old_wait_info & pending_apc_slot)
            return;
 
        new_wait_info = old_wait_info | pending_apc_slot;
    } while (mono_atomic_cas_i32 (&info->thread_wait_info, new_wait_info, old_wait_info) != old_wait_info);
 
    THREADS_INTERRUPT_DEBUG ("%06d - Interrupting/Aborting syscall in thread %06d", GetCurrentThreadId (), tid);
    QueueUserAPC (apc_callback, native_thread_handle, (ULONG_PTR)NULL);
}
 
static void CALLBACK
interrupt_apc (ULONG_PTR param)
{
    THREADS_INTERRUPT_DEBUG ("%06d - interrupt_apc () called", GetCurrentThreadId ());
}
 
void
mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
{
    request_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT, interrupt_apc, tid);
}
 
static void CALLBACK
abort_apc (ULONG_PTR param)
{
    THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ());
}
 
void
mono_win32_abort_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
{
    request_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT, abort_apc, tid);
}
 
static inline void
enter_alertable_wait (MonoThreadInfo *info)
{
    // Clear any previous flags. Set alertable wait flag.
    mono_atomic_xchg_i32 (&info->thread_wait_info, THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT);
}
 
static inline void
leave_alertable_wait (MonoThreadInfo *info)
{
    // Clear any previous flags. Thread is exiting alertable wait state, and info around pending interrupt/abort APC's
    // can now be discarded as well, thread is out of wait operation and can proceed it's execution.
    mono_atomic_xchg_i32 (&info->thread_wait_info, THREAD_WAIT_INFO_CLEARED);
}
 
DWORD
mono_win32_sleep_ex (DWORD timeout, BOOL alertable)
{
    DWORD result = WAIT_FAILED;
    MonoThreadInfo *info = mono_thread_info_current_unchecked ();
 
    if (alertable && info) {
        enter_alertable_wait (info);
    }
 
    result = SleepEx (timeout, alertable);
 
    // NOTE, leave_alertable_wait should not affect GetLastError but
    // if changed, GetLastError needs to be preserved and reset before returning.
    if (alertable && info) {
        leave_alertable_wait (info);
    }
 
    return result;
}
 
DWORD
mono_win32_wait_for_single_object_ex (HANDLE handle, DWORD timeout, BOOL alertable)
{
    DWORD result = WAIT_FAILED;
    MonoThreadInfo *info = mono_thread_info_current_unchecked ();
 
    if (alertable && info) {
        enter_alertable_wait (info);
    }
 
    result = WaitForSingleObjectEx (handle, timeout, alertable);
 
    // NOTE, leave_alertable_wait should not affect GetLastError but
    // if changed, GetLastError needs to be preserved and reset before returning.
    if (alertable && info) {
        leave_alertable_wait (info);
    }
 
    return result;
}
 
DWORD
mono_win32_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
{
    DWORD result = WAIT_FAILED;
    MonoThreadInfo *info = mono_thread_info_current_unchecked ();
 
    if (alertable && info) {
        enter_alertable_wait (info);
    }
 
    result = WaitForMultipleObjectsEx (count, handles, waitAll, timeout, alertable);
 
    // NOTE, leave_alertable_wait should not affect GetLastError but
    // if changed, GetLastError needs to be preserved and reset before returning.
    if (alertable && info) {
        leave_alertable_wait (info);
    }
 
    return result;
}
 
/* See comment above ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal */
#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
 
DWORD
mono_win32_signal_object_and_wait (HANDLE toSignal, HANDLE toWait, DWORD timeout, BOOL alertable)
{
    DWORD result = WAIT_FAILED;
    MonoThreadInfo *info = mono_thread_info_current_unchecked ();
 
    if (alertable && info) {
        enter_alertable_wait (info);
    }
 
    result = SignalObjectAndWait (toSignal, toWait, timeout, alertable);
 
    // NOTE, leave_alertable_wait should not affect GetLastError but
    // if changed, GetLastError needs to be preserved and reset before returning.
    if (alertable && info) {
        leave_alertable_wait (info);
    }
 
    return result;
}
 
#endif
 
#if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
DWORD
mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, DWORD timeout, DWORD wakeMask, DWORD flags)
{
    DWORD result = WAIT_FAILED;
    MonoThreadInfo *info = mono_thread_info_current_unchecked ();
    BOOL alertable = flags & MWMO_ALERTABLE;
 
    if (alertable && info) {
        enter_alertable_wait (info);
    }
 
    result = MsgWaitForMultipleObjectsEx (count, handles, timeout, wakeMask, flags);
 
    // NOTE, leave_alertable_wait should not affect GetLastError but
    // if changed, GetLastError needs to be preserved and reset before returning.
    if (alertable && info) {
        leave_alertable_wait (info);
    }
 
    return result;
}
#endif
 
DWORD
mono_win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
{
    DWORD result = WAIT_FAILED;
    MonoThreadInfo *info = mono_thread_info_current_unchecked ();
 
    if (alertable && info) {
        enter_alertable_wait (info);
    }
 
    result = WSAWaitForMultipleEvents (count, handles, waitAll, timeout, alertable);
 
    // NOTE, leave_alertable_wait should not affect GetLastError but
    // if changed, GetLastError needs to be preserved and reset before returning.
    if (alertable && info) {
        leave_alertable_wait (info);
    }
 
    return result;
}