FreeRDP
barrier.c
1 
21 #include <winpr/config.h>
22 
23 #include <winpr/synch.h>
24 #include <winpr/assert.h>
25 
26 #include "synch.h"
27 
28 #include <winpr/crt.h>
29 
30 #ifdef WINPR_SYNCHRONIZATION_BARRIER
31 
32 #include <winpr/sysinfo.h>
33 #include <winpr/library.h>
34 #include <winpr/interlocked.h>
35 #include <winpr/thread.h>
36 
47 #ifdef _WIN32
48 
49 static HMODULE g_Kernel32 = NULL;
50 static BOOL g_NativeBarrier = FALSE;
51 static INIT_ONCE g_InitOnce = INIT_ONCE_STATIC_INIT;
52 
53 typedef BOOL(WINAPI* fnInitializeSynchronizationBarrier)(LPSYNCHRONIZATION_BARRIER lpBarrier,
54  LONG lTotalThreads, LONG lSpinCount);
55 typedef BOOL(WINAPI* fnEnterSynchronizationBarrier)(LPSYNCHRONIZATION_BARRIER lpBarrier,
56  DWORD dwFlags);
57 typedef BOOL(WINAPI* fnDeleteSynchronizationBarrier)(LPSYNCHRONIZATION_BARRIER lpBarrier);
58 
59 static fnInitializeSynchronizationBarrier pfnInitializeSynchronizationBarrier = NULL;
60 static fnEnterSynchronizationBarrier pfnEnterSynchronizationBarrier = NULL;
61 static fnDeleteSynchronizationBarrier pfnDeleteSynchronizationBarrier = NULL;
62 
63 static BOOL CALLBACK InitOnce_Barrier(PINIT_ONCE once, PVOID param, PVOID* context)
64 {
65  g_Kernel32 = LoadLibraryA("kernel32.dll");
66 
67  if (!g_Kernel32)
68  return TRUE;
69 
70  pfnInitializeSynchronizationBarrier = GetProcAddressAs(
71  g_Kernel32, "InitializeSynchronizationBarrier", fnInitializeSynchronizationBarrier);
72 
73  pfnEnterSynchronizationBarrier =
74  GetProcAddressAs(g_Kernel32, "EnterSynchronizationBarrier", fnEnterSynchronizationBarrier);
75  pfnDeleteSynchronizationBarrier = GetProcAddressAs(g_Kernel32, "DeleteSynchronizationBarrier",
76  fnDeleteSynchronizationBarrier);
77 
78  if (pfnInitializeSynchronizationBarrier && pfnEnterSynchronizationBarrier &&
79  pfnDeleteSynchronizationBarrier)
80  {
81  g_NativeBarrier = TRUE;
82  }
83 
84  return TRUE;
85 }
86 
87 #endif
88 
89 BOOL WINAPI winpr_InitializeSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpBarrier,
90  LONG lTotalThreads, LONG lSpinCount)
91 {
92  SYSTEM_INFO sysinfo;
93  HANDLE hEvent0 = NULL;
94  HANDLE hEvent1 = NULL;
95 
96 #ifdef _WIN32
97  InitOnceExecuteOnce(&g_InitOnce, InitOnce_Barrier, NULL, NULL);
98 
99  if (g_NativeBarrier)
100  return pfnInitializeSynchronizationBarrier(lpBarrier, lTotalThreads, lSpinCount);
101 #endif
102 
103  if (!lpBarrier || lTotalThreads < 1 || lSpinCount < -1)
104  {
105  SetLastError(ERROR_INVALID_PARAMETER);
106  return FALSE;
107  }
108 
109  ZeroMemory(lpBarrier, sizeof(SYNCHRONIZATION_BARRIER));
110 
111  if (lSpinCount == -1)
112  lSpinCount = 2000;
113 
114  if (!(hEvent0 = CreateEvent(NULL, TRUE, FALSE, NULL)))
115  return FALSE;
116 
117  if (!(hEvent1 = CreateEvent(NULL, TRUE, FALSE, NULL)))
118  {
119  (void)CloseHandle(hEvent0);
120  return FALSE;
121  }
122 
123  GetNativeSystemInfo(&sysinfo);
124 
125  WINPR_ASSERT(lTotalThreads >= 0);
126  lpBarrier->Reserved1 = (DWORD)lTotalThreads;
127  lpBarrier->Reserved2 = (DWORD)lTotalThreads;
128  lpBarrier->Reserved3[0] = (ULONG_PTR)hEvent0;
129  lpBarrier->Reserved3[1] = (ULONG_PTR)hEvent1;
130  lpBarrier->Reserved4 = sysinfo.dwNumberOfProcessors;
131  WINPR_ASSERT(lSpinCount >= 0);
132  lpBarrier->Reserved5 = (DWORD)lSpinCount;
133 
134  return TRUE;
135 }
136 
137 BOOL WINAPI winpr_EnterSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpBarrier, DWORD dwFlags)
138 {
139  LONG remainingThreads = 0;
140  HANDLE hCurrentEvent = NULL;
141  HANDLE hDormantEvent = NULL;
142 
143 #ifdef _WIN32
144  if (g_NativeBarrier)
145  return pfnEnterSynchronizationBarrier(lpBarrier, dwFlags);
146 #endif
147 
148  if (!lpBarrier)
149  return FALSE;
150 
172  hCurrentEvent = (HANDLE)lpBarrier->Reserved3[0];
173  hDormantEvent = (HANDLE)lpBarrier->Reserved3[1];
174 
175  remainingThreads = InterlockedDecrement((LONG*)&lpBarrier->Reserved1);
176 
177  WINPR_ASSERT(remainingThreads >= 0);
178 
179  if (remainingThreads > 0)
180  {
181  DWORD dwProcessors = lpBarrier->Reserved4;
182  BOOL spinOnly = (dwFlags & SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY) ? TRUE : FALSE;
183  BOOL blockOnly = (dwFlags & SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY) ? TRUE : FALSE;
184  BOOL block = TRUE;
185 
195  if (spinOnly || (((ULONG)remainingThreads < dwProcessors) && !blockOnly))
196  {
197  DWORD dwSpinCount = lpBarrier->Reserved5;
198  DWORD sp = 0;
203  volatile ULONG_PTR* cmp = &lpBarrier->Reserved3[0];
204  /* we spin until the last thread _completed_ the event switch */
205  while ((block = (*cmp == (ULONG_PTR)hCurrentEvent)))
206  if (!spinOnly && ++sp > dwSpinCount)
207  break;
208  }
209 
210  if (block)
211  (void)WaitForSingleObject(hCurrentEvent, INFINITE);
212 
213  return FALSE;
214  }
215 
216  /* reset the dormant event first */
217  (void)ResetEvent(hDormantEvent);
218 
219  /* reset the remaining counter */
220  lpBarrier->Reserved1 = lpBarrier->Reserved2;
221 
222  /* switch events - this will also unblock the spinning threads */
223  lpBarrier->Reserved3[1] = (ULONG_PTR)hCurrentEvent;
224  lpBarrier->Reserved3[0] = (ULONG_PTR)hDormantEvent;
225 
226  /* signal the blocked threads */
227  (void)SetEvent(hCurrentEvent);
228 
229  return TRUE;
230 }
231 
232 BOOL WINAPI winpr_DeleteSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpBarrier)
233 {
234 #ifdef _WIN32
235  if (g_NativeBarrier)
236  return pfnDeleteSynchronizationBarrier(lpBarrier);
237 #endif
238 
245  if (!lpBarrier)
246  return TRUE;
247 
248  while (lpBarrier->Reserved1 != lpBarrier->Reserved2)
249  SwitchToThread();
250 
251  if (lpBarrier->Reserved3[0])
252  (void)CloseHandle((HANDLE)lpBarrier->Reserved3[0]);
253 
254  if (lpBarrier->Reserved3[1])
255  (void)CloseHandle((HANDLE)lpBarrier->Reserved3[1]);
256 
257  ZeroMemory(lpBarrier, sizeof(SYNCHRONIZATION_BARRIER));
258 
259  return TRUE;
260 }
261 
262 #endif