commit
ccef60a640
@ -384,7 +384,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
|
||||
const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
|
||||
defer allocator.free(zig_lib_dir);
|
||||
|
||||
var loop = try event.Loop.init(allocator);
|
||||
var loop: event.Loop = undefined;
|
||||
try loop.initMultiThreaded(allocator);
|
||||
|
||||
var module = try Module.create(
|
||||
&loop,
|
||||
@ -493,8 +494,6 @@ async fn processBuildEvents(module: *Module, watch: bool) void {
|
||||
switch (build_event) {
|
||||
Module.Event.Ok => {
|
||||
std.debug.warn("Build succeeded\n");
|
||||
// for now we stop after 1
|
||||
module.loop.stop();
|
||||
return;
|
||||
},
|
||||
Module.Event.Error => |err| {
|
||||
|
@ -2,6 +2,7 @@ const std = @import("std");
|
||||
const os = std.os;
|
||||
const io = std.io;
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const Buffer = std.Buffer;
|
||||
const llvm = @import("llvm.zig");
|
||||
const c = @import("c.zig");
|
||||
@ -13,6 +14,7 @@ const ArrayList = std.ArrayList;
|
||||
const errmsg = @import("errmsg.zig");
|
||||
const ast = std.zig.ast;
|
||||
const event = std.event;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
pub const Module = struct {
|
||||
loop: *event.Loop,
|
||||
@ -81,6 +83,8 @@ pub const Module = struct {
|
||||
link_out_file: ?[]const u8,
|
||||
events: *event.Channel(Event),
|
||||
|
||||
exported_symbol_names: event.Locked(Decl.Table),
|
||||
|
||||
// TODO handle some of these earlier and report them in a way other than error codes
|
||||
pub const BuildError = error{
|
||||
OutOfMemory,
|
||||
@ -232,6 +236,7 @@ pub const Module = struct {
|
||||
.test_name_prefix = null,
|
||||
.emit_file_type = Emit.Binary,
|
||||
.link_out_file = null,
|
||||
.exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
|
||||
});
|
||||
}
|
||||
|
||||
@ -272,38 +277,91 @@ pub const Module = struct {
|
||||
return;
|
||||
};
|
||||
await (async self.events.put(Event.Ok) catch unreachable);
|
||||
// for now we stop after 1
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async fn addRootSrc(self: *Module) !void {
|
||||
const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
|
||||
// TODO async/await os.path.real
|
||||
const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| {
|
||||
try printError("unable to get real path '{}': {}", root_src_path, err);
|
||||
return err;
|
||||
};
|
||||
errdefer self.a().free(root_src_real_path);
|
||||
|
||||
// TODO async/await readFileAlloc()
|
||||
const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| {
|
||||
try printError("unable to open '{}': {}", root_src_real_path, err);
|
||||
return err;
|
||||
};
|
||||
errdefer self.a().free(source_code);
|
||||
|
||||
var tree = try std.zig.parse(self.a(), source_code);
|
||||
defer tree.deinit();
|
||||
var parsed_file = ParsedFile{
|
||||
.tree = try std.zig.parse(self.a(), source_code),
|
||||
.realpath = root_src_real_path,
|
||||
};
|
||||
errdefer parsed_file.tree.deinit();
|
||||
|
||||
//var it = tree.root_node.decls.iterator();
|
||||
//while (it.next()) |decl_ptr| {
|
||||
// const decl = decl_ptr.*;
|
||||
// switch (decl.id) {
|
||||
// ast.Node.Comptime => @panic("TODO"),
|
||||
// ast.Node.VarDecl => @panic("TODO"),
|
||||
// ast.Node.UseDecl => @panic("TODO"),
|
||||
// ast.Node.FnDef => @panic("TODO"),
|
||||
// ast.Node.TestDecl => @panic("TODO"),
|
||||
// else => unreachable,
|
||||
// }
|
||||
//}
|
||||
const tree = &parsed_file.tree;
|
||||
|
||||
// create empty struct for it
|
||||
const decls = try Scope.Decls.create(self.a(), null);
|
||||
errdefer decls.destroy();
|
||||
|
||||
var it = tree.root_node.decls.iterator(0);
|
||||
while (it.next()) |decl_ptr| {
|
||||
const decl = decl_ptr.*;
|
||||
switch (decl.id) {
|
||||
ast.Node.Id.Comptime => @panic("TODO"),
|
||||
ast.Node.Id.VarDecl => @panic("TODO"),
|
||||
ast.Node.Id.FnProto => {
|
||||
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
|
||||
|
||||
const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
|
||||
@panic("TODO add compile error");
|
||||
//try self.addCompileError(
|
||||
// &parsed_file,
|
||||
// fn_proto.fn_token,
|
||||
// fn_proto.fn_token + 1,
|
||||
// "missing function name",
|
||||
//);
|
||||
continue;
|
||||
};
|
||||
|
||||
const fn_decl = try self.a().create(Decl.Fn{
|
||||
.base = Decl{
|
||||
.id = Decl.Id.Fn,
|
||||
.name = name,
|
||||
.visib = parseVisibToken(tree, fn_proto.visib_token),
|
||||
.resolution = Decl.Resolution.Unresolved,
|
||||
},
|
||||
.value = Decl.Fn.Val{ .Unresolved = {} },
|
||||
.fn_proto = fn_proto,
|
||||
});
|
||||
errdefer self.a().destroy(fn_decl);
|
||||
|
||||
// TODO make this parallel
|
||||
try await try async self.addTopLevelDecl(tree, &fn_decl.base);
|
||||
},
|
||||
ast.Node.Id.TestDecl => @panic("TODO"),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn addTopLevelDecl(self: *Module, tree: *ast.Tree, decl: *Decl) !void {
|
||||
const is_export = decl.isExported(tree);
|
||||
|
||||
{
|
||||
const exported_symbol_names = await try async self.exported_symbol_names.acquire();
|
||||
defer exported_symbol_names.release();
|
||||
|
||||
if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
|
||||
@panic("TODO report compile error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn link(self: *Module, out_file: ?[]const u8) !void {
|
||||
@ -350,3 +408,172 @@ fn printError(comptime format: []const u8, args: ...) !void {
|
||||
const out_stream = &stderr_file_out_stream.stream;
|
||||
try out_stream.print(format, args);
|
||||
}
|
||||
|
||||
fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
|
||||
if (optional_token_index) |token_index| {
|
||||
const token = tree.tokens.at(token_index);
|
||||
assert(token.id == Token.Id.Keyword_pub);
|
||||
return Visib.Pub;
|
||||
} else {
|
||||
return Visib.Private;
|
||||
}
|
||||
}
|
||||
|
||||
pub const Scope = struct {
|
||||
id: Id,
|
||||
parent: ?*Scope,
|
||||
|
||||
pub const Id = enum {
|
||||
Decls,
|
||||
Block,
|
||||
};
|
||||
|
||||
pub const Decls = struct {
|
||||
base: Scope,
|
||||
table: Decl.Table,
|
||||
|
||||
pub fn create(a: *Allocator, parent: ?*Scope) !*Decls {
|
||||
const self = try a.create(Decls{
|
||||
.base = Scope{
|
||||
.id = Id.Decls,
|
||||
.parent = parent,
|
||||
},
|
||||
.table = undefined,
|
||||
});
|
||||
errdefer a.destroy(self);
|
||||
|
||||
self.table = Decl.Table.init(a);
|
||||
errdefer self.table.deinit();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Decls) void {
|
||||
self.table.deinit();
|
||||
self.table.allocator.destroy(self);
|
||||
self.* = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Block = struct {
|
||||
base: Scope,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Visib = enum {
|
||||
Private,
|
||||
Pub,
|
||||
};
|
||||
|
||||
pub const Decl = struct {
|
||||
id: Id,
|
||||
name: []const u8,
|
||||
visib: Visib,
|
||||
resolution: Resolution,
|
||||
|
||||
pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
|
||||
|
||||
pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
|
||||
switch (base.id) {
|
||||
Id.Fn => {
|
||||
const fn_decl = @fieldParentPtr(Fn, "base", base);
|
||||
return fn_decl.isExported(tree);
|
||||
},
|
||||
else => return false,
|
||||
}
|
||||
}
|
||||
|
||||
pub const Resolution = enum {
|
||||
Unresolved,
|
||||
InProgress,
|
||||
Invalid,
|
||||
Ok,
|
||||
};
|
||||
|
||||
pub const Id = enum {
|
||||
Var,
|
||||
Fn,
|
||||
CompTime,
|
||||
};
|
||||
|
||||
pub const Var = struct {
|
||||
base: Decl,
|
||||
};
|
||||
|
||||
pub const Fn = struct {
|
||||
base: Decl,
|
||||
value: Val,
|
||||
fn_proto: *const ast.Node.FnProto,
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
|
||||
pub const Val = union {
|
||||
Unresolved: void,
|
||||
Ok: *Value.Fn,
|
||||
};
|
||||
|
||||
pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
|
||||
return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {
|
||||
const token = tree.tokens.at(tok_index);
|
||||
break :x switch (token.id) {
|
||||
Token.Id.Extern => tree.tokenSlicePtr(token),
|
||||
else => null,
|
||||
};
|
||||
} else null;
|
||||
}
|
||||
|
||||
pub fn isExported(self: Fn, tree: *ast.Tree) bool {
|
||||
if (self.fn_proto.extern_export_inline_token) |tok_index| {
|
||||
const token = tree.tokens.at(tok_index);
|
||||
return token.id == Token.Id.Keyword_export;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const CompTime = struct {
|
||||
base: Decl,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Value = struct {
|
||||
pub const Fn = struct {};
|
||||
};
|
||||
|
||||
pub const Type = struct {
|
||||
id: Id,
|
||||
|
||||
pub const Id = enum {
|
||||
Type,
|
||||
Void,
|
||||
Bool,
|
||||
NoReturn,
|
||||
Int,
|
||||
Float,
|
||||
Pointer,
|
||||
Array,
|
||||
Struct,
|
||||
ComptimeFloat,
|
||||
ComptimeInt,
|
||||
Undefined,
|
||||
Null,
|
||||
Optional,
|
||||
ErrorUnion,
|
||||
ErrorSet,
|
||||
Enum,
|
||||
Union,
|
||||
Fn,
|
||||
Opaque,
|
||||
Promise,
|
||||
};
|
||||
|
||||
pub const Struct = struct {
|
||||
base: Type,
|
||||
decls: *Scope.Decls,
|
||||
};
|
||||
};
|
||||
|
||||
pub const ParsedFile = struct {
|
||||
tree: ast.Tree,
|
||||
realpath: []const u8,
|
||||
};
|
||||
|
@ -13278,7 +13278,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
|
||||
FnTableEntry *fn_table_entry = fn_ref->value.data.x_bound_fn.fn;
|
||||
IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg;
|
||||
return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
|
||||
nullptr, first_arg_ptr, is_comptime, call_instruction->fn_inline);
|
||||
fn_ref, first_arg_ptr, is_comptime, call_instruction->fn_inline);
|
||||
} else {
|
||||
ir_add_error_node(ira, fn_ref->source_node,
|
||||
buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));
|
||||
|
@ -15,6 +15,8 @@ pub fn QueueMpsc(comptime T: type) type {
|
||||
|
||||
pub const Node = std.atomic.Stack(T).Node;
|
||||
|
||||
/// Not thread-safe. The call to init() must complete before any other functions are called.
|
||||
/// No deinitialization required.
|
||||
pub fn init() Self {
|
||||
return Self{
|
||||
.inboxes = []std.atomic.Stack(T){
|
||||
@ -26,12 +28,15 @@ pub fn QueueMpsc(comptime T: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
/// Fully thread-safe. put() may be called from any thread at any time.
|
||||
pub fn put(self: *Self, node: *Node) void {
|
||||
const inbox_index = @atomicLoad(usize, &self.inbox_index, AtomicOrder.SeqCst);
|
||||
const inbox = &self.inboxes[inbox_index];
|
||||
inbox.push(node);
|
||||
}
|
||||
|
||||
/// Must be called by only 1 consumer at a time. Every call to get() and isEmpty() must complete before
|
||||
/// the next call to get().
|
||||
pub fn get(self: *Self) ?*Node {
|
||||
if (self.outbox.pop()) |node| {
|
||||
return node;
|
||||
@ -43,6 +48,43 @@ pub fn QueueMpsc(comptime T: type) type {
|
||||
}
|
||||
return self.outbox.pop();
|
||||
}
|
||||
|
||||
/// Must be called by only 1 consumer at a time. Every call to get() and isEmpty() must complete before
|
||||
/// the next call to isEmpty().
|
||||
pub fn isEmpty(self: *Self) bool {
|
||||
if (!self.outbox.isEmpty()) return false;
|
||||
const prev_inbox_index = @atomicRmw(usize, &self.inbox_index, AtomicRmwOp.Xor, 0x1, AtomicOrder.SeqCst);
|
||||
const prev_inbox = &self.inboxes[prev_inbox_index];
|
||||
while (prev_inbox.pop()) |node| {
|
||||
self.outbox.push(node);
|
||||
}
|
||||
return self.outbox.isEmpty();
|
||||
}
|
||||
|
||||
/// For debugging only. No API guarantees about what this does.
|
||||
pub fn dump(self: *Self) void {
|
||||
{
|
||||
var it = self.outbox.root;
|
||||
while (it) |node| {
|
||||
std.debug.warn("0x{x} -> ", @ptrToInt(node));
|
||||
it = node.next;
|
||||
}
|
||||
}
|
||||
const inbox_index = self.inbox_index;
|
||||
const inboxes = []*std.atomic.Stack(T){
|
||||
&self.inboxes[self.inbox_index],
|
||||
&self.inboxes[1 - self.inbox_index],
|
||||
};
|
||||
for (inboxes) |inbox| {
|
||||
var it = inbox.root;
|
||||
while (it) |node| {
|
||||
std.debug.warn("0x{x} -> ", @ptrToInt(node));
|
||||
it = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
std.debug.warn("null\n");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,30 @@ pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, b
|
||||
pub extern "c" fn mach_absolute_time() u64;
|
||||
pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;
|
||||
|
||||
pub extern "c" fn kqueue() c_int;
|
||||
pub extern "c" fn kevent(
|
||||
kq: c_int,
|
||||
changelist: [*]const Kevent,
|
||||
nchanges: c_int,
|
||||
eventlist: [*]Kevent,
|
||||
nevents: c_int,
|
||||
timeout: ?*const timespec,
|
||||
) c_int;
|
||||
|
||||
pub extern "c" fn kevent64(
|
||||
kq: c_int,
|
||||
changelist: [*]const kevent64_s,
|
||||
nchanges: c_int,
|
||||
eventlist: [*]kevent64_s,
|
||||
nevents: c_int,
|
||||
flags: c_uint,
|
||||
timeout: ?*const timespec,
|
||||
) c_int;
|
||||
|
||||
pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
|
||||
pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
|
||||
pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int;
|
||||
|
||||
pub use @import("../os/darwin_errno.zig");
|
||||
|
||||
pub const _errno = __error;
|
||||
@ -86,3 +110,51 @@ pub const pthread_attr_t = extern struct {
|
||||
__sig: c_long,
|
||||
__opaque: [56]u8,
|
||||
};
|
||||
|
||||
/// Renamed from `kevent` to `Kevent` to avoid conflict with function name.
|
||||
pub const Kevent = extern struct {
|
||||
ident: usize,
|
||||
filter: i16,
|
||||
flags: u16,
|
||||
fflags: u32,
|
||||
data: isize,
|
||||
udata: usize,
|
||||
};
|
||||
|
||||
// sys/types.h on macos uses #pragma pack(4) so these checks are
|
||||
// to make sure the struct is laid out the same. These values were
|
||||
// produced from C code using the offsetof macro.
|
||||
const std = @import("../index.zig");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
comptime {
|
||||
assert(@offsetOf(Kevent, "ident") == 0);
|
||||
assert(@offsetOf(Kevent, "filter") == 8);
|
||||
assert(@offsetOf(Kevent, "flags") == 10);
|
||||
assert(@offsetOf(Kevent, "fflags") == 12);
|
||||
assert(@offsetOf(Kevent, "data") == 16);
|
||||
assert(@offsetOf(Kevent, "udata") == 24);
|
||||
}
|
||||
|
||||
pub const kevent64_s = extern struct {
|
||||
ident: u64,
|
||||
filter: i16,
|
||||
flags: u16,
|
||||
fflags: u32,
|
||||
data: i64,
|
||||
udata: u64,
|
||||
ext: [2]u64,
|
||||
};
|
||||
|
||||
// sys/types.h on macos uses #pragma pack() so these checks are
|
||||
// to make sure the struct is laid out the same. These values were
|
||||
// produced from C code using the offsetof macro.
|
||||
comptime {
|
||||
assert(@offsetOf(kevent64_s, "ident") == 0);
|
||||
assert(@offsetOf(kevent64_s, "filter") == 8);
|
||||
assert(@offsetOf(kevent64_s, "flags") == 10);
|
||||
assert(@offsetOf(kevent64_s, "fflags") == 12);
|
||||
assert(@offsetOf(kevent64_s, "data") == 16);
|
||||
assert(@offsetOf(kevent64_s, "udata") == 24);
|
||||
assert(@offsetOf(kevent64_s, "ext") == 32);
|
||||
}
|
||||
|
@ -12,6 +12,11 @@ const builtin = @import("builtin");
|
||||
pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
|
||||
pub const failing_allocator = FailingAllocator.init(global_allocator, 0);
|
||||
|
||||
pub const runtime_safety = switch (builtin.mode) {
|
||||
builtin.Mode.Debug, builtin.Mode.ReleaseSafe => true,
|
||||
builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false,
|
||||
};
|
||||
|
||||
/// Tries to write to stderr, unbuffered, and ignores any error returned.
|
||||
/// Does not append a newline.
|
||||
/// TODO atomic/multithread support
|
||||
@ -1125,7 +1130,7 @@ fn readILeb128(in_stream: var) !i64 {
|
||||
|
||||
/// This should only be used in temporary test programs.
|
||||
pub const global_allocator = &global_fixed_allocator.allocator;
|
||||
var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]);
|
||||
var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]);
|
||||
var global_allocator_mem: [100 * 1024]u8 = undefined;
|
||||
|
||||
// TODO make thread safe
|
||||
|
894
std/event.zig
894
std/event.zig
File diff suppressed because it is too large
Load Diff
99
std/heap.zig
99
std/heap.zig
@ -38,7 +38,7 @@ fn cFree(self: *Allocator, old_mem: []u8) void {
|
||||
}
|
||||
|
||||
/// This allocator makes a syscall directly for every allocation and free.
|
||||
/// TODO make this thread-safe. The windows implementation will need some atomics.
|
||||
/// Thread-safe and lock-free.
|
||||
pub const DirectAllocator = struct {
|
||||
allocator: Allocator,
|
||||
heap_handle: ?HeapHandle,
|
||||
@ -74,34 +74,34 @@ pub const DirectAllocator = struct {
|
||||
const alloc_size = if (alignment <= os.page_size) n else n + alignment;
|
||||
const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0);
|
||||
if (addr == p.MAP_FAILED) return error.OutOfMemory;
|
||||
|
||||
if (alloc_size == n) return @intToPtr([*]u8, addr)[0..n];
|
||||
|
||||
var aligned_addr = addr & ~usize(alignment - 1);
|
||||
aligned_addr += alignment;
|
||||
const aligned_addr = (addr & ~usize(alignment - 1)) + alignment;
|
||||
|
||||
//We can unmap the unused portions of our mmap, but we must only
|
||||
// pass munmap bytes that exist outside our allocated pages or it
|
||||
// will happily eat us too
|
||||
// We can unmap the unused portions of our mmap, but we must only
|
||||
// pass munmap bytes that exist outside our allocated pages or it
|
||||
// will happily eat us too.
|
||||
|
||||
//Since alignment > page_size, we are by definition on a page boundry
|
||||
// Since alignment > page_size, we are by definition on a page boundary.
|
||||
const unused_start = addr;
|
||||
const unused_len = aligned_addr - 1 - unused_start;
|
||||
|
||||
var err = p.munmap(unused_start, unused_len);
|
||||
debug.assert(p.getErrno(err) == 0);
|
||||
const err = p.munmap(unused_start, unused_len);
|
||||
assert(p.getErrno(err) == 0);
|
||||
|
||||
//It is impossible that there is an unoccupied page at the top of our
|
||||
// mmap.
|
||||
// It is impossible that there is an unoccupied page at the top of our
|
||||
// mmap.
|
||||
|
||||
return @intToPtr([*]u8, aligned_addr)[0..n];
|
||||
},
|
||||
Os.windows => {
|
||||
const amt = n + alignment + @sizeOf(usize);
|
||||
const heap_handle = self.heap_handle orelse blk: {
|
||||
const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) orelse return error.OutOfMemory;
|
||||
self.heap_handle = hh;
|
||||
break :blk hh;
|
||||
const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);
|
||||
const heap_handle = optional_heap_handle orelse blk: {
|
||||
const hh = os.windows.HeapCreate(0, amt, 0) orelse return error.OutOfMemory;
|
||||
const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh;
|
||||
_ = os.windows.HeapDestroy(hh);
|
||||
break :blk other_hh.?; // can't be null because of the cmpxchg
|
||||
};
|
||||
const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
|
||||
const root_addr = @ptrToInt(ptr);
|
||||
@ -361,6 +361,73 @@ pub const ThreadSafeFixedBufferAllocator = struct {
|
||||
fn free(allocator: *Allocator, bytes: []u8) void {}
|
||||
};
|
||||
|
||||
pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) StackFallbackAllocator(size) {
|
||||
return StackFallbackAllocator(size){
|
||||
.buffer = undefined,
|
||||
.fallback_allocator = fallback_allocator,
|
||||
.fixed_buffer_allocator = undefined,
|
||||
.allocator = Allocator{
|
||||
.allocFn = StackFallbackAllocator(size).alloc,
|
||||
.reallocFn = StackFallbackAllocator(size).realloc,
|
||||
.freeFn = StackFallbackAllocator(size).free,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn StackFallbackAllocator(comptime size: usize) type {
|
||||
return struct {
|
||||
const Self = this;
|
||||
|
||||
buffer: [size]u8,
|
||||
allocator: Allocator,
|
||||
fallback_allocator: *Allocator,
|
||||
fixed_buffer_allocator: FixedBufferAllocator,
|
||||
|
||||
pub fn get(self: *Self) *Allocator {
|
||||
self.fixed_buffer_allocator = FixedBufferAllocator.init(self.buffer[0..]);
|
||||
return &self.allocator;
|
||||
}
|
||||
|
||||
fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
|
||||
const self = @fieldParentPtr(Self, "allocator", allocator);
|
||||
return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator.allocator, n, alignment) catch
|
||||
self.fallback_allocator.allocFn(self.fallback_allocator, n, alignment);
|
||||
}
|
||||
|
||||
fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
|
||||
const self = @fieldParentPtr(Self, "allocator", allocator);
|
||||
const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and
|
||||
@ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len;
|
||||
if (in_buffer) {
|
||||
return FixedBufferAllocator.realloc(
|
||||
&self.fixed_buffer_allocator.allocator,
|
||||
old_mem,
|
||||
new_size,
|
||||
alignment,
|
||||
) catch {
|
||||
const result = try self.fallback_allocator.allocFn(
|
||||
self.fallback_allocator,
|
||||
new_size,
|
||||
alignment,
|
||||
);
|
||||
mem.copy(u8, result, old_mem);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
return self.fallback_allocator.reallocFn(self.fallback_allocator, old_mem, new_size, alignment);
|
||||
}
|
||||
|
||||
fn free(allocator: *Allocator, bytes: []u8) void {
|
||||
const self = @fieldParentPtr(Self, "allocator", allocator);
|
||||
const in_buffer = @ptrToInt(bytes.ptr) >= @ptrToInt(&self.buffer) and
|
||||
@ptrToInt(bytes.ptr) < @ptrToInt(&self.buffer) + self.buffer.len;
|
||||
if (!in_buffer) {
|
||||
return self.fallback_allocator.freeFn(self.fallback_allocator, bytes);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "c_allocator" {
|
||||
if (builtin.link_libc) {
|
||||
var slice = c_allocator.alloc(u8, 50) catch return;
|
||||
|
@ -6,7 +6,7 @@ const builtin = @import("builtin");
|
||||
const mem = this;
|
||||
|
||||
pub const Allocator = struct {
|
||||
const Error = error{OutOfMemory};
|
||||
pub const Error = error{OutOfMemory};
|
||||
|
||||
/// Allocate byte_count bytes and return them in a slice, with the
|
||||
/// slice's pointer aligned at least to alignment bytes.
|
||||
|
@ -264,6 +264,224 @@ pub const SIGUSR1 = 30;
|
||||
/// user defined signal 2
|
||||
pub const SIGUSR2 = 31;
|
||||
|
||||
/// no flag value
|
||||
pub const KEVENT_FLAG_NONE = 0x000;
|
||||
|
||||
/// immediate timeout
|
||||
pub const KEVENT_FLAG_IMMEDIATE = 0x001;
|
||||
|
||||
/// output events only include change
|
||||
pub const KEVENT_FLAG_ERROR_EVENTS = 0x002;
|
||||
|
||||
/// add event to kq (implies enable)
|
||||
pub const EV_ADD = 0x0001;
|
||||
|
||||
/// delete event from kq
|
||||
pub const EV_DELETE = 0x0002;
|
||||
|
||||
/// enable event
|
||||
pub const EV_ENABLE = 0x0004;
|
||||
|
||||
/// disable event (not reported)
|
||||
pub const EV_DISABLE = 0x0008;
|
||||
|
||||
/// only report one occurrence
|
||||
pub const EV_ONESHOT = 0x0010;
|
||||
|
||||
/// clear event state after reporting
|
||||
pub const EV_CLEAR = 0x0020;
|
||||
|
||||
/// force immediate event output
|
||||
/// ... with or without EV_ERROR
|
||||
/// ... use KEVENT_FLAG_ERROR_EVENTS
|
||||
/// on syscalls supporting flags
|
||||
pub const EV_RECEIPT = 0x0040;
|
||||
|
||||
/// disable event after reporting
|
||||
pub const EV_DISPATCH = 0x0080;
|
||||
|
||||
/// unique kevent per udata value
|
||||
pub const EV_UDATA_SPECIFIC = 0x0100;
|
||||
|
||||
/// ... in combination with EV_DELETE
|
||||
/// will defer delete until udata-specific
|
||||
/// event enabled. EINPROGRESS will be
|
||||
/// returned to indicate the deferral
|
||||
pub const EV_DISPATCH2 = EV_DISPATCH | EV_UDATA_SPECIFIC;
|
||||
|
||||
/// report that source has vanished
|
||||
/// ... only valid with EV_DISPATCH2
|
||||
pub const EV_VANISHED = 0x0200;
|
||||
|
||||
/// reserved by system
|
||||
pub const EV_SYSFLAGS = 0xF000;
|
||||
|
||||
/// filter-specific flag
|
||||
pub const EV_FLAG0 = 0x1000;
|
||||
|
||||
/// filter-specific flag
|
||||
pub const EV_FLAG1 = 0x2000;
|
||||
|
||||
/// EOF detected
|
||||
pub const EV_EOF = 0x8000;
|
||||
|
||||
/// error, data contains errno
|
||||
pub const EV_ERROR = 0x4000;
|
||||
|
||||
pub const EV_POLL = EV_FLAG0;
|
||||
pub const EV_OOBAND = EV_FLAG1;
|
||||
|
||||
pub const EVFILT_READ = -1;
|
||||
pub const EVFILT_WRITE = -2;
|
||||
|
||||
/// attached to aio requests
|
||||
pub const EVFILT_AIO = -3;
|
||||
|
||||
/// attached to vnodes
|
||||
pub const EVFILT_VNODE = -4;
|
||||
|
||||
/// attached to struct proc
|
||||
pub const EVFILT_PROC = -5;
|
||||
|
||||
/// attached to struct proc
|
||||
pub const EVFILT_SIGNAL = -6;
|
||||
|
||||
/// timers
|
||||
pub const EVFILT_TIMER = -7;
|
||||
|
||||
/// Mach portsets
|
||||
pub const EVFILT_MACHPORT = -8;
|
||||
|
||||
/// Filesystem events
|
||||
pub const EVFILT_FS = -9;
|
||||
|
||||
/// User events
|
||||
pub const EVFILT_USER = -10;
|
||||
|
||||
/// Virtual memory events
|
||||
pub const EVFILT_VM = -12;
|
||||
|
||||
/// Exception events
|
||||
pub const EVFILT_EXCEPT = -15;
|
||||
|
||||
pub const EVFILT_SYSCOUNT = 17;
|
||||
|
||||
/// On input, NOTE_TRIGGER causes the event to be triggered for output.
|
||||
pub const NOTE_TRIGGER = 0x01000000;
|
||||
|
||||
/// ignore input fflags
|
||||
pub const NOTE_FFNOP = 0x00000000;
|
||||
|
||||
/// and fflags
|
||||
pub const NOTE_FFAND = 0x40000000;
|
||||
|
||||
/// or fflags
|
||||
pub const NOTE_FFOR = 0x80000000;
|
||||
|
||||
/// copy fflags
|
||||
pub const NOTE_FFCOPY = 0xc0000000;
|
||||
|
||||
/// mask for operations
|
||||
pub const NOTE_FFCTRLMASK = 0xc0000000;
|
||||
pub const NOTE_FFLAGSMASK = 0x00ffffff;
|
||||
|
||||
/// low water mark
|
||||
pub const NOTE_LOWAT = 0x00000001;
|
||||
|
||||
/// OOB data
|
||||
pub const NOTE_OOB = 0x00000002;
|
||||
|
||||
/// vnode was removed
|
||||
pub const NOTE_DELETE = 0x00000001;
|
||||
|
||||
/// data contents changed
|
||||
pub const NOTE_WRITE = 0x00000002;
|
||||
|
||||
/// size increased
|
||||
pub const NOTE_EXTEND = 0x00000004;
|
||||
|
||||
/// attributes changed
|
||||
pub const NOTE_ATTRIB = 0x00000008;
|
||||
|
||||
/// link count changed
|
||||
pub const NOTE_LINK = 0x00000010;
|
||||
|
||||
/// vnode was renamed
|
||||
pub const NOTE_RENAME = 0x00000020;
|
||||
|
||||
/// vnode access was revoked
|
||||
pub const NOTE_REVOKE = 0x00000040;
|
||||
|
||||
/// No specific vnode event: to test for EVFILT_READ activation
|
||||
pub const NOTE_NONE = 0x00000080;
|
||||
|
||||
/// vnode was unlocked by flock(2)
|
||||
pub const NOTE_FUNLOCK = 0x00000100;
|
||||
|
||||
/// process exited
|
||||
pub const NOTE_EXIT = 0x80000000;
|
||||
|
||||
/// process forked
|
||||
pub const NOTE_FORK = 0x40000000;
|
||||
|
||||
/// process exec'd
|
||||
pub const NOTE_EXEC = 0x20000000;
|
||||
|
||||
/// shared with EVFILT_SIGNAL
|
||||
pub const NOTE_SIGNAL = 0x08000000;
|
||||
|
||||
/// exit status to be returned, valid for child process only
|
||||
pub const NOTE_EXITSTATUS = 0x04000000;
|
||||
|
||||
/// provide details on reasons for exit
|
||||
pub const NOTE_EXIT_DETAIL = 0x02000000;
|
||||
|
||||
/// mask for signal & exit status
|
||||
pub const NOTE_PDATAMASK = 0x000fffff;
|
||||
pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK);
|
||||
|
||||
pub const NOTE_EXIT_DETAIL_MASK = 0x00070000;
|
||||
pub const NOTE_EXIT_DECRYPTFAIL = 0x00010000;
|
||||
pub const NOTE_EXIT_MEMORY = 0x00020000;
|
||||
pub const NOTE_EXIT_CSERROR = 0x00040000;
|
||||
|
||||
/// will react on memory pressure
|
||||
pub const NOTE_VM_PRESSURE = 0x80000000;
|
||||
|
||||
/// will quit on memory pressure, possibly after cleaning up dirty state
|
||||
pub const NOTE_VM_PRESSURE_TERMINATE = 0x40000000;
|
||||
|
||||
/// will quit immediately on memory pressure
|
||||
pub const NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000;
|
||||
|
||||
/// there was an error
|
||||
pub const NOTE_VM_ERROR = 0x10000000;
|
||||
|
||||
/// data is seconds
|
||||
pub const NOTE_SECONDS = 0x00000001;
|
||||
|
||||
/// data is microseconds
|
||||
pub const NOTE_USECONDS = 0x00000002;
|
||||
|
||||
/// data is nanoseconds
|
||||
pub const NOTE_NSECONDS = 0x00000004;
|
||||
|
||||
/// absolute timeout
|
||||
pub const NOTE_ABSOLUTE = 0x00000008;
|
||||
|
||||
/// ext[1] holds leeway for power aware timers
|
||||
pub const NOTE_LEEWAY = 0x00000010;
|
||||
|
||||
/// system does minimal timer coalescing
|
||||
pub const NOTE_CRITICAL = 0x00000020;
|
||||
|
||||
/// system does maximum timer coalescing
|
||||
pub const NOTE_BACKGROUND = 0x00000040;
|
||||
pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;
|
||||
|
||||
/// data is mach absolute time units
|
||||
pub const NOTE_MACHTIME = 0x00000100;
|
||||
|
||||
fn wstatus(x: i32) i32 {
|
||||
return x & 0o177;
|
||||
}
|
||||
@ -385,6 +603,31 @@ pub fn getdirentries64(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usi
|
||||
return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep)));
|
||||
}
|
||||
|
||||
pub fn kqueue() usize {
|
||||
return errnoWrap(c.kqueue());
|
||||
}
|
||||
|
||||
pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize {
|
||||
return errnoWrap(c.kevent(
|
||||
kq,
|
||||
changelist.ptr,
|
||||
@intCast(c_int, changelist.len),
|
||||
eventlist.ptr,
|
||||
@intCast(c_int, eventlist.len),
|
||||
timeout,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn kevent64(
|
||||
kq: i32,
|
||||
changelist: []const kevent64_s,
|
||||
eventlist: []kevent64_s,
|
||||
flags: u32,
|
||||
timeout: ?*const timespec,
|
||||
) usize {
|
||||
return errnoWrap(c.kevent64(kq, changelist.ptr, changelist.len, eventlist.ptr, eventlist.len, flags, timeout));
|
||||
}
|
||||
|
||||
pub fn mkdir(path: [*]const u8, mode: u32) usize {
|
||||
return errnoWrap(c.mkdir(path, mode));
|
||||
}
|
||||
@ -393,6 +636,18 @@ pub fn symlink(existing: [*]const u8, new: [*]const u8) usize {
|
||||
return errnoWrap(c.symlink(existing, new));
|
||||
}
|
||||
|
||||
pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize {
|
||||
return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen));
|
||||
}
|
||||
|
||||
pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize {
|
||||
return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen));
|
||||
}
|
||||
|
||||
pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize {
|
||||
return errnoWrap(c.sysctlnametomib(name, wibp, sizep));
|
||||
}
|
||||
|
||||
pub fn rename(old: [*]const u8, new: [*]const u8) usize {
|
||||
return errnoWrap(c.rename(old, new));
|
||||
}
|
||||
@ -474,6 +729,10 @@ pub const dirent = c.dirent;
|
||||
pub const sa_family_t = c.sa_family_t;
|
||||
pub const sockaddr = c.sockaddr;
|
||||
|
||||
/// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall.
|
||||
pub const Kevent = c.Kevent;
|
||||
pub const kevent64_s = c.kevent64_s;
|
||||
|
||||
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
|
||||
pub const Sigaction = struct {
|
||||
handler: extern fn (i32) void,
|
||||
|
189
std/os/index.zig
189
std/os/index.zig
@ -61,6 +61,15 @@ pub const windowsLoadDll = windows_util.windowsLoadDll;
|
||||
pub const windowsUnloadDll = windows_util.windowsUnloadDll;
|
||||
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
|
||||
|
||||
pub const WindowsCreateIoCompletionPortError = windows_util.WindowsCreateIoCompletionPortError;
|
||||
pub const windowsCreateIoCompletionPort = windows_util.windowsCreateIoCompletionPort;
|
||||
|
||||
pub const WindowsPostQueuedCompletionStatusError = windows_util.WindowsPostQueuedCompletionStatusError;
|
||||
pub const windowsPostQueuedCompletionStatus = windows_util.windowsPostQueuedCompletionStatus;
|
||||
|
||||
pub const WindowsWaitResult = windows_util.WindowsWaitResult;
|
||||
pub const windowsGetQueuedCompletionStatus = windows_util.windowsGetQueuedCompletionStatus;
|
||||
|
||||
pub const WindowsWaitError = windows_util.WaitError;
|
||||
pub const WindowsOpenError = windows_util.OpenError;
|
||||
pub const WindowsWriteError = windows_util.WriteError;
|
||||
@ -2317,6 +2326,30 @@ pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usiz
|
||||
}
|
||||
}
|
||||
|
||||
pub const LinuxEventFdError = error{
|
||||
InvalidFlagValue,
|
||||
SystemResources,
|
||||
ProcessFdQuotaExceeded,
|
||||
SystemFdQuotaExceeded,
|
||||
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn linuxEventFd(initval: u32, flags: u32) LinuxEventFdError!i32 {
|
||||
const rc = posix.eventfd(initval, flags);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return @intCast(i32, rc),
|
||||
else => return unexpectedErrorPosix(err),
|
||||
|
||||
posix.EINVAL => return LinuxEventFdError.InvalidFlagValue,
|
||||
posix.EMFILE => return LinuxEventFdError.ProcessFdQuotaExceeded,
|
||||
posix.ENFILE => return LinuxEventFdError.SystemFdQuotaExceeded,
|
||||
posix.ENODEV => return LinuxEventFdError.SystemResources,
|
||||
posix.ENOMEM => return LinuxEventFdError.SystemResources,
|
||||
}
|
||||
}
|
||||
|
||||
pub const PosixGetSockNameError = error{
|
||||
/// Insufficient resources were available in the system to perform the operation.
|
||||
SystemResources,
|
||||
@ -2576,11 +2609,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
|
||||
thread: Thread,
|
||||
inner: Context,
|
||||
};
|
||||
extern fn threadMain(arg: windows.LPVOID) windows.DWORD {
|
||||
if (@sizeOf(Context) == 0) {
|
||||
return startFn({});
|
||||
} else {
|
||||
return startFn(@ptrCast(*Context, @alignCast(@alignOf(Context), arg)).*);
|
||||
extern fn threadMain(raw_arg: windows.LPVOID) windows.DWORD {
|
||||
const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
|
||||
switch (@typeId(@typeOf(startFn).ReturnType)) {
|
||||
builtin.TypeId.Int => {
|
||||
return startFn(arg);
|
||||
},
|
||||
builtin.TypeId.Void => {
|
||||
startFn(arg);
|
||||
return 0;
|
||||
},
|
||||
else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"),
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -2613,10 +2652,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
|
||||
|
||||
const MainFuncs = struct {
|
||||
extern fn linuxThreadMain(ctx_addr: usize) u8 {
|
||||
if (@sizeOf(Context) == 0) {
|
||||
return startFn({});
|
||||
} else {
|
||||
return startFn(@intToPtr(*const Context, ctx_addr).*);
|
||||
const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;
|
||||
|
||||
switch (@typeId(@typeOf(startFn).ReturnType)) {
|
||||
builtin.TypeId.Int => {
|
||||
return startFn(arg);
|
||||
},
|
||||
builtin.TypeId.Void => {
|
||||
startFn(arg);
|
||||
return 0;
|
||||
},
|
||||
else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"),
|
||||
}
|
||||
}
|
||||
extern fn posixThreadMain(ctx: ?*c_void) ?*c_void {
|
||||
@ -2725,3 +2771,128 @@ pub fn posixFStat(fd: i32) !posix.Stat {
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
pub const CpuCountError = error{
|
||||
OutOfMemory,
|
||||
PermissionDenied,
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize {
|
||||
switch (builtin.os) {
|
||||
builtin.Os.macosx => {
|
||||
var count: c_int = undefined;
|
||||
var count_len: usize = @sizeOf(c_int);
|
||||
const rc = posix.sysctlbyname(c"hw.ncpu", @ptrCast(*c_void, &count), &count_len, null, 0);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return @intCast(usize, count),
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.ENOMEM => return CpuCountError.OutOfMemory,
|
||||
posix.ENOTDIR => unreachable,
|
||||
posix.EISDIR => unreachable,
|
||||
posix.ENOENT => unreachable,
|
||||
posix.EPERM => unreachable,
|
||||
else => return os.unexpectedErrorPosix(err),
|
||||
}
|
||||
},
|
||||
builtin.Os.linux => {
|
||||
const usize_count = 16;
|
||||
const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get();
|
||||
|
||||
var set = try allocator.alloc(usize, usize_count);
|
||||
defer allocator.free(set);
|
||||
|
||||
while (true) {
|
||||
const rc = posix.sched_getaffinity(0, set);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => {
|
||||
if (rc < set.len * @sizeOf(usize)) {
|
||||
const result = set[0 .. rc / @sizeOf(usize)];
|
||||
var sum: usize = 0;
|
||||
for (result) |x| {
|
||||
sum += @popCount(x);
|
||||
}
|
||||
return sum;
|
||||
} else {
|
||||
set = try allocator.realloc(usize, set, set.len * 2);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EPERM => return CpuCountError.PermissionDenied,
|
||||
posix.ESRCH => unreachable,
|
||||
else => return os.unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
},
|
||||
builtin.Os.windows => {
|
||||
var system_info: windows.SYSTEM_INFO = undefined;
|
||||
windows.GetSystemInfo(&system_info);
|
||||
return @intCast(usize, system_info.dwNumberOfProcessors);
|
||||
},
|
||||
else => @compileError("unsupported OS"),
|
||||
}
|
||||
}
|
||||
|
||||
pub const BsdKQueueError = error{
|
||||
/// The per-process limit on the number of open file descriptors has been reached.
|
||||
ProcessFdQuotaExceeded,
|
||||
|
||||
/// The system-wide limit on the total number of open files has been reached.
|
||||
SystemFdQuotaExceeded,
|
||||
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn bsdKQueue() BsdKQueueError!i32 {
|
||||
const rc = posix.kqueue();
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return @intCast(i32, rc),
|
||||
posix.EMFILE => return BsdKQueueError.ProcessFdQuotaExceeded,
|
||||
posix.ENFILE => return BsdKQueueError.SystemFdQuotaExceeded,
|
||||
else => return unexpectedErrorPosix(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub const BsdKEventError = error{
|
||||
/// The process does not have permission to register a filter.
|
||||
AccessDenied,
|
||||
|
||||
/// The event could not be found to be modified or deleted.
|
||||
EventNotFound,
|
||||
|
||||
/// No memory was available to register the event.
|
||||
SystemResources,
|
||||
|
||||
/// The specified process to attach to does not exist.
|
||||
ProcessNotFound,
|
||||
};
|
||||
|
||||
pub fn bsdKEvent(
|
||||
kq: i32,
|
||||
changelist: []const posix.Kevent,
|
||||
eventlist: []posix.Kevent,
|
||||
timeout: ?*const posix.timespec,
|
||||
) BsdKEventError!usize {
|
||||
while (true) {
|
||||
const rc = posix.kevent(kq, changelist, eventlist, timeout);
|
||||
const err = posix.getErrno(rc);
|
||||
switch (err) {
|
||||
0 => return rc,
|
||||
posix.EACCES => return BsdKEventError.AccessDenied,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EBADF => unreachable,
|
||||
posix.EINTR => continue,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.ENOENT => return BsdKEventError.EventNotFound,
|
||||
posix.ENOMEM => return BsdKEventError.SystemResources,
|
||||
posix.ESRCH => return BsdKEventError.ProcessNotFound,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -523,6 +523,10 @@ pub const CLONE_NEWPID = 0x20000000;
|
||||
pub const CLONE_NEWNET = 0x40000000;
|
||||
pub const CLONE_IO = 0x80000000;
|
||||
|
||||
pub const EFD_SEMAPHORE = 1;
|
||||
pub const EFD_CLOEXEC = O_CLOEXEC;
|
||||
pub const EFD_NONBLOCK = O_NONBLOCK;
|
||||
|
||||
pub const MS_RDONLY = 1;
|
||||
pub const MS_NOSUID = 2;
|
||||
pub const MS_NODEV = 4;
|
||||
@ -1193,6 +1197,10 @@ pub fn fremovexattr(fd: usize, name: [*]const u8) usize {
|
||||
return syscall2(SYS_fremovexattr, fd, @ptrToInt(name));
|
||||
}
|
||||
|
||||
pub fn sched_getaffinity(pid: i32, set: []usize) usize {
|
||||
return syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), set.len * @sizeOf(usize), @ptrToInt(set.ptr));
|
||||
}
|
||||
|
||||
pub const epoll_data = packed union {
|
||||
ptr: usize,
|
||||
fd: i32,
|
||||
@ -1221,6 +1229,10 @@ pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout
|
||||
return syscall4(SYS_epoll_wait, @intCast(usize, epoll_fd), @ptrToInt(events), @intCast(usize, maxevents), @intCast(usize, timeout));
|
||||
}
|
||||
|
||||
pub fn eventfd(count: u32, flags: u32) usize {
|
||||
return syscall2(SYS_eventfd2, count, flags);
|
||||
}
|
||||
|
||||
pub fn timerfd_create(clockid: i32, flags: u32) usize {
|
||||
return syscall2(SYS_timerfd_create, @intCast(usize, clockid), @intCast(usize, flags));
|
||||
}
|
||||
|
@ -58,3 +58,8 @@ fn start2(ctx: *i32) u8 {
|
||||
_ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
test "cpu count" {
|
||||
const cpu_count = try std.os.cpuCount(a);
|
||||
assert(cpu_count >= 1);
|
||||
}
|
||||
|
@ -59,6 +59,9 @@ pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
|
||||
dwFlags: DWORD,
|
||||
) BOOLEAN;
|
||||
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
|
||||
@ -106,7 +109,9 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
|
||||
) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
|
||||
pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
|
||||
@ -129,6 +134,9 @@ pub extern "kernel32" stdcallcc fn MoveFileExA(
|
||||
dwFlags: DWORD,
|
||||
) BOOL;
|
||||
|
||||
|
||||
pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
|
||||
@ -204,6 +212,7 @@ pub const SIZE_T = usize;
|
||||
pub const TCHAR = if (UNICODE) WCHAR else u8;
|
||||
pub const UINT = c_uint;
|
||||
pub const ULONG_PTR = usize;
|
||||
pub const DWORD_PTR = ULONG_PTR;
|
||||
pub const UNICODE = false;
|
||||
pub const WCHAR = u16;
|
||||
pub const WORD = u16;
|
||||
@ -413,3 +422,22 @@ pub const FILETIME = extern struct {
|
||||
dwLowDateTime: DWORD,
|
||||
dwHighDateTime: DWORD,
|
||||
};
|
||||
|
||||
pub const SYSTEM_INFO = extern struct {
|
||||
anon1: extern union {
|
||||
dwOemId: DWORD,
|
||||
anon2: extern struct {
|
||||
wProcessorArchitecture: WORD,
|
||||
wReserved: WORD,
|
||||
},
|
||||
},
|
||||
dwPageSize: DWORD,
|
||||
lpMinimumApplicationAddress: LPVOID,
|
||||
lpMaximumApplicationAddress: LPVOID,
|
||||
dwActiveProcessorMask: DWORD_PTR,
|
||||
dwNumberOfProcessors: DWORD,
|
||||
dwProcessorType: DWORD,
|
||||
dwAllocationGranularity: DWORD,
|
||||
wProcessorLevel: WORD,
|
||||
wProcessorRevision: WORD,
|
||||
};
|
||||
|
@ -214,3 +214,50 @@ pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN3
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
pub const WindowsCreateIoCompletionPortError = error {
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_completion_port: ?windows.HANDLE, completion_key: usize, concurrent_thread_count: windows.DWORD) !windows.HANDLE {
|
||||
const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
|
||||
const err = windows.GetLastError();
|
||||
switch (err) {
|
||||
else => return os.unexpectedErrorWindows(err),
|
||||
}
|
||||
};
|
||||
return handle;
|
||||
}
|
||||
|
||||
pub const WindowsPostQueuedCompletionStatusError = error {
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void {
|
||||
if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
|
||||
const err = windows.GetLastError();
|
||||
switch (err) {
|
||||
else => return os.unexpectedErrorWindows(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const WindowsWaitResult = error {
|
||||
Normal,
|
||||
Aborted,
|
||||
};
|
||||
|
||||
pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult {
|
||||
if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) {
|
||||
if (std.debug.runtime_safety) {
|
||||
const err = windows.GetLastError();
|
||||
if (err != windows.ERROR.ABANDONED_WAIT_0) {
|
||||
std.debug.warn("err: {}\n", err);
|
||||
}
|
||||
assert(err == windows.ERROR.ABANDONED_WAIT_0);
|
||||
}
|
||||
return WindowsWaitResult.Aborted;
|
||||
}
|
||||
return WindowsWaitResult.Normal;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ fn test__extendhfsf2(a: u16, expected: u32) void {
|
||||
|
||||
if (rep == expected) {
|
||||
if (rep & 0x7fffffff > 0x7f800000) {
|
||||
return; // NaN is always unequal.
|
||||
return; // NaN is always unequal.
|
||||
}
|
||||
if (x == @bitCast(f32, expected)) {
|
||||
return;
|
||||
@ -86,33 +86,33 @@ test "extenddftf2" {
|
||||
}
|
||||
|
||||
test "extendhfsf2" {
|
||||
test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN
|
||||
test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN
|
||||
test__extendhfsf2(0x7c01, 0x7f802000); // sNaN
|
||||
test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN
|
||||
test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN
|
||||
test__extendhfsf2(0x7c01, 0x7f802000); // sNaN
|
||||
|
||||
test__extendhfsf2(0, 0); // 0
|
||||
test__extendhfsf2(0x8000, 0x80000000); // -0
|
||||
test__extendhfsf2(0, 0); // 0
|
||||
test__extendhfsf2(0x8000, 0x80000000); // -0
|
||||
|
||||
test__extendhfsf2(0x7c00, 0x7f800000); // inf
|
||||
test__extendhfsf2(0xfc00, 0xff800000); // -inf
|
||||
test__extendhfsf2(0x7c00, 0x7f800000); // inf
|
||||
test__extendhfsf2(0xfc00, 0xff800000); // -inf
|
||||
|
||||
test__extendhfsf2(0x0001, 0x33800000); // denormal (min), 2**-24
|
||||
test__extendhfsf2(0x8001, 0xb3800000); // denormal (min), -2**-24
|
||||
test__extendhfsf2(0x0001, 0x33800000); // denormal (min), 2**-24
|
||||
test__extendhfsf2(0x8001, 0xb3800000); // denormal (min), -2**-24
|
||||
|
||||
test__extendhfsf2(0x03ff, 0x387fc000); // denormal (max), 2**-14 - 2**-24
|
||||
test__extendhfsf2(0x83ff, 0xb87fc000); // denormal (max), -2**-14 + 2**-24
|
||||
test__extendhfsf2(0x03ff, 0x387fc000); // denormal (max), 2**-14 - 2**-24
|
||||
test__extendhfsf2(0x83ff, 0xb87fc000); // denormal (max), -2**-14 + 2**-24
|
||||
|
||||
test__extendhfsf2(0x0400, 0x38800000); // normal (min), 2**-14
|
||||
test__extendhfsf2(0x8400, 0xb8800000); // normal (min), -2**-14
|
||||
test__extendhfsf2(0x0400, 0x38800000); // normal (min), 2**-14
|
||||
test__extendhfsf2(0x8400, 0xb8800000); // normal (min), -2**-14
|
||||
|
||||
test__extendhfsf2(0x7bff, 0x477fe000); // normal (max), 65504
|
||||
test__extendhfsf2(0xfbff, 0xc77fe000); // normal (max), -65504
|
||||
test__extendhfsf2(0x7bff, 0x477fe000); // normal (max), 65504
|
||||
test__extendhfsf2(0xfbff, 0xc77fe000); // normal (max), -65504
|
||||
|
||||
test__extendhfsf2(0x3c01, 0x3f802000); // normal, 1 + 2**-10
|
||||
test__extendhfsf2(0xbc01, 0xbf802000); // normal, -1 - 2**-10
|
||||
test__extendhfsf2(0x3c01, 0x3f802000); // normal, 1 + 2**-10
|
||||
test__extendhfsf2(0xbc01, 0xbf802000); // normal, -1 - 2**-10
|
||||
|
||||
test__extendhfsf2(0x3555, 0x3eaaa000); // normal, approx. 1/3
|
||||
test__extendhfsf2(0xb555, 0xbeaaa000); // normal, approx. -1/3
|
||||
test__extendhfsf2(0x3555, 0x3eaaa000); // normal, approx. 1/3
|
||||
test__extendhfsf2(0xb555, 0xbeaaa000); // normal, approx. -1/3
|
||||
}
|
||||
|
||||
test "extendsftf2" {
|
||||
|
Loading…
x
Reference in New Issue
Block a user