1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | /*============================================================ |
6 | ** |
7 | ** Implementation: SynchronizationContextNative.cpp |
8 | ** |
9 | ** |
10 | ** Purpose: Native methods on System.Threading.SynchronizationContext. |
11 | ** |
12 | ** |
13 | ===========================================================*/ |
14 | |
15 | #include "common.h" |
16 | |
17 | #ifdef FEATURE_APPX |
18 | #include <roapi.h> |
19 | #include <windows.ui.core.h> |
20 | #include "winrtdispatcherqueue.h" |
21 | #endif |
22 | #include "synchronizationcontextnative.h" |
23 | |
24 | FCIMPL3(DWORD, SynchronizationContextNative::WaitHelper, PTRArray *handleArrayUNSAFE, CLR_BOOL waitAll, DWORD millis) |
25 | { |
26 | FCALL_CONTRACT; |
27 | |
28 | DWORD ret = 0; |
29 | |
30 | PTRARRAYREF handleArrayObj = (PTRARRAYREF) handleArrayUNSAFE; |
31 | HELPER_METHOD_FRAME_BEGIN_RET_1(handleArrayObj); |
32 | |
33 | CQuickArray<HANDLE> qbHandles; |
34 | int cHandles = handleArrayObj->GetNumComponents(); |
35 | |
36 | // Since DoAppropriateWait could cause a GC, we need to copy the handles to an unmanaged block |
37 | // of memory to ensure they aren't relocated during the call to DoAppropriateWait. |
38 | qbHandles.AllocThrows(cHandles); |
39 | memcpy(qbHandles.Ptr(), handleArrayObj->GetDataPtr(), cHandles * sizeof(HANDLE)); |
40 | |
41 | Thread * pThread = GetThread(); |
42 | ret = pThread->DoAppropriateWait(cHandles, qbHandles.Ptr(), waitAll, millis, |
43 | (WaitMode)(WaitMode_Alertable | WaitMode_IgnoreSyncCtx)); |
44 | |
45 | HELPER_METHOD_FRAME_END(); |
46 | return ret; |
47 | } |
48 | FCIMPLEND |
49 | |
50 | #ifdef FEATURE_APPX |
51 | |
52 | Volatile<ABI::Windows::UI::Core::ICoreWindowStatic*> g_pICoreWindowStatic; |
53 | |
54 | void* QCALLTYPE SynchronizationContextNative::GetWinRTDispatcherForCurrentThread() |
55 | { |
56 | QCALL_CONTRACT; |
57 | void* result = NULL; |
58 | BEGIN_QCALL; |
59 | |
60 | _ASSERTE(WinRTSupported()); |
61 | |
62 | // |
63 | // Get access to ICoreWindow's statics. We grab just one ICoreWindowStatic for the whole process. |
64 | // |
65 | ABI::Windows::UI::Core::ICoreWindowStatic* pICoreWindowStatic = g_pICoreWindowStatic; |
66 | if (pICoreWindowStatic == NULL) |
67 | { |
68 | SafeComHolderPreemp<ABI::Windows::UI::Core::ICoreWindowStatic> pNewICoreWindowStatic; |
69 | { |
70 | HRESULT hr = clr::winrt::GetActivationFactory(RuntimeClass_Windows_UI_Core_CoreWindow, (ABI::Windows::UI::Core::ICoreWindowStatic**)pNewICoreWindowStatic.GetAddr()); |
71 | |
72 | // |
73 | // Older Windows builds don't support ICoreWindowStatic. We should just return a null CoreDispatcher |
74 | // in that case, rather than throwing. |
75 | // |
76 | if (hr != E_NOTIMPL) |
77 | IfFailThrow(hr); |
78 | } |
79 | |
80 | if (pNewICoreWindowStatic != NULL) |
81 | { |
82 | ABI::Windows::UI::Core::ICoreWindowStatic* old = InterlockedCompareExchangeT<ABI::Windows::UI::Core::ICoreWindowStatic*>(&g_pICoreWindowStatic, pNewICoreWindowStatic, NULL); |
83 | if (old == NULL) |
84 | { |
85 | pNewICoreWindowStatic.SuppressRelease(); |
86 | pICoreWindowStatic = pNewICoreWindowStatic; |
87 | } |
88 | else |
89 | { |
90 | pICoreWindowStatic = old; |
91 | } |
92 | } |
93 | } |
94 | |
95 | |
96 | if (pICoreWindowStatic != NULL) |
97 | { |
98 | // |
99 | // Get the current ICoreWindow |
100 | // |
101 | SafeComHolderPreemp<ABI::Windows::UI::Core::ICoreWindow> pCoreWindow; |
102 | |
103 | // |
104 | // workaround: we're currently ignoring the HRESULT from get_Current, because Windows is returning errors for threads that have no CoreWindow. |
105 | // A better behavior would be to return S_OK, with a NULL CoreWindow. If/when Windows does the right thing here, we can change this |
106 | // back to checking the HRESULT. |
107 | // |
108 | pICoreWindowStatic->GetForCurrentThread(&pCoreWindow); |
109 | |
110 | if (pCoreWindow != NULL) |
111 | { |
112 | // |
113 | // Get the ICoreDispatcher for this window |
114 | // |
115 | SafeComHolderPreemp<ABI::Windows::UI::Core::ICoreDispatcher> pCoreDispatcher; |
116 | IfFailThrow(pCoreWindow->get_Dispatcher(&pCoreDispatcher)); |
117 | |
118 | if (pCoreDispatcher != NULL) |
119 | { |
120 | // |
121 | // Does the dispatcher belong to the current thread? |
122 | // |
123 | boolean hasThreadAccess = FALSE; |
124 | IfFailThrow(pCoreDispatcher->get_HasThreadAccess(&hasThreadAccess)); |
125 | if (hasThreadAccess) |
126 | { |
127 | // |
128 | // This is the dispatcher for the current thread. Return it. |
129 | // |
130 | pCoreDispatcher.SuppressRelease(); |
131 | result = (void*)pCoreDispatcher; |
132 | } |
133 | } |
134 | } |
135 | } |
136 | |
137 | // If we didn't find a CoreDispatcher for the thread, let's see if we can get a DispatcherQueue. |
138 | if (result == NULL) |
139 | { |
140 | SafeComHolderPreemp<Windows::System::IDispatcherQueueStatics> pDispatcherQueueStatics; |
141 | { |
142 | HRESULT hr = clr::winrt::GetActivationFactory(RuntimeClass_Windows_System_DispatcherQueue, |
143 | (Windows::System::IDispatcherQueueStatics**)pDispatcherQueueStatics.GetAddr()); |
144 | |
145 | // This interface was added in RS3 along with the public DispatcherQueue support. Older |
146 | // Windows builds don't support it and will return one of two HRESULTs from the call |
147 | // to GetActivationFactory above: |
148 | // - Pre-RS2 will return REGDB_E_CLASSNOTREG since Windows.System.DispatcherQueue |
149 | // does not exist at all. |
150 | // - RS2 will return E_NOINTERFACE since Windows.System.DispatcherQueue does exist |
151 | // in a limited fashion, but does not support the interface ID that we want. |
152 | // |
153 | // We should just return null if we see these two HRESULTs rather than throwing. |
154 | if (hr != REGDB_E_CLASSNOTREG && hr != E_NOINTERFACE) |
155 | { |
156 | IfFailThrow(hr); |
157 | } |
158 | } |
159 | |
160 | if (pDispatcherQueueStatics != NULL) |
161 | { |
162 | // |
163 | // Get the current IDispatcherQueue |
164 | // |
165 | SafeComHolderPreemp<Windows::System::IDispatcherQueue> pDispatcherQueue; |
166 | |
167 | pDispatcherQueueStatics->GetForCurrentThread(&pDispatcherQueue); |
168 | |
169 | if (pDispatcherQueue != NULL) |
170 | { |
171 | pDispatcherQueue.SuppressRelease(); |
172 | result = (void*)pDispatcherQueue; |
173 | } |
174 | } |
175 | } |
176 | |
177 | END_QCALL; |
178 | return result; |
179 | } |
180 | |
181 | void SynchronizationContextNative::Cleanup() |
182 | { |
183 | CONTRACTL |
184 | { |
185 | NOTHROW; |
186 | GC_TRIGGERS; |
187 | MODE_ANY; |
188 | } CONTRACTL_END; |
189 | |
190 | if (g_pICoreWindowStatic) |
191 | { |
192 | SafeRelease(g_pICoreWindowStatic); |
193 | g_pICoreWindowStatic = NULL; |
194 | } |
195 | } |
196 | |
197 | |
198 | |
199 | #endif //FEATURE_APPX |
200 | |