2004-03-03 00:56:51 +01:00
|
|
|
// misc. POSIX routines for Win32
|
|
|
|
//
|
2004-06-03 20:36:48 +02:00
|
|
|
// Copyright (c) 2004 Jan Wassenberg
|
2004-03-03 00:56:51 +01:00
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License as
|
|
|
|
// published by the Free Software Foundation; either version 2 of the
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful, but
|
|
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// General Public License for more details.
|
|
|
|
//
|
|
|
|
// Contact info:
|
|
|
|
// Jan.Wassenberg@stud.uni-karlsruhe.de
|
|
|
|
// http://www.stud.uni-karlsruhe.de/~urkt/
|
|
|
|
|
|
|
|
// collection of hacks :P
|
|
|
|
|
2004-05-08 03:11:51 +02:00
|
|
|
#include "precompiled.h"
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
#include "lib.h"
|
|
|
|
#include "win_internal.h"
|
2004-06-01 19:34:12 +02:00
|
|
|
|
2004-06-04 14:41:53 +02:00
|
|
|
#include <process.h>
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2004-06-01 19:34:12 +02:00
|
|
|
|
2004-08-28 13:14:23 +02:00
|
|
|
// cast intptr_t to HANDLE; centralized for easier changing, e.g. avoiding
|
|
|
|
// warnings. i = -1 converts to INVALID_HANDLE_VALUE (same value).
|
|
|
|
static inline HANDLE cast_to_HANDLE(intptr_t i)
|
2004-06-01 19:34:12 +02:00
|
|
|
{
|
|
|
|
return (HANDLE)((char*)0 + i);
|
|
|
|
}
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// file
|
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
2004-07-05 04:27:51 +02:00
|
|
|
int open(const char* fn, int oflag, ...)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-12-07 02:12:35 +01:00
|
|
|
const bool is_com_port = strncmp(fn, "/dev/tty", 8) == 0;
|
|
|
|
// also used later, before aio_reopen
|
2004-05-06 19:14:30 +02:00
|
|
|
|
2004-12-07 02:12:35 +01:00
|
|
|
// "/dev/tty?" => "COM?"
|
2004-05-06 19:14:30 +02:00
|
|
|
if(is_com_port)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-12-07 02:12:35 +01:00
|
|
|
char port[] = "COM ";
|
|
|
|
// Windows only supports COM1..COM4.
|
|
|
|
char c = fn[8]+1;
|
|
|
|
if(!('1' <= c && c <= '4'))
|
|
|
|
return -1;
|
2004-03-03 00:56:51 +01:00
|
|
|
port[3] = (char)(fn[8]+1);
|
|
|
|
fn = port;
|
|
|
|
}
|
|
|
|
|
2004-07-05 04:27:51 +02:00
|
|
|
mode_t mode = 0;
|
|
|
|
if(oflag & O_CREAT)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
va_start(args, oflag);
|
|
|
|
mode = va_arg(args, mode_t);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
2004-08-05 14:45:27 +02:00
|
|
|
WIN_SAVE_LAST_ERROR; // CreateFile
|
2004-07-05 04:27:51 +02:00
|
|
|
int fd = _open(fn, oflag, mode);
|
2004-07-12 16:25:39 +02:00
|
|
|
WIN_RESTORE_LAST_ERROR;
|
|
|
|
|
2004-08-05 14:45:27 +02:00
|
|
|
#ifdef PARANOIA
|
|
|
|
debug_out("open %s = %d\n", fn, fd);
|
|
|
|
#endif
|
2004-07-12 16:25:39 +02:00
|
|
|
|
2004-12-07 02:12:35 +01:00
|
|
|
// open it for async I/O as well (_open defaults to deny_none sharing):
|
|
|
|
// if not stdin/stdout/stderr and
|
2004-03-03 00:56:51 +01:00
|
|
|
if(fd > 2)
|
2004-05-06 19:14:30 +02:00
|
|
|
{
|
2004-12-07 02:12:35 +01:00
|
|
|
// not a COM port. don't currently need aio access for those;
|
2004-07-05 04:27:51 +02:00
|
|
|
// also, aio_reopen's CreateFile reports access denied when trying to open.
|
2004-05-06 19:14:30 +02:00
|
|
|
if(!is_com_port)
|
2004-07-05 04:27:51 +02:00
|
|
|
aio_reopen(fd, fn, oflag);
|
2004-05-06 19:14:30 +02:00
|
|
|
}
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-08-05 14:45:27 +02:00
|
|
|
// CRT doesn't like more than 255 files open.
|
|
|
|
// warn now, so that we notice why so many are open
|
2004-12-07 02:12:35 +01:00
|
|
|
#ifndef NDEBUG
|
2004-08-05 14:45:27 +02:00
|
|
|
if(fd > 256)
|
|
|
|
debug_warn("wposix: too many files open (CRT limitation)");
|
2004-12-07 02:12:35 +01:00
|
|
|
#endif
|
2004-08-05 14:45:27 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int close(int fd)
|
|
|
|
{
|
2004-08-05 14:45:27 +02:00
|
|
|
#ifdef PARANOIA
|
2004-08-03 14:57:06 +02:00
|
|
|
debug_out("close %d\n", fd);
|
2004-08-05 14:45:27 +02:00
|
|
|
#endif
|
|
|
|
|
2004-06-01 19:34:12 +02:00
|
|
|
assert(3 <= fd && fd < 256);
|
2004-03-03 00:56:51 +01:00
|
|
|
aio_close(fd);
|
|
|
|
return _close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int ioctl(int fd, int op, int* data)
|
|
|
|
{
|
2004-08-28 13:14:23 +02:00
|
|
|
const HANDLE h = cast_to_HANDLE(_get_osfhandle(fd));
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
switch(op)
|
|
|
|
{
|
|
|
|
case TIOCMGET:
|
|
|
|
/* TIOCM_* mapped directly to MS_*_ON */
|
|
|
|
GetCommModemStatus(h, (DWORD*)data);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TIOCMBIS:
|
|
|
|
/* only RTS supported */
|
|
|
|
if(*data & TIOCM_RTS)
|
|
|
|
EscapeCommFunction(h, SETRTS);
|
|
|
|
else
|
|
|
|
EscapeCommFunction(h, CLRRTS);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TIOCMIWAIT:
|
|
|
|
static DWORD mask;
|
|
|
|
DWORD new_mask = 0;
|
|
|
|
if(*data & TIOCM_CD)
|
|
|
|
new_mask |= EV_RLSD;
|
|
|
|
if(*data & TIOCM_CTS)
|
|
|
|
new_mask |= EV_CTS;
|
|
|
|
if(new_mask != mask)
|
|
|
|
SetCommMask(h, mask = new_mask);
|
|
|
|
WaitCommEvent(h, &mask, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// from wtime
|
|
|
|
extern time_t local_filetime_to_time_t(FILETIME* ft);
|
|
|
|
extern time_t utc_filetime_to_time_t(FILETIME* ft);
|
|
|
|
|
|
|
|
// convert Windows FILETIME to POSIX time_t (seconds-since-1970 UTC);
|
|
|
|
// used by stat and readdir_stat_np for st_mtime.
|
|
|
|
//
|
|
|
|
// path is used to determine the file system that recorded the time
|
|
|
|
// (workaround for a documented Windows bug in converting FAT file times)
|
|
|
|
//
|
|
|
|
// note: complicated because we need to ensure the time returned is correct:
|
|
|
|
// VFS mount logic considers files 'equal' if mtime and size are the same.
|
|
|
|
static time_t filetime_to_time_t(FILETIME* ft, const char* path)
|
|
|
|
{
|
|
|
|
// determine file system on volume containing path:
|
|
|
|
// .. assume relative path
|
|
|
|
const char* root = 0;
|
|
|
|
char drive_str[] = "?:\\";
|
|
|
|
// .. it's an absolute path
|
|
|
|
if(isalpha(path[0]) && path[1] == ':' && path[2] == '\\')
|
|
|
|
{
|
|
|
|
drive_str[0] = path[0]; // drive letter
|
|
|
|
root = drive_str;
|
|
|
|
}
|
|
|
|
char fs_name[16] = { 0 };
|
|
|
|
GetVolumeInformation(root, 0,0,0,0,0, fs_name, sizeof(fs_name));
|
|
|
|
// if this fails, fs_name != "FAT" => special-case is skipped.
|
|
|
|
|
|
|
|
// the FAT file system stores local file times, while
|
|
|
|
// NTFS records UTC. Windows does convert automatically,
|
|
|
|
// but uses the current DST settings. (boo!)
|
|
|
|
// we go back to local time, and convert properly.
|
|
|
|
if(!strncmp(fs_name, "FAT", 3)) // e.g. FAT32
|
|
|
|
{
|
|
|
|
FILETIME local_ft;
|
|
|
|
FileTimeToLocalFileTime(ft, &local_ft);
|
|
|
|
return local_filetime_to_time_t(&local_ft);
|
|
|
|
}
|
|
|
|
|
|
|
|
return utc_filetime_to_time_t(ft);
|
|
|
|
}
|
|
|
|
|
2004-12-07 02:12:35 +01:00
|
|
|
/*
|
2004-03-03 00:56:51 +01:00
|
|
|
// currently only sets st_mode (file or dir) and st_size.
|
|
|
|
int stat(const char* fn, struct stat* s)
|
|
|
|
{
|
|
|
|
memset(s, 0, sizeof(struct stat));
|
|
|
|
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA fad;
|
|
|
|
if(!GetFileAttributesEx(fn, GetFileExInfoStandard, &fad))
|
|
|
|
return -1;
|
|
|
|
|
2004-12-07 02:12:35 +01:00
|
|
|
s->st_mtime = fad.ftLastAccessTime
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
// dir
|
2004-05-15 05:02:26 +02:00
|
|
|
if(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
2004-03-03 00:56:51 +01:00
|
|
|
s->st_mode = S_IFDIR;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
s->st_mode = S_IFREG;
|
|
|
|
s->st_size = (off_t)((((u64)fad.nFileSizeHigh) << 32) | fad.nFileSizeLow);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2004-12-07 02:12:35 +01:00
|
|
|
*/
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// dir
|
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
char* realpath(const char* fn, char* path)
|
|
|
|
{
|
|
|
|
if(!GetFullPathName(fn, PATH_MAX, path, 0))
|
|
|
|
return 0;
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int mkdir(const char* path, mode_t)
|
|
|
|
{
|
|
|
|
return CreateDirectory(path, 0)? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
// opendir/readdir/closedir
|
|
|
|
//
|
|
|
|
// implementation rationale:
|
|
|
|
//
|
|
|
|
// opendir only performs minimal error checks (does directory exist?);
|
|
|
|
// readdir calls FindFirstFile. this is to ensure correct handling
|
|
|
|
// of empty directories. we need to store the path in WDIR anyway
|
|
|
|
// for filetime_to_time_t.
|
|
|
|
//
|
|
|
|
// we avoid opening directories or returning files that have hidden or system
|
|
|
|
// attributes set. this is to prevent returning something like
|
|
|
|
// "\system volume information", which raises an error upon opening.
|
|
|
|
|
|
|
|
struct WDIR
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2005-01-07 02:10:00 +01:00
|
|
|
HANDLE hFind;
|
2004-03-03 00:56:51 +01:00
|
|
|
WIN32_FIND_DATA fd;
|
2005-01-07 02:10:00 +01:00
|
|
|
|
|
|
|
struct dirent ent;
|
|
|
|
// can't be global - must not be overwritten
|
|
|
|
// by calls from different DIRs.
|
|
|
|
|
|
|
|
char path[PATH_MAX+1];
|
|
|
|
// can't be stored in fd or ent's path fields -
|
|
|
|
// needed by each readdir_stat_np (for filetime_to_time_t).
|
2004-03-03 00:56:51 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2004-09-02 04:47:11 +02:00
|
|
|
static const DWORD hs = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
|
2005-01-07 02:10:00 +01:00
|
|
|
// convenience
|
2004-09-02 04:47:11 +02:00
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
DIR* opendir(const char* path)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2005-01-07 02:10:00 +01:00
|
|
|
// make sure path exists and is a normal directory (see rationale above).
|
|
|
|
// note: this is the only error check we can do here -
|
|
|
|
// FindFirstFile is called in readdir (see rationale above).
|
|
|
|
DWORD fa = GetFileAttributes(path);
|
|
|
|
if((fa == INVALID_FILE_ATTRIBUTES) || !(fa & FILE_ATTRIBUTE_DIRECTORY) || (fa & hs))
|
2004-03-03 00:56:51 +01:00
|
|
|
return 0;
|
|
|
|
|
2005-01-23 18:57:52 +01:00
|
|
|
// note: zero-initializes everything (required).
|
|
|
|
WDIR* d = (WDIR*)calloc(1, sizeof(WDIR));
|
|
|
|
if(!d)
|
|
|
|
return 0;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
// note: "path\\dir" only returns information about that directory;
|
|
|
|
// trailing slashes aren't allowed. we have to append "\\*" to find files.
|
|
|
|
strncpy(d->path, path, MAX_PATH-2);
|
|
|
|
strcat(d->path, "\\*");
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
struct dirent* readdir(DIR* d_)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2005-01-07 02:10:00 +01:00
|
|
|
WDIR* const d = (WDIR*)d_;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
DWORD prev_err = GetLastError();
|
2004-07-12 16:25:39 +02:00
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
// bails if end of dir reached or error.
|
|
|
|
// called (again) if entry was rejected below.
|
|
|
|
get_another_entry:
|
|
|
|
|
|
|
|
// first time
|
|
|
|
if(d->hFind == 0)
|
|
|
|
{
|
|
|
|
d->hFind = FindFirstFile(d->path, &d->fd);
|
|
|
|
if(d->hFind != INVALID_HANDLE_VALUE) // success
|
|
|
|
goto have_entry;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if(FindNextFile(d->hFind, &d->fd)) // success
|
|
|
|
goto have_entry;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
// Find*File failed; determine why and bail.
|
|
|
|
// .. legit, end of dir reached. don't pollute last error code.
|
|
|
|
if(GetLastError() == ERROR_NO_MORE_FILES)
|
|
|
|
SetLastError(prev_err);
|
|
|
|
else
|
|
|
|
debug_warn("readdir: Find*File failed");
|
|
|
|
return 0;
|
2004-09-02 04:47:11 +02:00
|
|
|
|
2004-12-09 21:17:09 +01:00
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
// d->fd holds a valid entry, but we may have to get another below.
|
|
|
|
have_entry:
|
2004-12-09 21:17:09 +01:00
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
// we must not return hidden or system entries, so get another.
|
|
|
|
// (see rationale above).
|
|
|
|
if(d->fd.dwFileAttributes & hs)
|
|
|
|
goto get_another_entry;
|
2004-12-09 21:17:09 +01:00
|
|
|
|
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
// this entry has passed all checks; return information about it.
|
|
|
|
// .. d_ino zero-initialized by opendir
|
|
|
|
// .. POSIX requires d_name to be an array, so we copy there.
|
|
|
|
strncpy(d->ent.d_name, d->fd.cFileName, PATH_MAX);
|
2004-03-03 00:56:51 +01:00
|
|
|
return &d->ent;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
// return status for the dirent returned by the last successful
|
|
|
|
// readdir call from the given directory stream.
|
|
|
|
// currently sets st_size, st_mode, and st_mtime; the rest are zeroed.
|
|
|
|
// non-portable, but considerably faster than stat(). used by file_enum.
|
|
|
|
int readdir_stat_np(DIR* d_, struct stat* s)
|
|
|
|
{
|
|
|
|
WDIR* d = (WDIR*)d_;
|
|
|
|
|
|
|
|
memset(s, 0, sizeof(struct stat));
|
|
|
|
s->st_size = (off_t)((((u64)d->fd.nFileSizeHigh) << 32) | d->fd.nFileSizeLow);
|
|
|
|
s->st_mode = (d->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG;
|
|
|
|
s->st_mtime = filetime_to_time_t(&d->fd.ftLastWriteTime, d->path);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int closedir(DIR* d_)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2005-01-07 02:10:00 +01:00
|
|
|
WDIR* const d = (WDIR*)d_;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
FindClose(d->hFind);
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
memset(d, 0, sizeof(WDIR)); // safety
|
|
|
|
free(d);
|
2004-03-03 00:56:51 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// terminal
|
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
2004-06-01 19:34:12 +02:00
|
|
|
static HANDLE std_h[2] = { (HANDLE)((char*)0 + 3), (HANDLE)((char*)0 + 7) };
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
__declspec(naked) void _get_console()
|
|
|
|
{ __asm jmp dword ptr [AllocConsole] }
|
|
|
|
|
|
|
|
__declspec(naked) void _hide_console()
|
|
|
|
{ __asm jmp dword ptr [FreeConsole] }
|
|
|
|
|
|
|
|
|
|
|
|
int tcgetattr(int fd, struct termios* termios_p)
|
|
|
|
{
|
|
|
|
if(fd > 2)
|
|
|
|
return -1;
|
|
|
|
HANDLE h = std_h[fd];
|
|
|
|
|
|
|
|
DWORD mode;
|
|
|
|
GetConsoleMode(h, &mode);
|
|
|
|
termios_p->c_lflag = mode & (ENABLE_ECHO_INPUT|ENABLE_LINE_INPUT);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int tcsetattr(int fd, int /* optional_actions */, const struct termios* termios_p)
|
|
|
|
{
|
|
|
|
if(fd > 2)
|
|
|
|
return -1;
|
|
|
|
HANDLE h = std_h[fd];
|
|
|
|
SetConsoleMode(h, (DWORD)termios_p->c_lflag);
|
|
|
|
FlushConsoleInputBuffer(h);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int poll(struct pollfd /* fds */[], int /* nfds */, int /* timeout */)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// thread
|
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
__declspec(naked) pthread_t pthread_self(void)
|
2004-05-30 02:46:58 +02:00
|
|
|
{ __asm jmp dword ptr [GetCurrentThread] }
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
int pthread_getschedparam(pthread_t thread, int* policy, struct sched_param* param)
|
|
|
|
{
|
|
|
|
if(policy)
|
|
|
|
{
|
|
|
|
DWORD pc = GetPriorityClass(GetCurrentProcess());
|
|
|
|
*policy = (pc >= HIGH_PRIORITY_CLASS)? SCHED_FIFO : SCHED_RR;
|
|
|
|
}
|
|
|
|
if(param)
|
|
|
|
{
|
2004-08-28 13:14:23 +02:00
|
|
|
const HANDLE hThread = cast_to_HANDLE((intptr_t)thread);
|
2004-03-03 00:56:51 +01:00
|
|
|
param->sched_priority = GetThreadPriority(hThread);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param* param)
|
|
|
|
{
|
2004-12-07 04:01:12 +01:00
|
|
|
const int pri = param->sched_priority;
|
|
|
|
|
|
|
|
// additional boost for policy == SCHED_FIFO
|
|
|
|
DWORD pri_class = NORMAL_PRIORITY_CLASS;
|
2004-03-03 00:56:51 +01:00
|
|
|
if(policy == SCHED_FIFO)
|
2004-12-07 04:01:12 +01:00
|
|
|
{
|
|
|
|
pri_class = HIGH_PRIORITY_CLASS;
|
|
|
|
if(pri == 2)
|
|
|
|
pri_class = REALTIME_PRIORITY_CLASS;
|
|
|
|
}
|
|
|
|
SetPriorityClass(GetCurrentProcess(), pri_class);
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-12-07 04:01:12 +01:00
|
|
|
// choose fixed Windows values from pri
|
2004-08-28 13:14:23 +02:00
|
|
|
const HANDLE hThread = cast_to_HANDLE((intptr_t)thread);
|
2004-12-07 04:01:12 +01:00
|
|
|
SetThreadPriority(hThread, pri);
|
2004-03-03 00:56:51 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-06-21 16:22:07 +02:00
|
|
|
struct ThreadParam
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-06-21 16:22:07 +02:00
|
|
|
void*(*func)(void*);
|
|
|
|
void* user_arg;
|
2004-09-02 04:47:11 +02:00
|
|
|
ThreadParam(void*(*_func)(void*), void* _user_arg)
|
|
|
|
: func(_func), user_arg(_user_arg) {}
|
2004-06-21 16:22:07 +02:00
|
|
|
};
|
|
|
|
|
2004-09-02 04:47:11 +02:00
|
|
|
|
|
|
|
// trampoline to switch calling convention.
|
|
|
|
// param points to a heap-allocated ThreadParam (see pthread_create).
|
|
|
|
static unsigned __stdcall thread_start(void* param)
|
2004-06-21 16:22:07 +02:00
|
|
|
{
|
2004-09-02 04:47:11 +02:00
|
|
|
ThreadParam* f = (ThreadParam*)param;
|
|
|
|
void*(*func)(void*) = f->func;
|
|
|
|
void* user_arg = f->user_arg;
|
|
|
|
delete f;
|
2004-06-21 16:22:07 +02:00
|
|
|
|
2004-09-19 19:27:03 +02:00
|
|
|
// workaround for stupid "void* -> unsigned cast" warning
|
|
|
|
union { void* p; unsigned u; } v;
|
|
|
|
v.p = func(user_arg);
|
|
|
|
return v.u;
|
2004-06-21 16:22:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int pthread_create(pthread_t* thread, const void* attr, void*(*func)(void*), void* user_arg)
|
|
|
|
{
|
|
|
|
UNUSED(attr);
|
|
|
|
|
2004-09-02 04:47:11 +02:00
|
|
|
// notes:
|
|
|
|
// - don't call via asm: _beginthreadex might be a func ptr (if DLL CRT).
|
|
|
|
// - don't stack-allocate param: thread_start might not be called
|
|
|
|
// in the new thread before we exit this stack frame.
|
|
|
|
ThreadParam* param = new ThreadParam(func, user_arg);
|
2004-08-16 16:30:18 +02:00
|
|
|
*thread = (pthread_t)_beginthreadex(0, 0, thread_start, (void*)param, 0, 0);
|
2004-06-21 16:22:07 +02:00
|
|
|
return 0;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
void pthread_cancel(pthread_t thread)
|
|
|
|
{
|
|
|
|
HANDLE hThread = cast_to_HANDLE((intptr_t)thread);
|
|
|
|
TerminateThread(hThread, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void pthread_join(pthread_t thread, void** value_ptr)
|
|
|
|
{
|
|
|
|
HANDLE hThread = cast_to_HANDLE((intptr_t)thread);
|
|
|
|
|
|
|
|
// clean exit
|
|
|
|
if(WaitForSingleObject(hThread, 100) == WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
if(value_ptr)
|
|
|
|
GetExitCodeThread(hThread, (LPDWORD)value_ptr);
|
|
|
|
}
|
|
|
|
// force close
|
|
|
|
else
|
|
|
|
TerminateThread(hThread, 0);
|
|
|
|
if(value_ptr)
|
|
|
|
*value_ptr = (void*)-1;
|
|
|
|
CloseHandle(hThread);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-24 19:27:51 +02:00
|
|
|
// DeleteCriticalSection currently doesn't complain if we double-free
|
|
|
|
// (e.g. user calls destroy() and static initializer atexit runs),
|
|
|
|
// and dox are ambiguous.
|
|
|
|
|
2005-01-23 18:57:52 +01:00
|
|
|
// note: pthread_mutex_t must not be an opaque struct, because the
|
|
|
|
// initializer returns pthread_mutex_t directly and CRITICAL_SECTIONS
|
|
|
|
// shouldn't be copied.
|
|
|
|
//
|
|
|
|
// note: must not use new/malloc to allocate the critical section
|
|
|
|
// because mmgr.cpp uses a mutex and must not be called to allocate
|
|
|
|
// anything before it is initialized.
|
2004-08-24 19:27:51 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
pthread_mutex_t pthread_mutex_initializer()
|
|
|
|
{
|
2005-01-23 18:57:52 +01:00
|
|
|
CRITICAL_SECTION* cs = (CRITICAL_SECTION*)HeapAlloc(GetProcessHeap(), 0, sizeof(CRITICAL_SECTION));
|
|
|
|
InitializeCriticalSection(cs);
|
|
|
|
return (pthread_mutex_t)cs;
|
2004-08-24 19:27:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_mutex_destroy(pthread_mutex_t* m)
|
|
|
|
{
|
2005-01-23 18:57:52 +01:00
|
|
|
CRITICAL_SECTION* cs = (CRITICAL_SECTION*)(*m);
|
|
|
|
DeleteCriticalSection(cs);
|
|
|
|
HeapFree(GetProcessHeap(), 0, cs);
|
2004-08-24 19:27:51 +02:00
|
|
|
return 0;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t*)
|
|
|
|
{
|
2005-01-23 18:57:52 +01:00
|
|
|
*m = pthread_mutex_initializer();
|
2004-08-24 19:27:51 +02:00
|
|
|
return 0;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_mutex_lock(pthread_mutex_t* m)
|
|
|
|
{
|
2005-01-23 18:57:52 +01:00
|
|
|
CRITICAL_SECTION* cs = (CRITICAL_SECTION*)(*m);
|
|
|
|
EnterCriticalSection(cs);
|
2004-08-24 19:27:51 +02:00
|
|
|
return 0;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_mutex_trylock(pthread_mutex_t* m)
|
|
|
|
{
|
2005-01-23 18:57:52 +01:00
|
|
|
CRITICAL_SECTION* cs = (CRITICAL_SECTION*)(*m);
|
|
|
|
BOOL got_it = TryEnterCriticalSection(cs);
|
2004-08-24 19:27:51 +02:00
|
|
|
return got_it? 0 : -1;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_mutex_unlock(pthread_mutex_t* m)
|
|
|
|
{
|
2005-01-23 18:57:52 +01:00
|
|
|
CRITICAL_SECTION* cs = (CRITICAL_SECTION*)(*m);
|
|
|
|
LeaveCriticalSection(cs);
|
2004-08-24 19:27:51 +02:00
|
|
|
return 0;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_mutex_timedlock(pthread_mutex_t* m, const struct timespec* abs_timeout)
|
|
|
|
{
|
2005-01-23 18:57:52 +01:00
|
|
|
UNUSED(m);
|
|
|
|
UNUSED(abs_timeout);
|
2004-08-24 19:27:51 +02:00
|
|
|
return -ENOSYS;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-01-07 02:10:00 +01:00
|
|
|
|
|
|
|
|
|
|
|
int sem_init(sem_t* sem, int pshared, unsigned value)
|
|
|
|
{
|
2005-01-23 18:57:52 +01:00
|
|
|
UNUSED(pshared);
|
2005-01-07 02:10:00 +01:00
|
|
|
*sem = (uintptr_t)CreateSemaphore(0, (LONG)value, 0x7fffffff, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sem_post(sem_t* sem)
|
|
|
|
{
|
|
|
|
ReleaseSemaphore((HANDLE)*sem, 1, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sem_wait(sem_t* sem)
|
|
|
|
{
|
|
|
|
WaitForSingleObject((HANDLE)*sem, INFINITE);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sem_destroy(sem_t* sem)
|
|
|
|
{
|
|
|
|
CloseHandle((HANDLE)*sem);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// memory mapping
|
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
2004-12-10 00:12:02 +01:00
|
|
|
void* mmap(void* user_start, size_t len, int prot, int flags, int fd, off_t offset)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-08-27 02:29:46 +02:00
|
|
|
{
|
2004-07-12 16:25:39 +02:00
|
|
|
WIN_SAVE_LAST_ERROR;
|
|
|
|
|
2004-08-27 02:29:46 +02:00
|
|
|
// assume fd = -1 (requesting mapping backed by page file),
|
|
|
|
// so that we notice invalid file handles below.
|
|
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
|
|
if(fd != -1)
|
|
|
|
{
|
2004-08-28 13:14:23 +02:00
|
|
|
hFile = cast_to_HANDLE(_get_osfhandle(fd));
|
2004-08-27 02:29:46 +02:00
|
|
|
if(hFile == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
debug_warn("mmap: invalid file handle");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MapView.. will choose start address unless MAP_FIXED was specified.
|
|
|
|
void* start = 0;
|
|
|
|
if(flags & MAP_FIXED)
|
|
|
|
{
|
|
|
|
start = user_start;
|
|
|
|
if(start == 0) // assert below would fire
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
// figure out access rights.
|
|
|
|
// note: reads are always allowed (Win32 limitation).
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-08-27 02:29:46 +02:00
|
|
|
SECURITY_ATTRIBUTES sec = { sizeof(SECURITY_ATTRIBUTES), (void*)0, FALSE };
|
|
|
|
DWORD flProtect = PAGE_READONLY;
|
|
|
|
DWORD dwAccess = FILE_MAP_READ;
|
|
|
|
|
|
|
|
// .. no access: not possible on Win32.
|
|
|
|
if(prot == PROT_NONE)
|
|
|
|
goto fail;
|
|
|
|
// .. write or read/write (Win32 doesn't support write-only)
|
2004-03-03 00:56:51 +01:00
|
|
|
if(prot & PROT_WRITE)
|
|
|
|
{
|
|
|
|
flProtect = PAGE_READWRITE;
|
|
|
|
|
2004-08-27 02:29:46 +02:00
|
|
|
const bool shared = (flags & MAP_SHARED ) != 0;
|
|
|
|
const bool priv = (flags & MAP_PRIVATE) != 0;
|
|
|
|
// .. both aren't allowed
|
|
|
|
if(shared && priv)
|
|
|
|
goto fail;
|
|
|
|
// .. changes are shared & written to file
|
|
|
|
else if(shared)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-08-27 02:29:46 +02:00
|
|
|
sec.bInheritHandle = TRUE;
|
2004-03-03 00:56:51 +01:00
|
|
|
dwAccess = FILE_MAP_ALL_ACCESS;
|
|
|
|
}
|
2004-08-27 02:29:46 +02:00
|
|
|
// .. private copy-on-write mapping
|
|
|
|
else if(priv)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
|
|
|
flProtect = PAGE_WRITECOPY;
|
|
|
|
dwAccess = FILE_MAP_COPY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-27 02:29:46 +02:00
|
|
|
// now actually map.
|
|
|
|
const DWORD len_hi = (DWORD)((u64)len >> 32);
|
|
|
|
// careful! language doesn't allow shifting 32-bit types by 32 bits.
|
|
|
|
const DWORD len_lo = (DWORD)len & 0xffffffff;
|
|
|
|
const HANDLE hMap = CreateFileMapping(hFile, &sec, flProtect, len_hi, len_lo, (LPCSTR)0);
|
|
|
|
if(hMap == INVALID_HANDLE_VALUE)
|
|
|
|
// bail now so that MapView.. doesn't overwrite the last error value.
|
|
|
|
goto fail;
|
2004-03-03 00:56:51 +01:00
|
|
|
void* ptr = MapViewOfFileEx(hMap, dwAccess, len_hi, offset, len_lo, start);
|
|
|
|
|
2004-08-27 02:29:46 +02:00
|
|
|
// free the mapping object now, so that we don't have to hold on to its
|
|
|
|
// handle until munmap(). it's not actually released yet due to the
|
|
|
|
// reference held by MapViewOfFileEx (if it succeeded).
|
|
|
|
if(hMap != INVALID_HANDLE_VALUE) // avoid "invalid handle" error
|
|
|
|
CloseHandle(hMap);
|
|
|
|
|
|
|
|
if(!ptr)
|
|
|
|
// bail now, before the last error value is restored,
|
|
|
|
// but after freeing the mapping object.
|
|
|
|
goto fail;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-08-27 02:29:46 +02:00
|
|
|
assert(!(flags & MAP_FIXED) || (ptr == start));
|
|
|
|
// fixed => ptr = start
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-07-12 16:25:39 +02:00
|
|
|
WIN_RESTORE_LAST_ERROR;
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
return ptr;
|
2004-08-27 02:29:46 +02:00
|
|
|
}
|
|
|
|
fail:
|
|
|
|
return MAP_FAILED;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-12-10 00:12:02 +01:00
|
|
|
int munmap(void* start, size_t len)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-08-27 02:29:46 +02:00
|
|
|
UNUSED(len);
|
2004-07-12 16:25:39 +02:00
|
|
|
BOOL ok = UnmapViewOfFile(start);
|
|
|
|
return ok? 0 : -1;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int uname(struct utsname* un)
|
|
|
|
{
|
|
|
|
static OSVERSIONINFO vi;
|
|
|
|
vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
|
|
GetVersionEx(&vi);
|
|
|
|
|
|
|
|
// OS implementation name
|
|
|
|
const char* family = "??";
|
|
|
|
int ver = (vi.dwMajorVersion << 8) | vi.dwMinorVersion;
|
|
|
|
if(vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
|
|
|
|
family = (ver == 0x045a)? "ME" : "9x";
|
|
|
|
if(vi.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
|
|
|
{
|
|
|
|
if(ver == 0x0500)
|
|
|
|
family = "2k";
|
|
|
|
else if(ver == 0x0501)
|
|
|
|
family = "XP";
|
|
|
|
else
|
|
|
|
family = "NT";
|
|
|
|
}
|
|
|
|
sprintf(un->sysname, "Win%s", family);
|
|
|
|
|
|
|
|
// release info
|
|
|
|
const char* vs = vi.szCSDVersion;
|
|
|
|
int sp;
|
|
|
|
if(sscanf(vs, "Service Pack %d", &sp) == 1)
|
|
|
|
sprintf(un->release, "SP %d", sp);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const char* release = "";
|
|
|
|
if(vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
|
|
|
|
{
|
|
|
|
if(!strcmp(vs, " C"))
|
|
|
|
release = "OSR2";
|
|
|
|
else if(!strcmp(vs, " A"))
|
|
|
|
release = "SE";
|
|
|
|
}
|
|
|
|
strcpy(un->release, release);
|
|
|
|
}
|
|
|
|
|
|
|
|
// version
|
|
|
|
sprintf(un->version, "%lu.%02lu.%lu", vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber & 0xffff);
|
|
|
|
|
|
|
|
// node name
|
|
|
|
DWORD buf_size = sizeof(un->nodename);
|
2004-07-12 16:25:39 +02:00
|
|
|
DWORD last_err = GetLastError();
|
|
|
|
BOOL ok = GetComputerName(un->nodename, &buf_size);
|
|
|
|
// GetComputerName sets last error even on success - suppress.
|
|
|
|
if(ok)
|
|
|
|
SetLastError(last_err);
|
|
|
|
else
|
|
|
|
debug_warn("GetComputerName failed");
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
// hardware type
|
|
|
|
static SYSTEM_INFO si;
|
|
|
|
GetSystemInfo(&si);
|
|
|
|
if(si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
|
|
|
|
strcpy(un->machine, "AMD64");
|
|
|
|
else
|
|
|
|
strcpy(un->machine, "IA-32");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-05-18 02:38:39 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
long sysconf(int name)
|
|
|
|
{
|
|
|
|
// used by _SC_*_PAGES
|
|
|
|
static DWORD page_size;
|
2004-05-18 02:38:39 +02:00
|
|
|
static BOOL (WINAPI *pGlobalMemoryStatusEx)(MEMORYSTATUSEX*);
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-18 02:38:39 +02:00
|
|
|
ONCE(
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-05-18 02:38:39 +02:00
|
|
|
// get page size
|
|
|
|
// (used by _SC_PAGESIZE and _SC_*_PAGES)
|
2004-03-03 00:56:51 +01:00
|
|
|
SYSTEM_INFO si;
|
|
|
|
GetSystemInfo(&si); // can't fail => page_size always > 0.
|
2004-05-18 02:38:39 +02:00
|
|
|
page_size = si.dwPageSize;
|
|
|
|
|
2004-12-01 22:37:01 +01:00
|
|
|
// import GlobalMemoryStatusEx - it's not defined by the VC6 PSDK.
|
|
|
|
// used by _SC_*_PAGES if available (provides better results).
|
2004-05-18 02:38:39 +02:00
|
|
|
const HMODULE hKernel32Dll = LoadLibrary("kernel32.dll");
|
|
|
|
*(void**)&pGlobalMemoryStatusEx = GetProcAddress(hKernel32Dll, "GlobalMemoryStatusEx");
|
|
|
|
FreeLibrary(hKernel32Dll);
|
|
|
|
// make sure the reference is released so BoundsChecker
|
|
|
|
// doesn't complain. it won't actually be unloaded anyway -
|
|
|
|
// there is at least one other reference.
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
switch(name)
|
|
|
|
{
|
|
|
|
case _SC_PAGESIZE:
|
|
|
|
return page_size;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
case _SC_PHYS_PAGES:
|
|
|
|
case _SC_AVPHYS_PAGES:
|
2004-05-08 03:11:51 +02:00
|
|
|
{
|
2004-05-18 02:38:39 +02:00
|
|
|
u64 total_phys_mem;
|
|
|
|
u64 avail_phys_mem;
|
|
|
|
|
|
|
|
// first try GlobalMemoryStatus - cannot fail.
|
|
|
|
// override its results if GlobalMemoryStatusEx is available.
|
|
|
|
MEMORYSTATUS ms;
|
|
|
|
GlobalMemoryStatus(&ms);
|
|
|
|
// can't fail.
|
|
|
|
total_phys_mem = ms.dwTotalPhys;
|
|
|
|
avail_phys_mem = ms.dwAvailPhys;
|
|
|
|
|
|
|
|
// newer API is available: use it to report correct results
|
|
|
|
// (no overflow or wraparound) on systems with > 4 GB of memory.
|
|
|
|
MEMORYSTATUSEX mse = { sizeof(mse) };
|
2004-12-01 22:37:01 +01:00
|
|
|
if(pGlobalMemoryStatusEx && pGlobalMemoryStatusEx(&mse))
|
2004-05-18 02:38:39 +02:00
|
|
|
{
|
|
|
|
total_phys_mem = mse.ullTotalPhys;
|
|
|
|
avail_phys_mem = mse.ullAvailPhys;
|
|
|
|
}
|
2004-12-01 22:37:01 +01:00
|
|
|
// else: not an error, since this isn't available before Win2k / XP.
|
|
|
|
// we have results from GlobalMemoryStatus anyway.
|
2004-05-18 02:38:39 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
if(name == _SC_PHYS_PAGES)
|
2005-01-23 18:57:52 +01:00
|
|
|
return (long)(round_up((uintptr_t)total_phys_mem, 2*MiB) / page_size);
|
2004-05-08 03:11:51 +02:00
|
|
|
// Richter, "Programming Applications for Windows":
|
|
|
|
// reported value doesn't include non-paged pool reserved
|
|
|
|
// during boot; it's not considered available to kernel.
|
2005-01-23 18:57:52 +01:00
|
|
|
// it's 528 KiB on my 512 MiB machine (WinXP and Win2k).
|
2004-05-08 03:11:51 +02:00
|
|
|
else
|
2004-05-18 02:38:39 +02:00
|
|
|
return (long)(avail_phys_mem / page_size);
|
2004-05-08 03:11:51 +02:00
|
|
|
}
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|