critsection.c 18.8 KB
Newer Older
1 2 3 4
/*
 * Win32 critical sections
 *
 * Copyright 1998 Alexandre Julliard
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20
 */

21 22 23
#include "config.h"
#include "wine/port.h"

24
#include <assert.h>
25
#include <errno.h>
26
#include <stdarg.h>
27 28
#include <stdio.h>
#include <sys/types.h>
29
#include <time.h>
30 31
#include "ntstatus.h"
#define WIN32_NO_STATUS
32
#include "windef.h"
33
#include "winternl.h"
34
#include "wine/debug.h"
35
#include "ntdll_misc.h"
36

37 38
WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
WINE_DECLARE_DEBUG_CHANNEL(relay);
39

40
static inline LONG interlocked_inc( PLONG dest )
41
{
42
    return interlocked_xchg_add( dest, 1 ) + 1;
43 44
}

45
static inline LONG interlocked_dec( PLONG dest )
46
{
47
    return interlocked_xchg_add( dest, -1 ) - 1;
48 49
}

50
static inline void small_pause(void)
51 52 53 54 55 56 57 58
{
#ifdef __i386__
    __asm__ __volatile__( "rep;nop" : : : "memory" );
#else
    __asm__ __volatile__( "" : : : "memory" );
#endif
}

59 60 61 62 63 64 65 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
#if defined(linux) && defined(__i386__)

static inline int futex_wait( int *addr, int val, struct timespec *timeout )
{
    int res;
    __asm__ __volatile__( "xchgl %2,%%ebx\n\t"
                          "int $0x80\n\t"
                          "xchgl %2,%%ebx"
                          : "=a" (res)
                          : "0" (240) /* SYS_futex */, "D" (addr),
                            "c" (0) /* FUTEX_WAIT */, "d" (val), "S" (timeout) );
    return res;
}

static inline int futex_wake( int *addr, int val )
{
    int res;
    __asm__ __volatile__( "xchgl %2,%%ebx\n\t"
                          "int $0x80\n\t"
                          "xchgl %2,%%ebx"
                          : "=a" (res)
                          : "0" (240) /* SYS_futex */, "D" (addr),
                            "c" (1)  /* FUTEX_WAKE */, "d" (val) );
    return res;
}

static inline int use_futexes(void)
{
    static int supported = -1;

    if (supported == -1) supported = (futex_wait( &supported, 10, NULL ) != -ENOSYS);
    return supported;
}

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 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout )
{
    int val;
    struct timespec timespec;

    if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;

    timespec.tv_sec  = timeout;
    timespec.tv_nsec = 0;
    while ((val = interlocked_cmpxchg( (int *)&crit->LockSemaphore, 0, 1 )) != 1)
    {
        /* note: this may wait longer than specified in case of signals or */
        /*       multiple wake-ups, but that shouldn't be a problem */
        if (futex_wait( (int *)&crit->LockSemaphore, val, &timespec ) == -ETIMEDOUT)
            return STATUS_TIMEOUT;
    }
    return STATUS_WAIT_0;
}

static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit )
{
    if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;

    *(int *)&crit->LockSemaphore = 1;
    futex_wake( (int *)&crit->LockSemaphore, 1 );
    return STATUS_SUCCESS;
}

static inline void close_semaphore( RTL_CRITICAL_SECTION *crit )
{
    if (!use_futexes()) NtClose( crit->LockSemaphore );
}

#elif defined(__APPLE__)

#include <mach/mach.h>
#include <mach/task.h>
#include <mach/semaphore.h>

static inline semaphore_t get_mach_semaphore( RTL_CRITICAL_SECTION *crit )
{
    semaphore_t ret = *(int *)&crit->LockSemaphore;
    if (!ret)
    {
        semaphore_t sem;
        if (semaphore_create( mach_task_self(), &sem, SYNC_POLICY_FIFO, 0 )) return 0;
        if (!(ret = interlocked_cmpxchg( (int *)&crit->LockSemaphore, sem, 0 )))
            ret = sem;
        else
            semaphore_destroy( mach_task_self(), sem );  /* somebody beat us to it */
    }
    return ret;
}

