- Updated posix/win32 stdlib namespacing

- Process stdlib
- Fix to void expression blocks
This commit is contained in:
Christoffer Lerno
2023-06-18 22:12:01 +02:00
committed by Christoffer Lerno
parent 5c9eb264e8
commit ab93389031
22 changed files with 835 additions and 187 deletions

View File

@@ -38,6 +38,7 @@ jobs:
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\fannkuch-redux.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\contextfree\boolerr.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\process.c3
- name: Build testproject
@@ -254,6 +255,7 @@ jobs:
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
../build/c3c compile-run examples/process.c3
- name: Compile run unit tests
run: |
@@ -360,6 +362,7 @@ jobs:
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
../build/c3c compile-run examples/process.c3
- name: Compile run unit tests
run: |
@@ -430,6 +433,7 @@ jobs:
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/process.c3
../build/c3c compile-run examples/load_world.c3
- name: Compile run unit tests

View File

@@ -138,6 +138,7 @@ const NETBSD_LIBC @builtin = env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == NET
const WASI_LIBC @builtin = env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == WASI;
const WASM_NOLIBC @builtin = !env::COMPILER_LIBC_AVAILABLE && env::ARCH_TYPE == ArchType.WASM32 || env::ARCH_TYPE == ArchType.WASM64;
const bool NO_LIBC = !env::COMPILER_LIBC_AVAILABLE;
const bool LIBC = env::COMPILER_LIBC_AVAILABLE;
const bool WIN32 = OS_TYPE == WIN32;
const bool DARWIN = OS_TYPE == IOS || OS_TYPE == MACOS || OS_TYPE == TVOS || OS_TYPE == WATCHOS;
const bool LINUX = OS_TYPE == LINUX;

View File

@@ -11,6 +11,11 @@ fn File! open_path(Path path, String mode)
return { .file = os::native_fopen(path.as_str(), mode) };
}
fn File from_libc(CFile file)
{
return { .file = file };
}
/**
* @require file.file != null
**/

View File

@@ -21,7 +21,7 @@ macro void! native_chdir(Path path)
@pool()
{
// TODO improve with better error handling.
if (win32::win32_SetCurrentDirectoryW(path.as_str().to_temp_utf16()!!)) return;
if (win32::setCurrentDirectoryW(path.as_str().to_temp_utf16()!!)) return;
};
return IoError.GENERAL_ERROR?;
$default:

View File

@@ -29,8 +29,8 @@ fn void*! native_fopen(String filename, String mode) @inline
$else
@pool()
{
$if env::os_is_win32():
void* file = (CFile)_wfopen(filename.to_temp_utf16(), filename.to_temp_utf16())!;
$if env::WIN32:
void* file = libc::_wfopen(filename.to_temp_utf16(), filename.to_temp_utf16())!;
$else
void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy());
$endif
@@ -52,7 +52,7 @@ fn void*! native_freopen(void* file, String filename, String mode) @inline
@pool()
{
$if env::os_is_win32():
file = _wfreopen(filename.to_temp_utf16(), mode.to_temp_utf16(), file)!;
file = libc::_wfreopen(filename.to_temp_utf16(), mode.to_temp_utf16(), file)!;
$else
file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file);
$endif
@@ -67,12 +67,7 @@ fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline
if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode);
unreachable("Tried to call fseek without support.");
$else
$if env::os_is_win32():
bool success = _fseeki64(file, (long)offset, (int)seek_mode) == 0;
$else
bool success = libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode) == 0;
$endif
if (!success) return file_seek_errno()?;
if (libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode)) return file_seek_errno()?;
$endif
}
@@ -82,13 +77,8 @@ fn usz! native_ftell(CFile file) @inline
if (native_ftell_fn) return native_ftell_fn(file);
unreachable("Tried to call ftell without support.");
$else
$if env::os_is_win32():
long index = _ftelli64(file);
return index >= 0 ? index : file_seek_errno()?;
$else
SeekIndex index = libc::ftell(file);
return index >= 0 ? index : file_seek_errno()?;
$endif
long index = libc::ftell(file);
return index >= 0 ? (usz)index : file_seek_errno()?;
$endif
}
@@ -159,11 +149,5 @@ macro anyfault file_seek_errno() @local
}
}
// Win functions
extern fn void* _wfopen(Char16*, Char16*) @local @if(WIN32_LIBC);
extern fn void* _wfreopen(Char16*, Char16*, CFile) @local @if(WIN32_LIBC);
extern fn int _fseeki64(CFile, long, int) @local @if(WIN32_LIBC);
extern fn long _ftelli64(CFile) @local @if(WIN32_LIBC);
// Posix functions
extern fn CInt access(ZString path, CInt mode) @if(POSIX_LIBC);

View File

@@ -30,7 +30,7 @@ fn usz! native_file_size(String path)
{
Char16[] path16 = path.to_temp_utf16()!;
Win32_FILE_ATTRIBUTE_DATA data;
win32::win32_GetFileAttributesExW(path16, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
win32::getFileAttributesExW(path16, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
Win32_LARGE_INTEGER size;
size.lowPart = data.nFileSizeLow;
size.highPart = data.nFileSizeHigh;
@@ -42,7 +42,7 @@ fn bool native_file_or_dir_exists(String path)
{
@pool()
{
return (bool)win32::win32_PathFileExistsW(path.to_temp_utf16()) ?? false;
return (bool)win32::pathFileExistsW(path.to_temp_utf16()) ?? false;
};
}
@@ -64,11 +64,11 @@ fn void! native_rmtree(Path path)
Win32_WIN32_FIND_DATAW find_data;
String s = path.as_str().tconcat("\\*");
Win32_HANDLE find = win32::win32_FindFirstFileW(s.to_utf16(mem::temp()), &find_data)!;
Win32_HANDLE find = win32::findFirstFileW(s.to_utf16(mem::temp()), &find_data)!;
if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?;
defer win32::win32_FindClose(find);
defer win32::findClose(find);
do
{
String filename = string::from_zutf16(&find_data.cFileName, mem::temp())!;
@@ -80,9 +80,9 @@ fn void! native_rmtree(Path path)
}
else
{
win32::win32_DeleteFileW(file_path.as_str().to_utf16(mem::temp()));
win32::deleteFileW(file_path.as_str().to_utf16(mem::temp()));
}
} while (win32::win32_FindNextFileW(find, &find_data) != 0);
} while (win32::findNextFileW(find, &find_data) != 0);
os::native_rmdir(path)!;
}
@@ -90,10 +90,10 @@ fn Path! native_temp_directory(Allocator* using = mem::heap())
{
@stack_mem(256; Allocator* mem)
{
Win32_DWORD len = win32::win32_GetTempPathW(0, null);
Win32_DWORD len = win32::getTempPathW(0, null);
if (!len) return IoError.GENERAL_ERROR?;
Char16[] buff = malloc(Char16, len + 1, .using = mem);
if (!win32::win32_GetTempPathW(len, buff)) return IoError.GENERAL_ERROR?;
if (!win32::getTempPathW(len, buff)) return IoError.GENERAL_ERROR?;
return path::new(string::from_utf16(buff[:len], .using = mem), using);
};
}

View File

