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
24FCIMPL3(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}
48FCIMPLEND
49
50#ifdef FEATURE_APPX
51
52Volatile<ABI::Windows::UI::Core::ICoreWindowStatic*> g_pICoreWindowStatic;
53
54void* 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
181void 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