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

External messaging. #1807

Merged
merged 13 commits into from
May 13, 2024
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ endif()
set(TOIT_GENERIC_FLAGS "${TOIT_GENERIC_FLAGS} -Wall -ffunction-sections -fdata-sections")

include_directories(
"${TOIT_SDK_SOURCE_DIR}/include"
"${IDF_PATH}/components/mbedtls/mbedtls/include"
)

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ else
endif

.PHONY: all
all: sdk
all: sdk build-test-assets

.PHONY: debug
debug:
LOCAL_CXXFLAGS="-O0" $(MAKE) BUILD_TYPE=Debug
cmake -E env LOCAL_CFLAGS="-O0" LOCAL_CXXFLAGS="-O0" $(MAKE) BUILD_TYPE=Debug

.PHONY: sdk
sdk: tools toit-tools version-file
Expand Down
86 changes: 86 additions & 0 deletions include/toit/toit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (C) 2024 Toitware ApS.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 only.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// The license can be found in the file `LICENSE` in the top level
// directory of this repository.

#pragma once

#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif

/*
* C interface for Toit's external API.
*/

typedef enum {
// The operation succeeded.
TOIT_ERR_SUCCESS = 0,
// The operation encountered an out-of-memory error.
TOIT_ERR_OOM,
// An error, for when the receiver of a system message didn't exist.
TOIT_ERR_NO_SUCH_RECEIVER,
// The corresponding resource was not found.
TOIT_ERR_NOT_FOUND,
// An unknown error.
TOIT_ERR_ERROR,
} toit_err_t;

const int TOIT_MSG_RESERVED_TYPES = 64;

struct toit_msg_context_t;
typedef struct toit_msg_context_t toit_msg_context_t;

typedef struct {
int sender;
int request_handle;
toit_msg_context_t* context;
} toit_msg_request_handle_t;

typedef toit_err_t (*toit_msg_on_created_cb_t)(void* user_data, toit_msg_context_t* context);
typedef toit_err_t (*toit_msg_on_message_cb_t)(void* user_data, int sender, void* data, int length);
typedef toit_err_t (*toit_msg_on_request_cb_t)(void* user_data,
int sender,
int function,
toit_msg_request_handle_t rpc_handle,
void* data, int length);
typedef toit_err_t (*toit_msg_on_removed_cb_t)(void* user_data);

typedef struct toit_msg_cbs_t {
toit_msg_on_created_cb_t on_created;
toit_msg_on_message_cb_t on_message;
toit_msg_on_request_cb_t on_rpc_request;
toit_msg_on_removed_cb_t on_removed;
} toit_msg_cbs_t;

toit_err_t toit_msg_add_handler(const char* id,
void* user_data,
toit_msg_cbs_t cbs);

toit_err_t toit_msg_remove_handler(toit_msg_context_t* context);

toit_err_t toit_msg_notify(toit_msg_context_t* context,
int target_pid,
void* data, int length,
bool free_on_failure);

toit_err_t toit_msg_request_reply(toit_msg_request_handle_t handle, void* data, int length, bool free_on_failure);
toit_err_t toit_msg_request_fail(toit_msg_request_handle_t handle, const char* error);

toit_err_t toit_gc();

#ifdef __cplusplus
}
#endif
19 changes: 19 additions & 0 deletions lib/core/message_.toit
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ SYSTEM-RPC-CANCEL_ ::= 5
SYSTEM-RPC-NOTIFY-TERMINATED_ ::= 6
SYSTEM-RPC-NOTIFY-RESOURCE_ ::= 7

// System message types for external notifications.
SYSTEM-EXTERNAL_NOTIFICATION_ ::= 8

RESERVED-MESSAGE-TYPES_ ::= 64

/**
Sends the $message with $type to the process identified by $pid and
returns whether the $message was delivered.
Expand All @@ -29,6 +34,14 @@ process-send_ pid/int type/int message -> bool:
serialization-failure_ it[0]
throw it

/**
Returns the process ID for the process with the given external $id.

If no process with the external ID exists, returns -1.
*/
pid-for-external-id_ id/string -> int:
#primitive.core.pid-for-external-id

/** Registered system message handlers for this process. */
system-message-handlers_ ::= {:}

Expand All @@ -45,6 +58,12 @@ Sets the $handler as the system message handler for message of the $type.
set-system-message-handler_ type/int handler/SystemMessageHandler_:
system-message-handlers_[type] = handler

