Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve port API and update generic_unix socket_driver to use it #553

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .clang-format-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ src/platforms/esp32/build/*
src/platforms/esp32/components/*
src/platforms/generic_unix/lib/platform_defaultatoms.c
src/platforms/generic_unix/lib/platform_nifs.c
src/platforms/generic_unix/lib/socket_driver.c
src/platforms/stm32/src/lib/platform_defaultatoms.c
src/platforms/stm32/src/lib/platform_nifs.c
src/platforms/stm32/build/*
Expand Down
40 changes: 19 additions & 21 deletions src/libAtomVM/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,39 +87,37 @@ void port_send_message_nolock(GlobalContext *glb, term pid, term msg)
globalcontext_send_message_nolock(glb, local_process_id, msg);
}

void port_ensure_available(Context *ctx, size_t size)
{
if (context_avail_free_memory(ctx) < size) {
switch (memory_ensure_free(ctx, size)) {
case MEMORY_GC_OK:
break;
case MEMORY_GC_ERROR_FAILED_ALLOCATION:
// TODO Improve error handling
fprintf(stderr, "Failed to allocate additional heap storage: [%s:%i]\n", __FILE__, __LINE__);
AVM_ABORT();
case MEMORY_GC_DENIED_ALLOCATION:
// TODO Improve error handling
fprintf(stderr, "Not permitted to allocate additional heap storage: [%s:%i]\n", __FILE__, __LINE__);
AVM_ABORT();
}
void port_ensure_available_with_roots(Context *ctx, size_t size, size_t num_roots, term *roots, enum MemoryShrinkMode shrink_mode)
{
switch (memory_ensure_free_with_roots(ctx, size, num_roots, roots, shrink_mode)) {
case MEMORY_GC_OK:
break;
case MEMORY_GC_ERROR_FAILED_ALLOCATION:
// TODO Improve error handling
fprintf(stderr, "Failed to allocate additional heap storage: [%s:%i]\n", __FILE__, __LINE__);
AVM_ABORT();
case MEMORY_GC_DENIED_ALLOCATION:
// TODO Improve error handling
fprintf(stderr, "Not permitted to allocate additional heap storage: [%s:%i]\n", __FILE__, __LINE__);
AVM_ABORT();
}
}

int port_is_standard_port_command(term t)
bool port_is_standard_port_command(term t)
{
if (!term_is_tuple(t)) {
return 0;
return false;
} else if (term_get_tuple_arity(t) != 3) {
return 0;
return false;
} else {
term pid = term_get_tuple_element(t, 0);
term ref = term_get_tuple_element(t, 1);
if (!term_is_pid(pid)) {
return 0;
return false;
} else if (!term_is_reference(ref)) {
return 0;
return false;
} else {
return 1;
return true;
}
}
}
Expand Down
95 changes: 92 additions & 3 deletions src/libAtomVM/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,19 @@
extern "C" {
#endif

#include <stdbool.h>

#include "context.h"
#include "defaultatoms.h"
#include "globalcontext.h"
#include "term.h"

// Constants to use with port_ensure_available*
#define PORT_ERROR_TUPLE_SIZE TUPLE_SIZE(2)
#define PORT_SYS_ERROR_TUPLE_SIZE TUPLE_SIZE(2) + PORT_ERROR_TUPLE_SIZE
#define PORT_OK_TUPLE_SIZE TUPLE_SIZE(2)
#define PORT_REPLY_SIZE TUPLE_SIZE(2)

// All port_* functions with a given ctx may only be called from the
// executed ctx.
term port_create_tuple2(Context *ctx, term a, term b);
Expand All @@ -41,8 +49,12 @@ term port_create_ok_tuple(Context *ctx, term t);
term port_create_reply(Context *ctx, term ref, term payload);
void port_send_message(GlobalContext *glb, term pid, term msg);
void port_send_message_nolock(GlobalContext *glb, term pid, term msg);
void port_ensure_available(Context *ctx, size_t size);
int port_is_standard_port_command(term msg);
void port_ensure_available_with_roots(Context *ctx, size_t size, size_t num_roots, term *roots, enum MemoryShrinkMode shrink_mode);
static inline void port_ensure_available(Context *ctx, size_t size)
{
port_ensure_available_with_roots(ctx, size, 0, NULL, MEMORY_NO_SHRINK);
}
bool port_is_standard_port_command(term msg);

// Sometimes ports need to send messages while not executed in a given ctx
// (typically event handlers).
Expand All @@ -54,9 +66,86 @@ term port_heap_create_sys_error_tuple(term **heap_ptr, term syscall, int errno);
term port_heap_create_ok_tuple(term **heap_ptr, term t);
term port_heap_create_reply(term **heap_ptr, term ref, term payload);

// Helper to send a message from NIFs or from the native handler.
/**
* @brief Send a reply to a process, typically a port client
* @details Send a reply tuple `{Ref, Payload}` to process pid. This function
* ensures memory is available, eventually garbage collecting terms except
* `ref` and `payload`.
* @param ctx the current context (native handler for ports)
* @param pid the pid to send the reply to
* @param ref the ref that tags the reply
* @param payload reply payload
*/
static inline void port_send_reply(Context *ctx, term pid, term ref, term payload)
{
term roots[2];
roots[0] = ref;
roots[1] = payload;
port_ensure_available_with_roots(ctx, PORT_REPLY_SIZE, 2, roots, MEMORY_NO_SHRINK);
term reply = port_create_reply(ctx, ref, payload);
port_send_message(ctx->global, pid, reply);
}

