Skip to content

Commit 6ce3881

Browse files
committed
utils: Different lock initialisation for Windows
Rewrite code to initialise mutexes in Windows. To allow static initialisation of mutex MCS locks were used. However the implementation required potentially (in case of contention) resource usages (Windows events) and more system calls. As before is there are no contention no system calls are needed (except InitializeCriticalSection). Advantages: - reduced code; - reduced tds_raw_mutex structure size; - no allocation needed; - less atomic operations needed in case of no contention. Disadvantages: - for systems till Vista on contention polling is used; however there should not be much contention anyway and there should be very few use cases of such old systems. Signed-off-by: Frediano Ziglio <freddy77@gmail.com>
1 parent 47e26a9 commit 6ce3881

File tree

5 files changed

+67
-246
lines changed

5 files changed

+67
-246
lines changed

README.Windows

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ These instructions should suffice in order to set up any project file you need.
1414
2. Every library have a proper directory where you can include every C file you find
1515
inside (not recursively)
1616

17-
* replacements here there is an exception, do not include ptw32_MCS_lock.c file
17+
* replacements
1818
* tds
1919
* dblib
2020
* ctlib

include/freetds/thread.h

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -128,49 +128,46 @@ static inline int tds_thread_is_current(tds_thread_id th)
128128
#define ETIMEDOUT 138
129129
#endif
130130

