mfplat: Implement IMFAttributes::GetItemByIndex().
[wine.git] / dlls / ntoskrnl.exe / sync.c
blob926df056b6011f12a08bb6ec90f973c00b8df654
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 "config.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"
32 #include "wine/debug.h"
34 #include "ntoskrnl_private.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(ntoskrnl);
38 enum object_type
40 TYPE_MANUAL_EVENT = 0,
41 TYPE_AUTO_EVENT = 1,
42 TYPE_MUTEX = 2,
43 TYPE_SEMAPHORE = 5,
44 TYPE_MANUAL_TIMER = 8,
45 TYPE_AUTO_TIMER = 9,
48 static CRITICAL_SECTION sync_cs;
49 static CRITICAL_SECTION_DEBUG sync_cs_debug =
51 0, 0, &sync_cs,
52 { &sync_cs_debug.ProcessLocksList, &sync_cs_debug.ProcessLocksList },
53 0, 0, { (DWORD_PTR)(__FILE__ ": sync_cs") }
55 static CRITICAL_SECTION sync_cs = { &sync_cs_debug, -1, 0, 0, 0, 0 };
57 /***********************************************************************
58 * KeWaitForMultipleObjects (NTOSKRNL.EXE.@)
60 NTSTATUS WINAPI KeWaitForMultipleObjects(ULONG count, void *pobjs[],
61 WAIT_TYPE wait_type, KWAIT_REASON reason, KPROCESSOR_MODE mode,
62 BOOLEAN alertable, LARGE_INTEGER *timeout, KWAIT_BLOCK *wait_blocks)
64 DISPATCHER_HEADER **objs = (DISPATCHER_HEADER **)pobjs;
65 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
66 NTSTATUS ret;
67 ULONG i;
69 TRACE("count %u, objs %p, wait_type %u, reason %u, mode %d, alertable %u, timeout %p, wait_blocks %p.\n",
70 count, objs, wait_type, reason, mode, alertable, timeout, wait_blocks);
72 /* We co-opt DISPATCHER_HEADER.WaitListHead:
73 * Blink stores a handle to the synchronization object,
74 * Flink stores the number of threads currently waiting on this object. */
76 EnterCriticalSection( &sync_cs );
77 for (i = 0; i < count; i++)
79 ++*((ULONG_PTR *)&objs[i]->WaitListHead.Flink);
80 if (!objs[i]->WaitListHead.Blink)
82 switch (objs[i]->Type)
84 case TYPE_MANUAL_EVENT:
85 objs[i]->WaitListHead.Blink = CreateEventW( NULL, TRUE, objs[i]->SignalState, NULL );
86 break;
87 case TYPE_AUTO_EVENT:
88 objs[i]->WaitListHead.Blink = CreateEventW( NULL, FALSE, objs[i]->SignalState, NULL );
89 break;
90 case TYPE_MUTEX:
91 objs[i]->WaitListHead.Blink = CreateMutexW( NULL, FALSE, NULL );
92 break;
93 case TYPE_SEMAPHORE:
95 KSEMAPHORE *semaphore = CONTAINING_RECORD(objs[i], KSEMAPHORE, Header);
96 objs[i]->WaitListHead.Blink = CreateSemaphoreW( NULL,
97 semaphore->Header.SignalState, semaphore->Limit, NULL );
98 break;
100 case TYPE_MANUAL_TIMER:
101 case TYPE_AUTO_TIMER:
102 break;
106 handles[i] = objs[i]->WaitListHead.Blink;
108 LeaveCriticalSection( &sync_cs );
110 ret = NtWaitForMultipleObjects( count, handles, (wait_type == WaitAny), alertable, timeout );
112 EnterCriticalSection( &sync_cs );
113 for (i = 0; i < count; i++)
115 if (ret == i || (!ret && wait_type == WaitAll))
117 switch (objs[i]->Type)
119 case TYPE_AUTO_EVENT:
120 case TYPE_AUTO_TIMER:
121 objs[i]->SignalState = FALSE;
122 break;
123 case TYPE_MUTEX:
124 case TYPE_SEMAPHORE:
125 --objs[i]->SignalState;
126 break;
130 if (!--*((ULONG_PTR *)&objs[i]->WaitListHead.Flink))
132 switch (objs[i]->Type)
134 case TYPE_MANUAL_EVENT:
135 case TYPE_AUTO_EVENT:
136 case TYPE_SEMAPHORE:
137 CloseHandle(objs[i]->WaitListHead.Blink);
138 objs[i]->WaitListHead.Blink = NULL;
139 break;
140 case TYPE_MUTEX:
141 /* Native will panic if a mutex is destroyed while held, so we
142 * don't have to worry about leaking the handle here. */
143 if (objs[i]->SignalState == 1)
145 CloseHandle(objs[i]->WaitListHead.Blink);
146 objs[i]->WaitListHead.Blink = NULL;
148 break;
152 LeaveCriticalSection( &sync_cs );
154 return ret;
157 /***********************************************************************
158 * KeWaitForSingleObject (NTOSKRNL.EXE.@)
160 NTSTATUS WINAPI KeWaitForSingleObject( void *obj, KWAIT_REASON reason,
161 KPROCESSOR_MODE mode, BOOLEAN alertable, LARGE_INTEGER *timeout )
163 return KeWaitForMultipleObjects( 1, &obj, WaitAny, reason, mode, alertable, timeout, NULL );
166 /***********************************************************************
167 * KeWaitForMutexObject (NTOSKRNL.EXE.@)
169 NTSTATUS WINAPI KeWaitForMutexObject( PRKMUTEX mutex, KWAIT_REASON reason,
170 KPROCESSOR_MODE mode, BOOLEAN alertable, LARGE_INTEGER *timeout)
172 return KeWaitForSingleObject( mutex, reason, mode, alertable, timeout );
176 /***********************************************************************
177 * KeInitializeEvent (NTOSKRNL.EXE.@)
179 void WINAPI KeInitializeEvent( PRKEVENT event, EVENT_TYPE type, BOOLEAN state )
181 TRACE("event %p, type %u, state %u.\n", event, type, state);
183 event->Header.Type = type;
184 event->Header.SignalState = state;
185 event->Header.WaitListHead.Blink = NULL;
186 event->Header.WaitListHead.Flink = NULL;
189 static const WCHAR event_type_name[] = {'E','v','e','n','t',0};
191 static struct _OBJECT_TYPE event_type = {
192 event_type_name,
195 POBJECT_TYPE ExEventObjectType = &event_type;
197 /***********************************************************************
198 * KeSetEvent (NTOSKRNL.EXE.@)
200 LONG WINAPI KeSetEvent( PRKEVENT event, KPRIORITY increment, BOOLEAN wait )
202 HANDLE handle;
203 LONG ret;
205 TRACE("event %p, increment %d, wait %u.\n", event, increment, wait);
207 EnterCriticalSection( &sync_cs );
208 ret = InterlockedExchange( &event->Header.SignalState, TRUE );
209 if ((handle = event->Header.WaitListHead.Blink))
210 SetEvent( handle );
211 LeaveCriticalSection( &sync_cs );
213 return ret;
216 /***********************************************************************
217 * KeResetEvent (NTOSKRNL.EXE.@)
219 LONG WINAPI KeResetEvent( PRKEVENT event )
221 HANDLE handle;
222 LONG ret;
224 TRACE("event %p.\n", event);
226 EnterCriticalSection( &sync_cs );
227 ret = InterlockedExchange( &event->Header.SignalState, FALSE );
228 if ((handle = event->Header.WaitListHead.Blink))
229 ResetEvent( handle );
230 LeaveCriticalSection( &sync_cs );
232 return ret;
235 /***********************************************************************
236 * KeClearEvent (NTOSKRNL.EXE.@)
238 void WINAPI KeClearEvent( PRKEVENT event )
240 KeResetEvent( event );
243 /***********************************************************************
244 * KeInitializeSemaphore (NTOSKRNL.EXE.@)
246 void WINAPI KeInitializeSemaphore( PRKSEMAPHORE semaphore, LONG count, LONG limit )
248 TRACE("semaphore %p, count %d, limit %d.\n", semaphore, count, limit);
250 semaphore->Header.Type = TYPE_SEMAPHORE;
251 semaphore->Header.SignalState = count;
252 semaphore->Header.WaitListHead.Blink = NULL;
253 semaphore->Header.WaitListHead.Flink = NULL;
254 semaphore->Limit = limit;
257 /***********************************************************************
258 * KeReleaseSemaphore (NTOSKRNL.EXE.@)
260 LONG WINAPI KeReleaseSemaphore( PRKSEMAPHORE semaphore, KPRIORITY increment,
261 LONG count, BOOLEAN wait )
263 HANDLE handle;
264 LONG ret;
266 TRACE("semaphore %p, increment %d, count %d, wait %u.\n",
267 semaphore, increment, count, wait);
269 EnterCriticalSection( &sync_cs );
270 ret = InterlockedExchangeAdd( &semaphore->Header.SignalState, count );
271 if ((handle = semaphore->Header.WaitListHead.Blink))
272 ReleaseSemaphore( handle, count, NULL );
273 LeaveCriticalSection( &sync_cs );
275 return ret;
278 static const WCHAR semaphore_type_name[] = {'S','e','m','a','p','h','o','r','e',0};
280 static struct _OBJECT_TYPE semaphore_type =
282 semaphore_type_name
285 POBJECT_TYPE ExSemaphoreObjectType = &semaphore_type;
287 /***********************************************************************
288 * KeInitializeMutex (NTOSKRNL.EXE.@)
290 void WINAPI KeInitializeMutex( PRKMUTEX mutex, ULONG level )
292 TRACE("mutex %p, level %u.\n", mutex, level);
294 mutex->Header.Type = TYPE_MUTEX;
295 mutex->Header.SignalState = 1;
296 mutex->Header.WaitListHead.Blink = NULL;
297 mutex->Header.WaitListHead.Flink = NULL;
300 /***********************************************************************
301 * KeReleaseMutex (NTOSKRNL.EXE.@)
303 LONG WINAPI KeReleaseMutex( PRKMUTEX mutex, BOOLEAN wait )
305 LONG ret;
307 TRACE("mutex %p, wait %u.\n", mutex, wait);
309 EnterCriticalSection( &sync_cs );
310 ret = mutex->Header.SignalState++;
311 if (!ret && !mutex->Header.WaitListHead.Flink)
313 CloseHandle( mutex->Header.WaitListHead.Blink );
314 mutex->Header.WaitListHead.Blink = NULL;
316 LeaveCriticalSection( &sync_cs );
318 return ret;
321 /***********************************************************************
322 * KeInitializeTimerEx (NTOSKRNL.EXE.@)
324 void WINAPI KeInitializeTimerEx( KTIMER *timer, TIMER_TYPE type )
326 TRACE("timer %p, type %u.\n", timer, type);
328 RtlZeroMemory(timer, sizeof(KTIMER));
329 timer->Header.Type = (type == NotificationTimer) ? TYPE_MANUAL_TIMER : TYPE_AUTO_TIMER;
330 timer->Header.SignalState = FALSE;
331 timer->Header.Inserted = FALSE;
332 timer->Header.WaitListHead.Blink = NULL;
333 timer->Header.WaitListHead.Flink = NULL;
336 /***********************************************************************
337 * KeInitializeTimer (NTOSKRNL.EXE.@)
339 void WINAPI KeInitializeTimer( KTIMER *timer )
341 KeInitializeTimerEx(timer, NotificationTimer);
344 /***********************************************************************
345 * KeSetTimerEx (NTOSKRNL.EXE.@)
347 BOOLEAN WINAPI KeSetTimerEx( KTIMER *timer, LARGE_INTEGER duetime, LONG period, KDPC *dpc )
349 BOOL ret;
351 TRACE("timer %p, duetime %s, period %d, dpc %p.\n",
352 timer, wine_dbgstr_longlong(duetime.QuadPart), period, dpc);
354 if (dpc)
356 FIXME("Unhandled DPC %p.\n", dpc);
357 return FALSE;
360 EnterCriticalSection( &sync_cs );
362 ret = timer->Header.Inserted;
363 timer->Header.Inserted = TRUE;
364 timer->Header.WaitListHead.Blink = CreateWaitableTimerW( NULL, timer->Header.Type == TYPE_MANUAL_TIMER, NULL );
365 SetWaitableTimer( timer->Header.WaitListHead.Blink, &duetime, period, NULL, NULL, FALSE );
367 LeaveCriticalSection( &sync_cs );
369 return ret;
372 BOOLEAN WINAPI KeCancelTimer( KTIMER *timer )
374 BOOL ret;
376 TRACE("timer %p.\n", timer);
378 EnterCriticalSection( &sync_cs );
379 ret = timer->Header.Inserted;
380 timer->Header.Inserted = FALSE;
381 CloseHandle(timer->Header.WaitListHead.Blink);
382 timer->Header.WaitListHead.Blink = NULL;
383 LeaveCriticalSection( &sync_cs );
385 return ret;
388 /***********************************************************************
389 * KeDelayExecutionThread (NTOSKRNL.EXE.@)
391 NTSTATUS WINAPI KeDelayExecutionThread( KPROCESSOR_MODE mode, BOOLEAN alertable, LARGE_INTEGER *timeout )
393 TRACE("mode %d, alertable %u, timeout %p.\n", mode, alertable, timeout);
394 return NtDelayExecution( alertable, timeout );
397 /***********************************************************************
398 * KeInitializeSpinLock (NTOSKRNL.EXE.@)
400 void WINAPI KeInitializeSpinLock( KSPIN_LOCK *lock )
402 TRACE("lock %p.\n", lock);
403 *lock = 0;
406 static inline void small_pause(void)
408 #ifdef __x86_64__
409 __asm__ __volatile__( "rep;nop" : : : "memory" );
410 #else
411 __asm__ __volatile__( "" : : : "memory" );
412 #endif
415 /***********************************************************************
416 * KeAcquireSpinLockAtDpcLevel (NTOSKRNL.EXE.@)
418 void WINAPI KeAcquireSpinLockAtDpcLevel( KSPIN_LOCK *lock )
420 TRACE("lock %p.\n", lock);
421 while (!InterlockedCompareExchangePointer( (void **)lock, (void *)1, (void *)0 ))
422 small_pause();
425 /***********************************************************************
426 * KeReleaseSpinLockFromDpcLevel (NTOSKRNL.EXE.@)
428 void WINAPI KeReleaseSpinLockFromDpcLevel( KSPIN_LOCK *lock )
430 TRACE("lock %p.\n", lock);
431 InterlockedExchangePointer( (void **)lock, 0 );
434 #define QUEUED_SPINLOCK_OWNED 0x2
436 /***********************************************************************
437 * KeAcquireInStackQueuedSpinLockAtDpcLevel (NTOSKRNL.EXE.@)
439 DEFINE_FASTCALL_WRAPPER( KeAcquireInStackQueuedSpinLockAtDpcLevel, 8 )
440 void WINAPI KeAcquireInStackQueuedSpinLockAtDpcLevel( KSPIN_LOCK *lock, KLOCK_QUEUE_HANDLE *queue )
442 KSPIN_LOCK_QUEUE *tail;
444 TRACE("lock %p, queue %p.\n", lock, queue);
446 queue->LockQueue.Next = NULL;
448 if (!(tail = InterlockedExchangePointer( (void **)lock, &queue->LockQueue )))
449 queue->LockQueue.Lock = (KSPIN_LOCK *)((ULONG_PTR)lock | QUEUED_SPINLOCK_OWNED);
450 else
452 queue->LockQueue.Lock = lock;
453 InterlockedExchangePointer( (void **)&tail->Next, &queue->LockQueue );
455 while (!((ULONG_PTR)InterlockedCompareExchangePointer( (void **)&queue->LockQueue.Lock, 0, 0 )
456 & QUEUED_SPINLOCK_OWNED))
458 small_pause();
463 /***********************************************************************
464 * KeReleaseInStackQueuedSpinLockFromDpcLevel (NTOSKRNL.EXE.@)
466 DEFINE_FASTCALL1_WRAPPER( KeReleaseInStackQueuedSpinLockFromDpcLevel )
467 void WINAPI KeReleaseInStackQueuedSpinLockFromDpcLevel( KLOCK_QUEUE_HANDLE *queue )
469 KSPIN_LOCK *lock = (KSPIN_LOCK *)((ULONG_PTR)queue->LockQueue.Lock & ~QUEUED_SPINLOCK_OWNED);
470 KSPIN_LOCK_QUEUE *next;
472 TRACE("lock %p, queue %p.\n", lock, queue);
474 queue->LockQueue.Lock = NULL;
476 if (!(next = queue->LockQueue.Next))
478 /* If we are truly the last in the queue, the lock will point to us. */
479 if (InterlockedCompareExchangePointer( (void **)lock, NULL, &queue->LockQueue ) == queue)
480 return;
482 /* Otherwise, someone just queued themselves, but hasn't yet set
483 * themselves as successor. Spin waiting for them to do so. */
484 while (!(next = queue->LockQueue.Next))
485 small_pause();
488 InterlockedExchangePointer( (void **)&next->Lock, (KSPIN_LOCK *)((ULONG_PTR)lock | QUEUED_SPINLOCK_OWNED) );
491 #ifndef __i386__
492 /***********************************************************************
493 * KeReleaseSpinLock (NTOSKRNL.EXE.@)
495 void WINAPI KeReleaseSpinLock( KSPIN_LOCK *lock, KIRQL irql )
497 TRACE("lock %p, irql %u.\n", lock, irql);
498 KeReleaseSpinLockFromDpcLevel( lock );
501 /***********************************************************************
502 * KeAcquireSpinLockRaiseToDpc (NTOSKRNL.EXE.@)
504 KIRQL WINAPI KeAcquireSpinLockRaiseToDpc( KSPIN_LOCK *lock )
506 TRACE("lock %p.\n", lock);
507 KeAcquireSpinLockAtDpcLevel( lock );
508 return 0;
511 /***********************************************************************
512 * KeAcquireInStackQueuedSpinLock (NTOSKRNL.EXE.@)
514 void WINAPI KeAcquireInStackQueuedSpinLock( KSPIN_LOCK *lock, KLOCK_QUEUE_HANDLE *queue )
516 TRACE("lock %p, queue %p.\n", lock, queue);
517 KeAcquireInStackQueuedSpinLockAtDpcLevel( lock, queue );
520 /***********************************************************************
521 * KeReleaseInStackQueuedSpinLock (NTOSKRNL.EXE.@)
523 void WINAPI KeReleaseInStackQueuedSpinLock( KLOCK_QUEUE_HANDLE *queue )
525 TRACE("queue %p.\n", queue);
526 KeReleaseInStackQueuedSpinLockFromDpcLevel( queue );
528 #endif
530 static KSPIN_LOCK cancel_lock;
532 /***********************************************************************
533 * IoAcquireCancelSpinLock (NTOSKRNL.EXE.@)
535 void WINAPI IoAcquireCancelSpinLock( KIRQL *irql )
537 TRACE("irql %p.\n", irql);
538 KeAcquireSpinLock( &cancel_lock, irql );
541 /***********************************************************************
542 * IoReleaseCancelSpinLock (NTOSKRNL.EXE.@)
544 void WINAPI IoReleaseCancelSpinLock( KIRQL irql )
546 TRACE("irql %u.\n", irql);
547 KeReleaseSpinLock( &cancel_lock, irql );
550 /***********************************************************************
551 * ExfInterlockedRemoveHeadList (NTOSKRNL.EXE.@)
553 DEFINE_FASTCALL_WRAPPER( ExfInterlockedRemoveHeadList, 8 )
554 PLIST_ENTRY WINAPI ExfInterlockedRemoveHeadList( LIST_ENTRY *list, KSPIN_LOCK *lock )
556 return ExInterlockedRemoveHeadList( list, lock );
559 /***********************************************************************
560 * ExInterlockedRemoveHeadList (NTOSKRNL.EXE.@)
562 LIST_ENTRY * WINAPI ExInterlockedRemoveHeadList( LIST_ENTRY *list, KSPIN_LOCK *lock )
564 LIST_ENTRY *ret;
565 KIRQL irql;
567 TRACE("list %p, lock %p.\n", list, lock);
569 KeAcquireSpinLock( lock, &irql );
570 ret = RemoveHeadList( list );
571 KeReleaseSpinLock( lock, irql );
573 return ret;
577 /***********************************************************************
578 * InterlockedPopEntrySList (NTOSKRNL.EXE.@)
580 DEFINE_FASTCALL1_WRAPPER( NTOSKRNL_InterlockedPopEntrySList )
581 PSLIST_ENTRY WINAPI NTOSKRNL_InterlockedPopEntrySList( PSLIST_HEADER list )
583 return RtlInterlockedPopEntrySList( list );
587 /***********************************************************************
588 * InterlockedPushEntrySList (NTOSKRNL.EXE.@)
590 DEFINE_FASTCALL_WRAPPER( NTOSKRNL_InterlockedPushEntrySList, 8 )
591 PSLIST_ENTRY WINAPI NTOSKRNL_InterlockedPushEntrySList( PSLIST_HEADER list, PSLIST_ENTRY entry )
593 return RtlInterlockedPushEntrySList( list, entry );
597 /***********************************************************************
598 * ExInterlockedPopEntrySList (NTOSKRNL.EXE.@)
600 DEFINE_FASTCALL_WRAPPER( NTOSKRNL_ExInterlockedPopEntrySList, 8 )
601 PSLIST_ENTRY WINAPI NTOSKRNL_ExInterlockedPopEntrySList( PSLIST_HEADER list, PKSPIN_LOCK lock )
603 return RtlInterlockedPopEntrySList( list );
607 /***********************************************************************
608 * ExInterlockedPushEntrySList (NTOSKRNL.EXE.@)
610 DEFINE_FASTCALL_WRAPPER( NTOSKRNL_ExInterlockedPushEntrySList, 12 )
611 PSLIST_ENTRY WINAPI NTOSKRNL_ExInterlockedPushEntrySList( PSLIST_HEADER list, PSLIST_ENTRY entry, PKSPIN_LOCK lock )
613 return RtlInterlockedPushEntrySList( list, entry );
617 /***********************************************************************
618 * ExAcquireFastMutexUnsafe (NTOSKRNL.EXE.@)
620 DEFINE_FASTCALL1_WRAPPER(ExAcquireFastMutexUnsafe)
621 void WINAPI ExAcquireFastMutexUnsafe( FAST_MUTEX *mutex )
623 LONG count;
625 TRACE("mutex %p.\n", mutex);
627 count = InterlockedDecrement( &mutex->Count );
628 if (count < 0)
629 KeWaitForSingleObject( &mutex->Event, Executive, KernelMode, FALSE, NULL );
632 /***********************************************************************
633 * ExReleaseFastMutexUnsafe (NTOSKRNL.EXE.@)
635 DEFINE_FASTCALL1_WRAPPER(ExReleaseFastMutexUnsafe)
636 void WINAPI ExReleaseFastMutexUnsafe( FAST_MUTEX *mutex )
638 LONG count;
640 TRACE("mutex %p.\n", mutex);
642 count = InterlockedIncrement( &mutex->Count );
643 if (count < 1)
644 KeSetEvent( &mutex->Event, IO_NO_INCREMENT, FALSE );