/**
* @brief Send an ok tuple to a process, typically a port client
* @details Send a reply tuple `{Ref, {ok, T}}` to process pid. This
* function ensures memory is available, eventually garbage collecting terms
* except `ref` and `t`.
* @param ctx the current context (native handler for ports)
* @param pid the pid to send the reply to
* @param ref the ref that tags the reply
* @param t term returned with the ok tuple (can be an atom or any term)
*/
static inline void port_send_ok_tuple(Context *ctx, term pid, term ref, term t)
{
term roots[2];
roots[0] = ref;
roots[1] = t;
port_ensure_available_with_roots(ctx, PORT_REPLY_SIZE + PORT_OK_TUPLE_SIZE, 2, roots, MEMORY_NO_SHRINK);
term payload = port_create_ok_tuple(ctx, t);
term reply = port_create_reply(ctx, ref, payload);
port_send_message(ctx->global, pid, reply);
}

/**
* @brief Send an error tuple to a process, typically a port client
* @details Send a reply tuple `{Ref, {error, Reason}}` to process pid. This
* function ensures memory is available, eventually garbage collecting terms
* except `ref` and `reason`.
* @param ctx the current context (native handler for ports)
* @param pid the pid to send the reply to
* @param ref the ref that tags the reply
* @param reason error reason (can be an atom or any term)
*/
static inline void port_send_error_tuple(Context *ctx, term pid, term ref, term reason)
{
term roots[2];
roots[0] = ref;
roots[1] = reason;
port_ensure_available_with_roots(ctx, PORT_REPLY_SIZE + PORT_ERROR_TUPLE_SIZE, 2, roots, MEMORY_NO_SHRINK);
term payload = port_create_error_tuple(ctx, reason);
term reply = port_create_reply(ctx, ref, payload);
port_send_message(ctx->global, pid, reply);
}