131-
struct ptw32_mcs_node_t_;
132-
133-
typedef struct {
134-
struct ptw32_mcs_node_t_ *lock;
131+
typedef struct
132+
{
135133
LONG done;
136134
DWORD thread_id;
137135
CRITICAL_SECTION crit;
138136
} tds_raw_mutex;
139137

140-
#define TDS_RAW_MUTEX_INITIALIZER { NULL, 0, 0 }
138+
#define TDS_RAW_MUTEX_INITIALIZER { 0, 0 }
141139

142140
static inline int
143141
tds_raw_mutex_init(tds_raw_mutex *mtx)
144142
{
145-
mtx->lock = NULL;
146143
mtx->done = 0;
147144
mtx->thread_id = 0;
148145
return 0;
149146
}
150147

151-
void tds_win_mutex_lock(tds_raw_mutex *mutex);
148+
void tds_win_mutex_init(tds_raw_mutex * mutex);
152149

153-
static inline void tds_raw_mutex_lock(tds_raw_mutex *mtx)
150+
static inline void
151+
tds_raw_mutex_lock(tds_raw_mutex *mtx)
154152
{
155-
if (mtx->done) {
156-
EnterCriticalSection(&mtx->crit);
157-
mtx->thread_id = GetCurrentThreadId();
158-
} else {
159-
tds_win_mutex_lock(mtx);
160-
}
153+
if (mtx->done >= 0)
154+
tds_win_mutex_init(mtx);
155+
EnterCriticalSection(&mtx->crit);
156+
mtx->thread_id = GetCurrentThreadId();
161157
}
162158

163-
int tds_raw_mutex_trylock(tds_raw_mutex *mtx);
159+
int tds_raw_mutex_trylock(tds_raw_mutex * mtx);
164160

165161
static inline void tds_raw_mutex_unlock(tds_raw_mutex *mtx)
166162
{
167163
mtx->thread_id = 0;
168164
LeaveCriticalSection(&mtx->crit);
169165
}
170166

171-
static inline void tds_raw_mutex_free(tds_raw_mutex *mtx)
167+
static inline void
168+
tds_raw_mutex_free(tds_raw_mutex *mtx)
172169
{
173-
if (mtx->done) {
170+
if (mtx->done < 0) {
174171
DeleteCriticalSection(&mtx->crit);
175172
mtx->done = 0;
176173
}

src/utils/Makefile.am

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,4 @@ libtdsutils_la_SOURCES += win_mutex.c
2525
endif
2626
EXTRA_DIST = \
2727
CMakeLists.txt \
28-
ptw32_MCS_lock.c \
2928
$(NULL)

src/utils/ptw32_MCS_lock.c

Lines changed: 0 additions & 206 deletions
This file was deleted.

src/utils/win_mutex.c

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,41 +29,72 @@
2929
#include <string.h>
3030
#endif /* HAVE_STRING_H */
3131

32+
#include <limits.h>
33+
3234
#include <freetds/windows.h>
35+
#include <freetds/macros.h>
3336
#include <freetds/thread.h>
37+
#include <freetds/bool.h>
38+
39+
typedef BOOL WINAPI WaitOnAddressFunc(volatile VOID * Address, PVOID CompareAddress, SIZE_T AddressSize, DWORD dwMilliseconds);
40+
typedef VOID WINAPI WakeByAddressAllFunc(PVOID Address);
41+
42+
static WaitOnAddressFunc *pWaitOnAddress = NULL;
43+
static WakeByAddressAllFunc *pWakeByAddressAll = NULL;
3444

35-
#include "ptw32_MCS_lock.c"
45+
static bool wait_funcs_initialized = false;
3646

3747
void
38-
tds_win_mutex_lock(tds_raw_mutex * mutex)
48+
tds_win_mutex_init(tds_raw_mutex *mutex)
3949
{
40-
if (!InterlockedExchangeAdd(&mutex->done, 0)) { /* MBR fence */
41-
ptw32_mcs_local_node_t node;
50+
long l;
4251

43-
ptw32_mcs_lock_acquire(&mutex->lock, &node);
44-
if (!mutex->done) {
45-
InitializeCriticalSection(&mutex->crit);
46-
mutex->done = 1;
52+
/* Get functions we need.
53+
* Potentially executed multiple times if called at the same time from
54+
* multiple threads but will do the same things.
55+
*/
56+
if (TDS_UNLIKELY(!wait_funcs_initialized)) {
57+
HANDLE mod = GetModuleHandle("API-MS-Win-Core-Synch-l1-2-0.dll");
58+
59+
if (mod) {
60+
pWaitOnAddress = (WaitOnAddressFunc *) GetProcAddress(mod, "WaitOnAddress");
61+
pWakeByAddressAll = (WakeByAddressAllFunc *) GetProcAddress(mod, "WakeByAddressAll");
4762
}
48-
ptw32_mcs_lock_release(&node);
63+
wait_funcs_initialized = true;
64+
}
65+
66+
/* Count number of initializers. */
67+
l = _InterlockedIncrement(&mutex->done) - 1;
68+
69+
/* If not initialized do it. */
70+
if (l == 0) {
71+
InitializeCriticalSection(&mutex->crit);
72+
l = _InterlockedExchange(&mutex->done, LONG_MIN);
73+
/* Notify other threads (only if there are any). */
74+
if (l != 1 && pWakeByAddressAll)
75+
pWakeByAddressAll((void *) &mutex->done);
76+
return;
77+
}
78+
79+
/* In the unlikely case multiple threads need to initialize the structure
80+
* at the same time wait till the other thread initialize it. */
81+
while (l >= 0) {
82+
if (pWaitOnAddress)
83+
/* From Windows 8, wait variable change. */
84+
pWaitOnAddress(&mutex->done, &l, sizeof(l), INFINITE);
85+
else
86+
/* Older versions, poll. Pretty unlikely anyways. */
87+
Sleep(0);
88+
l = mutex->done;
4989
}
50-
EnterCriticalSection(&mutex->crit);
51-
mutex->thread_id = GetCurrentThreadId();
5290
}
5391

5492
int
55-
tds_raw_mutex_trylock(tds_raw_mutex * mutex)
93+
tds_raw_mutex_trylock(tds_raw_mutex *mutex)
5694
{
57-
if (!mutex->done && !InterlockedExchangeAdd(&mutex->done, 0)) { /* MBR fence */
58-
ptw32_mcs_local_node_t node;
95+
if (mutex->done >= 0)
96+
tds_win_mutex_init(mutex);
5997

60-
ptw32_mcs_lock_acquire(&mutex->lock, &node);
61-
if (!mutex->done) {
62-
InitializeCriticalSection(&mutex->crit);
63-
mutex->done = 1;
64-
}
65-
ptw32_mcs_lock_release(&node);
66-
}
6798
if (TryEnterCriticalSection(&mutex->crit)) {
6899
DWORD thread_id = GetCurrentThreadId();
69100
if (mutex->thread_id == thread_id) {

0 commit comments

Comments
 (0)