/ Check-in [f3676b24]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Added experimental mutex implementation using interlocked ops and spin locks.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | experimental
Files: files | file ages | folders
SHA1: f3676b243d94229fe6b646ef47066cd707935811
User & Date: shaneh 2010-02-12 22:43:48
Context
2010-02-12
22:43
Added experimental mutex implementation using interlocked ops and spin locks. Closed-Leaf check-in: f3676b24 user: shaneh tags: experimental
2010-02-05
16:28
Avoid truncating non-in-memory sub-journals when releasing a savepoint for a small performance improvement. check-in: 27dc5b1c user: shaneh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/mutex_w32.c.

19
20
21
22
23
24
25



26

27
28
29



30










31
32
33
34
35
36
37
..
66
67
68
69
70
71
72
73



74
75

76
77
78
79
80
81
82
83








84
85
86
87
88
89
90

91
92

93
94
95
96
97
98
99
100
101
102
103
104
105

106
107
108
109

110
111
112
113
114
115
116
117
118
119
120

121
122
123
124
125
126
127
...
168
169
170
171
172
173
174

175

176
177
178
179

180

181
182
183
184
185
186
187
...
194
195
196
197
198
199
200

201

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216

217
















218
219
220




221


222

223
224











225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243






244
245
246
247
248
249
250
251
252
253
254

255
256








257
258
259






260
261
262
263
264
265
266
*/
#ifdef SQLITE_MUTEX_W32

/*
** Each recursive mutex is an instance of the following structure.
*/
struct sqlite3_mutex {



  CRITICAL_SECTION mutex;    /* Mutex controlling the lock */

  int id;                    /* Mutex type */
  int nRef;                  /* Number of enterances */
  DWORD owner;               /* Thread holding this mutex */



};











/*
** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
** or WinCE.  Return false (zero) for Win95, Win98, or WinME.
**
** Here is an interesting observation:  Win95, Win98, and WinME lack
** the LockFileEx() API.  But we can still statically link against that
................................................................................
#ifdef SQLITE_DEBUG
/*
** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
** intended for use only inside assert() statements.
*/
static int winMutexHeld(sqlite3_mutex *p){
  return p->nRef!=0 && p->owner==GetCurrentThreadId();
}



static int winMutexNotheld(sqlite3_mutex *p){
  return p->nRef==0 || p->owner!=GetCurrentThreadId();

}
#endif


/*
** Initialize and deinitialize the mutex subsystem.
*/
static sqlite3_mutex winMutex_staticMutexes[6];








static int winMutex_isInit = 0;
/* As winMutexInit() and winMutexEnd() are called as part
** of the sqlite3_initialize and sqlite3_shutdown()
** processing, the "interlocked" magic is probably not
** strictly necessary.
*/
static long winMutex_lock = 0;


static int winMutexInit(void){ 

  /* The first to increment to 1 does actual initialization */
  if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){
    int i;
    for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
      InitializeCriticalSection(&winMutex_staticMutexes[i].mutex);
    }
    winMutex_isInit = 1;
  }else{
    /* Someone else is in the process of initing the static mutexes */
    while( !winMutex_isInit ){
      Sleep(1);
    }
  }

  return SQLITE_OK; 
}

static int winMutexEnd(void){ 

  /* The first to decrement to 0 does actual shutdown 
  ** (which should be the last to shutdown.) */
  if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){
    if( winMutex_isInit==1 ){
      int i;
      for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
        DeleteCriticalSection(&winMutex_staticMutexes[i].mutex);
      }
      winMutex_isInit = 0;
    }
  }

  return SQLITE_OK; 
}