/**
* @brief Send a sys error tuple to a process, typically a port client
* @details Send a reply tuple `{Ref, {error, {Syscall, Errno}}` to process pid.
* This function ensures memory is available, eventually garbage collecting
* terms except `ref` and `syscall`.
* @param ctx the current context (native handler for ports)
* @param pid the pid to send the reply to
* @param ref the ref that tags the reply
* @param syscall error syscall (can be an atom or any term)
* @param errno error number
*/
static inline void port_send_sys_error_tuple(Context *ctx, term pid, term ref, term syscall, int errno)
{
term roots[2];
roots[0] = ref;
roots[1] = syscall;
port_ensure_available_with_roots(ctx, PORT_REPLY_SIZE + PORT_SYS_ERROR_TUPLE_SIZE, 2, roots, MEMORY_NO_SHRINK);
term payload = port_create_sys_error_tuple(ctx, syscall, errno);
term reply = port_create_reply(ctx, ref, payload);
port_send_message(ctx->global, pid, reply);
}
Expand Down
2 changes: 2 additions & 0 deletions src/platforms/generic_unix/lib/platform_defaultatoms.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ static const char *const tcp_atom = "\x3" "tcp";
static const char *const socket_atom = "\x6" "socket";
static const char *const fcntl_atom = "\x5" "fcntl";
static const char *const bind_atom = "\x4" "bind";
static const char *const getpeername_atom = "\xB" "getpeername";
static const char *const getsockname_atom = "\xB" "getsockname";
static const char *const recvfrom_atom = "\x8" "recvfrom";
static const char *const recv_atom = "\x4" "recv";
Expand Down Expand Up @@ -61,6 +62,7 @@ void platform_defaultatoms_init(GlobalContext *glb)
ok &= globalcontext_insert_atom(glb, socket_atom) == SOCKET_ATOM_INDEX;
ok &= globalcontext_insert_atom(glb, fcntl_atom) == FCNTL_ATOM_INDEX;
ok &= globalcontext_insert_atom(glb, bind_atom) == BIND_ATOM_INDEX;
ok &= globalcontext_insert_atom(glb, getpeername_atom) == GETPEERNAME_ATOM_INDEX;
ok &= globalcontext_insert_atom(glb, getsockname_atom) == GETSOCKNAME_ATOM_INDEX;
ok &= globalcontext_insert_atom(glb, recvfrom_atom) == RECVFROM_ATOM_INDEX;
ok &= globalcontext_insert_atom(glb, recv_atom) == RECV_ATOM_INDEX;
Expand Down
42 changes: 22 additions & 20 deletions src/platforms/generic_unix/lib/platform_defaultatoms.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,37 +29,39 @@
#define SOCKET_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 3)
#define FCNTL_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 4)
#define BIND_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 5)
#define GETSOCKNAME_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 6)
#define RECVFROM_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 7)
#define RECV_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 8)
#define SENDTO_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 9)
#define SEND_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 10)
#define GETPEERNAME_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 6)
#define GETSOCKNAME_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 7)
#define RECVFROM_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 8)
#define RECV_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 9)
#define SENDTO_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 10)
#define SEND_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 11)

#define STA_GOT_IP_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 11)
#define STA_CONNECTED_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 12)
#define STA_GOT_IP_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 12)
#define STA_CONNECTED_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 13)

#define ADDRESS_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 13)
#define CONTROLLING_PROCESS_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 14)
#define ACTIVE_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 15)
#define BUFFER_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 16)
#define ADDRESS_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 14)
#define CONTROLLING_PROCESS_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 15)
#define ACTIVE_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 16)
#define BUFFER_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 17)

#define GETADDRINFO_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 17)
#define NO_SUCH_HOST_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 18)
#define CONNECT_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 19)
#define TCP_CLOSED_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 20)
#define GETADDRINFO_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 18)
#define NO_SUCH_HOST_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 19)
#define CONNECT_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 20)
#define TCP_CLOSED_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 21)

#define LISTEN_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 21)
#define BACKLOG_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 22)
#define ACCEPT_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 23)
#define FD_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 24)
#define GENERIC_UNIX_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 25)
#define LISTEN_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 22)
#define BACKLOG_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 23)
#define ACCEPT_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 24)
#define FD_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 25)
#define GENERIC_UNIX_ATOM_INDEX (PLATFORM_ATOMS_BASE_INDEX + 26)

#define PROTO_ATOM term_from_atom_index(PROTO_ATOM_INDEX)
#define UDP_ATOM term_from_atom_index(UDP_ATOM_INDEX)
#define TCP_ATOM term_from_atom_index(TCP_ATOM_INDEX)
#define SOCKET_ATOM term_from_atom_index(SOCKET_ATOM_INDEX)
#define FCNTL_ATOM term_from_atom_index(FCNTL_ATOM_INDEX)
#define BIND_ATOM term_from_atom_index(BIND_ATOM_INDEX)
#define GETPEERNAME_ATOM term_from_atom_index(GETPEERNAME_ATOM_INDEX)
#define GETSOCKNAME_ATOM term_from_atom_index(GETSOCKNAME_ATOM_INDEX)
#define RECVFROM_ATOM term_from_atom_index(RECVFROM_ATOM_INDEX)
#define RECV_ATOM term_from_atom_index(RECV_ATOM_INDEX)
Expand Down
Loading