@@ -29,8 +29,8 @@ macro bool! native_mkdir(Path path, MkdirPermissions permissions)
@pool()
{
// TODO security attributes
if (win32::win32_CreateDirectoryW(path.as_str().to_temp_utf16()!!, null)) return true;
switch (win32::win32_GetLastError())
if (win32::createDirectoryW(path.as_str().to_temp_utf16()!!, null)) return true;
switch (win32::getLastError())
{
case win32::ERROR_ACCESS_DENIED:
return IoError.NO_PERMISSION?;

View File

@@ -26,8 +26,8 @@ macro bool! native_rmdir(Path path)
$case WIN32_LIBC:
@pool()
{
if (win32::win32_RemoveDirectoryW(path.as_str().to_temp_utf16()!!)) return true;
switch (win32::win32_GetLastError())
if (win32::removeDirectoryW(path.as_str().to_temp_utf16()!!)) return true;
switch (win32::getLastError())
{
case win32::ERROR_ACCESS_DENIED:
return IoError.NO_PERMISSION?;

View File

@@ -37,14 +37,18 @@ def JmpBuf = uptr[$$JMP_BUF_SIZE];
module libc @if(env::COMPILER_LIBC_AVAILABLE);
extern fn void abort();
extern fn double atof(char* str);
extern fn int atoi(char* str);
extern fn CLongLong atoll(char* str);
extern fn CInt close(CInt fd) @if(!env::WIN32);
extern fn CFile fdopen(CInt fd, char* mode) @if(!env::WIN32);
extern fn void atexit(TerminateFunction f);
extern fn double strtod(char* str, char** endptr);
extern fn CLong strtol(char* str, char** endptr, int base);
extern fn CULong stroul(char* str, char** endptr, int base);
extern fn void abort();
extern fn void atexit(TerminateFunction f);
extern fn long labs(long x);
extern fn LongDivResult ldiv(long number, long denom);
extern fn void exit(int status);
extern fn ZString getenv(ZString name);
extern fn int setenv(ZString name, ZString value, int overwrite);
@@ -53,8 +57,6 @@ extern fn int system(char* str);
extern fn void bsearch(void* key, void *base, usz items, usz size, CompareFunction compare);
extern fn void qsort(void* base, usz items, usz size, CompareFunction compare);
extern fn DivResult div(int numer, int denom);
extern fn long labs(long x);
extern fn LongDivResult ldiv(long number, long denom);
extern fn int rand();
extern fn void srand(uint seed);
@@ -106,9 +108,9 @@ extern fn CFile fopen(ZString filename, ZString mode);
extern fn usz fread(void* ptr, usz size, usz nmemb, CFile stream);
extern fn CFile freopen(ZString filename, ZString mode, CFile stream);
extern fn CFile fmemopen(void* ptr, usz size, ZString mode);
extern fn int fseek(CFile stream, SeekIndex offset, int whence);
extern fn int fseek(CFile stream, SeekIndex offset, int whence) @if(!env::WIN32);
extern fn int fsetpos(CFile stream, Fpos* pos);
extern fn SeekIndex ftell(CFile stream);
extern fn SeekIndex ftell(CFile stream) @if(!env::WIN32);
extern fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream);
extern fn int remove(char* filename);
extern fn int rename(char* old_name, char* new_name);
@@ -134,10 +136,10 @@ extern fn int puts(char* str);
extern fn int ungetc(int c, CFile stream);
extern fn void perror(char* str);
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
extern fn CInt fileno(CFile strem) @if(!env::WIN32);
extern fn int timespec_get(TimeSpec* ts, int base);
extern fn int nanosleep(TimeSpec* req, TimeSpec* remaining);
extern fn isz read(CInt fd, void* buf, usz nbyte);
extern fn ZString asctime(Tm *timeptr);
extern fn Clock_t clock();
extern fn ZString ctime(Time_t *timer);
@@ -152,6 +154,10 @@ def SignalFunction = fn void(int);
extern fn SignalFunction signal(int sig, SignalFunction function);
// Incomplete
const CInt STDIN_FD = 0;
const CInt STDOUT_FD = 1;
const CInt STDERR_FD = 2;
module libc @if(LINUX_LIBC);
extern CFile __stdin @extern("stdin");
extern CFile __stdout @extern("stdout");
@@ -177,9 +183,9 @@ module libc @if(WIN32_LIBC);
extern fn CFile __acrt_iob_func(CInt c);
extern fn usz _msize(void* ptr);
macro usz malloc_size(void* ptr) => _msize(ptr);
macro CFile stdin() => __acrt_iob_func(0);
macro CFile stdout() => __acrt_iob_func(1);
macro CFile stderr() => __acrt_iob_func(2);
macro CFile stdin() => __acrt_iob_func(STDIN_FD);
macro CFile stdout() => __acrt_iob_func(STDOUT_FD);
macro CFile stderr() => __acrt_iob_func(STDERR_FD);
extern fn Tm* _gmtime64_s(Tm* buf, Time_t *timer);
extern fn Tm* _localtime64_s(Tm* buf, Time_t *timer);
extern fn void _get_timezone(CLong *timezone);
@@ -189,9 +195,9 @@ extern fn Time_t mktime(Tm *timeptr) @extern("_mktime64");
extern fn Time_t timegm(Tm *timeptr) @extern("_mkgmtime64");
module libc @if(env::COMPILER_LIBC_AVAILABLE && !env::WIN32 && !env::LINUX && !env::DARWIN);
macro CFile stdin() { return (CFile*)(uptr)0; }
macro CFile stdout() { return (CFile*)(uptr)1; }
macro CFile stderr() { return (CFile*)(uptr)2; }
macro CFile stdin() { return (CFile*)(uptr)STDIN_FD; }
macro CFile stdout() { return (CFile*)(uptr)STDOUT_FD; }
macro CFile stderr() { return (CFile*)(uptr)STDERR_FD; }
module libc @if(env::COMPILER_LIBC_AVAILABLE && !env::WIN32);
extern fn Tm* gmtime_r(Time_t *timer, Tm* buf);

10
lib/std/libc/os/win32.c3 Normal file
View File

@@ -0,0 +1,10 @@
module libc @if(env::WIN32);
extern fn int _fseeki64(CFile, long, int); def fseek = _fseeki64 @if(env::WIN32);
extern fn long _ftelli64(CFile); def ftell = _ftelli64 @if(env::WIN32);
extern fn CFile _wfopen(Char16*, Char16*);
extern fn CFile _wfreopen(Char16*, Char16*, CFile);
extern fn int _close(CInt fd); def close = _close;
extern fn CFile _fdopen(CInt fd, char* mode); def fdopen = _fdopen;
extern fn CInt _fileno(CFile stream); def fileno = _fileno;

View File

@@ -1,6 +1,11 @@
module std::os::posix @if(POSIX_LIBC);
import libc;
extern fn int rmdir(ZString);
extern fn int mkdir(ZString, ushort mode_t);
extern fn int chdir(ZString);
extern fn ZString getcwd(char* pwd, usz len);
def Mode_t = uint;
def Pid_t = int;
extern fn CInt pipe(CInt[2]* pipes);
extern fn CFile fdopen(CInt fd, ZString mode);

View File

@@ -0,0 +1,3 @@
module std::os::posix;
extern ZString* environ;

View File

@@ -0,0 +1,49 @@
module std::os::posix @if(POSIX_LIBC);
struct Posix_spawn_file_actions_t
{
int __allocated;
int __used;
void* __actions;
int[16] __pad;
}
struct Posix_spawnattr_t
{
void*[42] opaque;
}
extern fn CInt posix_spawn_file_actions_init(Posix_spawn_file_actions_t *file_actions);
extern fn CInt posix_spawn_file_actions_destroy(Posix_spawn_file_actions_t *file_actions);
extern fn CInt posix_spawn_file_actions_addclose(Posix_spawn_file_actions_t *file_actions, CInt fd);
extern fn CInt posix_spawn_file_actions_adddup2(Posix_spawn_file_actions_t *file_actions, CInt fd, CInt newfd);
def spawn_file_actions_init = posix_spawn_file_actions_init;
def spawn_file_actions_destroy = posix_spawn_file_actions_destroy;
def spawn_file_actions_addclose = posix_spawn_file_actions_addclose;
def spawn_file_actions_adddup2 = posix_spawn_file_actions_adddup2;
extern fn CInt posix_spawnp(Pid_t* pid, char* file, Posix_spawn_file_actions_t* file_actions,
Posix_spawnattr_t* attrp, ZString* argv, ZString* envp);
extern fn CInt posix_spawn(Pid_t* pid, char* file, Posix_spawn_file_actions_t* file_actions,
Posix_spawnattr_t* attrp, ZString* argv, ZString* envp);
def spawnp = posix_spawnp;
def spawn = posix_spawn;
extern fn CInt kill(Pid_t pid, CInt sig);
extern fn Pid_t waitpid(Pid_t pid, CInt* stat_loc, int options);
macro CInt wEXITSTATUS(CInt status) => (status & 0xff00) >> 8;
macro CInt wTERMSIG(CInt status) => status & 0x7f;
macro CInt wSTOPSIG(CInt status) => wEXITSTATUS(status);
macro bool wIFEXITED(CInt status) => wTERMSIG(status) == 0;
macro bool wIFSIGNALED(CInt status) => ((ichar) ((status & 0x7f) + 1) >> 1) > 0;
macro bool wIFSTOPPED(CInt status) => (status & 0xff) == 0x7f;
macro bool wIFCONTINUED(CInt status) => status == __W_CONTINUED;
macro CInt wWCOREDUMP(CInt status) => status & __WCOREFLAG;
macro CInt w_EXITCODE(CInt ret, CInt sig) => (ret << 8) | sig;
macro CInt w_STOPCODE(CInt sig) => (sig << 8) | 0x7f;
const CInt __WCOREFLAG = 0x80;
const CInt __W_CONTINUED = 0xffff;
const CInt WNOHANG = 1;
const CInt WUNTRACES = 2;

475
lib/std/os/subprocess.c3 Normal file
View File

@@ -0,0 +1,475 @@
module std::os::process @if(env::WIN32 || env::POSIX);
import std::io::file;
import libc;
// This code is based on https://github.com/sheredom/subprocess.h
fault SubProcessResult
{
FAILED_TO_CREATE_PIPE,
FAILED_TO_OPEN_STDIN,
FAILED_TO_OPEN_STDOUT,
FAILED_TO_OPEN_STDERR,
FAILED_TO_START_PROCESS,
FAILED_TO_INITIALIZE_ACTIONS,
PROCESS_JOIN_FAILED,
PROCESS_TERMINATION_FAILED,
READ_FAILED,
}
struct SubProcess
{
CFile stdin_file;
CFile stdout_file;
CFile stderr_file;
Win32_HANDLE hProcess @if(env::WIN32);
Win32_HANDLE hStdInput @if(env::WIN32);
Win32_HANDLE hEventOutput @if(env::WIN32);
Win32_HANDLE hEventError @if(env::WIN32);
Pid_t child @if(!env::WIN32);
int return_status @if(!env::WIN32);
bool is_alive;
}
bitstruct SubProcessOptions : int
{
// Combine stdout and stderr to the same file
bool combined_stdout_stderr;
// Child process should inhert env variables of parent process
bool inherit_environment;
// Enable async reading of stdout/stderr before completion
bool read_async;
// Spawn child process without window if supported
bool no_window;
// Search for program names in the PATH variable. Always enabled on Windows.
// Note: this will **not** search for paths in any provided custom environment
// and instead uses the PATH of the spawning process.
bool search_user_path;
}
fn void! create_named_pipe_helper(void** rd, void **wr) @local @if(env::WIN32)
{
Win32_SECURITY_ATTRIBUTES sa_attr = { Win32_SECURITY_ATTRIBUTES.sizeof, null, 1 };
tlocal long index = 0;
long unique = index++;
@pool()
{
String s = string::tprintf(`\\.\pipe\c3_subprocess.%08x.%08x.%d`, win32::getCurrentProcessId(), win32::getCurrentThreadId(), unique);
Win32_LPCSTR str = (Win32_LPCSTR)s.ptr;
*rd = win32::createNamedPipeA(
str,
win32::PIPE_ACCESS_INBOUND | win32::FILE_FLAG_OVERLAPPED,
win32::PIPE_TYPE_BYTE | win32::PIPE_WAIT,
1, 4096, 4096, 0, &sa_attr);
if (win32::INVALID_HANDLE_VALUE == *rd) return SubProcessResult.FAILED_TO_CREATE_PIPE?;
*wr = win32::createFileA(
str, win32::GENERIC_WRITE, 0, &sa_attr,
win32::OPEN_EXISTING, win32::FILE_ATTRIBUTE_NORMAL, null);
if (win32::INVALID_HANDLE_VALUE == *wr) return SubProcessResult.FAILED_TO_CREATE_PIPE?;
};
}
fn Char16* convert_command_line_win32(String[] command_line) @inline @if(env::WIN32) @local
{
DString str;
str.tinit(512);
foreach (i, s : command_line)
{
if (i != 0) str.append(' ');
bool needs_escape = {|
foreach (c : s)
{
switch (c)
{
case '\t':
case ' ':
case '\v':
return true;
}
}
return false;
|};
if (!needs_escape)
{
str.append(s);
continue;
}
str.append('"');
foreach (j, c : s)
{
switch (c)
{
case '\\':
if (j != s.len - 1 && s[j + 1] == '"') str.append('\\');
case '"':
str.append('\\');
}
str.append(c);
}
str.append('"');
}
str.append('\0');
return str.str().to_utf16(.using = mem::temp())!!;
}
/**
* @require !environment || !options.inherit_environment
**/
fn SubProcess! create(String[] command_line, SubProcessOptions options = {}, String[] environment = {}) @if(env::WIN32)
{
void* rd, wr;
Win32_DWORD flags = win32::CREATE_UNICODE_ENVIRONMENT;
Win32_PROCESS_INFORMATION process_info;
Win32_SECURITY_ATTRIBUTES sa_attr = { Win32_SECURITY_ATTRIBUTES.sizeof, null, 1 };
Win32_STARTUPINFOW start_info = {
.cb = Win32_STARTUPINFOW.sizeof,
.dwFlags = win32::STARTF_USESTDHANDLES
};
if (options.no_window) flags |= win32::CREATE_NO_WINDOW;
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return SubProcessResult.FAILED_TO_CREATE_PIPE?;
// TODO defer catch
if (!win32::setHandleInformation(wr, win32::HANDLE_FLAG_INHERIT, 0)) return SubProcessResult.FAILED_TO_CREATE_PIPE?;
CFile stdin;
CFile stdout;
CFile stderr;
@pool()
{
Char16* used_environment = null;
if (!options.inherit_environment)
{
DString env;
env.tinit();
if (!environment.len)
{
env.append("\0");
}
foreach (String s : environment)
{
env.append(s);
env.append("\0");
}
env.append("\0");
used_environment = env.str().to_utf16(.using = mem::temp()).ptr!;
}
int fd = win32::_open_osfhandle((iptr)wr, 0);
if (fd != -1)
{
stdin = win32::_fdopen(fd, "wb");
if (!stdin) return SubProcessResult.FAILED_TO_OPEN_STDIN?;
}
start_info.hStdInput = rd;
if (options.read_async)
{
create_named_pipe_helper(&rd, &wr)!;
}
else
{
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return SubProcessResult.FAILED_TO_CREATE_PIPE?;
}
if (!win32::setHandleInformation(rd, win32::HANDLE_FLAG_INHERIT, 0)) return SubProcessResult.FAILED_TO_CREATE_PIPE?;
fd = win32::_open_osfhandle((iptr)rd, 0);
if (fd != -1)
{
stdout = win32::_fdopen(fd, "rb");
if (!stdout) return SubProcessResult.FAILED_TO_OPEN_STDOUT?;
}
start_info.hStdOutput = wr;
{|
if (options.combined_stdout_stderr)
{
stderr = stdout;
start_info.hStdError = start_info.hStdOutput;
return;
}
if (options.read_async)
{
create_named_pipe_helper(&rd, &wr)!;
}
else
{
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return SubProcessResult.FAILED_TO_CREATE_PIPE?;
}
if (!win32::setHandleInformation(rd, win32::HANDLE_FLAG_INHERIT, 0)) return SubProcessResult.FAILED_TO_CREATE_PIPE?;
fd = win32::_open_osfhandle((iptr)rd, 0);
if (fd != -1)
{
stderr = win32::_fdopen(fd, "rb");
if (!stderr) return SubProcessResult.FAILED_TO_OPEN_STDERR?;
}
start_info.hStdError = wr;
|};
void *event_output;
void *event_error;
if (options.read_async)
{
event_output = win32::createEventA(&sa_attr, 1, 1, null);
event_error = win32::createEventA(&sa_attr, 1, 1, null);
}
if (!win32::createProcessW(
null,
convert_command_line_win32(command_line),
null, // process security attributes
null, // primary thread security attributes
1, // handles are inherited
flags, // creation flags
used_environment, // environment
null, // use parent dir
&start_info, // startup info ptr
&process_info)) return SubProcessResult.FAILED_TO_START_PROCESS?;
};
// We don't need the handle of the primary thread in the called process.
win32::closeHandle(process_info.hThread);
if (start_info.hStdOutput)
{
win32::closeHandle(start_info.hStdOutput);
if (start_info.hStdOutput != start_info.hStdError) win32::closeHandle(start_info.hStdError);
}
return {
.hProcess = process_info.hProcess,
.hStdInput = start_info.hStdInput,
.stdin_file = stdin,
.stdout_file = stdout,
.stderr_file = stderr,
.is_alive = true,
};
}
/**
* @require command_line.len > 0
**/
fn ZString* tcopy_command_line(String[] command_line) @local @inline @if(env::POSIX)
{
ZString* copy = tmalloc(ZString, command_line.len + 1);
foreach (i, str : command_line)
{
copy[i] = str.zstr_tcopy();
}
copy[command_line.len] = null;
return copy;
}
const ZString[1] EMPTY_ENVIRONMENT @if(env::POSIX) = { null };
fn ZString* tcopy_env(String[] environment) @local @inline @if(env::POSIX)
{
if (!environment.len) return &EMPTY_ENVIRONMENT;
ZString* copy = tmalloc(ZString, environment.len + 1);
copy[environment.len] = null;
foreach (i, str : environment)
{
copy[i] = str.zstr_tcopy();
}
return copy;
}
/**
* @require !environment || !options.inherit_environment
**/
fn SubProcess! create(String[] command_line, SubProcessOptions options = {}, String[] environment = {}) @if(env::POSIX)
{
CInt[2] stdinfd;
CInt[2] stdoutfd;
CInt[2] stderrfd;
if (posix::pipe(&stdinfd)) return SubProcessResult.FAILED_TO_OPEN_STDIN?;
if (posix::pipe(&stdoutfd)) return SubProcessResult.FAILED_TO_OPEN_STDOUT?;
if (!options.combined_stdout_stderr && posix::pipe(&stderrfd)) return SubProcessResult.FAILED_TO_OPEN_STDERR?;
Posix_spawn_file_actions_t actions;
if (posix::spawn_file_actions_init(&actions)) return SubProcessResult.FAILED_TO_INITIALIZE_ACTIONS?;
defer posix::spawn_file_actions_destroy(&actions);
if (posix::spawn_file_actions_addclose(&actions, stdinfd[1])) return SubProcessResult.FAILED_TO_OPEN_STDIN?;
if (posix::spawn_file_actions_adddup2(&actions, stdinfd[0], libc::STDIN_FD)) return SubProcessResult.FAILED_TO_OPEN_STDIN?;
if (posix::spawn_file_actions_addclose(&actions, stdoutfd[0])) return SubProcessResult.FAILED_TO_OPEN_STDOUT?;
if (posix::spawn_file_actions_adddup2(&actions, stdoutfd[1], libc::STDOUT_FD)) return SubProcessResult.FAILED_TO_OPEN_STDOUT?;
if (options.combined_stdout_stderr)
{
if (posix::spawn_file_actions_adddup2(&actions, libc::STDOUT_FD, libc::STDERR_FD)) return SubProcessResult.FAILED_TO_OPEN_STDERR?;
}
else
{
if (posix::spawn_file_actions_addclose(&actions, stderrfd[0])) return SubProcessResult.FAILED_TO_OPEN_STDERR?;
if (posix::spawn_file_actions_adddup2(&actions, stderrfd[1], libc::STDERR_FD)) return SubProcessResult.FAILED_TO_OPEN_STDERR?;
}
Pid_t child;
@pool()
{
ZString* command_line_copy = tcopy_command_line(command_line);
ZString* used_environment = options.inherit_environment ? posix::environ : tcopy_env(environment);
if (options.search_user_path)
{
if (posix::spawnp(&child, command_line_copy[0], &actions, null, command_line_copy, used_environment)) return SubProcessResult.FAILED_TO_START_PROCESS?;
}
else
{
if (posix::spawnp(&child, command_line_copy[0], &actions, null, command_line_copy, used_environment)) return SubProcessResult.FAILED_TO_START_PROCESS?;
}
};
libc::close(stdinfd[0]);
CFile stdin = libc::fdopen(stdinfd[1], "wb");
libc::close(stdoutfd[1]);
CFile stdout = libc::fdopen(stdoutfd[0], "rb");
CFile stderr = {|
if (options.combined_stdout_stderr) return stdout;
libc::close(stderrfd[1]);
return libc::fdopen(stderrfd[0], "rb");
|};
return {
.stdin_file = stdin,
.stdout_file = stdout,
.stderr_file = stderr,
.child = child,
.is_alive = true,
};
}
fn CInt! SubProcess.join(SubProcess *this) @if(env::POSIX)
{
if (this.stdin_file)
{
libc::fclose(this.stdin_file);
this.stdin_file = null;
}
CInt status;
if (this.child && this.child != posix::waitpid(this.child, &status, 0)) return SubProcessResult.PROCESS_JOIN_FAILED?;
this.child = 0;
this.is_alive = false;
return this.return_status = posix::wIFEXITED(status) ? posix::wEXITSTATUS(status) : libc::EXIT_FAILURE;
}
fn File SubProcess.stdout(SubProcess* this)
{
return file::from_libc(this.stdout_file);
}
fn CInt! SubProcess.join(SubProcess *this) @if(env::WIN32)
{
if (this.stdin_file)
{
libc::fclose(this.stdin_file);
this.stdin_file = null;
}
if (this.hStdInput)
{
win32::closeHandle(this.hStdInput);
this.hStdInput = null;
}
win32::waitForSingleObject(this.hProcess, win32::INFINITE);
Win32_DWORD return_code @noinit;
if (!win32::getExitCodeProcess(this.hProcess, &return_code)) return SubProcessResult.PROCESS_JOIN_FAILED?;
this.is_alive = false;
return return_code;
}
fn bool SubProcess.destroy(SubProcess* this)
{
if (this.stdin_file) libc::fclose(this.stdin_file);
if (this.stdout_file)
{
libc::fclose(this.stdout_file);
if (this.stdout_file != this.stderr_file) libc::fclose(this.stderr_file);
}
this.stdin_file = this.stdout_file = this.stderr_file = null;
$if env::WIN32:
if (this.hProcess) win32::closeHandle(this.hProcess);
if (this.hStdInput) win32::closeHandle(this.hStdInput);
if (this.hEventOutput) win32::closeHandle(this.hEventOutput);
if (this.hEventError) win32::closeHandle(this.hEventError);
this.hProcess = this.hStdInput = this.hEventOutput = this.hEventError = null;
$endif;
return true;
}
fn void! SubProcess.terminate(SubProcess* this)
{
$if env::WIN32:
if (!win32::terminateProcess(this.hProcess, 99)) return SubProcessResult.PROCESS_TERMINATION_FAILED?;
$else
if (posix::kill(this.child, 9)) return SubProcessResult.PROCESS_TERMINATION_FAILED?;
$endif
}
/**
* @require size <= Win32_DWORD.max
**/
fn usz! read_from_file_win32(CFile file, Win32_HANDLE event_handle, char* buffer, usz size) @if(env::WIN32) @local
{
CInt fd = libc::fileno(file);
Win32_DWORD bytes_read = 0;
Win32_OVERLAPPED overlapped = { .hEvent = event_handle };
Win32_HANDLE handle = (Win32_HANDLE)win32::_get_osfhandle(fd);
if (!win32::readFile(handle, buffer, (Win32_DWORD)size, &bytes_read, &overlapped))
{
// Means we've got an async read!
if (win32::getLastError() == win32::ERROR_IO_PENDING)
{
if (!win32::getOverlappedResult(handle, &overlapped, &bytes_read, 1))
{
switch (win32::getLastError())
{
case win32::ERROR_IO_INCOMPLETE:
case win32::ERROR_HANDLE_EOF:
break;
default:
return SubProcessResult.READ_FAILED?;
}
}
}
}
return bytes_read;
}
fn usz! read_from_file_posix(CFile file, char* buffer, usz size) @if(env::POSIX) @local
{
isz bytes_read = libc::read(libc::fileno(file), buffer, size);
if (bytes_read < 0) return SubProcessResult.READ_FAILED?;
return bytes_read;
}
fn usz! SubProcess.read_stdout(SubProcess* this, char* buffer, usz size)
{
$if env::WIN32:
return read_from_file_win32(this.stdout_file, this.hEventOutput, buffer, size);
$else
return read_from_file_posix(this.stdout_file, buffer, size);
$endif
}
fn usz! SubProcess.read_stderr(SubProcess* this, char* buffer, usz size)
{
$if env::WIN32:
return read_from_file_win32(this.stderr_file, this.hEventError, buffer, size);
$else
return read_from_file_posix(this.stderr_file, buffer, size);
$endif
}
fn bool! SubProcess.is_alive(SubProcess* this)
{
if (!this.is_alive) return false;
$if env::WIN32:
bool is_alive = win32::waitForSingleObject(this.hProcess, 0) != win32::WAIT_OBJECT_0;
if (!is_alive) this.is_alive = false;
return is_alive;
$else
CInt status;
bool is_alive = posix::waitpid(this.child, &status, posix::WNOHANG) == 0;
if (is_alive) return true;
this.is_alive = false;
this.return_status = posix::wIFEXITED(status) ? posix::wEXITSTATUS(status) : libc::EXIT_FAILURE;
this.child = 0;
this.join()!;
return false;
$endif
}

View File

@@ -1,5 +1,5 @@
module std::os::win32 @if(WIN32_LIBC);
import libc;
enum Win32_GET_FILEEX_INFO_LEVELS
{
STANDARD,
@@ -39,22 +39,44 @@ struct Win32_WIN32_FIND_DATAW
def Win32_LPWIN32_FIND_DATAW = Win32_WIN32_FIND_DATAW*;
extern fn Win32_BOOL win32_CloseHandle(Win32_HANDLE) @extern("CloseHandle");
extern fn Win32_BOOL win32_CreatePipe(Win32_PHANDLE hReadPipe, Win32_PHANDLE hWritePipe, Win32_LPSECURITY_ATTRIBUTES lpPipeAttributes, Win32_DWORD nSize) @extern("CreatePipe");
extern fn Win32_BOOL win32_GetFileAttributesExW(Win32_LPCWSTR, Win32_GET_FILEEX_INFO_LEVELS, Win32_LPVOID) @extern("GetFileAttributesExW");
extern fn Win32_BOOL win32_PathFileExistsW(Win32_LPCWSTR) @extern("PathFileExistsW");
extern fn Win32_DWORD win32_GetTempPathW(Win32_DWORD nBufferLength, Win32_LPWSTR lpBuffer) @extern("GetTempPathW");
extern fn Win32_BOOL win32_SetCurrentDirectoryW(Win32_LPCTSTR buffer) @extern("SetCurrentDirectoryW");
extern fn Win32_BOOL win32_RemoveDirectoryW(Win32_LPCWSTR lpPathName) @extern("RemoveDirectoryW");
extern fn Win32_BOOL win32_CreateDirectoryW(Win32_LPCWSTR lpPathName, Win32_LPSECURITY_ATTRIBUTES lpPipeAttributes) @extern("CreateDirectoryW");
extern fn Win32_BOOL win32_DeleteFileW(Win32_LPCWSTR lpFileName) @extern("DeleteFileW");
extern fn Win32_HANDLE win32_FindFirstFileW(Win32_LPCWSTR lpFileName, Win32_LPWIN32_FIND_DATAW lpFindFileData) @extern("FindFirstFileW");
extern fn Win32_BOOL win32_FindNextFileW(Win32_HANDLE hFindFile, Win32_LPWIN32_FIND_DATAW lpFindFileData) @extern("FindNextFileW");
extern fn Win32_BOOL win32_FindClose(Win32_HANDLE hFindFile) @extern("FindClose");
extern fn Win32_BOOL closeHandle(Win32_HANDLE) @extern("CloseHandle");
extern fn Win32_BOOL createPipe(Win32_PHANDLE hReadPipe, Win32_PHANDLE hWritePipe, Win32_LPSECURITY_ATTRIBUTES lpPipeAttributes, Win32_DWORD nSize) @extern("CreatePipe");
extern fn Win32_BOOL getFileAttributesExW(Win32_LPCWSTR, Win32_GET_FILEEX_INFO_LEVELS, Win32_LPVOID) @extern("GetFileAttributesExW");
extern fn Win32_BOOL pathFileExistsW(Win32_LPCWSTR) @extern("PathFileExistsW");
extern fn Win32_DWORD getTempPathW(Win32_DWORD nBufferLength, Win32_LPWSTR lpBuffer) @extern("GetTempPathW");
extern fn Win32_BOOL setCurrentDirectoryW(Win32_LPCTSTR buffer) @extern("SetCurrentDirectoryW");
extern fn Win32_BOOL removeDirectoryW(Win32_LPCWSTR lpPathName) @extern("RemoveDirectoryW");
extern fn Win32_BOOL createDirectoryW(Win32_LPCWSTR lpPathName, Win32_LPSECURITY_ATTRIBUTES lpPipeAttributes) @extern("CreateDirectoryW");
extern fn Win32_BOOL deleteFileW(Win32_LPCWSTR lpFileName) @extern("DeleteFileW");
extern fn Win32_HANDLE findFirstFileW(Win32_LPCWSTR lpFileName, Win32_LPWIN32_FIND_DATAW lpFindFileData) @extern("FindFirstFileW");
extern fn Win32_BOOL findNextFileW(Win32_HANDLE hFindFile, Win32_LPWIN32_FIND_DATAW lpFindFileData) @extern("FindNextFileW");
extern fn Win32_BOOL findClose(Win32_HANDLE hFindFile) @extern("FindClose");
const Win32_DWORD GENERIC_WRITE = 0x40000000;
const Win32_DWORD OPEN_EXISTING = 3;
const Win32_DWORD FILE_ATTRIBUTE_NORMAL = 0x00000080;
extern fn Win32_HANDLE createFileA(
Win32_LPCSTR lpFileName,
Win32_DWORD dwDesiredAccess,
Win32_DWORD dwShareMode,
Win32_LPSECURITY_ATTRIBUTES lpSecurityAttributes,
Win32_DWORD dwCreationDisposition,
Win32_DWORD dwFlagsAndAttributes,
Win32_HANDLE hTemplateFile
) @extern("CreateFileA");
extern fn Win32_BOOL readFile(Win32_HANDLE hFile, Win32_LPVOID lpBuffer, Win32_DWORD nNumberOfBytesToRead,
Win32_LPDWORD lpNumberOfBytesRead, Win32_LPOVERLAPPED lpOverlapped
) @extern("ReadFile");
extern fn Char16* _wgetcwd(Char16* buffer, int maxlen);
extern fn usz wcslen(Char16* str);
extern fn int _open_osfhandle(iptr osfhandle, int flags);
extern fn iptr _get_osfhandle(int fd);
extern fn CFile _fdopen(int fd, ZString mode);
/*
extern ulong _win32_GetCurrentDirectoryW(ulong, Char16* buffer) @extern("GetCurrentDirectoryW");
extern bool _win32_CreateSymbolicLinkW(Char16* symlink_file, Char16* target_file, ulong flags) @extern("CreateSymbolicLinkW");

View File

@@ -1,6 +1,6 @@
module std::os::win32 @if(WIN32_LIBC);
extern fn Win32_DWORD win32_GetLastError() @extern("GetLastError");
extern fn Win32_DWORD getLastError() @extern("GetLastError");
const Win32_DWORD ERROR_INVALID_FUNCTION = 0x1;
const Win32_DWORD ERROR_FILE_NOT_FOUND = 0x2;
@@ -221,4 +221,5 @@ const Win32_DWORD ERROR_DEVICE_FEATURE_NOT_SUPPORTED = 0x13C;
const Win32_DWORD ERROR_MR_MID_NOT_FOUND = 0x13D;
const Win32_DWORD ERROR_SCOPE_NOT_FOUND = 0x13E;
const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F;
const Win32_DWORD ERROR_IO_INCOMPLETE = 0x3E4;
const Win32_DWORD ERROR_IO_PENDING = 0x3E5;

View File

@@ -1,6 +1,100 @@
module std::os::win32 @if(WIN32_LIBC);
extern fn bool win32_CreateProcessW(
const Win32_DWORD STARTF_USESTDHANDLES = 0x00000100;
const Win32_DWORD CREATE_NO_WINDOW = 0x08000000;
const Win32_DWORD CREATE_PROTECTED_PROCESS = 0x00040000;
const Win32_DWORD CREATE_UNICODE_ENVIRONMENT = 0x00000400;
const uint WAIT_OBJECT_0 = 0;
const uint WAIT_ABANDONED = 128;
const uint WAIT_IO_COMPLETION = 192;
const uint WAIT_FAILED = (uint)-1;
const Win32_DWORD HANDLE_FLAG_INHERIT = 1;
const Win32_DWORD HANDLE_FLAG_PROTECT_FROM_CLOSE = 2;
const uint INFINITE = (uint)-1;
const Win32_DWORD PIPE_ACCESS_DUPLEX = 0x00000003;
const Win32_DWORD PIPE_ACCESS_INBOUND = 0x00000001;
const Win32_DWORD PIPE_ACCESS_OUTBOUND = 0x00000002;
const Win32_DWORD FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000;
const Win32_DWORD FILE_FLAG_WRITE_THROUGH = 0x80000000;
const Win32_DWORD FILE_FLAG_OVERLAPPED = 0x40000000;
const Win32_DWORD WRITE_DAC = 0x00040000;
const Win32_DWORD WRITE_OWNER = 0x00080000;
const Win32_DWORD ACCESS_SYSTEM_SECURITY = 0x01000000;
const Win32_DWORD PIPE_TYPE_BYTE = 0;
const Win32_DWORD PIPE_TYPE_MESSAGE = 4;
const Win32_DWORD PIPE_READMODE_BYTE = 0;
const Win32_DWORD PIPE_READMODE_MESSAGE = 2;
const Win32_DWORD PIPE_WAIT = 0;
const Win32_DWORD PIPE_NOWAIT = 1;
const Win32_DWORD PIPE_ACCEPT_REMOTE_CLIENTS = 0;
const Win32_DWORD PIPE_REJECT_REMOTE_CLIENTS = 8;
def NativeThread = Win32_HANDLE;
struct NativeMutex
{
union
{
Win32_CRITICAL_SECTION critical_section;
Win32_HANDLE handle;
}
bool already_locked;
bool recursive;
bool timed;
}
struct NativeOnceFlag
{
int status;
Win32_CRITICAL_SECTION lock;
}
struct NativeConditionVariable
{
union
{
struct
{
Win32_HANDLE event_one;
Win32_HANDLE event_all;
}
Win32_HANDLE[2] events;
}
uint waiters_count;
Win32_CRITICAL_SECTION waiters_count_lock;
}
extern fn void initializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection");
extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection");
extern fn Win32_HANDLE createMutex(void*, bool, void*) @extern("CreateMutexA");
extern fn Win32_BOOL releaseMutex(Win32_HANDLE) @extern("ReleaseMutex");
extern fn void enterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("EnterCriticalSection");
extern fn void leaveCriticalSection(Win32_CRITICAL_SECTION* section) @extern("LeaveCriticalSection");
extern fn Win32_BOOL tryEnterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("TryEnterCriticalSection");
extern fn uint waitForSingleObject(Win32_HANDLE, uint milliseconds) @extern("WaitForSingleObject");
extern fn void sleep(uint ms) @extern("Sleep");
extern fn uint waitForMultipleObjects(uint count, Win32_HANDLE* handles, bool wait_all, uint ms) @extern("WaitForMultipleObjects");
extern fn Win32_BOOL resetEvent(Win32_HANDLE event) @extern("ResetEvent");
extern fn Win32_BOOL setEvent(Win32_HANDLE handle) @extern("SetEvent");
extern fn long interlockedCompareExchange(int* dest, int exchange, int comperand) @extern("InterlockedCompareExchange");
extern fn uint sleepEx(uint ms, bool alertable) @extern("SleepEx");
extern fn Win32_HANDLE createThread(void* attributes, usz stack, ThreadFn func, void* arg, uint flags, uint* thread_id) @extern("CreateThread");
extern fn Win32_BOOL getExitCodeThread(Win32_HANDLE handle, uint* exit_code) @extern("GetExitCodeThread");
extern fn Win32_BOOL getExitCodeProcess(Win32_HANDLE hProcess, Win32_LPDWORD lpExitCode) @extern("GetExitCodeProcess");
extern fn uint getThreadId(Win32_HANDLE) @extern("GetThreadId");
extern fn void exitThread(uint res) @noreturn @extern("ExitThread");
extern fn Win32_HANDLE getCurrentThread() @extern("GetCurrentThread");
extern fn Win32_BOOL terminateProcess(Win32_HANDLE hProcess, Win32_UINT uExitCode) @extern("TerminateProcess");
extern fn Win32_DWORD getCurrentProcessId() @extern("GetCurrentProcessId");
extern fn Win32_DWORD getCurrentThreadId() @extern("GetCurrentThreadId");
extern fn Win32_BOOL setHandleInformation(Win32_HANDLE hObject, Win32_DWORD dwMask, Win32_DWORD dwFlags) @extern("SetHandleInformation");
extern fn Win32_HANDLE createEventA(
Win32_LPSECURITY_ATTRIBUTES lpEventAttributes,
Win32_BOOL bManualReset,
Win32_BOOL bInitialState,
Win32_LPCSTR lpName
) @extern("CreateEventA");
extern fn Win32_BOOL createProcessW(
Win32_LPCWSTR lpApplicationName,
Win32_LPWSTR lpCommandLine,
Win32_LPSECURITY_ATTRIBUTES lpProcessAttributes,
@@ -12,3 +106,15 @@ extern fn bool win32_CreateProcessW(
Win32_LPSTARTUPINFOW lpStartupInfo,
Win32_LPPROCESS_INFORMATION lpProcessInformation
) @extern("CreateProcessW");
extern fn Win32_HANDLE createNamedPipeA(
Win32_LPCSTR lpName, Win32_DWORD dwOpenMode, Win32_DWORD dwPipeMode,
Win32_DWORD nMaxInstances, Win32_DWORD nOutBufferSize, Win32_DWORD nInBufferSize,
Win32_DWORD nDefaultTimeOut, Win32_LPSECURITY_ATTRIBUTES lpSecurityAttributes
) @extern("CreateNamedPipeA");
extern fn Win32_BOOL getOverlappedResult(
Win32_HANDLE hFile,
Win32_LPOVERLAPPED lpOverlapped,
Win32_LPDWORD lpNumberOfBytesTransferred,
Win32_BOOL bWait
) @extern("GetOverlappedResult");

View File

@@ -196,6 +196,8 @@ struct Win32_SECURITY_ATTRIBUTES
Win32_BOOL bInheritHandle;
}
def Win32_LPSECURITY_ATTRIBUTES = Win32_SECURITY_ATTRIBUTES*;
def Win32_PSECURITY_ATTRIBUTES = Win32_SECURITY_ATTRIBUTES*;
@@ -221,6 +223,21 @@ struct Win32_STARTUPINFOW
Win32_HANDLE hStdError;
}
struct Win32_OVERLAPPED
{
Win32_ULONG_PTR internal;
Win32_ULONG_PTR internalHigh;
union {
struct {
Win32_DWORD offset;
Win32_DWORD offsetHigh;
}
Win32_PVOID pointer;
}
Win32_HANDLE hEvent;
}
def Win32_LPOVERLAPPED = Win32_OVERLAPPED*;
def Win32_LPSTARTUPINFOW = Win32_STARTUPINFOW*;
struct Win32_STARTUPINFOEXW

View File

@@ -1,68 +1,5 @@
module std::thread::os @if(thread::THREAD_MODEL == ThreadModel.WIN32);
def NativeThread = Win32_HANDLE;
struct NativeMutex
{
union
{
Win32_CRITICAL_SECTION critical_section;
Win32_HANDLE handle;
}
bool already_locked;
bool recursive;
bool timed;
}
struct NativeOnceFlag
{
int status;
Win32_CRITICAL_SECTION lock;
}
struct NativeConditionVariable
{
union
{
struct
{
Win32_HANDLE event_one;
Win32_HANDLE event_all;
}
Win32_HANDLE[2] events;
}
uint waiters_count;
Win32_CRITICAL_SECTION waiters_count_lock;
}
extern fn void win32_InitializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection");
extern fn void win32_DeleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection");
extern fn Win32_HANDLE win32_CreateMutex(void*, bool, void*) @extern("CreateMutexA");
extern fn bool win32_CloseHandle(Win32_HANDLE) @extern("CloseHandle");
extern fn bool win32_ReleaseMutex(Win32_HANDLE) @extern("ReleaseMutex");
extern fn void win32_EnterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("EnterCriticalSection");
extern fn void win32_LeaveCriticalSection(Win32_CRITICAL_SECTION* section) @extern("LeaveCriticalSection");
extern fn bool win32_TryEnterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("TryEnterCriticalSection");
extern fn uint win32_WaitForSingleObject(Win32_HANDLE, uint milliseconds) @extern("WaitForSingleObject");
extern fn void win32_Sleep(uint ms) @extern("Sleep");
extern fn uint win32_WaitForMultipleObjects(uint count, Win32_HANDLE* handles, bool wait_all, uint ms) @extern("WaitForMultipleObjects");
extern fn Win32_HANDLE win32_CreateEventA(void* attributes, bool manual_reset, bool initial_state, char* name) @extern("CreateEventA");
extern fn bool win32_ResetEvent(Win32_HANDLE event) @extern("ResetEvent");
extern fn bool win32_SetEvent(Win32_HANDLE handle) @extern("SetEvent");
extern fn long win32_InterlockedCompareExchange(int* dest, int exchange, int comperand) @extern("InterlockedCompareExchange");
extern fn uint win32_SleepEx(uint ms, bool alertable) @extern("SleepEx");
extern fn Win32_HANDLE win32_CreateThread(void* attributes, usz stack, ThreadFn func, void* arg, uint flags, uint* thread_id) @extern("CreateThread");
extern fn bool win32_GetExitCodeThread(Win32_HANDLE handle, uint* exit_code) @extern("GetExitCodeThread");
extern fn uint win32_GetThreadId(Win32_HANDLE) @extern("GetThreadId");
extern fn void win32_ExitThread(uint res) @noreturn @extern("ExitThread");
extern fn Win32_HANDLE win32_GetCurrentThread() @extern("GetCurrentThread");
const uint WAIT_OBJECT_0 = 0;
const uint WAIT_ABANDONED = 128;
const uint WAIT_TIMEOUT = 258;
const uint WAIT_IO_COMPLETION = 192;
const uint WAIT_FAILED = (uint)-1;
const uint INFINITE = (uint)-1;
module std::thread::os @if(env::WIN32);
import std::os::win32;
fn void! NativeMutex.init(NativeMutex* mtx, MutexType type)
{
@@ -71,35 +8,35 @@ fn void! NativeMutex.init(NativeMutex* mtx, MutexType type)
mtx.timed = (bool)(type & thread::MUTEX_TIMED);
if (!mtx.timed)
{
win32_InitializeCriticalSection(&(mtx.critical_section));
win32::initializeCriticalSection(&(mtx.critical_section));
return;
}
if (!(mtx.handle = win32_CreateMutex(null, false, null))) return ThreadFault.INIT_FAILED?;
if (!(mtx.handle = win32::createMutex(null, false, null))) return ThreadFault.INIT_FAILED?;
}
fn void! NativeMutex.destroy(NativeMutex* mtx)
{
if (!mtx.timed)
{
win32_DeleteCriticalSection(&mtx.critical_section);
win32::deleteCriticalSection(&mtx.critical_section);
return;
}
if (!win32_CloseHandle(mtx.handle)) return ThreadFault.DESTROY_FAILED?;
if (!win32::closeHandle(mtx.handle)) return ThreadFault.DESTROY_FAILED?;
}
fn void! NativeMutex.lock(NativeMutex* mtx)
{
if (!mtx.timed)
{
win32_EnterCriticalSection(&mtx.critical_section);
win32::enterCriticalSection(&mtx.critical_section);
}
else
{
switch (win32_WaitForSingleObject(mtx.handle, INFINITE))
switch (win32::waitForSingleObject(mtx.handle, win32::INFINITE))
{
case WAIT_OBJECT_0:
case win32::WAIT_OBJECT_0:
break;
case WAIT_ABANDONED:
case win32::WAIT_ABANDONED:
default:
return ThreadFault.LOCK_FAILED?;
@@ -107,7 +44,7 @@ fn void! NativeMutex.lock(NativeMutex* mtx)
}
if (!mtx.recursive)
{
while (mtx.already_locked) win32_Sleep(1);
while (mtx.already_locked) win32::sleep(1);
}
mtx.already_locked = true;
}
@@ -118,19 +55,19 @@ fn void! NativeMutex.lock(NativeMutex* mtx)
**/
fn void! NativeMutex.lock_timeout(NativeMutex* mtx, uint ms)
{
switch (win32_WaitForSingleObject(mtx.handle, ms))
switch (win32::waitForSingleObject(mtx.handle, ms))
{
case WAIT_OBJECT_0:
case win32::WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
case win32::WAIT_TIMEOUT:
return ThreadFault.LOCK_TIMEOUT?;
case WAIT_ABANDONED:
case win32::WAIT_ABANDONED:
default:
return ThreadFault.LOCK_FAILED?;
}
if (!mtx.recursive)
{
while (mtx.already_locked) win32_Sleep(1);
while (mtx.already_locked) win32::sleep(1);
mtx.already_locked = true;
}
}
@@ -138,8 +75,8 @@ fn void! NativeMutex.lock_timeout(NativeMutex* mtx, uint ms)
fn bool NativeMutex.try_lock(NativeMutex* mtx)
{
bool success = mtx.timed
? win32_WaitForSingleObject(mtx.handle, 0) == WAIT_OBJECT_0
: win32_TryEnterCriticalSection(&mtx.critical_section);
? win32::waitForSingleObject(mtx.handle, 0) == win32::WAIT_OBJECT_0
: (bool)win32::tryEnterCriticalSection(&mtx.critical_section);
if (!success) return false;
if (!mtx.recursive)
@@ -147,7 +84,7 @@ fn bool NativeMutex.try_lock(NativeMutex* mtx)
if (mtx.already_locked)
{
assert(!mtx.timed);
win32_LeaveCriticalSection(&mtx.critical_section);
win32::leaveCriticalSection(&mtx.critical_section);
return false;
}
mtx.already_locked = true;
@@ -160,10 +97,10 @@ fn void! NativeMutex.unlock(NativeMutex* mtx)
mtx.already_locked = false;
if (!mtx.timed)
{
win32_LeaveCriticalSection(&mtx.critical_section);
win32::leaveCriticalSection(&mtx.critical_section);
return;
}
if (!win32_ReleaseMutex(mtx.handle)) return ThreadFault.UNLOCK_FAILED?;
if (!win32::releaseMutex(mtx.handle)) return ThreadFault.UNLOCK_FAILED?;
}
const int CONDITION_EVENT_ONE = 0;
@@ -172,17 +109,17 @@ const int CONDITION_EVENT_ALL = 1;
fn void! NativeConditionVariable.init(NativeConditionVariable* cond)
{
cond.waiters_count = 0;
win32_InitializeCriticalSection(&cond.waiters_count_lock);
cond.event_one = win32_CreateEventA(null, false, false, null);
win32::initializeCriticalSection(&cond.waiters_count_lock);
cond.event_one = win32::createEventA(null, 0, 0, null);
if (!cond.event_one)
{
cond.event_all = (Win32_HANDLE)0;
return ThreadFault.INIT_FAILED?;
}
cond.event_all = win32_CreateEventA(null, true, false, null);
cond.event_all = win32::createEventA(null, 1, 0, null);
if (!cond.event_all)
{
win32_CloseHandle(cond.event_one);
win32::closeHandle(cond.event_one);
cond.event_one = (Win32_HANDLE)0;
return ThreadFault.INIT_FAILED?;
}
@@ -190,57 +127,57 @@ fn void! NativeConditionVariable.init(NativeConditionVariable* cond)
fn void! NativeConditionVariable.destroy(NativeConditionVariable* cond) @maydiscard
{
if (cond.event_one) win32_CloseHandle(cond.event_one);
if (cond.event_all) win32_CloseHandle(cond.event_all);
win32_DeleteCriticalSection(&cond.waiters_count_lock);
if (cond.event_one) win32::closeHandle(cond.event_one);
if (cond.event_all) win32::closeHandle(cond.event_all);
win32::deleteCriticalSection(&cond.waiters_count_lock);
}
fn void! NativeConditionVariable.signal(NativeConditionVariable* cond)
{
win32_EnterCriticalSection(&cond.waiters_count_lock);
win32::enterCriticalSection(&cond.waiters_count_lock);
bool have_waiters = cond.waiters_count > 0;
win32_LeaveCriticalSection(&cond.waiters_count_lock);
if (have_waiters && !win32_SetEvent(cond.event_one)) return ThreadFault.SIGNAL_FAILED?;
win32::leaveCriticalSection(&cond.waiters_count_lock);
if (have_waiters && !win32::setEvent(cond.event_one)) return ThreadFault.SIGNAL_FAILED?;
}
fn void! NativeConditionVariable.broadcast(NativeConditionVariable* cond)
{
win32_EnterCriticalSection(&cond.waiters_count_lock);
win32::enterCriticalSection(&cond.waiters_count_lock);
bool have_waiters = cond.waiters_count > 0;
win32_LeaveCriticalSection(&cond.waiters_count_lock);
if (have_waiters && !win32_SetEvent(cond.event_all)) return ThreadFault.SIGNAL_FAILED?;
win32::leaveCriticalSection(&cond.waiters_count_lock);
if (have_waiters && !win32::setEvent(cond.event_all)) return ThreadFault.SIGNAL_FAILED?;
}
fn void! timedwait(NativeConditionVariable* cond, NativeMutex* mtx, uint timeout) @private
{
win32_EnterCriticalSection(&cond.waiters_count_lock);
win32::enterCriticalSection(&cond.waiters_count_lock);
cond.waiters_count++;
win32_LeaveCriticalSection(&cond.waiters_count_lock);
win32::leaveCriticalSection(&cond.waiters_count_lock);
mtx.unlock()!;
uint result = win32_WaitForMultipleObjects(2, &cond.events, false, timeout);
uint result = win32::waitForMultipleObjects(2, &cond.events, false, timeout);
switch (result)
{
case WAIT_TIMEOUT:
case win32::WAIT_TIMEOUT:
mtx.lock()!;
return ThreadFault.WAIT_TIMEOUT?;
case WAIT_FAILED:
case win32::WAIT_FAILED:
mtx.lock()!;
return ThreadFault.WAIT_FAILED?;
default:
break;
}
win32_EnterCriticalSection(&cond.waiters_count_lock);
win32::enterCriticalSection(&cond.waiters_count_lock);
cond.waiters_count--;
// If event all && no waiters
bool last_waiter = result == 1 && !cond.waiters_count;
win32_LeaveCriticalSection(&cond.waiters_count_lock);
win32::leaveCriticalSection(&cond.waiters_count_lock);
if (last_waiter)
{
if (!win32_ResetEvent(cond.event_all))
if (!win32::resetEvent(cond.event_all))
{
mtx.lock()!;
return ThreadFault.WAIT_FAILED?;
@@ -252,7 +189,7 @@ fn void! timedwait(NativeConditionVariable* cond, NativeMutex* mtx, uint timeout
fn void! NativeConditionVariable.wait(NativeConditionVariable* cond, NativeMutex* mtx) @inline
{
return timedwait(cond, mtx, INFINITE) @inline;
return timedwait(cond, mtx, win32::INFINITE) @inline;
}
fn void! NativeConditionVariable.wait_timeout(NativeConditionVariable* cond, NativeMutex* mtx, uint time) @inline
@@ -262,23 +199,23 @@ fn void! NativeConditionVariable.wait_timeout(NativeConditionVariable* cond, Nat
fn void! NativeThread.create(NativeThread* thread, ThreadFn func, void* args)
{
if (!(*thread = win32_CreateThread(null, 0, func, args, 0, null))) return ThreadFault.INIT_FAILED?;
if (!(*thread = win32::createThread(null, 0, func, args, 0, null))) return ThreadFault.INIT_FAILED?;
}
fn void! NativeThread.detach(NativeThread thread) @inline
{
if (!win32_CloseHandle(thread)) return ThreadFault.DETACH_FAILED?;
if (!win32::closeHandle(thread)) return ThreadFault.DETACH_FAILED?;
}
fn void native_thread_exit(int result) @inline
{
win32_ExitThread((uint)result);
win32::exitThread((uint)result);
}
fn void native_thread_yield()
{
win32_Sleep(0);
win32::sleep(0);
}
fn void NativeOnceFlag.call_once(NativeOnceFlag* flag, OnceFn func)
@@ -290,20 +227,20 @@ fn void NativeOnceFlag.call_once(NativeOnceFlag* flag, OnceFn func)
case 0:
if (mem::compare_exchange_volatile(&flag.status, 1, 0, AtomicOrdering.SEQ_CONSISTENT, AtomicOrdering.SEQ_CONSISTENT) == 0)
{
win32_InitializeCriticalSection(&flag.lock);
win32_EnterCriticalSection(&flag.lock);
win32::initializeCriticalSection(&flag.lock);
win32::enterCriticalSection(&flag.lock);
@volatile_store(flag.status, 2);
func();
@volatile_store(flag.status, 3);
win32_LeaveCriticalSection(&flag.lock);
win32::leaveCriticalSection(&flag.lock);
return;
}
break;
case 1:
break;
case 2:
win32_EnterCriticalSection(&flag.lock);
win32_LeaveCriticalSection(&flag.lock);
win32::enterCriticalSection(&flag.lock);
win32::leaveCriticalSection(&flag.lock);
break;
}
}
@@ -311,19 +248,19 @@ fn void NativeOnceFlag.call_once(NativeOnceFlag* flag, OnceFn func)
fn void! NativeThread.join(NativeThread thread, int *res)
{
if (win32_WaitForSingleObject(thread, INFINITE) == WAIT_FAILED) return ThreadFault.JOIN_FAILED?;
if (!win32_GetExitCodeThread(thread, (uint*)res)) return ThreadFault.JOIN_FAILED?;
defer win32_CloseHandle(thread);
if (win32::waitForSingleObject(thread, win32::INFINITE) == win32::WAIT_FAILED) return ThreadFault.JOIN_FAILED?;
if (!win32::getExitCodeThread(thread, (uint*)res)) return ThreadFault.JOIN_FAILED?;
defer win32::closeHandle(thread);
}
fn NativeThread native_thread_current()
{
return win32_GetCurrentThread();
return win32::getCurrentThread();
}
fn bool NativeThread.equals(NativeThread this, NativeThread other)
{
return win32_GetThreadId(this) == win32_GetThreadId(other);
return win32::getThreadId(this) == win32::getThreadId(other);
}
/**
@@ -331,7 +268,7 @@ fn bool NativeThread.equals(NativeThread this, NativeThread other)
**/
fn void! native_sleep_ms(ulong ms)
{
if (win32_SleepEx((uint)ms, true) == WAIT_IO_COMPLETION) return ThreadFault.INTERRUPTED?;
if (win32::sleepEx((uint)ms, true) == win32::WAIT_IO_COMPLETION) return ThreadFault.INTERRUPTED?;
}
fn void! native_sleep(double s)

View File

@@ -3,6 +3,7 @@
## 0.5.0 Change List
### Changes / improvements
- Fix to void expression blocks
- Temporary objects may now invoke methods using ref parameters.
- Delete object files after successful linking.
- `@if` introduced, other top level conditional compilation removed.
@@ -86,6 +87,8 @@
- `@ensure` checks only non-optional results.
### Stdlib changes
- Updated posix/win32 stdlib namespacing
- `process` stdlib
- Stdlib updates to string.
- Many additions to `List`: `remove`, `array_view`, `add_all`, `compact` etc
- Added dstringwriter.

View File

@@ -0,0 +1,16 @@
module test;
import std::os::process;
import std::io;
fn void! main()
{
String command = env::WIN32 ? "dir" : "ls";
SubProcess x = process::create({ command }, { .search_user_path = true })!!;
x.join()!;
Stream stream = x.stdout().as_stream();
while (try char b = stream.read_byte())
{
io::printf("%c", b);
}
io::printn("...Done");
}

View File

@@ -1712,7 +1712,7 @@ static inline Type *context_unify_returns(SemaContext *context)
}
// 3. Same type -> we're done.
if (common_type == rtype) continue;
if (common_type == rtype || (common_type == type_void && rtype == type_wildcard)) continue;
// 4. Find the max of the old and new.
Type *max = type_find_max_type(common_type, rtype);
@@ -1744,6 +1744,10 @@ static inline Type *context_unify_returns(SemaContext *context)
Ast *return_stmt = context->returns[i];
if (!return_stmt) continue;
Expr *ret_expr = return_stmt->return_stmt.expr;
if (!ret_expr)
{
context_unify_returns(context);
}
// 8. All casts should work.
if (!cast_implicit(context, ret_expr, common_type))
{