/*
** The sqlite3_mutex_alloc() routine allocates a new
** mutex and returns a pointer to it.  If it returns NULL
** that means that a mutex could not be allocated.  SQLite
................................................................................

  switch( iType ){
    case SQLITE_MUTEX_FAST:
    case SQLITE_MUTEX_RECURSIVE: {
      p = sqlite3MallocZero( sizeof(*p) );
      if( p ){  
        p->id = iType;

        InitializeCriticalSection(&p->mutex);

      }
      break;
    }
    default: {

      assert( winMutex_isInit==1 );

      assert( iType-2 >= 0 );
      assert( iType-2 < ArraySize(winMutex_staticMutexes) );
      p = &winMutex_staticMutexes[iType-2];
      p->id = iType;
      break;
    }
  }
................................................................................
** allocated mutex.  SQLite is careful to deallocate every
** mutex that it allocates.
*/
static void winMutexFree(sqlite3_mutex *p){
  assert( p );
  assert( p->nRef==0 );
  assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );

  DeleteCriticalSection(&p->mutex);

  sqlite3_free(p);
}

/*
** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
** to enter a mutex.  If another thread is already within the mutex,
** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
** SQLITE_BUSY.  The sqlite3_mutex_try() interface returns SQLITE_OK
** upon successful entry.  Mutexes created using SQLITE_MUTEX_RECURSIVE can
** be entered multiple times by the same thread.  In such cases the,
** mutex must be exited an equal number of times before another thread
** can enter.  If the same thread tries to enter any other kind of mutex
** more than once, the behavior is undefined.
*/
static void winMutexEnter(sqlite3_mutex *p){

  assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld(p) );
















  EnterCriticalSection(&p->mutex);
  p->owner = GetCurrentThreadId(); 
  p->nRef++;




}


static int winMutexTry(sqlite3_mutex *p){

  int rc = SQLITE_BUSY;
  assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld(p) );











  /*
  ** The sqlite3_mutex_try() routine is very rarely used, and when it
  ** is used it is merely an optimization.  So it is OK for it to always
  ** fail.  
  **
  ** The TryEnterCriticalSection() interface is only available on WinNT.
  ** And some windows compilers complain if you try to use it without
  ** first doing some #defines that prevent SQLite from building on Win98.
  ** For that reason, we will omit this optimization for now.  See
  ** ticket #2685.
  */
#if 0
  if( mutexIsNT() && TryEnterCriticalSection(&p->mutex) ){
    p->owner = GetCurrentThreadId();
    p->nRef++;
    rc = SQLITE_OK;
  }
#else
  UNUSED_PARAMETER(p);






#endif
  return rc;
}

/*
** The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread.  The behavior
** is undefined if the mutex is not currently entered or
** is not currently allocated.  SQLite will never do either.
*/
static void winMutexLeave(sqlite3_mutex *p){

  assert( p->nRef>0 );
  assert( p->owner==GetCurrentThreadId() );








  p->nRef--;
  assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
  LeaveCriticalSection(&p->mutex);






}

