libwine: Remove __wine_main_arg* from the public header.
[wine/zf.git] / dlls / ntoskrnl.exe / sync.c
blob647d427ee5df71007f89834faa4a9f6c9b2f08b2
1 /*
2 * Kernel synchronization
4 * Copyright (C) 2018 Zebediah Figura
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * 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
21 #include <limits.h>
22 #include <stdarg.h>
24 #include "ntstatus.h"
25 #define WIN32_NO_STATUS
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winternl.h"
29 #include "ddk/ntddk.h"
30 #include "ddk/wdm.h"
31 #include "ddk/ntifs.h"
33 #include "wine/asm.h"
34 #include "wine/debug.h"
35 #include "wine/heap.h"
37 #include "ntoskrnl_private.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(ntoskrnl);
41 enum object_type
43 TYPE_MANUAL_EVENT = 0,
44 TYPE_AUTO_EVENT = 1,
45 TYPE_MUTEX = 2,
46 TYPE_SEMAPHORE = 5,
47 TYPE_MANUAL_TIMER = 8,
48 TYPE_AUTO_TIMER = 9,
51 DECLARE_CRITICAL_SECTION(sync_cs);
53 /***********************************************************************
54 * KeWaitForMultipleObjects (NTOSKRNL.EXE.@)
56 NTSTATUS WINAPI KeWaitForMultipleObjects(ULONG count, void *pobjs[],
57 WAIT_TYPE wait_type, KWAIT_REASON reason, KPROCESSOR_MODE mode,
58 BOOLEAN alertable, LARGE_INTEGER *timeout, KWAIT_BLOCK *wait_blocks)
60 DISPATCHER_HEADER **objs = (DISPATCHER_HEADER **)pobjs;
61 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
62 NTSTATUS ret;
63 ULONG i;
65 TRACE("count %u, objs %p, wait_type %u, reason %u, mode %d, alertable %u, timeout %p, wait_blocks %p.\n",
66 count, objs, wait_type, reason, mode, alertable, timeout, wait_blocks);
68 /* We co-opt DISPATCHER_HEADER.WaitListHead:
69 * Blink stores a handle to the synchronization object,
70 * Flink stores the number of threads currently waiting on this object. */
72 EnterCriticalSection( &sync_cs );
73 for (i = 0; i < count; i++)
75 if (objs[i]->WaitListHead.Blink == INVALID_HANDLE_VALUE)
77 ObOpenObjectByPointer( objs[i], OBJ_KERNEL_HANDLE, NULL, SYNCHRONIZE, NULL, KernelMode, &handles[i] );
78 continue;
81 ++*((ULONG_PTR *)&objs[i]->WaitListHead.Flink);
82 if (!objs[i]->WaitListHead.Blink)
84 switch (objs[i]->Type)
86 case TYPE_MANUAL_EVENT:
87 objs[i]->WaitListHead.Blink = CreateEventW( NULL, TRUE, objs[i]->SignalState, NULL );
88 break;
89 case TYPE_AUTO_EVENT:
90 objs[i]->WaitListHead.Blink = CreateEventW( NULL, FALSE, objs[i]->SignalState, NULL );
91 break;
92 case TYPE_MUTEX:
93 objs[i]->WaitListHead.Blink = CreateMutexW( NULL, FALSE, NULL );
94 break;
95 case TYPE_SEMAPHORE:
97 KSEMAPHORE *semaphore = CONTAINING_RECORD(objs[i], KSEMAPHORE, Header);
98 objs[i]->WaitListHead.Blink = CreateSemaphoreW( NULL,
99 semaphore->Header.SignalState, semaphore->Limit, NULL );
100 break;
102 case TYPE_MANUAL_TIMER:
103 case TYPE_AUTO_TIMER:
104 break;
108 handles[i] = objs[i]->WaitListHead.Blink;
110 LeaveCriticalSection( &sync_cs );
112 ret = NtWaitForMultipleObjects( count, handles, (wait_type == WaitAny), alertable, timeout );
114 EnterCriticalSection( &sync_cs );
115 for (i = 0; i < count; i++)
117 if (ret == i || (!ret && wait_type == WaitAll))
119 switch (objs[i]->Type)
121 case TYPE_AUTO_EVENT:
122 case TYPE_AUTO_TIMER:
123 objs[i]->SignalState = FALSE;
124 break;
125 case TYPE_MUTEX:
126 case TYPE_SEMAPHORE:
127 --objs[i]->SignalState;
128 break;
132 if (objs[i]->WaitListHead.Blink == INVALID_HANDLE_VALUE)
134 NtClose( handles[i] );
136 else if (!--*((ULONG_PTR *)&objs[i]->WaitListHead.Flink))
138 switch (objs[i]->Type)
140 case TYPE_MANUAL_EVENT:
141 case TYPE_AUTO_EVENT:
142 case TYPE_SEMAPHORE:
143 CloseHandle(objs[i]->WaitListHead.Blink);
144 objs[i]->WaitListHead.Blink = NULL;
145 break;
146 case TYPE_MUTEX:
147 /* Native will panic if a mutex is destroyed while held, so we
148 * don't have to worry about leaking the handle here. */
149 if (objs[i]->SignalState == 1)
151 CloseHandle(objs[i]->WaitListHead.Blink);
152 objs[i]->WaitListHead.Blink = NULL;
154 break;
158 LeaveCriticalSection( &sync_cs );
160 return ret;
163 /***********************************************************************
164 * KeWaitForSingleObject (NTOSKRNL.EXE.@)
166 NTSTATUS WINAPI KeWaitForSingleObject( void *obj, KWAIT_REASON reason,
167 KPROCESSOR_MODE mode, BOOLEAN alertable, LARGE_INTEGER *timeout )
169 return KeWaitForMultipleObjects( 1, &obj, WaitAny, reason, mode, alertable, timeout, NULL );
172 /***********************************************************************
173 * KeWaitForMutexObject (NTOSKRNL.EXE.@)
175 NTSTATUS WINAPI KeWaitForMutexObject( PRKMUTEX mutex, KWAIT_REASON reason,
176 KPROCESSOR_MODE mode, BOOLEAN alertable, LARGE_INTEGER *timeout)
178 return KeWaitForSingleObject( mutex, reason, mode, alertable, timeout );
182 /***********************************************************************
183 * KeInitializeEvent (NTOSKRNL.EXE.@)
185 void WINAPI KeInitializeEvent( PRKEVENT event, EVENT_TYPE type, BOOLEAN state )
187 TRACE("event %p, type %u, state %u.\n", event, type, state);
189 event->Header.Type = type;
190 event->Header.SignalState = state;
191 event->Header.WaitListHead.Blink = NULL;
192 event->Header.WaitListHead.Flink = NULL;
195 static void *create_event_object( HANDLE handle )
197 EVENT_BASIC_INFORMATION info;
198 KEVENT *event;
200 if (!(event = alloc_kernel_object( ExEventObjectType, handle, sizeof(*event), 0 ))) return NULL;
202 if (!NtQueryEvent( handle, EventBasicInformation, &info, sizeof(info), NULL ))
203 KeInitializeEvent( event, info.EventType, info.EventState );
204 event->Header.WaitListHead.Blink = INVALID_HANDLE_VALUE; /* mark as kernel object */
205 return event;
208 static const WCHAR event_type_name[] = {'E','v','e','n','t',0};
210 static struct _OBJECT_TYPE event_type = {
211 event_type_name,
212 create_event_object
215 POBJECT_TYPE ExEventObjectType = &event_type;
217 /***********************************************************************
218 * IoCreateSynchronizationEvent (NTOSKRNL.EXE.@)
220 PKEVENT WINAPI IoCreateSynchronizationEvent( UNICODE_STRING *name, HANDLE *ret_handle )
222 OBJECT_ATTRIBUTES attr;
223 HANDLE handle;
224 KEVENT *event;
225 NTSTATUS ret;
227 TRACE( "(%p %p)\n", name, ret_handle );
229 InitializeObjectAttributes( &attr, name, 0, 0, NULL );
230 ret = NtCreateEvent( &handle, EVENT_ALL_ACCESS, &attr, SynchronizationEvent, TRUE );
231 if (ret) return NULL;
233 if (kernel_object_from_handle( handle, ExEventObjectType, (void**)&event ))
235 NtClose( handle);
236 return NULL;
239 *ret_handle = handle;
240 return event;
243 /***********************************************************************
244 * KeSetEvent (NTOSKRNL.EXE.@)
246 LONG WINAPI KeSetEvent( PRKEVENT event, KPRIORITY increment, BOOLEAN wait )
248 HANDLE handle;
249 LONG ret = 0;
251 TRACE("event %p, increment %d, wait %u.\n", event, increment, wait);
253 if (event->Header.WaitListHead.Blink != INVALID_HANDLE_VALUE)
255 EnterCriticalSection( &sync_cs );
256 ret = InterlockedExchange( &event->Header.SignalState, TRUE );
257 if ((handle = event->Header.WaitListHead.Blink))
258 SetEvent( handle );
259 LeaveCriticalSection( &sync_cs );
261 else
263 if (!ObOpenObjectByPointer( event, OBJ_KERNEL_HANDLE, NULL, EVENT_MODIFY_STATE, NULL, KernelMode, &handle ))
265 NtSetEvent( handle, &ret );
266 NtClose( handle );
268 event->Header.SignalState = TRUE;
271 return ret;
274 /***********************************************************************
275 * KeResetEvent (NTOSKRNL.EXE.@)
277 LONG WINAPI KeResetEvent( PRKEVENT event )
279 HANDLE handle;
280 LONG ret = 0;
282 TRACE("event %p.\n", event);
284 if (event->Header.WaitListHead.Blink != INVALID_HANDLE_VALUE)
286 EnterCriticalSection( &sync_cs );
287 ret = InterlockedExchange( &event->Header.SignalState, FALSE );
288 if ((handle = event->Header.WaitListHead.Blink))
289 ResetEvent( handle );
290 LeaveCriticalSection( &sync_cs );
292 else
294 if (!ObOpenObjectByPointer( event, OBJ_KERNEL_HANDLE, NULL, EVENT_MODIFY_STATE, NULL, KernelMode, &handle ))
296 NtResetEvent( handle, &ret );
297 NtClose( handle );
299 event->Header.SignalState = FALSE;
302 return ret;
305 /***********************************************************************
306 * KeClearEvent (NTOSKRNL.EXE.@)
308 void WINAPI KeClearEvent( PRKEVENT event )
310 KeResetEvent( event );
313 /***********************************************************************
314 * KeInitializeSemaphore (NTOSKRNL.EXE.@)
316 void WINAPI KeInitializeSemaphore( PRKSEMAPHORE semaphore, LONG count, LONG limit )
318 TRACE("semaphore %p, count %d, limit %d.\n", semaphore, count, limit);
320 semaphore->Header.Type = TYPE_SEMAPHORE;
321 semaphore->Header.SignalState = count;
322 semaphore->Header.WaitListHead.Blink = NULL;
323 semaphore->Header.WaitListHead.Flink = NULL;
324 semaphore->Limit = limit;
327 /***********************************************************************
328 * KeReleaseSemaphore (NTOSKRNL.EXE.@)
330 LONG WINAPI KeReleaseSemaphore( PRKSEMAPHORE semaphore, KPRIORITY increment,
331 LONG count, BOOLEAN wait )
333 HANDLE handle;
334 LONG ret;
336 TRACE("semaphore %p, increment %d, count %d, wait %u.\n",
337 semaphore, increment, count, wait);
339 EnterCriticalSection( &sync_cs );
340 ret = InterlockedExchangeAdd( &semaphore->Header.SignalState, count );
341 if ((handle = semaphore->Header.WaitListHead.Blink))
342 ReleaseSemaphore( handle, count, NULL );
343 LeaveCriticalSection( &sync_cs );
345 return ret;
348 static const WCHAR semaphore_type_name[] = {'S','e','m','a','p','h','o','r','e',0};
350 static struct _OBJECT_TYPE semaphore_type =
352 semaphore_type_name
355 POBJECT_TYPE ExSemaphoreObjectType = &semaphore_type;
357 /***********************************************************************
358 * KeInitializeMutex (NTOSKRNL.EXE.@)
360 void WINAPI KeInitializeMutex( PRKMUTEX mutex, ULONG level )
362 TRACE("mutex %p, level %u.\n", mutex, level);
364 mutex->Header.Type = TYPE_MUTEX;
365 mutex->Header.SignalState = 1;
366 mutex->Header.WaitListHead.Blink = NULL;
367 mutex->Header.WaitListHead.Flink = NULL;
370 /***********************************************************************
371 * KeReleaseMutex (NTOSKRNL.EXE.@)
373 LONG WINAPI KeReleaseMutex( PRKMUTEX mutex, BOOLEAN wait )
375 LONG ret;
377 TRACE("mutex %p, wait %u.\n", mutex, wait);
379 EnterCriticalSection( &sync_cs );
380 ret = mutex->Header.SignalState++;
381 if (!ret && !mutex->Header.WaitListHead.Flink)
383 CloseHandle( mutex->Header.WaitListHead.Blink );
384 mutex->Header.WaitListHead.Blink = NULL;
386 LeaveCriticalSection( &sync_cs );
388 return ret;
391 /***********************************************************************
392 * KeInitializeTimerEx (NTOSKRNL.EXE.@)
394 void WINAPI KeInitializeTimerEx( KTIMER *timer, TIMER_TYPE type )
396 TRACE("timer %p, type %u.\n", timer, type);
398 RtlZeroMemory(timer, sizeof(KTIMER));
399 timer->Header.Type = (type == NotificationTimer) ? TYPE_MANUAL_TIMER : TYPE_AUTO_TIMER;
400 timer->Header.SignalState = FALSE;
401 timer->Header.Inserted = FALSE;
402 timer->Header.WaitListHead.Blink = NULL;
403 timer->Header.WaitListHead.Flink = NULL;
406 /***********************************************************************
407 * KeInitializeTimer (NTOSKRNL.EXE.@)
409 void WINAPI KeInitializeTimer( KTIMER *timer )
411 KeInitializeTimerEx(timer, NotificationTimer);
414 /***********************************************************************
415 * KeSetTimerEx (NTOSKRNL.EXE.@)
417 BOOLEAN WINAPI KeSetTimerEx( KTIMER *timer, LARGE_INTEGER duetime, LONG period, KDPC *dpc )
419 BOOL ret;
421 TRACE("timer %p, duetime %s, period %d, dpc %p.\n",
422 timer, wine_dbgstr_longlong(duetime.QuadPart), period, dpc);
424 if (dpc)
426 FIXME("Unhandled DPC %p.\n", dpc);
427 return FALSE;
430 EnterCriticalSection( &sync_cs );
432 ret = timer->Header.Inserted;
433 timer->Header.Inserted = TRUE;
434 timer->Header.WaitListHead.Blink = CreateWaitableTimerW( NULL, timer->Header.Type == TYPE_MANUAL_TIMER, NULL );
435 SetWaitableTimer( timer->Header.WaitListHead.Blink, &duetime, period, NULL, NULL, FALSE );
437 LeaveCriticalSection( &sync_cs );
439 return ret;
442 BOOLEAN WINAPI KeCancelTimer( KTIMER *timer )
444 BOOL ret;
446 TRACE("timer %p.\n", timer);
448 EnterCriticalSection( &sync_cs );
449 ret = timer->Header.Inserted;
450 timer->Header.Inserted = FALSE;
451 CloseHandle(timer->Header.WaitListHead.Blink);
452 timer->Header.WaitListHead.Blink = NULL;
453 LeaveCriticalSection( &sync_cs );
455 return ret;
458 /***********************************************************************
459 * KeDelayExecutionThread (NTOSKRNL.EXE.@)
461 NTSTATUS WINAPI KeDelayExecutionThread( KPROCESSOR_MODE mode, BOOLEAN alertable, LARGE_INTEGER *timeout )
463 TRACE("mode %d, alertable %u, timeout %p.\n", mode, alertable, timeout);
464 return NtDelayExecution( alertable, timeout );
467 /***********************************************************************
468 * KeInitializeSpinLock (NTOSKRNL.EXE.@)
470 void WINAPI KeInitializeSpinLock( KSPIN_LOCK *lock )
472 TRACE("lock %p.\n", lock);
473 *lock = 0;
476 static inline void small_pause(void)
478 #ifdef __x86_64__
479 __asm__ __volatile__( "rep;nop" : : : "memory" );
480 #else
481 __asm__ __volatile__( "" : : : "memory" );
482 #endif
485 /***********************************************************************
486 * KeAcquireSpinLockAtDpcLevel (NTOSKRNL.EXE.@)
488 void WINAPI KeAcquireSpinLockAtDpcLevel( KSPIN_LOCK *lock )
490 TRACE("lock %p.\n", lock);
491 while (!InterlockedCompareExchangePointer( (void **)lock, (void *)1, (void *)0 ))
492 small_pause();
495 /***********************************************************************
496 * KeReleaseSpinLockFromDpcLevel (NTOSKRNL.EXE.@)
498 void WINAPI KeReleaseSpinLockFromDpcLevel( KSPIN_LOCK *lock )
500 TRACE("lock %p.\n", lock);
501 InterlockedExchangePointer( (void **)lock, 0 );
504 #define QUEUED_SPINLOCK_OWNED 0x2
506 /***********************************************************************
507 * KeAcquireInStackQueuedSpinLockAtDpcLevel (NTOSKRNL.EXE.@)
509 DEFINE_FASTCALL_WRAPPER( KeAcquireInStackQueuedSpinLockAtDpcLevel, 8 )
510 void FASTCALL KeAcquireInStackQueuedSpinLockAtDpcLevel( KSPIN_LOCK *lock, KLOCK_QUEUE_HANDLE *queue )
512 KSPIN_LOCK_QUEUE *tail;
514 TRACE("lock %p, queue %p.\n", lock, queue);
516 queue->LockQueue.Next = NULL;
518 if (!(tail = InterlockedExchangePointer( (void **)lock, &queue->LockQueue )))
519 queue->LockQueue.Lock = (KSPIN_LOCK *)((ULONG_PTR)lock | QUEUED_SPINLOCK_OWNED);
520 else
522 queue->LockQueue.Lock = lock;
523 InterlockedExchangePointer( (void **)&tail->Next, &queue->LockQueue );
525 while (!((ULONG_PTR)InterlockedCompareExchangePointer( (void **)&queue->LockQueue.Lock, 0, 0 )
526 & QUEUED_SPINLOCK_OWNED))
528 small_pause();
533 /***********************************************************************
534 * KeReleaseInStackQueuedSpinLockFromDpcLevel (NTOSKRNL.EXE.@)
536 DEFINE_FASTCALL1_WRAPPER( KeReleaseInStackQueuedSpinLockFromDpcLevel )
537 void FASTCALL KeReleaseInStackQueuedSpinLockFromDpcLevel( KLOCK_QUEUE_HANDLE *queue )
539 KSPIN_LOCK *lock = (KSPIN_LOCK *)((ULONG_PTR)queue->LockQueue.Lock & ~QUEUED_SPINLOCK_OWNED);
540 KSPIN_LOCK_QUEUE *next;
542 TRACE("lock %p, queue %p.\n", lock, queue);
544 queue->LockQueue.Lock = NULL;
546 if (!(next = queue->LockQueue.Next))
548 /* If we are truly the last in the queue, the lock will point to us. */
549 if (InterlockedCompareExchangePointer( (void **)lock, NULL, &queue->LockQueue ) == queue)
550 return;
552 /* Otherwise, someone just queued themselves, but hasn't yet set
553 * themselves as successor. Spin waiting for them to do so. */
554 while (!(next = queue->LockQueue.Next))
555 small_pause();
558 InterlockedExchangePointer( (void **)&next->Lock, (KSPIN_LOCK *)((ULONG_PTR)lock | QUEUED_SPINLOCK_OWNED) );
561 #ifndef __i386__
562 /***********************************************************************
563 * KeReleaseSpinLock (NTOSKRNL.EXE.@)
565 void WINAPI KeReleaseSpinLock( KSPIN_LOCK *lock, KIRQL irql )
567 TRACE("lock %p, irql %u.\n", lock, irql);
568 KeReleaseSpinLockFromDpcLevel( lock );
571 /***********************************************************************
572 * KeAcquireSpinLockRaiseToDpc (NTOSKRNL.EXE.@)
574 KIRQL WINAPI KeAcquireSpinLockRaiseToDpc( KSPIN_LOCK *lock )
576 TRACE("lock %p.\n", lock);
577 KeAcquireSpinLockAtDpcLevel( lock );
578 return 0;
581 /***********************************************************************
582 * KeAcquireInStackQueuedSpinLock (NTOSKRNL.EXE.@)
584 void WINAPI KeAcquireInStackQueuedSpinLock( KSPIN_LOCK *lock, KLOCK_QUEUE_HANDLE *queue )
586 TRACE("lock %p, queue %p.\n", lock, queue);
587 KeAcquireInStackQueuedSpinLockAtDpcLevel( lock, queue );
590 /***********************************************************************
591 * KeReleaseInStackQueuedSpinLock (NTOSKRNL.EXE.@)
593 void WINAPI KeReleaseInStackQueuedSpinLock( KLOCK_QUEUE_HANDLE *queue )
595 TRACE("queue %p.\n", queue);
596 KeReleaseInStackQueuedSpinLockFromDpcLevel( queue );
598 #endif
600 static KSPIN_LOCK cancel_lock;
602 /***********************************************************************
603 * IoAcquireCancelSpinLock (NTOSKRNL.EXE.@)
605 void WINAPI IoAcquireCancelSpinLock( KIRQL *irql )
607 TRACE("irql %p.\n", irql);
608 KeAcquireSpinLock( &cancel_lock, irql );
611 /***********************************************************************
612 * IoReleaseCancelSpinLock (NTOSKRNL.EXE.@)
614 void WINAPI IoReleaseCancelSpinLock( KIRQL irql )
616 TRACE("irql %u.\n", irql);
617 KeReleaseSpinLock( &cancel_lock, irql );
620 /***********************************************************************
621 * ExfInterlockedRemoveHeadList (NTOSKRNL.EXE.@)
623 DEFINE_FASTCALL_WRAPPER( ExfInterlockedRemoveHeadList, 8 )
624 PLIST_ENTRY FASTCALL ExfInterlockedRemoveHeadList( LIST_ENTRY *list, KSPIN_LOCK *lock )
626 return ExInterlockedRemoveHeadList( list, lock );
629 /***********************************************************************
630 * ExInterlockedRemoveHeadList (NTOSKRNL.EXE.@)
632 LIST_ENTRY * WINAPI ExInterlockedRemoveHeadList( LIST_ENTRY *list, KSPIN_LOCK *lock )
634 LIST_ENTRY *ret;
635 KIRQL irql;
637 TRACE("list %p, lock %p.\n", list, lock);
639 KeAcquireSpinLock( lock, &irql );
640 ret = RemoveHeadList( list );
641 KeReleaseSpinLock( lock, irql );
643 return ret;
647 /***********************************************************************
648 * InterlockedPopEntrySList (NTOSKRNL.EXE.@)
650 DEFINE_FASTCALL1_WRAPPER( NTOSKRNL_InterlockedPopEntrySList )
651 PSLIST_ENTRY FASTCALL NTOSKRNL_InterlockedPopEntrySList( PSLIST_HEADER list )
653 return RtlInterlockedPopEntrySList( list );
657 /***********************************************************************
658 * InterlockedPushEntrySList (NTOSKRNL.EXE.@)
660 DEFINE_FASTCALL_WRAPPER( NTOSKRNL_InterlockedPushEntrySList, 8 )
661 PSLIST_ENTRY FASTCALL NTOSKRNL_InterlockedPushEntrySList( PSLIST_HEADER list, PSLIST_ENTRY entry )
663 return RtlInterlockedPushEntrySList( list, entry );
667 /***********************************************************************
668 * ExInterlockedPopEntrySList (NTOSKRNL.EXE.@)
670 DEFINE_FASTCALL_WRAPPER( NTOSKRNL_ExInterlockedPopEntrySList, 8 )
671 PSLIST_ENTRY FASTCALL NTOSKRNL_ExInterlockedPopEntrySList( PSLIST_HEADER list, PKSPIN_LOCK lock )
673 return RtlInterlockedPopEntrySList( list );
677 /***********************************************************************
678 * ExInterlockedPushEntrySList (NTOSKRNL.EXE.@)
680 DEFINE_FASTCALL_WRAPPER( NTOSKRNL_ExInterlockedPushEntrySList, 12 )
681 PSLIST_ENTRY FASTCALL NTOSKRNL_ExInterlockedPushEntrySList( PSLIST_HEADER list, PSLIST_ENTRY entry, PKSPIN_LOCK lock )
683 return RtlInterlockedPushEntrySList( list, entry );
687 /***********************************************************************
688 * ExInterlockedFlushSList (NTOSKRNL.EXE.@)
690 DEFINE_FASTCALL1_WRAPPER( NTOSKRNL_ExInterlockedFlushSList )
691 PSLIST_ENTRY FASTCALL NTOSKRNL_ExInterlockedFlushSList( PSLIST_HEADER list )
693 return RtlInterlockedFlushSList( list );
697 /***********************************************************************
698 * ExAcquireFastMutexUnsafe (NTOSKRNL.EXE.@)
700 DEFINE_FASTCALL1_WRAPPER(ExAcquireFastMutexUnsafe)
701 void FASTCALL ExAcquireFastMutexUnsafe( FAST_MUTEX *mutex )
703 LONG count;
705 TRACE("mutex %p.\n", mutex);
707 count = InterlockedDecrement( &mutex->Count );
708 if (count < 0)
709 KeWaitForSingleObject( &mutex->Event, Executive, KernelMode, FALSE, NULL );
712 /***********************************************************************
713 * ExReleaseFastMutexUnsafe (NTOSKRNL.EXE.@)
715 DEFINE_FASTCALL1_WRAPPER(ExReleaseFastMutexUnsafe)
716 void FASTCALL ExReleaseFastMutexUnsafe( FAST_MUTEX *mutex )
718 LONG count;
720 TRACE("mutex %p.\n", mutex);
722 count = InterlockedIncrement( &mutex->Count );
723 if (count < 1)
724 KeSetEvent( &mutex->Event, IO_NO_INCREMENT, FALSE );
727 #ifndef __i386__
729 /***********************************************************************
730 * ExAcquireFastMutex (NTOSKRNL.@)
732 void WINAPI ExAcquireFastMutex( FAST_MUTEX *mutex )
734 /* FIXME: lower IRQL */
735 ExAcquireFastMutexUnsafe( mutex );
738 /***********************************************************************
739 * ExReleaseFastMutex (NTOSKRNL.@)
741 void WINAPI ExReleaseFastMutex( FAST_MUTEX *mutex )
743 ExReleaseFastMutexUnsafe( mutex );
744 /* FIXME: restore IRQL */
747 #endif /* __i386__ */
749 /* Use of the fields of an ERESOURCE structure seems to vary wildly between
750 * Windows versions. The below implementation uses them as follows:
752 * OwnerTable - contains a list of shared owners, including threads which do
753 * not currently own the resource
754 * OwnerTable[i].OwnerThread - shared owner TID
755 * OwnerTable[i].OwnerCount - recursion count of this shared owner (may be 0)
756 * OwnerEntry.OwnerThread - the owner TID if exclusively owned
757 * OwnerEntry.TableSize - the number of entries in OwnerTable, including threads
758 * which do not currently own the resource
759 * ActiveEntries - total number of acquisitions (incl. recursive ones)
762 /***********************************************************************
763 * ExInitializeResourceLite (NTOSKRNL.EXE.@)
765 NTSTATUS WINAPI ExInitializeResourceLite( ERESOURCE *resource )
767 TRACE("resource %p.\n", resource);
768 memset(resource, 0, sizeof(*resource));
769 return STATUS_SUCCESS;
772 /***********************************************************************
773 * ExDeleteResourceLite (NTOSKRNL.EXE.@)
775 NTSTATUS WINAPI ExDeleteResourceLite( ERESOURCE *resource )
777 TRACE("resource %p.\n", resource);
778 heap_free(resource->OwnerTable);
779 heap_free(resource->ExclusiveWaiters);
780 heap_free(resource->SharedWaiters);
781 return STATUS_SUCCESS;
784 /* Find an existing entry in the shared owner list, or create a new one. */
785 static OWNER_ENTRY *resource_get_shared_entry( ERESOURCE *resource, ERESOURCE_THREAD thread )
787 ULONG i, count;
789 for (i = 0; i < resource->OwnerEntry.TableSize; ++i)
791 if (resource->OwnerTable[i].OwnerThread == thread)
792 return &resource->OwnerTable[i];
795 count = ++resource->OwnerEntry.TableSize;
796 resource->OwnerTable = heap_realloc(resource->OwnerTable, count * sizeof(*resource->OwnerTable));
797 resource->OwnerTable[count - 1].OwnerThread = thread;
798 resource->OwnerTable[count - 1].OwnerCount = 0;
800 return &resource->OwnerTable[count - 1];
803 /***********************************************************************
804 * ExAcquireResourceExclusiveLite (NTOSKRNL.EXE.@)
806 BOOLEAN WINAPI ExAcquireResourceExclusiveLite( ERESOURCE *resource, BOOLEAN wait )
808 KIRQL irql;
810 TRACE("resource %p, wait %u.\n", resource, wait);
812 KeAcquireSpinLock( &resource->SpinLock, &irql );
814 if (resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)KeGetCurrentThread())
816 resource->ActiveEntries++;
817 KeReleaseSpinLock( &resource->SpinLock, irql );
818 return TRUE;
820 /* In order to avoid a race between waiting for the ExclusiveWaiters event
821 * and grabbing the lock, do not grab the resource if it is unclaimed but
822 * has waiters; instead queue ourselves. */
823 else if (!resource->ActiveEntries && !resource->NumberOfExclusiveWaiters && !resource->NumberOfSharedWaiters)
825 resource->Flag |= ResourceOwnedExclusive;
826 resource->OwnerEntry.OwnerThread = (ERESOURCE_THREAD)KeGetCurrentThread();
827 resource->ActiveEntries++;
828 KeReleaseSpinLock( &resource->SpinLock, irql );
829 return TRUE;
831 else if (!wait)
833 KeReleaseSpinLock( &resource->SpinLock, irql );
834 return FALSE;
837 if (!resource->ExclusiveWaiters)
839 resource->ExclusiveWaiters = heap_alloc( sizeof(*resource->ExclusiveWaiters) );
840 KeInitializeEvent( resource->ExclusiveWaiters, SynchronizationEvent, FALSE );
842 resource->NumberOfExclusiveWaiters++;
844 KeReleaseSpinLock( &resource->SpinLock, irql );
846 KeWaitForSingleObject( resource->ExclusiveWaiters, Executive, KernelMode, FALSE, NULL );
848 KeAcquireSpinLock( &resource->SpinLock, &irql );
850 resource->Flag |= ResourceOwnedExclusive;
851 resource->OwnerEntry.OwnerThread = (ERESOURCE_THREAD)KeGetCurrentThread();
852 resource->ActiveEntries++;
853 resource->NumberOfExclusiveWaiters--;
855 KeReleaseSpinLock( &resource->SpinLock, irql );
857 return TRUE;
860 /***********************************************************************
861 * ExAcquireResourceSharedLite (NTOSKRNL.EXE.@)
863 BOOLEAN WINAPI ExAcquireResourceSharedLite( ERESOURCE *resource, BOOLEAN wait )
865 OWNER_ENTRY *entry;
866 KIRQL irql;
868 TRACE("resource %p, wait %u.\n", resource, wait);
870 KeAcquireSpinLock( &resource->SpinLock, &irql );
872 entry = resource_get_shared_entry( resource, (ERESOURCE_THREAD)KeGetCurrentThread() );
874 if (resource->Flag & ResourceOwnedExclusive)
876 if (resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)KeGetCurrentThread())
878 /* We own the resource exclusively, so increase recursion. */
879 resource->ActiveEntries++;
880 KeReleaseSpinLock( &resource->SpinLock, irql );
881 return TRUE;
884 else if (entry->OwnerCount || !resource->NumberOfExclusiveWaiters)
886 /* Either we already own the resource shared, or there are no exclusive
887 * owners or waiters, so we can grab it shared. */
888 entry->OwnerCount++;
889 resource->ActiveEntries++;
890 KeReleaseSpinLock( &resource->SpinLock, irql );
891 return TRUE;
894 if (!wait)
896 KeReleaseSpinLock( &resource->SpinLock, irql );
897 return FALSE;
900 if (!resource->SharedWaiters)
902 resource->SharedWaiters = heap_alloc( sizeof(*resource->SharedWaiters) );
903 KeInitializeSemaphore( resource->SharedWaiters, 0, INT_MAX );
905 resource->NumberOfSharedWaiters++;
907 KeReleaseSpinLock( &resource->SpinLock, irql );
909 KeWaitForSingleObject( resource->SharedWaiters, Executive, KernelMode, FALSE, NULL );
911 KeAcquireSpinLock( &resource->SpinLock, &irql );
913 entry->OwnerCount++;
914 resource->ActiveEntries++;
915 resource->NumberOfSharedWaiters--;
917 KeReleaseSpinLock( &resource->SpinLock, irql );
919 return TRUE;
922 /***********************************************************************
923 * ExAcquireSharedStarveExclusive (NTOSKRNL.EXE.@)
925 BOOLEAN WINAPI ExAcquireSharedStarveExclusive( ERESOURCE *resource, BOOLEAN wait )
927 OWNER_ENTRY *entry;
928 KIRQL irql;
930 TRACE("resource %p, wait %u.\n", resource, wait);
932 KeAcquireSpinLock( &resource->SpinLock, &irql );
934 entry = resource_get_shared_entry( resource, (ERESOURCE_THREAD)KeGetCurrentThread() );
936 if (resource->Flag & ResourceOwnedExclusive)
938 if (resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)KeGetCurrentThread())
940 resource->ActiveEntries++;
941 KeReleaseSpinLock( &resource->SpinLock, irql );
942 return TRUE;
945 /* We are starving exclusive waiters, but we cannot steal the resource out
946 * from under an exclusive waiter who is about to acquire it. (Because of
947 * locking, and because exclusive waiters are always waked first, this is
948 * guaranteed to be the case if the resource is unowned and there are
949 * exclusive waiters.) */
950 else if (!(!resource->ActiveEntries && resource->NumberOfExclusiveWaiters))
952 entry->OwnerCount++;
953 resource->ActiveEntries++;
954 KeReleaseSpinLock( &resource->SpinLock, irql );
955 return TRUE;
958 if (!wait)
960 KeReleaseSpinLock( &resource->SpinLock, irql );
961 return FALSE;
964 if (!resource->SharedWaiters)
966 resource->SharedWaiters = heap_alloc( sizeof(*resource->SharedWaiters) );
967 KeInitializeSemaphore( resource->SharedWaiters, 0, INT_MAX );
969 resource->NumberOfSharedWaiters++;
971 KeReleaseSpinLock( &resource->SpinLock, irql );
973 KeWaitForSingleObject( resource->SharedWaiters, Executive, KernelMode, FALSE, NULL );
975 KeAcquireSpinLock( &resource->SpinLock, &irql );
977 entry->OwnerCount++;
978 resource->ActiveEntries++;
979 resource->NumberOfSharedWaiters--;
981 KeReleaseSpinLock( &resource->SpinLock, irql );
983 return TRUE;
986 /***********************************************************************
987 * ExAcquireSharedWaitForExclusive (NTOSKRNL.EXE.@)
989 BOOLEAN WINAPI ExAcquireSharedWaitForExclusive( ERESOURCE *resource, BOOLEAN wait )
991 OWNER_ENTRY *entry;
992 KIRQL irql;
994 TRACE("resource %p, wait %u.\n", resource, wait);
996 KeAcquireSpinLock( &resource->SpinLock, &irql );
998 entry = resource_get_shared_entry( resource, (ERESOURCE_THREAD)KeGetCurrentThread() );
1000 if (resource->Flag & ResourceOwnedExclusive)
1002 if (resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)KeGetCurrentThread())
1004 /* We own the resource exclusively, so increase recursion. */
1005 resource->ActiveEntries++;
1006 KeReleaseSpinLock( &resource->SpinLock, irql );
1007 return TRUE;
1010 /* We may only grab the resource if there are no exclusive waiters, even if
1011 * we already own it shared. */
1012 else if (!resource->NumberOfExclusiveWaiters)
1014 entry->OwnerCount++;
1015 resource->ActiveEntries++;
1016 KeReleaseSpinLock( &resource->SpinLock, irql );
1017 return TRUE;
1020 if (!wait)
1022 KeReleaseSpinLock( &resource->SpinLock, irql );
1023 return FALSE;
1026 if (!resource->SharedWaiters)
1028 resource->SharedWaiters = heap_alloc( sizeof(*resource->SharedWaiters) );
1029 KeInitializeSemaphore( resource->SharedWaiters, 0, INT_MAX );
1031 resource->NumberOfSharedWaiters++;
1033 KeReleaseSpinLock( &resource->SpinLock, irql );
1035 KeWaitForSingleObject( resource->SharedWaiters, Executive, KernelMode, FALSE, NULL );
1037 KeAcquireSpinLock( &resource->SpinLock, &irql );
1039 entry->OwnerCount++;
1040 resource->ActiveEntries++;
1041 resource->NumberOfSharedWaiters--;
1043 KeReleaseSpinLock( &resource->SpinLock, irql );
1045 return TRUE;
1048 /***********************************************************************
1049 * ExReleaseResourceForThreadLite (NTOSKRNL.EXE.@)
1051 void WINAPI ExReleaseResourceForThreadLite( ERESOURCE *resource, ERESOURCE_THREAD thread )
1053 OWNER_ENTRY *entry;
1054 KIRQL irql;
1056 TRACE("resource %p, thread %#lx.\n", resource, thread);
1058 KeAcquireSpinLock( &resource->SpinLock, &irql );
1060 if (resource->Flag & ResourceOwnedExclusive)
1062 if (resource->OwnerEntry.OwnerThread == thread)
1064 if (!--resource->ActiveEntries)
1066 resource->OwnerEntry.OwnerThread = 0;
1067 resource->Flag &= ~ResourceOwnedExclusive;
1070 else
1072 ERR("Trying to release %p for thread %#lx, but resource is exclusively owned by %#lx.\n",
1073 resource, thread, resource->OwnerEntry.OwnerThread);
1074 return;
1077 else
1079 entry = resource_get_shared_entry( resource, thread );
1080 if (entry->OwnerCount)
1082 entry->OwnerCount--;
1083 resource->ActiveEntries--;
1085 else
1087 ERR("Trying to release %p for thread %#lx, but resource is not owned by that thread.\n", resource, thread);
1088 return;
1092 if (!resource->ActiveEntries)
1094 if (resource->NumberOfExclusiveWaiters)
1096 KeSetEvent( resource->ExclusiveWaiters, IO_NO_INCREMENT, FALSE );
1098 else if (resource->NumberOfSharedWaiters)
1100 KeReleaseSemaphore( resource->SharedWaiters, IO_NO_INCREMENT,
1101 resource->NumberOfSharedWaiters, FALSE );
1105 KeReleaseSpinLock( &resource->SpinLock, irql );
1108 /***********************************************************************
1109 * ExReleaseResourceLite (NTOSKRNL.EXE.@)
1111 DEFINE_FASTCALL1_WRAPPER( ExReleaseResourceLite )
1112 void FASTCALL ExReleaseResourceLite( ERESOURCE *resource )
1114 ExReleaseResourceForThreadLite( resource, (ERESOURCE_THREAD)KeGetCurrentThread() );
1117 /***********************************************************************
1118 * ExGetExclusiveWaiterCount (NTOSKRNL.EXE.@)
1120 ULONG WINAPI ExGetExclusiveWaiterCount( ERESOURCE *resource )
1122 ULONG count;
1123 KIRQL irql;
1125 TRACE("resource %p.\n", resource);
1127 KeAcquireSpinLock( &resource->SpinLock, &irql );
1129 count = resource->NumberOfExclusiveWaiters;
1131 KeReleaseSpinLock( &resource->SpinLock, irql );
1133 return count;
1136 /***********************************************************************
1137 * ExGetSharedWaiterCount (NTOSKRNL.EXE.@)
1139 ULONG WINAPI ExGetSharedWaiterCount( ERESOURCE *resource )
1141 ULONG count;
1142 KIRQL irql;
1144 TRACE("resource %p.\n", resource);
1146 KeAcquireSpinLock( &resource->SpinLock, &irql );
1148 count = resource->NumberOfSharedWaiters;
1150 KeReleaseSpinLock( &resource->SpinLock, irql );
1152 return count;
1155 /***********************************************************************
1156 * ExIsResourceAcquiredExclusiveLite (NTOSKRNL.EXE.@)
1158 BOOLEAN WINAPI ExIsResourceAcquiredExclusiveLite( ERESOURCE *resource )
1160 BOOLEAN ret;
1161 KIRQL irql;
1163 TRACE("resource %p.\n", resource);
1165 KeAcquireSpinLock( &resource->SpinLock, &irql );
1167 ret = (resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)KeGetCurrentThread());
1169 KeReleaseSpinLock( &resource->SpinLock, irql );
1171 return ret;
1174 /***********************************************************************
1175 * ExIsResourceAcquiredSharedLite (NTOSKRNL.EXE.@)
1177 ULONG WINAPI ExIsResourceAcquiredSharedLite( ERESOURCE *resource )
1179 ULONG ret;
1180 KIRQL irql;
1182 TRACE("resource %p.\n", resource);
1184 KeAcquireSpinLock( &resource->SpinLock, &irql );
1186 if (resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)KeGetCurrentThread())
1187 ret = resource->ActiveEntries;
1188 else
1190 OWNER_ENTRY *entry = resource_get_shared_entry( resource, (ERESOURCE_THREAD)KeGetCurrentThread() );
1191 ret = entry->OwnerCount;
1194 KeReleaseSpinLock( &resource->SpinLock, irql );
1196 return ret;
1199 /***********************************************************************
1200 * IoInitializeRemoveLockEx (NTOSKRNL.EXE.@)
1202 void WINAPI IoInitializeRemoveLockEx( IO_REMOVE_LOCK *lock, ULONG tag,
1203 ULONG max_minutes, ULONG max_count, ULONG size )
1205 TRACE("lock %p, tag %#x, max_minutes %u, max_count %u, size %u.\n",
1206 lock, tag, max_minutes, max_count, size);
1208 KeInitializeEvent( &lock->Common.RemoveEvent, NotificationEvent, FALSE );
1209 lock->Common.Removed = FALSE;
1210 lock->Common.IoCount = 0;
1213 /***********************************************************************
1214 * IoAcquireRemoveLockEx (NTOSKRNL.EXE.@)
1216 NTSTATUS WINAPI IoAcquireRemoveLockEx( IO_REMOVE_LOCK *lock, void *tag,
1217 const char *file, ULONG line, ULONG size )
1219 TRACE("lock %p, tag %p, file %s, line %u, size %u.\n", lock, tag, debugstr_a(file), line, size);
1221 if (lock->Common.Removed)
1222 return STATUS_DELETE_PENDING;
1224 InterlockedIncrement( &lock->Common.IoCount );
1225 return STATUS_SUCCESS;
1228 /***********************************************************************
1229 * IoReleaseRemoveLockEx (NTOSKRNL.EXE.@)
1231 void WINAPI IoReleaseRemoveLockEx( IO_REMOVE_LOCK *lock, void *tag, ULONG size )
1233 LONG count;
1235 TRACE("lock %p, tag %p, size %u.\n", lock, tag, size);
1237 if (!(count = InterlockedDecrement( &lock->Common.IoCount )) && lock->Common.Removed)
1238 KeSetEvent( &lock->Common.RemoveEvent, IO_NO_INCREMENT, FALSE );
1239 else if (count < 0)
1240 ERR("Lock %p is not acquired!\n", lock);
1243 /***********************************************************************
1244 * IoReleaseRemoveLockAndWaitEx (NTOSKRNL.EXE.@)
1246 void WINAPI IoReleaseRemoveLockAndWaitEx( IO_REMOVE_LOCK *lock, void *tag, ULONG size )
1248 LONG count;
1250 TRACE("lock %p, tag %p, size %u.\n", lock, tag, size);
1252 lock->Common.Removed = TRUE;
1254 if (!(count = InterlockedDecrement( &lock->Common.IoCount )))
1255 KeSetEvent( &lock->Common.RemoveEvent, IO_NO_INCREMENT, FALSE );
1256 else if (count < 0)
1257 ERR("Lock %p is not acquired!\n", lock);
1258 else if (count > 0)
1259 KeWaitForSingleObject( &lock->Common.RemoveEvent, Executive, KernelMode, FALSE, NULL );