| 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 |  | 
|---|