sqlite3_mutex_methods *sqlite3DefaultMutex(void){
  static sqlite3_mutex_methods sMutex = {
    winMutexInit,
    winMutexEnd,
    winMutexAlloc,







>
>
>

>

|

>
>
>

>
>
>
>
>
>
>
>
>
>







 








>
>
>

|
>







|
>
>
>
>
>
>
>
>







>


>













>




>











>







 







>

>




>

>







 







>

>











|



>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|

>
>
>
>
|
>
>

>

|
>
>
>
>
>
>
>
>
>
>
>













|





>
>
>
>
>
>











>

|
>
>
>
>
>
>
>
>



>
>
>
>
>
>







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
..
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
...
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
*/
#ifdef SQLITE_MUTEX_W32

/*
** Each recursive mutex is an instance of the following structure.
*/
struct sqlite3_mutex {
#ifdef SQLITE_W32_INTERLOCK_MUTEX
  LONG lock;
#else
  CRITICAL_SECTION mutex;    /* Mutex controlling the lock */
#endif
  int id;                    /* Mutex type */
  LONG nRef;                 /* Number of enterances */
  DWORD owner;               /* Thread holding this mutex */
#ifdef SQLITE_DEBUG
  int trace;                 /* True to trace changes */
#endif
};
#ifdef SQLITE_W32_INTERLOCK_MUTEX
#define SQLITE_W32_MUTEX_INITIALIZER 0L
#else
#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
#endif
#ifdef SQLITE_DEBUG
#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, 0L, (DWORD)0, 0 }
#else
#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, 0L, (DWORD)0 }
#endif

/*
** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
** or WinCE.  Return false (zero) for Win95, Win98, or WinME.
**
** Here is an interesting observation:  Win95, Win98, and WinME lack
** the LockFileEx() API.  But we can still statically link against that
................................................................................
#ifdef SQLITE_DEBUG
/*
** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
** intended for use only inside assert() statements.
*/
static int winMutexHeld(sqlite3_mutex *p){
  return p->nRef!=0 && p->owner==GetCurrentThreadId();
}
static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){
  return p->nRef==0 || p->owner!=tid;
}
static int winMutexNotheld(sqlite3_mutex *p){
  DWORD tid = GetCurrentThreadId(); 
  return winMutexNotheld2(p, tid);
}
#endif


/*
** Initialize and deinitialize the mutex subsystem.
*/
static sqlite3_mutex winMutex_staticMutexes[6] = {
  SQLITE3_MUTEX_INITIALIZER,
  SQLITE3_MUTEX_INITIALIZER,
  SQLITE3_MUTEX_INITIALIZER,
  SQLITE3_MUTEX_INITIALIZER,
  SQLITE3_MUTEX_INITIALIZER,
  SQLITE3_MUTEX_INITIALIZER
};
#ifndef SQLITE_W32_INTERLOCK_MUTEX
static int winMutex_isInit = 0;
/* As winMutexInit() and winMutexEnd() are called as part
** of the sqlite3_initialize and sqlite3_shutdown()
** processing, the "interlocked" magic is probably not
** strictly necessary.
*/
static long winMutex_lock = 0;
#endif

static int winMutexInit(void){ 
#ifndef SQLITE_W32_INTERLOCK_MUTEX
  /* The first to increment to 1 does actual initialization */
  if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){
    int i;
    for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
      InitializeCriticalSection(&winMutex_staticMutexes[i].mutex);
    }
    winMutex_isInit = 1;
  }else{
    /* Someone else is in the process of initing the static mutexes */
    while( !winMutex_isInit ){
      Sleep(1);
    }
  }
#endif
  return SQLITE_OK; 
}

static int winMutexEnd(void){ 
#ifndef SQLITE_W32_INTERLOCK_MUTEX
  /* The first to decrement to 0 does actual shutdown 
  ** (which should be the last to shutdown.) */
  if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){
    if( winMutex_isInit==1 ){
      int i;
      for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
        DeleteCriticalSection(&winMutex_staticMutexes[i].mutex);
      }
      winMutex_isInit = 0;
    }
  }
#endif
  return SQLITE_OK; 
}

/*
** The sqlite3_mutex_alloc() routine allocates a new
** mutex and returns a pointer to it.  If it returns NULL
** that means that a mutex could not be allocated.  SQLite
................................................................................

  switch( iType ){
    case SQLITE_MUTEX_FAST:
    case SQLITE_MUTEX_RECURSIVE: {
      p = sqlite3MallocZero( sizeof(*p) );
      if( p ){  
        p->id = iType;
#ifndef SQLITE_W32_INTERLOCK_MUTEX
        InitializeCriticalSection(&p->mutex);
#endif
      }
      break;
    }
    default: {
#ifndef SQLITE_W32_INTERLOCK_MUTEX
      assert( winMutex_isInit==1 );
#endif
      assert( iType-2 >= 0 );
      assert( iType-2 < ArraySize(winMutex_staticMutexes) );
      p = &winMutex_staticMutexes[iType-2];
      p->id = iType;
      break;
    }
  }
................................................................................
** allocated mutex.  SQLite is careful to deallocate every
** mutex that it allocates.
*/
static void winMutexFree(sqlite3_mutex *p){
  assert( p );
  assert( p->nRef==0 );
  assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
#ifndef SQLITE_W32_INTERLOCK_MUTEX
  DeleteCriticalSection(&p->mutex);
#endif
  sqlite3_free(p);
}

