add page cache with read-ahead and dirty writeback, file-backed and anonymous mmap with CoW, VAD-based virtual memory replacing the old user region model, PFN database with eviction and reclaim, file identity for cross-open section sharing, FAT32 path cache and per-file cluster chain cache, serial.sys for COM1 keyboard input, xHCI MSI with INTx and polled fallback, PciEnableMsiAuto, waitable input queue, KeWaitForAnyEvent, per-process thread info syscall, expanded kernel stats (PFN/PTE/VAD/file-cache/disk), pool allocation tags, async I/O user buffer locking, file flush on close, add docs.md.
This commit is contained in:
1 parent
61ec2442eb
commit
2c4a68a75f
114 files changed
+18069
-2620
No files matched your search
@@ -72,12 +72,27 @@ extern int errno; // defined in posix_fd.c; single global in v1
|
||||
#define SEEK_CUR 1
|
||||
#define SEEK_END 2
|
||||
|
||||
// mmap() protection and mapping flags.
|
||||
#define PROT_NONE 0x0
|
||||
#define PROT_READ 0x1
|
||||
#define PROT_WRITE 0x2
|
||||
#define PROT_EXEC 0x4
|
||||
#define MAP_SHARED 0x01
|
||||
#define MAP_PRIVATE 0x02
|
||||
#define MAP_FAILED ((void*)(intptr_t)-1)
|
||||
#define MS_SYNC 0x04
|
||||
#define MS_ASYNC 0x01
|
||||
#define MS_INVALIDATE 0x02
|
||||
|
||||
// Scalar types.
|
||||
typedef int32_t pid_t;
|
||||
typedef int32_t off_t;
|
||||
typedef uint32_t mode_t;
|
||||
typedef int64_t ssize_t;
|
||||
|
||||
__declspec(dllexport) void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
|
||||
__declspec(dllexport) int munmap(void* addr, size_t length);
|
||||
__declspec(dllexport) int msync(void* addr, size_t length, int flags);
|
||||
|
||||
// Sockets.
|
||||
typedef uint16_t sa_family_t;
|
||||
@@ -188,7 +203,7 @@ __declspec(dllexport) int getaddrinfo(const char* node, const char* service, con
|
||||
__declspec(dllexport) void freeaddrinfo(struct addrinfo* res);
|
||||
|
||||
// pthread types.
|
||||
typedef struct { uint32_t Tid; } pthread_t;
|
||||
typedef struct { uint32_t Handle; uint32_t Tid; } pthread_t;
|
||||
typedef struct { uint32_t Handle; } pthread_mutex_t;
|
||||
typedef void* pthread_attr_t; // unused in v1
|
||||
typedef void* pthread_mutexattr_t;
|
||||
|
||||
@@ -81,7 +81,8 @@ __declspec(dllexport) ssize_t read(int fd, void* buf, size_t count) {
|
||||
return (ssize_t)count;
|
||||
case FD_FILE: {
|
||||
size_t got = 0;
|
||||
uint32_t s = SysReadFile(e->KHandle, buf, count, &got);
|
||||
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(e->KHandle, buf, count, &got);
|
||||
uint32_t s = SysReadFile(&req);
|
||||
if (s != 0 && got == 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)got;
|
||||
}
|
||||
@@ -115,7 +116,8 @@ __declspec(dllexport) ssize_t write(int fd, const void* buf, size_t count) {
|
||||
return (ssize_t)count;
|
||||
case FD_FILE: {
|
||||
size_t written = 0;
|
||||
uint32_t s = SysWriteFile(e->KHandle, buf, count, &written);
|
||||
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(e->KHandle, buf, count, &written);
|
||||
uint32_t s = SysWriteFile(&req);
|
||||
if (s != 0 && written == 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)written;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// POSIX compatibility support for mmap-style file mappings.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
#include "posix_fd.h"
|
||||
|
||||
static uint32_t PosixProtToSysMapAccess(int prot, int flags) {
|
||||
uint32_t access = 0;
|
||||
if (prot & PROT_EXEC) access |= SYS_MAP_ACCESS_EXECUTE;
|
||||
if (prot & PROT_WRITE) {
|
||||
access |= (flags & MAP_SHARED) ? SYS_MAP_ACCESS_WRITE : SYS_MAP_ACCESS_COPY;
|
||||
} else if (flags & MAP_PRIVATE) {
|
||||
access |= SYS_MAP_ACCESS_COPY;
|
||||
}
|
||||
if (prot & PROT_READ) access |= SYS_MAP_ACCESS_READ;
|
||||
if (!access) access = SYS_MAP_ACCESS_READ;
|
||||
return access;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void* mmap(void* addr, size_t length, int prot,
|
||||
int flags, int fd, off_t offset) {
|
||||
if (addr || length == 0 || offset < 0 || ((uint64_t)offset & 0xFFFu)) {
|
||||
errno = EINVAL;
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
if (!(flags & MAP_PRIVATE) && !(flags & MAP_SHARED)) {
|
||||
errno = EINVAL;
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
FD_ENTRY* e = FdGet(pid, fd);
|
||||
if (!e) return MAP_FAILED;
|
||||
if (e->Type != FD_FILE) {
|
||||
errno = EBADF;
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
uint64_t maxSize = (uint64_t)offset + (uint64_t)length;
|
||||
if (maxSize < (uint64_t)offset) {
|
||||
errno = EINVAL;
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
uint32_t protect = (prot & PROT_WRITE) ? SYS_VM_READWRITE : SYS_VM_READ;
|
||||
if (prot & PROT_EXEC)
|
||||
protect = (prot & PROT_WRITE) ? SYS_VM_EXECUTE_READWRITE : SYS_VM_EXECUTE_READ;
|
||||
|
||||
uint32_t mapping = 0;
|
||||
uint32_t s = SysCreateFileMapping(e->KHandle, protect, SYS_MAP_FLAG_NONE,
|
||||
maxSize, &mapping);
|
||||
if (s != SYS_STATUS_SUCCESS) {
|
||||
errno = PosixStatusToErrno(s);
|
||||
return MAP_FAILED;
|
||||
}
|
||||
|
||||
void* base = (void*)0;
|
||||
s = SysMapFile(mapping, (uint64_t)offset, length,
|
||||
PosixProtToSysMapAccess(prot, flags),
|
||||
SYS_MAP_FLAG_NONE, &base);
|
||||
SysCloseHandle(mapping);
|
||||
if (s != SYS_STATUS_SUCCESS) {
|
||||
errno = PosixStatusToErrno(s);
|
||||
return MAP_FAILED;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int munmap(void* addr, size_t length) {
|
||||
(void)length;
|
||||
if (!addr) { errno = EINVAL; return -1; }
|
||||
uint32_t s = SysFreeVm(addr);
|
||||
if (s != SYS_STATUS_SUCCESS) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int msync(void* addr, size_t length, int flags) {
|
||||
(void)addr;
|
||||
(void)length;
|
||||
(void)flags;
|
||||
return 0;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ __declspec(dllexport) pid_t waitpid(pid_t pid, int* status, int options) {
|
||||
uint32_t handle = ProcTableFind(pid);
|
||||
if (!handle) { errno = ESRCH; return -1; }
|
||||
|
||||
SysWaitForProcess(handle);
|
||||
SysWait(handle, SYS_WAIT_FLAG_NONE);
|
||||
|
||||
int exitCode = 0;
|
||||
SysGetProcessExitCode(handle, &exitCode);
|
||||
|
||||
@@ -84,28 +84,41 @@ __declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* att
|
||||
tr->Pid = pid;
|
||||
|
||||
uint32_t tid = 0;
|
||||
uint32_t s = SysCreateThread((uint64_t)PthreadTrampoline,
|
||||
(uint64_t)tr, 1, &tid);
|
||||
uint32_t handle = 0;
|
||||
SYS_CREATE_THREAD_REQUEST req = {
|
||||
(uintptr_t)PthreadTrampoline,
|
||||
(uintptr_t)tr,
|
||||
1,
|
||||
0,
|
||||
&handle,
|
||||
&tid
|
||||
};
|
||||
uint32_t s = SysCreateThread(&req);
|
||||
if (s != 0) {
|
||||
free(tr);
|
||||
return EAGAIN;
|
||||
}
|
||||
// The trampoline allocates the retval slot itself after it knows its real
|
||||
// TID, avoiding the creator/new-thread race.
|
||||
if (t) t->Tid = tid;
|
||||
if (t) {
|
||||
t->Handle = handle;
|
||||
t->Tid = tid;
|
||||
} else {
|
||||
SysCloseHandle(handle);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implements the pthread join operation.
|
||||
__declspec(dllexport) int pthread_join(pthread_t t, void** retval) {
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
uint32_t s = SysWaitThread(t.Tid);
|
||||
// STATUS_NOT_FOUND (0x22) means already dead - treat as success.
|
||||
if (s != 0 && s != 0x0022) return ESRCH;
|
||||
uint32_t s = SysWait(t.Handle, SYS_WAIT_FLAG_NONE);
|
||||
if (s != 0) return ESRCH;
|
||||
|
||||
PTHREAD_SLOT* slot = PthreadSlotFind(pid, t.Tid);
|
||||
if (retval) *retval = slot ? slot->RetVal : (void*)0;
|
||||
if (slot) slot->Used = false;
|
||||
SysCloseHandle(t.Handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -138,7 +151,7 @@ __declspec(dllexport) int pthread_mutex_destroy(pthread_mutex_t* m) {
|
||||
|
||||
// Implements the pthread mutex lock operation.
|
||||
__declspec(dllexport) int pthread_mutex_lock(pthread_mutex_t* m) {
|
||||
uint32_t s = SysAcquireMutex(m->Handle);
|
||||
uint32_t s = SysWait(m->Handle, SYS_WAIT_FLAG_NONE);
|
||||
return s == 0 ? 0 : EINVAL;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,101 +63,16 @@ static int* srcX_map = NULL;
|
||||
static int currentDestCapacity = 0;
|
||||
static int currentXMapCapacity = 0;
|
||||
|
||||
__declspec(dllexport) int StretchDIBits(
|
||||
HDC hdc,
|
||||
int xDest, int yDest, int DestWidth, int DestHeight,
|
||||
__declspec(dllexport) int StretchDIBits(HDC hdc, int xDest, int yDest, int DestWidth, int DestHeight,
|
||||
int xSrc, int ySrc, int SrcWidth, int SrcHeight,
|
||||
const void *lpBits, const BITMAPINFO *lpbmi,
|
||||
UINT iUsage, DWORD rop)
|
||||
{
|
||||
(void)hdc; (void)lpbmi; (void)iUsage; (void)rop;
|
||||
(void)hdc; (void)xDest; (void)yDest; (void)DestWidth; (void)DestHeight;
|
||||
(void)xSrc; (void)ySrc; (void)SrcWidth; (void)SrcHeight;
|
||||
(void)lpBits; (void)lpbmi; (void)iUsage; (void)rop;
|
||||
|
||||
// Safety check to prevent division by zero.
|
||||
if (DestWidth <= 0 || DestHeight <= 0 || SrcWidth <= 0 || SrcHeight <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle trivial case (1:1 copy) to bypass allocation and scaling overhead.
|
||||
if (DestWidth == SrcWidth && DestHeight == SrcHeight && xSrc == 0 && ySrc == 0) {
|
||||
SysBlitScreen(xDest, yDest, DestWidth, DestHeight, (const uint32_t*)lpBits);
|
||||
return DestHeight;
|
||||
}
|
||||
|
||||
const uint32_t* srcPixels = (const uint32_t*)lpBits;
|
||||
|
||||
// Only reallocate if the new size is LARGER than what we already have.
|
||||
int requiredPixels = DestWidth * DestHeight;
|
||||
if (requiredPixels > currentDestCapacity) {
|
||||
free(destBuffer);
|
||||
destBuffer = (uint32_t*)malloc(requiredPixels * sizeof(uint32_t));
|
||||
currentDestCapacity = requiredPixels;
|
||||
}
|
||||
|
||||
if (DestWidth > currentXMapCapacity) {
|
||||
free(srcX_map);
|
||||
srcX_map = (int*)malloc(DestWidth * sizeof(int));
|
||||
currentXMapCapacity = DestWidth;
|
||||
}
|
||||
|
||||
for (int dx = 0; dx < DestWidth; ++dx) {
|
||||
// Nearest neighbor coordinate mapping.
|
||||
srcX_map[dx] = xSrc + (dx * SrcWidth) / DestWidth;
|
||||
|
||||
// Clamp to safe bounds.
|
||||
if (srcX_map[dx] < 0) srcX_map[dx] = 0;
|
||||
if (srcX_map[dx] >= SrcWidth) srcX_map[dx] = SrcWidth - 1;
|
||||
}
|
||||
|
||||
// Process the image row by row.
|
||||
for (int dy = 0; dy < DestHeight; ++dy) {
|
||||
// Map destination Y to source Y.
|
||||
int sy = ySrc + (dy * SrcHeight) / DestHeight;
|
||||
if (sy < 0) sy = 0;
|
||||
if (sy >= SrcHeight) sy = SrcHeight - 1;
|
||||
|
||||
// Assumes stride == SrcWidth (tightly packed 32bpp).
|
||||
const uint32_t* srcRow = srcPixels + (sy * SrcWidth);
|
||||
uint32_t* dstRow = destBuffer + (dy * DestWidth);
|
||||
|
||||
int dx = 0;
|
||||
|
||||
#if defined(__SSE2__) || defined(_M_X64)
|
||||
// Process 4 pixels (16 bytes) per loop iteration using SSE2.
|
||||
for (; dx <= DestWidth - 4; dx += 4) {
|
||||
// Gather 4 source pixels using our precomputed X map.
|
||||
// Note: _mm_set_epi32 takes arguments in reverse order (3, 2, 1, 0).
|
||||
__m128i pixels = _mm_set_epi32(
|
||||
srcRow[srcX_map[dx + 3]],
|
||||
srcRow[srcX_map[dx + 2]],
|
||||
srcRow[srcX_map[dx + 1]],
|
||||
srcRow[srcX_map[dx + 0]]
|
||||
);
|
||||
// Issue a single 128-bit unaligned store.
|
||||
_mm_storeu_si128((__m128i*)&dstRow[dx], pixels);
|
||||
}
|
||||
#elif defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(_M_ARM64)
|
||||
// Process 4 pixels per loop iteration using ARM NEON.
|
||||
for (; dx <= DestWidth - 4; dx += 4) {
|
||||
// Initialize a vector and populate lanes (avoids stack-memory hop).
|
||||
uint32x4_t pixels = vdupq_n_u32(0);
|
||||
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 0]], pixels, 0);
|
||||
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 1]], pixels, 1);
|
||||
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 2]], pixels, 2);
|
||||
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 3]], pixels, 3);
|
||||
|
||||
// Issue a single 128-bit store.
|
||||
vst1q_u32(&dstRow[dx], pixels);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Epilogue: Handle remaining pixels if DestWidth isn't a multiple of 4.
|
||||
for (; dx < DestWidth; ++dx) {
|
||||
dstRow[dx] = srcRow[srcX_map[dx]];
|
||||
}
|
||||
}
|
||||
|
||||
// Direct mapping to kernel screen blit using the stretched buffer.
|
||||
SysBlitScreen(xDest, yDest, DestWidth, DestHeight, destBuffer);
|
||||
|
||||
return DestHeight;
|
||||
// There is no implicit display blit ABI. Native graphics clients own a
|
||||
// surface and present it explicitly.
|
||||
return 0;
|
||||
}
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <intrin.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
extern size_t _malloc_usable_size(const void* ptr);
|
||||
|
||||
// Types.
|
||||
typedef void* HANDLE;
|
||||
typedef unsigned long DWORD;
|
||||
@@ -58,6 +60,7 @@ typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
|
||||
#define MEM_RELEASE 0x00008000UL
|
||||
#define MEM_FREE 0x00010000UL
|
||||
#define MEM_PRIVATE 0x00020000UL
|
||||
#define MEM_MAPPED 0x00040000UL
|
||||
#define PAGE_NOACCESS 0x01UL
|
||||
#define PAGE_READONLY 0x02UL
|
||||
#define PAGE_READWRITE 0x04UL
|
||||
@@ -65,6 +68,13 @@ typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
|
||||
#define PAGE_EXECUTE 0x10UL
|
||||
#define PAGE_EXECUTE_READ 0x20UL
|
||||
#define PAGE_EXECUTE_READWRITE 0x40UL
|
||||
#define SEC_COMMIT 0x08000000UL
|
||||
|
||||
#define FILE_MAP_COPY 0x00000001UL
|
||||
#define FILE_MAP_WRITE 0x00000002UL
|
||||
#define FILE_MAP_READ 0x00000004UL
|
||||
#define FILE_MAP_EXECUTE 0x00000020UL
|
||||
#define FILE_MAP_ALL_ACCESS 0x000F001FUL
|
||||
|
||||
typedef struct _MEMORY_BASIC_INFORMATION {
|
||||
void* BaseAddress;
|
||||
@@ -90,6 +100,26 @@ static uint32_t WinProtectToSysVm(DWORD protect) {
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t WinFileMappingProtectToSysVm(DWORD protect) {
|
||||
// Strip section attributes; the low byte carries PAGE_* protection.
|
||||
DWORD pageProtect = protect & 0xFFu;
|
||||
if (pageProtect == PAGE_WRITECOPY)
|
||||
return SYS_VM_READ; // MapViewOfFile(FILE_MAP_COPY) asks for the private writable view.
|
||||
return WinProtectToSysVm(pageProtect ? pageProtect : PAGE_READONLY);
|
||||
}
|
||||
|
||||
static uint32_t WinFileMapAccessToSys(DWORD desiredAccess) {
|
||||
uint32_t access = 0;
|
||||
if (desiredAccess & FILE_MAP_EXECUTE) access |= SYS_MAP_ACCESS_EXECUTE;
|
||||
if (desiredAccess & FILE_MAP_COPY) access |= SYS_MAP_ACCESS_COPY;
|
||||
if (desiredAccess & FILE_MAP_WRITE) access |= SYS_MAP_ACCESS_WRITE;
|
||||
if (desiredAccess & FILE_MAP_READ) access |= SYS_MAP_ACCESS_READ;
|
||||
if ((desiredAccess & FILE_MAP_ALL_ACCESS) == FILE_MAP_ALL_ACCESS)
|
||||
access |= SYS_MAP_ACCESS_READ | SYS_MAP_ACCESS_COPY;
|
||||
if (!access) access = SYS_MAP_ACCESS_READ;
|
||||
return access;
|
||||
}
|
||||
|
||||
// Implements the protect Vm To Win operation.
|
||||
static DWORD SysProtectVmToWin(uint32_t protect) {
|
||||
switch (protect) {
|
||||
@@ -153,24 +183,9 @@ typedef struct _WIN32_FIND_DATAA {
|
||||
static inline HANDLE KH_TO_HANDLE(uint32_t kh) { return (HANDLE)(uintptr_t)kh; }
|
||||
static inline uint32_t HANDLE_TO_KH(HANDLE h) { return (uint32_t)(uintptr_t)h; }
|
||||
|
||||
// Thread IDs are not kernel handles. Keep Win32 thread pseudo-handles out of
|
||||
// the low numeric handle namespace so CloseHandle/WaitForSingleObject cannot
|
||||
// accidentally operate on an unrelated file/process handle with the same value.
|
||||
#define WIN32_THREAD_HANDLE_TAG ((uintptr_t)1ULL << 63)
|
||||
#define WIN32_MUTEX_SENTINEL ((uintptr_t)0x4D555458UL) // 'MUTX'
|
||||
#define WIN32_SEMA_SENTINEL ((uintptr_t)0x53454D41UL) // 'SEMA'
|
||||
static inline HANDLE TID_TO_THREAD_HANDLE(uint32_t tid) {
|
||||
return (HANDLE)(WIN32_THREAD_HANDLE_TAG | (uintptr_t)tid);
|
||||
}
|
||||
static inline BOOL IS_THREAD_HANDLE(HANDLE h) {
|
||||
return h && h != INVALID_HANDLE_VALUE && (((uintptr_t)h & WIN32_THREAD_HANDLE_TAG) != 0);
|
||||
}
|
||||
static inline uint32_t THREAD_HANDLE_TO_TID(HANDLE h) {
|
||||
return (uint32_t)((uintptr_t)h & 0xFFFFFFFFu);
|
||||
}
|
||||
static inline BOOL IS_NOOP_SYNC_HANDLE(HANDLE h) {
|
||||
static inline BOOL IS_VALID_KERNEL_HANDLE(HANDLE h) {
|
||||
uintptr_t v = (uintptr_t)h;
|
||||
return v == WIN32_MUTEX_SENTINEL || v == WIN32_SEMA_SENTINEL;
|
||||
return v != 0 && v != (uintptr_t)INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
// Path translator.
|
||||
@@ -268,11 +283,14 @@ static void K32DebugPrintA(const char* s) {
|
||||
}
|
||||
|
||||
#define WIN32_TLS_SLOTS 64
|
||||
#define FLS_MAX_SLOTS 64
|
||||
#define FLS_OUT_OF_INDEXES 0xFFFFFFFFUL
|
||||
|
||||
typedef struct WIN32_THREAD_ENV {
|
||||
DWORD LastError;
|
||||
DWORD Reserved0;
|
||||
void* TlsSlots[WIN32_TLS_SLOTS];
|
||||
void* FlsSlots[FLS_MAX_SLOTS];
|
||||
} WIN32_THREAD_ENV;
|
||||
|
||||
static WIN32_THREAD_ENV gFallbackThreadEnv;
|
||||
@@ -418,11 +436,27 @@ __declspec(dllexport) uint64_t GetTickCount64(void) {
|
||||
}
|
||||
|
||||
// Memory (heap argument is ignored - single user-mode heap).
|
||||
#define HEAP_ZERO_MEMORY 0x00000008UL
|
||||
#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010UL
|
||||
|
||||
__declspec(dllexport) void* HeapAlloc(void* heap, DWORD flags, size_t size) {
|
||||
(void)heap; (void)flags;
|
||||
(void)heap;
|
||||
if (!size) return NULL;
|
||||
|
||||
void* ptr = malloc(size);
|
||||
if (!ptr) gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
|
||||
if (!ptr) {
|
||||
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Bink and the Microsoft CRT routinely allocate internal state with
|
||||
// HEAP_ZERO_MEMORY. Ignoring this flag only works accidentally while the
|
||||
// heap keeps receiving fresh demand-zero pages; under low memory / swap the
|
||||
// allocator reuses dirty freed blocks and stale pitch/pointer fields can
|
||||
// corrupt the decoder.
|
||||
if (flags & HEAP_ZERO_MEMORY)
|
||||
memset(ptr, 0, size);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -436,9 +470,28 @@ __declspec(dllexport) BOOL HeapFree(void* heap, DWORD flags, void* ptr) {
|
||||
|
||||
__declspec(dllexport) void* HeapReAlloc(void* heap, DWORD flags,
|
||||
void* ptr, size_t size) {
|
||||
(void)heap; (void)flags;
|
||||
(void)heap;
|
||||
|
||||
if (flags & HEAP_REALLOC_IN_PLACE_ONLY) {
|
||||
// The user heap does not expose an in-place grow primitive. Preserve
|
||||
// Windows semantics by failing instead of silently moving the block.
|
||||
size_t oldSize = _malloc_usable_size(ptr);
|
||||
if (oldSize != (size_t)-1 && oldSize >= size)
|
||||
return ptr;
|
||||
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t oldSize = ptr ? _malloc_usable_size(ptr) : 0;
|
||||
void* newptr = realloc(ptr, size);
|
||||
if (!newptr && size) gLastError = 8;
|
||||
if (!newptr && size) {
|
||||
gLastError = 8;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (newptr && (flags & HEAP_ZERO_MEMORY) && oldSize != (size_t)-1 && size > oldSize)
|
||||
memset((unsigned char*)newptr + oldSize, 0, size - oldSize);
|
||||
|
||||
return newptr;
|
||||
}
|
||||
|
||||
@@ -508,10 +561,88 @@ __declspec(dllexport) size_t VirtualQuery(const void* lpAddress,
|
||||
lpBuffer->RegionSize = info.PageCount * 4096u;
|
||||
lpBuffer->State = MEM_COMMIT;
|
||||
lpBuffer->Protect = protect;
|
||||
lpBuffer->Type = MEM_PRIVATE;
|
||||
lpBuffer->Type = (info.Type == 7u) ? MEM_MAPPED : MEM_PRIVATE; // 7 == MmVadMappedFile
|
||||
return sizeof(MEMORY_BASIC_INFORMATION);
|
||||
}
|
||||
|
||||
__declspec(dllexport) HANDLE CreateFileMappingA(HANDLE hFile,
|
||||
void* lpFileMappingAttributes,
|
||||
DWORD flProtect,
|
||||
DWORD dwMaximumSizeHigh,
|
||||
DWORD dwMaximumSizeLow,
|
||||
const char* lpName) {
|
||||
(void)lpFileMappingAttributes;
|
||||
(void)lpName;
|
||||
if (!hFile || hFile == INVALID_HANDLE_VALUE) {
|
||||
gLastError = 6; // ERROR_INVALID_HANDLE
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t maximumSize = ((uint64_t)dwMaximumSizeHigh << 32) | (uint64_t)dwMaximumSizeLow;
|
||||
uint32_t kh = 0;
|
||||
uint32_t s = SysCreateFileMapping(HANDLE_TO_KH(hFile),
|
||||
WinFileMappingProtectToSysVm(flProtect),
|
||||
SYS_MAP_FLAG_NONE,
|
||||
maximumSize,
|
||||
&kh);
|
||||
if (s != SYS_STATUS_SUCCESS) {
|
||||
gLastError = (s == SYS_STATUS_NOT_SUPPORTED) ? 120 : ((s == SYS_STATUS_NO_MEMORY) ? 8 : 87);
|
||||
return NULL;
|
||||
}
|
||||
return KH_TO_HANDLE(kh);
|
||||
}
|
||||
|
||||
__declspec(dllexport) HANDLE CreateFileMappingW(HANDLE hFile,
|
||||
void* lpFileMappingAttributes,
|
||||
DWORD flProtect,
|
||||
DWORD dwMaximumSizeHigh,
|
||||
DWORD dwMaximumSizeLow,
|
||||
const unsigned short* lpName) {
|
||||
(void)lpName;
|
||||
return CreateFileMappingA(hFile, lpFileMappingAttributes, flProtect,
|
||||
dwMaximumSizeHigh, dwMaximumSizeLow, NULL);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject,
|
||||
DWORD dwDesiredAccess,
|
||||
DWORD dwFileOffsetHigh,
|
||||
DWORD dwFileOffsetLow,
|
||||
size_t dwNumberOfBytesToMap) {
|
||||
if (!hFileMappingObject || hFileMappingObject == INVALID_HANDLE_VALUE) {
|
||||
gLastError = 6;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t offset = ((uint64_t)dwFileOffsetHigh << 32) | (uint64_t)dwFileOffsetLow;
|
||||
void* base = NULL;
|
||||
uint32_t s = SysMapFile(HANDLE_TO_KH(hFileMappingObject),
|
||||
offset,
|
||||
dwNumberOfBytesToMap,
|
||||
WinFileMapAccessToSys(dwDesiredAccess),
|
||||
SYS_MAP_FLAG_NONE,
|
||||
&base);
|
||||
if (s != SYS_STATUS_SUCCESS) {
|
||||
gLastError = (s == SYS_STATUS_NOT_SUPPORTED) ? 120 : ((s == SYS_STATUS_NO_MEMORY) ? 8 : 87);
|
||||
return NULL;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
__declspec(dllexport) BOOL UnmapViewOfFile(const void* lpBaseAddress) {
|
||||
if (!lpBaseAddress) { gLastError = 87; return FALSE; }
|
||||
uint32_t s = SysFreeVm((void*)lpBaseAddress);
|
||||
if (s != SYS_STATUS_SUCCESS) { gLastError = 87; return FALSE; }
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
__declspec(dllexport) BOOL FlushViewOfFile(const void* lpBaseAddress, size_t dwNumberOfBytesToFlush) {
|
||||
(void)dwNumberOfBytesToFlush;
|
||||
if (!lpBaseAddress) { gLastError = 87; return FALSE; }
|
||||
// Native flush is file-handle based for now; unmap/close also harvests dirty
|
||||
// PTEs, so FlushViewOfFile remains a successful cache-coherency hint.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// --- Win32 API ---.
|
||||
|
||||
// GetAsyncKeyState implementation over the canonical input key namespace.
|
||||
@@ -720,7 +851,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
|
||||
// GetFileSize - fills optional high-DWORD pointer, returns low DWORD.
|
||||
// Returns 0xFFFFFFFF on error, matching Win32 behaviour.
|
||||
__declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) {
|
||||
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) { gLastError = 6; return 0xFFFFFFFFUL; }
|
||||
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; }
|
||||
uint64_t sz = SysGetFileSize(HANDLE_TO_KH(hFile));
|
||||
if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32);
|
||||
return (DWORD)(sz & 0xFFFFFFFFULL);
|
||||
@@ -731,25 +862,27 @@ __declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
|
||||
DWORD* bytesRead, void* overlapped) {
|
||||
(void)overlapped;
|
||||
if (bytesRead) *bytesRead = 0;
|
||||
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile) || !buf) {
|
||||
if (!hFile || hFile == INVALID_HANDLE_VALUE || !buf) {
|
||||
gLastError = 6;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
size_t got = 0;
|
||||
uint32_t s = SysReadFile(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got);
|
||||
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got);
|
||||
uint32_t s = SysReadFile(&req);
|
||||
if (bytesRead) *bytesRead = (DWORD)got;
|
||||
|
||||
// Harden callers that accidentally look past *bytesRead. EOF/short reads
|
||||
// are legal for files, but consuming stale stack/heap bytes after them makes
|
||||
// the failure mode look like random game-data corruption.
|
||||
if (got < (size_t)toRead)
|
||||
memset((unsigned char*)buf + got, 0, (size_t)toRead - got);
|
||||
|
||||
if (s != 0) {
|
||||
gLastError = 1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Harden callers that accidentally look past *bytesRead. EOF/short reads
|
||||
// are legal for files, but do not touch the caller's remaining buffer after
|
||||
// a failed read because that can fault in megabytes under low memory.
|
||||
if (got < (size_t)toRead)
|
||||
memset((unsigned char*)buf + got, 0, (size_t)toRead - got);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -758,9 +891,10 @@ __declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
|
||||
__declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrite,
|
||||
DWORD* bytesWritten, void* overlapped) {
|
||||
(void)overlapped;
|
||||
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile) || !buf) { gLastError = 6; return FALSE; }
|
||||
if (!hFile || hFile == INVALID_HANDLE_VALUE || !buf) { gLastError = 6; return FALSE; }
|
||||
size_t written = 0;
|
||||
uint32_t s = SysWriteFile(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written);
|
||||
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written);
|
||||
uint32_t s = SysWriteFile(&req);
|
||||
if (bytesWritten) *bytesWritten = (DWORD)written;
|
||||
if (s != 0) { gLastError = 29; return FALSE; } // ERROR_WRITE_FAULT
|
||||
return TRUE;
|
||||
@@ -770,7 +904,7 @@ __declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrit
|
||||
// Returns new position low DWORD, or 0xFFFFFFFF on error.
|
||||
__declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
|
||||
long* distHigh, DWORD method) {
|
||||
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) {
|
||||
if (!hFile || hFile == INVALID_HANDLE_VALUE) {
|
||||
gLastError = 6;
|
||||
return 0xFFFFFFFFUL;
|
||||
}
|
||||
@@ -813,7 +947,7 @@ __declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
|
||||
|
||||
// SetEndOfFile - truncate or extend the file to the current seek position.
|
||||
__declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) {
|
||||
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) { gLastError = 6; return FALSE; }
|
||||
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
|
||||
uint32_t kh = HANDLE_TO_KH(hFile);
|
||||
uint64_t pos = SysGetFilePos(kh);
|
||||
if (pos == (uint64_t)-1) { gLastError = 6; return FALSE; }
|
||||
@@ -834,14 +968,10 @@ __declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// CloseHandle - unified close for files, mutexes, semaphores, events, queues.
|
||||
// CloseHandle - unified close for files, threads, mutexes, semaphores, events, queues, and processes.
|
||||
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
|
||||
if (!hObject || hObject == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
|
||||
|
||||
// Our thread/sentinel sync handles are not entries in the kernel handle table.
|
||||
// There is nothing to close; the thread object is reaped by the scheduler.
|
||||
if (IS_THREAD_HANDLE(hObject) || IS_NOOP_SYNC_HANDLE(hObject)) return TRUE;
|
||||
|
||||
SysCloseHandle(HANDLE_TO_KH(hObject));
|
||||
return TRUE;
|
||||
}
|
||||
@@ -870,8 +1000,11 @@ __declspec(dllexport) BOOL HeapDestroy(HANDLE hHeap) {
|
||||
|
||||
// Implements the heap Size operation.
|
||||
__declspec(dllexport) size_t HeapSize(HANDLE heap, DWORD flags, const void* ptr) {
|
||||
(void)heap; (void)flags; (void)ptr;
|
||||
return (size_t)-1; // Win32 returns (SIZE_T)-1 on error / unsupported
|
||||
(void)heap; (void)flags;
|
||||
size_t size = _malloc_usable_size(ptr);
|
||||
if (size == (size_t)-1)
|
||||
gLastError = 87; // ERROR_INVALID_PARAMETER
|
||||
return size;
|
||||
}
|
||||
|
||||
__declspec(dllexport) BOOL HeapSetInformation(HANDLE HeapHandle,
|
||||
@@ -1050,9 +1183,29 @@ __declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
|
||||
|
||||
// Implements the terminate Process operation.
|
||||
__declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) {
|
||||
(void)hProcess;
|
||||
SysExitProcess((int)uExitCode);
|
||||
while (true) ;
|
||||
if (hProcess == GetCurrentProcess()) {
|
||||
SysExitProcess((int)uExitCode);
|
||||
while (true) ;
|
||||
}
|
||||
|
||||
if (!hProcess) {
|
||||
gLastError = 6; // ERROR_INVALID_HANDLE
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
uint32_t pid = 0;
|
||||
uint32_t s = SysGetProcessPid(HANDLE_TO_KH(hProcess), &pid);
|
||||
if (s != 0 || pid == 0) {
|
||||
gLastError = 6; // ERROR_INVALID_HANDLE
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
s = SysKillProcess(pid, (int)uExitCode);
|
||||
if (s != 0) {
|
||||
gLastError = 5; // ERROR_ACCESS_DENIED / generic failure for now
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// STARTUPINFOA - only cb matters for our stub.
|
||||
@@ -1099,35 +1252,42 @@ __declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
|
||||
DWORD* lpThreadId) {
|
||||
(void)lpThreadAttributes; (void)dwStackSize; (void)dwCreationFlags;
|
||||
uint32_t tid = 0;
|
||||
uint32_t s = SysCreateThread((uint64_t)(uintptr_t)lpStartAddress,
|
||||
(uint64_t)(uintptr_t)lpParameter,
|
||||
0, &tid);
|
||||
uint32_t kh = 0;
|
||||
SYS_CREATE_THREAD_REQUEST req = {
|
||||
(uintptr_t)lpStartAddress,
|
||||
(uintptr_t)lpParameter,
|
||||
0,
|
||||
0,
|
||||
&kh,
|
||||
&tid
|
||||
};
|
||||
uint32_t s = SysCreateThread(&req);
|
||||
if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; }
|
||||
if (lpThreadId) *lpThreadId = (DWORD)tid;
|
||||
return TID_TO_THREAD_HANDLE(tid);
|
||||
return KH_TO_HANDLE(kh);
|
||||
}
|
||||
|
||||
// WaitForSingleObject - blocks on process handles via SysWaitForProcess;
|
||||
// thread and other handle types are not yet waitable so return immediately.
|
||||
// WaitForSingleObject - blocks on native waitable handles. A zero timeout is
|
||||
// only a probe for mutexes/semaphores until native timeout waits exist.
|
||||
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
|
||||
(void)dwMilliseconds;
|
||||
if (!hHandle || hHandle == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; }
|
||||
|
||||
if (IS_THREAD_HANDLE(hHandle)) {
|
||||
uint32_t tid = THREAD_HANDLE_TO_TID(hHandle);
|
||||
uint32_t s = SysWaitThread(tid);
|
||||
if (s != 0 && s != 0x0022) { gLastError = 6; return 0xFFFFFFFFUL; }
|
||||
return WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
if (IS_NOOP_SYNC_HANDLE(hHandle)) return WAIT_OBJECT_0;
|
||||
if (!IS_VALID_KERNEL_HANDLE(hHandle)) { gLastError = 6; return 0xFFFFFFFFUL; }
|
||||
|
||||
uint32_t kh = HANDLE_TO_KH(hHandle);
|
||||
if (kh) {
|
||||
uint32_t s = SysWaitForProcess(kh);
|
||||
if (s != 0) { gLastError = 6; return 0xFFFFFFFFUL; }
|
||||
if (dwMilliseconds == 0) {
|
||||
uint32_t s = SysTryAcquireMutex(kh);
|
||||
if (s == 0) return WAIT_OBJECT_0;
|
||||
|
||||
s = SysTryWaitSem(kh);
|
||||
if (s == 0) return WAIT_OBJECT_0;
|
||||
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
return WAIT_OBJECT_0;
|
||||
|
||||
uint32_t s = SysWait(kh, SYS_WAIT_FLAG_NONE);
|
||||
if (s == 0) return WAIT_OBJECT_0;
|
||||
|
||||
gLastError = 6;
|
||||
return 0xFFFFFFFFUL;
|
||||
}
|
||||
|
||||
|
||||
@@ -1250,7 +1410,7 @@ __declspec(dllexport) BOOL CreateProcessA(
|
||||
__declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode) {
|
||||
if (!lpExitCode) { gLastError = 87; return FALSE; }
|
||||
uint32_t kh = HANDLE_TO_KH(hProcess);
|
||||
if (!kh || IS_THREAD_HANDLE(hProcess)) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
|
||||
if (!kh) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
|
||||
int code = 0;
|
||||
uint32_t s = SysGetProcessExitCode(kh, &code);
|
||||
if (s != 0) { gLastError = 6; return FALSE; }
|
||||
@@ -1791,57 +1951,123 @@ __declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBu
|
||||
|
||||
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
|
||||
const char* lpName) {
|
||||
(void)lpMutexAttributes; (void)bInitialOwner; (void)lpName;
|
||||
return (HANDLE)WIN32_MUTEX_SENTINEL;
|
||||
(void)lpMutexAttributes; (void)lpName;
|
||||
uint32_t kh = 0;
|
||||
uint32_t s = SysCreateMutex(&kh);
|
||||
if (s != 0 || !kh) { gLastError = 8; return NULL; }
|
||||
if (bInitialOwner) {
|
||||
s = SysWait(kh, SYS_WAIT_FLAG_NONE);
|
||||
if (s != 0) {
|
||||
SysCloseHandle(kh);
|
||||
gLastError = 1;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return KH_TO_HANDLE(kh);
|
||||
}
|
||||
|
||||
// Releases mutex.
|
||||
__declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) {
|
||||
(void)hMutex;
|
||||
if (!IS_VALID_KERNEL_HANDLE(hMutex)) { gLastError = 6; return FALSE; }
|
||||
uint32_t s = SysReleaseMutex(HANDLE_TO_KH(hMutex));
|
||||
if (s != 0) { gLastError = 6; return FALSE; }
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
__declspec(dllexport) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes,
|
||||
long lInitialCount, long lMaximumCount,
|
||||
const char* lpName) {
|
||||
(void)lpSemaphoreAttributes; (void)lInitialCount;
|
||||
(void)lMaximumCount; (void)lpName;
|
||||
return (HANDLE)WIN32_SEMA_SENTINEL;
|
||||
(void)lpSemaphoreAttributes; (void)lpName;
|
||||
uint32_t kh = 0;
|
||||
uint32_t s = SysCreateSem((int32_t)lInitialCount, (int32_t)lMaximumCount, &kh);
|
||||
if (s != 0 || !kh) { gLastError = 8; return NULL; }
|
||||
return KH_TO_HANDLE(kh);
|
||||
}
|
||||
|
||||
__declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCount,
|
||||
long* lpPreviousCount) {
|
||||
(void)hSemaphore; (void)lReleaseCount;
|
||||
if (!IS_VALID_KERNEL_HANDLE(hSemaphore) || lReleaseCount < 0) { gLastError = 6; return FALSE; }
|
||||
if (lpPreviousCount) *lpPreviousCount = 0;
|
||||
for (long i = 0; i < lReleaseCount; ++i) {
|
||||
uint32_t s = SysSignalSem(HANDLE_TO_KH(hSemaphore));
|
||||
if (s != 0) { gLastError = 6; return FALSE; }
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Critical Sections & Condition Variables
|
||||
// Single-threaded: all locks are no-ops.
|
||||
|
||||
// Minimal opaque types; callers allocate these from their Windows headers -
|
||||
// we just need the pointer to be accepted by our stubs.
|
||||
// Critical Sections & Condition Variables.
|
||||
// Match the size of Win64 RTL_CRITICAL_SECTION (5 pointer-sized fields) while
|
||||
// using the storage as a small recursive spin lock. Bink uses these from its
|
||||
// worker/copy paths, so the old no-op implementation allowed internal state
|
||||
// races as soon as CreateThread started working.
|
||||
typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION;
|
||||
typedef struct { void* _ptr; } CONDITION_VARIABLE;
|
||||
|
||||
#define K32_CS_LOCK 0
|
||||
#define K32_CS_OWNER 1
|
||||
#define K32_CS_RECURSION 2
|
||||
|
||||
static volatile long* K32CsLockWord(CRITICAL_SECTION* cs) {
|
||||
return (volatile long*)&cs->_opaque[K32_CS_LOCK];
|
||||
}
|
||||
|
||||
static DWORD K32CurrentTidOrOne(void) {
|
||||
DWORD tid = (DWORD)SysGetCurrentTid();
|
||||
return tid ? tid : 1;
|
||||
}
|
||||
|
||||
// Initializes critical Section.
|
||||
__declspec(dllexport) void InitializeCriticalSection(CRITICAL_SECTION* lpCS) {
|
||||
(void)lpCS;
|
||||
if (!lpCS) return;
|
||||
memset(lpCS, 0, sizeof(*lpCS));
|
||||
}
|
||||
__declspec(dllexport) BOOL InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION* lpCS,
|
||||
DWORD dwSpinCount) {
|
||||
(void)lpCS; (void)dwSpinCount;
|
||||
(void)dwSpinCount;
|
||||
InitializeCriticalSection(lpCS);
|
||||
return TRUE;
|
||||
}
|
||||
// Releases critical Section.
|
||||
__declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
|
||||
// Implements the enter Critical Section operation.
|
||||
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
|
||||
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) {
|
||||
if (!lpCS) return;
|
||||
DWORD tid = K32CurrentTidOrOne();
|
||||
if ((DWORD)lpCS->_opaque[K32_CS_OWNER] == tid) {
|
||||
lpCS->_opaque[K32_CS_RECURSION]++;
|
||||
return;
|
||||
}
|
||||
while (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0)
|
||||
_mm_pause();
|
||||
lpCS->_opaque[K32_CS_OWNER] = tid;
|
||||
lpCS->_opaque[K32_CS_RECURSION] = 1;
|
||||
}
|
||||
// Implements the leave Critical Section operation.
|
||||
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
|
||||
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) {
|
||||
if (!lpCS) return;
|
||||
DWORD tid = K32CurrentTidOrOne();
|
||||
if ((DWORD)lpCS->_opaque[K32_CS_OWNER] != tid || lpCS->_opaque[K32_CS_RECURSION] == 0)
|
||||
return;
|
||||
lpCS->_opaque[K32_CS_RECURSION]--;
|
||||
if (lpCS->_opaque[K32_CS_RECURSION] == 0) {
|
||||
lpCS->_opaque[K32_CS_OWNER] = 0;
|
||||
_ReadWriteBarrier();
|
||||
_InterlockedExchange(K32CsLockWord(lpCS), 0);
|
||||
}
|
||||
}
|
||||
// Implements the try Enter Critical Section operation.
|
||||
__declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION* lpCS) {
|
||||
(void)lpCS; return TRUE;
|
||||
if (!lpCS) return FALSE;
|
||||
DWORD tid = K32CurrentTidOrOne();
|
||||
if ((DWORD)lpCS->_opaque[K32_CS_OWNER] == tid) {
|
||||
lpCS->_opaque[K32_CS_RECURSION]++;
|
||||
return TRUE;
|
||||
}
|
||||
if (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0)
|
||||
return FALSE;
|
||||
lpCS->_opaque[K32_CS_OWNER] = tid;
|
||||
lpCS->_opaque[K32_CS_RECURSION] = 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Initializes condition Variable.
|
||||
@@ -2357,14 +2583,12 @@ __declspec(dllexport) int GetLocaleInfoA(unsigned int Locale, DWORD LCType,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fiber Local Storage (FLS)
|
||||
// We implement a minimal 64-slot static table so callers that use FLS for
|
||||
// thread-local-ish state don't immediately fail.
|
||||
// Fiber Local Storage (FLS).
|
||||
// Allocation state is process-global, but the values themselves are per-thread,
|
||||
// matching the contract that Bink and the CRT expect when worker threads are
|
||||
// enabled. The old single global value array let unrelated threads overwrite
|
||||
// each other's decoder-local state.
|
||||
|
||||
#define FLS_MAX_SLOTS 64
|
||||
#define FLS_OUT_OF_INDEXES 0xFFFFFFFFUL
|
||||
|
||||
static void* gFlsSlots[FLS_MAX_SLOTS];
|
||||
static int gFlsUsed[FLS_MAX_SLOTS]; // 0 = free, 1 = allocated
|
||||
|
||||
// Implements the fls Alloc operation.
|
||||
@@ -2372,8 +2596,8 @@ __declspec(dllexport) DWORD FlsAlloc(void* lpCallback) {
|
||||
(void)lpCallback;
|
||||
for (int i = 0; i < FLS_MAX_SLOTS; i++) {
|
||||
if (!gFlsUsed[i]) {
|
||||
gFlsUsed[i] = 1;
|
||||
gFlsSlots[i] = NULL;
|
||||
gFlsUsed[i] = 1;
|
||||
K32ThreadEnv()->FlsSlots[i] = NULL;
|
||||
return (DWORD)i;
|
||||
}
|
||||
}
|
||||
@@ -2387,7 +2611,7 @@ __declspec(dllexport) void* FlsGetValue(DWORD dwFlsIndex) {
|
||||
gLastError = 87; // ERROR_INVALID_PARAMETER
|
||||
return NULL;
|
||||
}
|
||||
return gFlsSlots[dwFlsIndex];
|
||||
return K32ThreadEnv()->FlsSlots[dwFlsIndex];
|
||||
}
|
||||
|
||||
// Implements the fls Set Value operation.
|
||||
@@ -2396,7 +2620,7 @@ __declspec(dllexport) BOOL FlsSetValue(DWORD dwFlsIndex, void* lpFlsData) {
|
||||
gLastError = 87;
|
||||
return FALSE;
|
||||
}
|
||||
gFlsSlots[dwFlsIndex] = lpFlsData;
|
||||
K32ThreadEnv()->FlsSlots[dwFlsIndex] = lpFlsData;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -2406,8 +2630,8 @@ __declspec(dllexport) BOOL FlsFree(DWORD dwFlsIndex) {
|
||||
gLastError = 87;
|
||||
return FALSE;
|
||||
}
|
||||
gFlsUsed[dwFlsIndex] = 0;
|
||||
gFlsSlots[dwFlsIndex] = NULL;
|
||||
gFlsUsed[dwFlsIndex] = 0;
|
||||
K32ThreadEnv()->FlsSlots[dwFlsIndex] = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user