static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout )
{
    mach_timespec_t timespec;
    semaphore_t sem = get_mach_semaphore( crit );

    timespec.tv_sec = timeout;
    timespec.tv_nsec = 0;
    for (;;)
    {
        switch( semaphore_timedwait( sem, timespec ))
        {
        case KERN_SUCCESS:
            return STATUS_WAIT_0;
        case KERN_ABORTED:
            continue;  /* got a signal, restart */
        case KERN_OPERATION_TIMED_OUT:
            return STATUS_TIMEOUT;
        default:
            return STATUS_INVALID_HANDLE;
        }
    }
}

static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit )
{
    semaphore_t sem = get_mach_semaphore( crit );
    semaphore_signal( sem );
    return STATUS_SUCCESS;
}

static inline void close_semaphore( RTL_CRITICAL_SECTION *crit )
{
    semaphore_destroy( mach_task_self(), *(int *)&crit->LockSemaphore );
}

#else  /* __APPLE__ */

static inline NTSTATUS fast_wait( RTL_CRITICAL_SECTION *crit, int timeout )
{
    return STATUS_NOT_IMPLEMENTED;
}
188

189 190 191 192 193 194 195 196 197
static inline NTSTATUS fast_wake( RTL_CRITICAL_SECTION *crit )
{
    return STATUS_NOT_IMPLEMENTED;
}

static inline void close_semaphore( RTL_CRITICAL_SECTION *crit )
{
    NtClose( crit->LockSemaphore );
}
198 199 200

#endif

201 202 203 204 205 206 207 208 209 210
/***********************************************************************
 *           get_semaphore
 */
static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit )
{
    HANDLE ret = crit->LockSemaphore;
    if (!ret)
    {
        HANDLE sem;
        if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0;
211
        if (!(ret = interlocked_cmpxchg_ptr( &crit->LockSemaphore, sem, 0 )))
212 213 214 215 216 217 218
            ret = sem;
        else
            NtClose(sem);  /* somebody beat us to it */
    }
    return ret;
}

219 220 221 222 223
/***********************************************************************
 *           wait_semaphore
 */
static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout )
{
224
    NTSTATUS ret;
225

226 227
    /* debug info is cleared by MakeCriticalSectionGlobal */
    if (!crit->DebugInfo || ((ret = fast_wait( crit, timeout )) == STATUS_NOT_IMPLEMENTED))
228 229 230 231 232
    {
        HANDLE sem = get_semaphore( crit );
        LARGE_INTEGER time;

        time.QuadPart = timeout * (LONGLONG)-10000000;
233
        ret = NTDLL_wait_for_multiple_objects( 1, &sem, 0, &time, 0 );
234
    }
235
    return ret;
236 237
}

238 239
/***********************************************************************
 *           RtlInitializeCriticalSection   (NTDLL.@)
Jon Griffiths's avatar
Jon Griffiths committed
240
 *
241
 * Initialises a new critical section.
Jon Griffiths's avatar
Jon Griffiths committed
242 243 244 245
 *
 * PARAMS
 *  crit [O] Critical section to initialise
 *
246
 * RETURNS
Jon Griffiths's avatar
Jon Griffiths committed
247
 *  STATUS_SUCCESS.
248 249
 *
 * SEE
250
 *  RtlInitializeCriticalSectionEx(),
251 252 253
 *  RtlInitializeCriticalSectionAndSpinCount(), RtlDeleteCriticalSection(),
 *  RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
 *  RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
254 255
 */
NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit )
256
{
257
    return RtlInitializeCriticalSectionEx( crit, 0, 0 );
258 259 260 261 262
}

/***********************************************************************
 *           RtlInitializeCriticalSectionAndSpinCount   (NTDLL.@)
 *
263
 * Initialises a new critical section with a given spin count.
264 265 266 267 268 269 270 271 272
 *
 * PARAMS
 *   crit      [O] Critical section to initialise
 *   spincount [I] Spin count for crit
 * 
 * RETURNS
 *  STATUS_SUCCESS.
 *
 * NOTES
273 274 275
 *  Available on NT4 SP3 or later.
 *
 * SEE
276
 *  RtlInitializeCriticalSectionEx(),
277 278 279
 *  RtlInitializeCriticalSection(), RtlDeleteCriticalSection(),
 *  RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
 *  RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
280 281
 */
NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
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
    return RtlInitializeCriticalSectionEx( crit, spincount, 0 );
}