/**
Removes the handler for the given $type.
*/
clear-system-message-handler_ type/int:
system-message-handlers_.remove type

/** Flag to track if we're currently processing messages. */
is-processing-messages_ := false

Expand Down
83 changes: 83 additions & 0 deletions lib/system/external.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (C) 2024 Toitware ApS. All rights reserved.
// Use of this source code is governed by an MIT-style license that can be
// found in the lib/LICENSE file.

import io
import rpc

clients_ ::= {:}
notification-listeners_ := 0
notification-handler_/ExternalMessageHandler_? := null

class Client:
pid/int
id/string
on-notify_/Lambda? := null
is-closed_/bool := false

constructor.private_ .pid .id .on-notify_:

static open id/string --on-notify/Lambda?=null -> Client?:
pid := pid-for-external-id_ id
if pid == -1: throw "NOT_FOUND"
if clients_.contains pid: throw "ALREADY_IN_USE"
result := Client.private_ pid id on-notify
clients_[pid] = result
return result

close -> none:
if is-closed_: return
// Go through the function so that the ref-counting is correct.
set-on-notify null
is-closed_ = true

is-closed -> bool:
return is-closed_

/** Helper to convert the $message to a ByteArray. */
encode-message_ message/io.Data --copy/bool -> ByteArray:
bytes/ByteArray := ?
if copy or message is not ByteArray:
bytes = ByteArray message.byte-size
message.write-to-byte-array bytes --at=0 0 message.byte-size
else:
bytes = message as ByteArray
return bytes

notify message/io.Data --copy/bool=true:
if is-closed_: throw "ALREADY_CLOSED"
bytes := encode-message_ message --copy=copy
process-send_ pid SYSTEM-EXTERNAL-NOTIFICATION_ bytes

request function/int message/ByteArray --copy/bool=true -> any:
if is-closed_: throw "ALREADY_CLOSED"
bytes := encode-message_ message --copy=copy
return rpc.invoke pid function bytes

set-on-notify callback/Lambda? -> none:
if is-closed_: throw "ALREADY_CLOSED"
if on-notify_ != null: notification-listeners_--
on-notify_ = callback
if callback:
notification-listeners_++
ExternalMessageHandler_.handle-listener-change

class ExternalMessageHandler_ implements SystemMessageHandler_:
static TYPE ::= SYSTEM-EXTERNAL-NOTIFICATION_

close -> none:
clear-system-message-handler_ TYPE

on-message type/int gid/int pid/int argument -> none:
client/Client? := clients_.get pid
if not client: return
if client.on-notify_:
client.on-notify_.call argument

static handle-listener-change -> none:
if notification-listeners_ > 0 and not notification-handler_:
notification-handler_ = ExternalMessageHandler_
set-system-message-handler_ TYPE notification-handler_
else if notification-listeners_ <= 0 and notification-handler_:
notification-handler_.close
notification-handler_ = null
3 changes: 2 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ set_source_files_properties(interpreter_run.cc PROPERTIES COMPILE_OPTIONS "-O3;$
set_source_files_properties(utils.cc PROPERTIES COMPILE_FLAGS "-DTOIT_MODEL=\"\\\"${TOIT_MODEL}\\\"\" -DVM_GIT_INFO=\"\\\"${VM_GIT_INFO}\\\"\" -DVM_GIT_VERSION=\"\\\"${TOIT_GIT_VERSION}\\\"\"")

set(GEN_DIR "${PROJECT_BINARY_DIR}/generated")

set(BOOT_SNAPSHOT ${GEN_DIR}/toit.run.snapshot)
set(BOOT_SNAPSHOT_CC ${GEN_DIR}/toit.run.snapshot.cc)
set(TOIT_SNAPSHOT ${GEN_DIR}/toit.snapshot)
Expand All @@ -131,6 +130,8 @@ add_custom_command(
WORKING_DIRECTORY "${GEN_DIR}"
)

add_custom_target(build_boot_snapshot DEPENDS ${BOOT_SNAPSHOT_CC})

add_custom_command(
OUTPUT "${TOIT_SNAPSHOT_CC}"
# We can't use ${TOIT_SNAPSHOT} here, as that would include the full path.
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/propagation/type_primitive_core.cc
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ TYPE_PRIMITIVE(process_send) {
failure.add_array(program);
}

TYPE_PRIMITIVE_INT(pid_for_external_id)

TYPE_PRIMITIVE(spawn) {
result.add_smi(program);
failure.add_string(program);
Expand Down