/*
** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
** to enter a mutex.  If another thread is already within the mutex,
** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
** SQLITE_BUSY.  The sqlite3_mutex_try() interface returns SQLITE_OK
** upon successful entry.  Mutexes created using SQLITE_MUTEX_RECURSIVE can
** be entered multiple times by the same thread.  In such cases the,
** mutex must be exited an equal number of times before another thread
** can enter.  If the same thread tries to enter any o(1ther kind of mutex
** more than once, the behavior is undefined.
*/
static void winMutexEnter(sqlite3_mutex *p){
  DWORD tid = GetCurrentThreadId(); 
  assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
#ifdef SQLITE_W32_INTERLOCK_MUTEX
  while( 1 ) {
    /* get exclusive access to mutex struct (using spinlock) */
    while( InterlockedCompareExchange(&p->lock, 1, 0)!=0 ) Sleep(0);
    /* if not held or already held by us */
    if ((p->owner == 0) || (p->owner == tid) ){
      p->owner = tid; 
      p->nRef++;
      p->lock = 0; /* release lock */
      break;
    } else {
      p->lock = 0; /* release lock */
      /* and wait some more... */
    }
  }
#else
  EnterCriticalSection(&p->mutex);
  p->owner = tid; 
  p->nRef++;
#endif
#ifdef SQLITE_DEBUG
  if( p->trace ){
    printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
  }
#endif
}
static int winMutexTry(sqlite3_mutex *p){
  DWORD tid = GetCurrentThreadId(); 
  int rc = SQLITE_BUSY;
  assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
#ifdef SQLITE_W32_INTERLOCK_MUTEX
  /* get exclusive access to mutex struct (using spinlock) */
  while( InterlockedCompareExchange(&p->lock, 1, 0)!=0 ) Sleep(0);
  /* if not held or already held by us */
  if ((p->owner == 0) || (p->owner == tid) ){
    p->owner = tid; 
    p->nRef++;
    rc = SQLITE_OK;
  }
  p->lock = 0; /* release lock */
#else
  /*
  ** The sqlite3_mutex_try() routine is very rarely used, and when it
  ** is used it is merely an optimization.  So it is OK for it to always
  ** fail.  
  **
  ** The TryEnterCriticalSection() interface is only available on WinNT.
  ** And some windows compilers complain if you try to use it without
  ** first doing some #defines that prevent SQLite from building on Win98.
  ** For that reason, we will omit this optimization for now.  See
  ** ticket #2685.
  */
#if 0
  if( mutexIsNT() && TryEnterCriticalSection(&p->mutex) ){
    p->owner = tid;
    p->nRef++;
    rc = SQLITE_OK;
  }
#else
  UNUSED_PARAMETER(p);
#endif
#endif
#ifdef SQLITE_DEBUG
  if( rc==SQLITE_OK && p->trace ){
    printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
  }
#endif
  return rc;
}

/*
** The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread.  The behavior
** is undefined if the mutex is not currently entered or
** is not currently allocated.  SQLite will never do either.
*/
static void winMutexLeave(sqlite3_mutex *p){
  DWORD tid = GetCurrentThreadId(); 
  assert( p->nRef>0 );
  assert( p->owner==tid );
#ifdef SQLITE_W32_INTERLOCK_MUTEX
  /* get exclusive access to mutex struct (using spinlock) */
  while( InterlockedCompareExchange(&p->lock, 1L, 0L)!=0L ) Sleep(0);
  p->nRef--;
  assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
  if( p->nRef==0 ) p->owner=0;
  p->lock = 0; /* release lock */
#else
  p->nRef--;
  assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
  LeaveCriticalSection(&p->mutex);
#endif
#ifdef SQLITE_DEBUG
  if( p->trace ){
    printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
  }
#endif
}

sqlite3_mutex_methods *sqlite3DefaultMutex(void){
  static sqlite3_mutex_methods sMutex = {
    winMutexInit,
    winMutexEnd,
    winMutexAlloc,