/***********************************************************************
 *           RtlInitializeCriticalSectionEx   (NTDLL.@)
 *
 * Initialises a new critical section with a given spin count and flags.
 *
 * PARAMS
 *   crit      [O] Critical section to initialise.
 *   spincount [I] Number of times to spin upon contention.
 *   flags     [I] RTL_CRITICAL_SECTION_FLAG_ flags from winnt.h.
 *
 * RETURNS
 *  STATUS_SUCCESS.
 *
 * NOTES
 *  Available on Vista or later.
 *
 * SEE
 *  RtlInitializeCriticalSection(), RtlDeleteCriticalSection(),
 *  RtlEnterCriticalSection(), RtlLeaveCriticalSection(),
 *  RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount()
 */
NTSTATUS WINAPI RtlInitializeCriticalSectionEx( RTL_CRITICAL_SECTION *crit, ULONG spincount, ULONG flags )
{
    if (flags & (RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN|RTL_CRITICAL_SECTION_FLAG_STATIC_INIT))
        FIXME("(%p,%u,0x%08x) semi-stub\n", crit, spincount, flags);

    /* FIXME: if RTL_CRITICAL_SECTION_FLAG_STATIC_INIT is given, we should use
     * memory from a static pool to hold the debug info. Then heap.c could pass
     * this flag rather than initialising the process heap CS by hand. If this
     * is done, then debug info should be managed through Rtlp[Allocate|Free]DebugInfo
     * so (e.g.) MakeCriticalSectionGlobal() doesn't free it using HeapFree().
     */
    if (flags & RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO)
        crit->DebugInfo = NULL;
    else
        crit->DebugInfo = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG));

323
    if (crit->DebugInfo)
324
    {
325 326 327 328 329 330 331
        crit->DebugInfo->Type = 0;
        crit->DebugInfo->CreatorBackTraceIndex = 0;
        crit->DebugInfo->CriticalSection = crit;
        crit->DebugInfo->ProcessLocksList.Blink = &(crit->DebugInfo->ProcessLocksList);
        crit->DebugInfo->ProcessLocksList.Flink = &(crit->DebugInfo->ProcessLocksList);
        crit->DebugInfo->EntryCount = 0;
        crit->DebugInfo->ContentionCount = 0;
332
        memset( crit->DebugInfo->Spare, 0, sizeof(crit->DebugInfo->Spare) );
333
    }
334 335 336 337
    crit->LockCount      = -1;
    crit->RecursionCount = 0;
    crit->OwningThread   = 0;
    crit->LockSemaphore  = 0;
338 339
    if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
    crit->SpinCount = spincount & ~0x80000000;
340 341 342
    return STATUS_SUCCESS;
}

343
/***********************************************************************
344
 *           RtlSetCriticalSectionSpinCount   (NTDLL.@)
Jon Griffiths's avatar
Jon Griffiths committed
345
 *
346
 * Sets the spin count of a critical section.
Jon Griffiths's avatar
Jon Griffiths committed
347 348
 *
 * PARAMS
349
 *   crit      [I/O] Critical section
Jon Griffiths's avatar
Jon Griffiths committed
350
 *   spincount [I] Spin count for crit
351
 *
Jon Griffiths's avatar
Jon Griffiths committed
352
 * RETURNS
353
 *  The previous spin count.
Jon Griffiths's avatar
Jon Griffiths committed
354 355
 *
 * NOTES
356
 *  If the system is not SMP, spincount is ignored and set to 0.
357 358
 *
 * SEE
359
 *  RtlInitializeCriticalSectionEx(),
360 361 362
 *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
 *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
 *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
363
 */
364
ULONG WINAPI RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount )
365
{
366 367
    ULONG oldspincount = crit->SpinCount;
    if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0;
368
    crit->SpinCount = spincount;
369
    return oldspincount;
370 371
}

372 373
/***********************************************************************
 *           RtlDeleteCriticalSection   (NTDLL.@)
Jon Griffiths's avatar
Jon Griffiths committed
374
 *
375
 * Frees the resources used by a critical section.
Jon Griffiths's avatar
Jon Griffiths committed
376 377 378 379 380 381
 *
 * PARAMS
 *  crit [I/O] Critical section to free
 *
 * RETURNS
 *  STATUS_SUCCESS.
382 383
 *
 * SEE
384
 *  RtlInitializeCriticalSectionEx(),
385 386 387
 *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
 *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
 *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
388 389 390 391 392 393
 */
NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit )
{
    crit->LockCount      = -1;
    crit->RecursionCount = 0;
    crit->OwningThread   = 0;
394 395 396
    if (crit->DebugInfo)
    {
        /* only free the ones we made in here */
397
        if (!crit->DebugInfo->Spare[0])
398
        {
399
            RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo );
400 401
            crit->DebugInfo = NULL;
        }
402
        close_semaphore( crit );
403
    }
404 405
    else NtClose( crit->LockSemaphore );
    crit->LockSemaphore = 0;
406 407 408 409 410 411
    return STATUS_SUCCESS;
}


/***********************************************************************
 *           RtlpWaitForCriticalSection   (NTDLL.@)
Jon Griffiths's avatar
Jon Griffiths committed
412
 *
413
 * Waits for a busy critical section to become free.
Jon Griffiths's avatar
Jon Griffiths committed
414 415 416 417 418 419
 * 
 * PARAMS
 *  crit [I/O] Critical section to wait for
 *
 * RETURNS
 *  STATUS_SUCCESS.
420 421 422 423 424 425
 *
 * NOTES
 *  Use RtlEnterCriticalSection() instead of this function as it is often much
 *  faster.
 *
 * SEE
426
 *  RtlInitializeCriticalSectionEx(),
427 428 429
 *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
 *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
 *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
430 431 432 433 434 435
 */
NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit )
{
    for (;;)
    {
        EXCEPTION_RECORD rec;
436
        NTSTATUS status = wait_semaphore( crit, 5 );
437

438
        if ( status == STATUS_TIMEOUT )
439
        {
440
            const char *name = NULL;
441
            if (crit->DebugInfo) name = (char *)crit->DebugInfo->Spare[0];
442
            if (!name) name = "?";
443
            ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (60 sec)\n",
444
                 crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) );
445
            status = wait_semaphore( crit, 60 );
446
            if ( status == STATUS_TIMEOUT && TRACE_ON(relay) )
447
            {
448
                ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (5 min)\n",
449
                     crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) );
450
                status = wait_semaphore( crit, 300 );
451 452
            }
        }
453
        if (status == STATUS_WAIT_0) break;
454

455
        /* Throw exception only for Wine internal locks */
456
        if ((!crit->DebugInfo) || (!crit->DebugInfo->Spare[0])) continue;
457

458
        rec.ExceptionCode    = STATUS_POSSIBLE_DEADLOCK;
459 460 461 462
        rec.ExceptionFlags   = 0;
        rec.ExceptionRecord  = NULL;
        rec.ExceptionAddress = RtlRaiseException;  /* sic */
        rec.NumberParameters = 1;
463
        rec.ExceptionInformation[0] = (ULONG_PTR)crit;
464 465
        RtlRaiseException( &rec );
    }
466 467
    if (crit->DebugInfo) crit->DebugInfo->ContentionCount++;
    return STATUS_SUCCESS;
468 469 470 471 472
}


/***********************************************************************
 *           RtlpUnWaitCriticalSection   (NTDLL.@)
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
 *
 * Notifies other threads waiting on the busy critical section that it has
 * become free.
 * 
 * PARAMS
 *  crit [I/O] Critical section
 *
 * RETURNS
 *  Success: STATUS_SUCCESS.
 *  Failure: Any error returned by NtReleaseSemaphore()
 *
 * NOTES
 *  Use RtlLeaveCriticalSection() instead of this function as it is often much
 *  faster.
 *
 * SEE
489
 *  RtlInitializeCriticalSectionEx(),
490 491 492
 *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
 *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
 *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
493 494 495
 */
NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit )
{
496 497 498 499
    NTSTATUS ret;

    /* debug info is cleared by MakeCriticalSectionGlobal */
    if (!crit->DebugInfo || ((ret = fast_wake( crit )) == STATUS_NOT_IMPLEMENTED))
500 501
    {
        HANDLE sem = get_semaphore( crit );
502
        ret = NtReleaseSemaphore( sem, 1, NULL );
503
    }
504 505
    if (ret) RtlRaiseStatus( ret );
    return ret;
506 507 508 509 510
}


