--- /dev/null
+package core.alloc.atomic
+
+use core {sync}
+
+AtomicAllocator :: struct {
+ a: Allocator;
+ m: sync.Mutex;
+}
+
+make :: (a: Allocator) -> AtomicAllocator {
+ atomic: AtomicAllocator = .{ a = a };
+
+ sync.mutex_init(^atomic.m);
+
+ return atomic;
+}
+
+make_allocator :: (atomic: ^AtomicAllocator) =>
+ Allocator.{ atomic, atomic_alloc };
+
+#overload
+core.alloc.as_allocator :: make_allocator
+
+
+#local
+atomic_alloc :: (atomic: ^AtomicAllocator, aa: AllocationAction, size: u32, align: u32, oldptr: rawptr) -> rawptr {
+ sync.scoped_mutex(^atomic.m);
+ return atomic.a.func(atomic.a.data, aa, size, align, oldptr);
+}
+
+
#overload #precedence 10000
as_iterator :: (x: ^$T/ImplicitIterator) => {
x->iter_open();
- return generator(x, T.iter_next, T.iter_close);
+ return generator_no_copy(x, T.iter_next, T.iter_close);
}
};
}
+generator_no_copy :: #match #local {}
+
+#overload
+generator_no_copy :: (ctx: ^$Ctx, gen: (^Ctx) -> ($T, bool)) =>
+ Iterator(T).{ ctx, gen }
+
+#overload
+generator_no_copy :: (ctx: ^$Ctx, gen: (^Ctx) -> ($T, bool), close: (^Ctx) -> void) =>
+ Iterator(T).{ ctx, gen, close }
+
+
#if runtime.Multi_Threading_Enabled {
#local sync :: core.sync
listen :: socket_listen
accept :: socket_accept
connect :: socket_connect
+ shutdown :: socket_shutdown
send :: socket_send
sendto :: socket_sendto
sendall :: socket_sendall
}
SocketSetting :: enum {
- NonBlocking :: 0x01;
- Broadcast :: 0x02;
+ NonBlocking :: 0x01;
+ Broadcast :: 0x02;
+ ReuseAddress :: 0x03;
+}
+
+SocketShutdown :: enum {
+ Read :: 0;
+ Write :: 1;
+ ReadWrite :: 2;
}
#local UNIX_SOCKET_PATH_LEN :: 256
__net_listen(s.handle, backlog);
}
+socket_shutdown :: (s: ^Socket, how: SocketShutdown) {
+ __net_shutdown(s.handle, cast(u32) how);
+}
+
socket_accept :: (s: ^Socket) -> (Socket, Socket_Address) {
new_socket: Socket;
new_addr: Socket_Address;
#package __net_accept :: (handle: Socket.Handle, out_address: ^Socket_Address) -> Socket.Handle ---
#package __net_connect_unix :: (handle: Socket.Handle, path: str) -> SocketError ---
#package __net_connect_ipv4 :: (handle: Socket.Handle, host: str, port: u16) -> SocketError ---
+ #package __net_shutdown :: (handle: Socket.Handle, how: u32) -> void ---
#package __net_send :: (handle: Socket.Handle, data: [] u8) -> i32 ---
#package __net_sendto :: (handle: Socket.Handle, data: [] u8, addr: ^Socket_Address) -> i32 ---
#package __net_recv :: (handle: Socket.Handle, data: [] u8, async_would_block: ^bool) -> i32 ---
event_allocator: Allocator;
events: [..] TCP_Event;
event_cursor := 0;
+
+ event_mutex: sync.Mutex;
}
TCP_Event :: struct {
}
}
-tcp_get_events :: (use conn: ^TCP_Connection) => {
- conn.event_cursor = 0;
-
- return iter.generator(
- conn,
+// Iterator implementation for TCP_Connection
+#inject TCP_Connection {
+ iter_open :: (use conn: ^TCP_Connection) {
+ conn.event_cursor = 0;
+ }
- (use conn: ^TCP_Connection) -> (TCP_Event, bool) {
- if event_cursor == events.count do return .{}, false;
+ iter_next :: (use conn: ^TCP_Connection) -> (TCP_Event, bool) {
+ if event_cursor == events.count do return .{}, false;
- defer event_cursor += 1;
- return events[event_cursor], true;
- },
+ defer event_cursor += 1;
+ return events[event_cursor], true;
+ }
- (use conn: ^TCP_Connection) {
- for events {
- switch it.kind {
- case .Data {
- raw_free(event_allocator, (cast(^TCP_Event.Data) it.data).contents.data);
- }
+ iter_close :: (use conn: ^TCP_Connection) {
+ for events {
+ switch it.kind {
+ case .Data {
+ raw_free(event_allocator, (cast(^TCP_Event.Data) it.data).contents.data);
}
-
- raw_free(event_allocator, it.data);
}
- array.clear(^events);
- });
+ raw_free(event_allocator, it.data);
+ }
+
+ array.clear(^events);
+ }
}
emit_data_events := true;
- get_events :: tcp_get_events
listen :: tcp_server_listen
+ stop :: tcp_server_stop
pulse :: tcp_server_pulse
send :: tcp_server_send
broadcast :: tcp_server_broadcast
server.clients = make([] ^TCP_Server.Client, max_clients, allocator=allocator);
array.fill(server.clients, null);
+ sync.mutex_init(^server.event_mutex);
+
return server;
}
#local tcp_server_listener :: (use server: ^TCP_Server) {
while server.alive {
if server.client_count == server.clients.count {
- os.sleep(100);
+ os.sleep(1);
continue;
}
client_socket, client_addr := socket->accept();
+ if !client_socket.vtable {
+ continue;
+ }
client := new(TCP_Server.Client, allocator=client_allocator);
client.state = .Alive;
conn_event := new(TCP_Event.Connection, allocator=server.event_allocator);
conn_event.address = ^client.address;
conn_event.client = client;
- server.events << .{ .Connection, conn_event }; // @Threading // This operation should be protected by a mutex?
+
+ sync.critical_section(^server.event_mutex) {
+ server.events << .{ .Connection, conn_event }; // @Threading
+ }
}
}
return true;
}
+tcp_server_stop :: (use server: ^TCP_Server) {
+ server.alive = false;
+
+ for clients {
+ if !it do continue;
+
+ if it.state == .Alive do server->kill_client(it);
+ }
+
+ server.socket->close();
+ thread.kill(^listener_thread);
+}
+
tcp_server_pulse :: (use server: ^TCP_Server) -> bool {
for^ clients {
client := *it;
- if client == null do continue;
+ if !client do continue;
+
switch client.state {
case .Being_Killed {
// Before, there was not a "being killed" state and the code made
data_event.client = it;
data_event.address = ^it.address;
data_event.contents = memory.copy_slice(msg_buffer[0 .. bytes_read], allocator=server.event_allocator);
- server.events << .{ .Data, data_event }; // @Threading // See comment above.
+ sync.critical_section(^server.event_mutex) {
+ server.events << .{ .Data, data_event }; // @Threading // See comment above.
+ }
} elseif !it.recv_ready_event_present {
it.recv_ready_event_present = true;
ready_event := new(TCP_Event.Ready, allocator=server.event_allocator);
ready_event.client = it;
ready_event.address = ^it.address;
- server.events << .{ .Ready, ready_event }; // @Threading // See comment above.
+ sync.critical_section(^server.event_mutex) {
+ server.events << .{ .Ready, ready_event }; // @Threading // See comment above.
+ }
}
}
disconnect_event := new(TCP_Event.Disconnection, allocator=server.event_allocator);
disconnect_event.client = it;
disconnect_event.address = ^it.address;
- server.events << .{ .Disconnection, disconnect_event }; // @Threading // See comment above.
+ sync.critical_section(^server.event_mutex) {
+ server.events << .{ .Disconnection, disconnect_event }; // @Threading // See comment above.
+ }
}
}
tcp_server_handle_events :: macro (server: ^TCP_Server, handler: Code) {
while server->pulse() {
- for server->get_events() do switch it.kind do #unquote handler;
+ for iter.as_iterator(^server.connection) {
+ switch it.kind do #unquote handler;
+ }
}
}
tcp_server_kill_client :: (use server: ^TCP_Server, client: ^TCP_Server.Client) {
client.state = .Being_Killed;
+ client.socket->shutdown(.ReadWrite);
client.socket->close();
client.socket.vtable = null;
}
TCP_Client :: struct {
use connection: TCP_Connection;
-
- get_events :: tcp_get_events
}
--- /dev/null
+package core.os
+
+Fault_Handler :: struct {
+ handle: (rawptr) -> void;
+ ctx: rawptr;
+}
+
+register_fault_handler :: (ctx: rawptr, handle: (rawptr) -> void) {
+ fault_handlers << .{ handle, ctx };
+
+ if !global_fault_handle_registered {
+ assert(__register_cleanup(#export_name global_fault_handler), "Failed to register global fault handler");
+ global_fault_handle_registered = true;
+ }
+}
+
+#local {
+ #foreign "onyx_runtime" {
+ __register_cleanup :: (name: str) -> bool ---
+ }
+
+ global_fault_handler :: () {
+ for fault_handlers {
+ it.handle(it.ctx);
+ }
+ }
+
+ fault_handlers: [..] Fault_Handler
+ global_fault_handle_registered := false;
+}
+
+
+
#load "./onyx/fs"
#load "./onyx/cptr"
#load "./onyx/cbindgen"
+ #load "./onyx/fault_handling"
}
#if runtime.runtime == .Wasi {
#load "./wasi/wasi"
. ../settings.sh
-# FLAGS="-g3"
+# FLAGS="-g3 -DOVM_DEBUG=1"
# FLAGS="-g3 -DOVM_VERBOSE=1"
FLAGS="-Ofast"
LIBS="-pthread"
break;
brk_send_error:
+ printf("[WARN ] Failed to set breakpoint at %s:%d (%x)\n", filename, line, instr);
+
send_response_header(debug, msg_id);
send_bool(debug, false);
send_int(debug, -1);
#undef CMPXCHG
case OVMI_BREAK:
- printf("onyx: exiting early due to reaching an unreachable instruction.\n");
-
if (state->debug) {
state->debug->state = debug_state_pausing;
state->debug->pause_reason = debug_pause_exception;
sem_wait(&state->debug->wait_semaphore);
}
+ printf("onyx: exiting early due to reaching an unreachable instruction.\n");
+
return ((ovm_value_t) {0});
default:
ONYX_FUNC(__exit)
ONYX_FUNC(__sleep)
ONYX_FUNC(__time)
+ ONYX_FUNC(__register_cleanup)
ONYX_FUNC(__time_localtime)
ONYX_FUNC(__time_gmtime)
ONYX_FUNC(__net_accept)
ONYX_FUNC(__net_connect_unix)
ONYX_FUNC(__net_connect_ipv4)
+ ONYX_FUNC(__net_shutdown)
ONYX_FUNC(__net_send)
ONYX_FUNC(__net_sendto)
ONYX_FUNC(__net_recv)
setsockopt(s, SOL_SOCKET, SO_BROADCAST, (void *) ¶ms->data[2].of.i32, sizeof(int));
break;
}
+
+ case 3: { // :EnumDependent Reuse-Address
+ int s = params->data[0].of.i32;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *) ¶ms->data[2].of.i32, sizeof(int));
+ break;
+ }
}
#endif
#endif
}
+ONYX_DEF(__net_shutdown, (WASM_I32, WASM_I32), ()) {
+ #ifdef _BH_LINUX
+ shutdown(params->data[0].of.i32, params->data[1].of.i32);
+ return NULL;
+ #endif
+
+ #ifdef _BH_WINDOWS
+ #endif
+}
+
ONYX_DEF(__net_send, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
#ifdef _BH_LINUX
// TODO: The flags at the end should be controllable.
return NULL;
}
+
+
+
+
+
+#ifdef _BH_LINUX
+static wasm_func_t *wasm_cleanup_func;
+
+static void unix_signal_handler(int signo, siginfo_t *info, void *context) {
+ wasm_val_vec_t args = WASM_EMPTY_VEC;
+ wasm_val_vec_t results = WASM_EMPTY_VEC;
+ runtime->wasm_func_call(wasm_cleanup_func, &args, &results);
+}
+#endif
+
+ONYX_DEF(__register_cleanup, (WASM_I32, WASM_I32), (WASM_I32)) {
+ #ifdef _BH_LINUX
+
+ int len = (127 < params->data[1].of.i32 ? 127 : params->data[1].of.i32);
+ char name[128];
+ memcpy(name, ONYX_PTR(params->data[0].of.i32), len);
+ name[len] = '\0';
+
+ wasm_extern_t *signal_extern = runtime->wasm_extern_lookup_by_name(
+ runtime->wasm_module,
+ runtime->wasm_instance,
+ name);
+
+ wasm_cleanup_func = runtime->wasm_extern_as_func(signal_extern);
+ if (!wasm_cleanup_func) {
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+ }
+
+ // This is probably not the most complete list, but seems
+ // sufficient for now.
+ if (
+ (signal(SIGSEGV, &unix_signal_handler) == SIG_ERR) ||
+ (signal(SIGQUIT, &unix_signal_handler) == SIG_ERR) ||
+ (signal(SIGINT, &unix_signal_handler) == SIG_ERR) ||
+ (signal(SIGPIPE, &unix_signal_handler) == SIG_ERR) ||
+ (signal(SIGTERM, &unix_signal_handler) == SIG_ERR) ||
+ (signal(SIGHUP, &unix_signal_handler) == SIG_ERR) ||
+ (signal(SIGFPE, &unix_signal_handler) == SIG_ERR))
+ {
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+ }
+
+ results->data[0] = WASM_I32_VAL(1);
+ return NULL;
+ #endif
+
+ #ifdef _BH_WINDOWS
+ #endif
+}
+
#ifdef _BH_LINUX
// This leads to some weirdness and bugs...
//
- pthread_kill(thread->thread, SIGKILL);
+ // pthread_kill(thread->thread, SIGKILL);
+ pthread_cancel(thread->thread);
#endif
#ifdef _BH_WINDOWS