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
25 #define WIN32_NO_STATUS
29 #include "ddk/ntddk.h"
32 #include "wine/debug.h"
34 #include "ntoskrnl_private.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(ntoskrnl
);
40 TYPE_MANUAL_EVENT
= 0,
44 TYPE_MANUAL_TIMER
= 8,
48 static CRITICAL_SECTION sync_cs
;
49 static CRITICAL_SECTION_DEBUG sync_cs_debug
=
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
];
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
);
88 objs
[i
]->WaitListHead
.Blink
= CreateEventW( NULL
, FALSE
, objs
[i
]->SignalState
, NULL
);
91 objs
[i
]->WaitListHead
.Blink
= CreateMutexW( NULL
, FALSE
, NULL
);
95 KSEMAPHORE
*semaphore
= CONTAINING_RECORD(objs
[i
], KSEMAPHORE
, Header
);
96 objs
[i
]->WaitListHead
.Blink
= CreateSemaphoreW( NULL
,
97 semaphore
->Header
.SignalState
, semaphore
->Limit
, NULL
);
100 case TYPE_MANUAL_TIMER
:
101 case TYPE_AUTO_TIMER
:
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
;
125 --objs
[i
]->SignalState
;
130 if (!--*((ULONG_PTR
*)&objs
[i
]->WaitListHead
.Flink
))
132 switch (objs
[i
]->Type
)
134 case TYPE_MANUAL_EVENT
:
135 case TYPE_AUTO_EVENT
:
137 CloseHandle(objs
[i
]->WaitListHead
.Blink
);
138 objs
[i
]->WaitListHead
.Blink
= NULL
;
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
;
152 LeaveCriticalSection( &sync_cs
);
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
= {
195 POBJECT_TYPE ExEventObjectType
= &event_type
;
197 /***********************************************************************
198 * KeSetEvent (NTOSKRNL.EXE.@)
200 LONG WINAPI
KeSetEvent( PRKEVENT event
, KPRIORITY increment
, BOOLEAN wait
)
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
))
211 LeaveCriticalSection( &sync_cs
);
216 /***********************************************************************
217 * KeResetEvent (NTOSKRNL.EXE.@)
219 LONG WINAPI
KeResetEvent( PRKEVENT event
)
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
);
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
)
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
);
278 static const WCHAR semaphore_type_name
[] = {'S','e','m','a','p','h','o','r','e',0};
280 static struct _OBJECT_TYPE semaphore_type
=
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
)
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
);
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
)
351 TRACE("timer %p, duetime %s, period %d, dpc %p.\n",
352 timer
, wine_dbgstr_longlong(duetime
.QuadPart
), period
, dpc
);
356 FIXME("Unhandled DPC %p.\n", dpc
);
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
);
372 BOOLEAN WINAPI
KeCancelTimer( KTIMER
*timer
)
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
);
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
);
406 static inline void small_pause(void)
409 __asm__
__volatile__( "rep;nop" : : : "memory" );
411 __asm__
__volatile__( "" : : : "memory" );
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 ))
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
);
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
))
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
)
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
))
488 InterlockedExchangePointer( (void **)&next
->Lock
, (KSPIN_LOCK
*)((ULONG_PTR
)lock
| QUEUED_SPINLOCK_OWNED
) );
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
);
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
);
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
)
567 TRACE("list %p, lock %p.\n", list
, lock
);
569 KeAcquireSpinLock( lock
, &irql
);
570 ret
= RemoveHeadList( list
);
571 KeReleaseSpinLock( lock
, irql
);
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
)
625 TRACE("mutex %p.\n", mutex
);
627 count
= InterlockedDecrement( &mutex
->Count
);
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
)
640 TRACE("mutex %p.\n", mutex
);
642 count
= InterlockedIncrement( &mutex
->Count
);
644 KeSetEvent( &mutex
->Event
, IO_NO_INCREMENT
, FALSE
);