/***********************************************************************
 *           RtlEnterCriticalSection   (NTDLL.@)
Jon Griffiths's avatar
Jon Griffiths committed
511
 *
512
 * Enters a critical section, waiting for it to become available if necessary.
Jon Griffiths's avatar
Jon Griffiths committed
513 514 515 516 517 518 519
 *
 * PARAMS
 *  crit [I/O] Critical section to enter
 *
 * RETURNS
 *  STATUS_SUCCESS. The critical section is held by the caller.
 *  
520
 * SEE
521
 *  RtlInitializeCriticalSectionEx(),
522 523 524
 *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
 *  RtlDeleteCriticalSection(), RtlSetCriticalSectionSpinCount(),
 *  RtlLeaveCriticalSection(), RtlTryEnterCriticalSection()
525 526 527
 */
NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
{
528 529 530 531 532 533 534 535 536 537
    if (crit->SpinCount)
    {
        ULONG count;

        if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS;
        for (count = crit->SpinCount; count > 0; count--)
        {
            if (crit->LockCount > 0) break;  /* more than one waiter, don't bother spinning */
            if (crit->LockCount == -1)       /* try again */
            {
538
                if (interlocked_cmpxchg( &crit->LockCount, 0, -1 ) == -1) goto done;
539 540 541 542 543
            }
            small_pause();
        }
    }

544 545
    if (interlocked_inc( &crit->LockCount ))
    {
546
        if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
547 548 549 550 551 552 553 554
        {
            crit->RecursionCount++;
            return STATUS_SUCCESS;
        }

        /* Now wait for it */
        RtlpWaitForCriticalSection( crit );
    }
555
done:
556
    crit->OwningThread   = ULongToHandle(GetCurrentThreadId());
557 558 559 560 561 562 563
    crit->RecursionCount = 1;
    return STATUS_SUCCESS;
}


/***********************************************************************
 *           RtlTryEnterCriticalSection   (NTDLL.@)
Jon Griffiths's avatar
Jon Griffiths committed
564
 *
565
 * Tries to enter a critical section without waiting.
Jon Griffiths's avatar
Jon Griffiths committed
566 567 568 569 570 571 572
 *
 * PARAMS
 *  crit [I/O] Critical section to enter
 *
 * RETURNS
 *  Success: TRUE. The critical section is held by the caller.
 *  Failure: FALSE. The critical section is currently held by another thread.
573 574
 *
 * SEE
575
 *  RtlInitializeCriticalSectionEx(),
576 577 578
 *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
 *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
 *  RtlLeaveCriticalSection(), RtlSetCriticalSectionSpinCount()
579 580 581 582
 */
BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit )
{
    BOOL ret = FALSE;
583
    if (interlocked_cmpxchg( &crit->LockCount, 0, -1 ) == -1)
584
    {
585
        crit->OwningThread   = ULongToHandle(GetCurrentThreadId());
586 587 588
        crit->RecursionCount = 1;
        ret = TRUE;
    }
589
    else if (crit->OwningThread == ULongToHandle(GetCurrentThreadId()))
590 591 592 593 594 595 596 597 598 599 600
    {
        interlocked_inc( &crit->LockCount );
        crit->RecursionCount++;
        ret = TRUE;
    }
    return ret;
}


/***********************************************************************
 *           RtlLeaveCriticalSection   (NTDLL.@)
Jon Griffiths's avatar
Jon Griffiths committed
601
 *
602
 * Leaves a critical section.
Jon Griffiths's avatar
Jon Griffiths committed
603 604
 *
 * PARAMS
605
 *  crit [I/O] Critical section to leave.
Jon Griffiths's avatar
Jon Griffiths committed
606 607 608
 *
 * RETURNS
 *  STATUS_SUCCESS.
609 610
 *
 * SEE
611
 *  RtlInitializeCriticalSectionEx(),
612 613 614
 *  RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(),
 *  RtlDeleteCriticalSection(), RtlEnterCriticalSection(),
 *  RtlSetCriticalSectionSpinCount(), RtlTryEnterCriticalSection()
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
 */
NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
{
    if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
    else
    {
        crit->OwningThread = 0;
        if (interlocked_dec( &crit->LockCount ) >= 0)
        {
            /* someone is waiting */
            RtlpUnWaitCriticalSection( crit );
        }
    }
    return STATUS_SUCCESS;
}