4.8 KiB
4.8 KiB
Actor System
**Actors** work as an abstraction over data storage and messaging.
It allows for all systems *(GUI, Programs, etc.)* to work together, and rely on the same features.
It reduces work of implementation, and all implementations can use the functions.
Features
- Petnames
- OCAP security
- HMAC message verification
Format
// Different possible types of actors (more to be added)
enum ActorType {
GUI(photon::Widget),
ProgramInterface,
}
// Possible states an actor can be in
enum ActorState {
Receive,
Send,
Work,
Idle,
}
// Cryptographic keypair
struct KeyPair {
privkey: u128,
pubkey: u128,
}
// The actor itself
struct Actor<D: DataInterface> {
petname: Option<String>, // Human-meaningful petname (explored further down)
uuid: Uuid, // Unique identifier
namespace: Uuid, // Parent namespace of this actor
actor_type: ActorType,
state: ActorState,
keys: Option<KeyPair>, // Cryptographic keypair
creation_date: DateTime,
modified_date: DateTime,
data: Option<D>, // Optional data of the generic D type
}
impl Actor {
fn new(namespace: Uuid, a_type: ActorType) -> Self {
Actor {
petname: None,
uuid: Uuid::new(),
namespace: namespace,
actor_type: a_type,
state: ActorState::Idle,
keys: None,
creation_date:: now(),
modified_date: now(),
data: None,
}
};
}
impl KeyPair {
async fn generate_keypair(&mut self) -> Self; // Generate a public/private keypair (threaded)
fn get_pubkey(&self) -> u128; // Return the keypair of an Actor
async fn sign(&self, &[u8]) -> Result<&[u8], Error>; // Sign some data with a private key (threaded)
async fn verify_signature(&[u8], u128) -> Result<(), Error>; // Verify signed data (threaded)
}
trait FilesystemInterface { // Interfacing with the filesystem
async fn read(&mut self) -> Result<(), Error>; // Read the data from the disk into the Actor using the Uuid as a search key
async fn write(&self) -> Result<(), Error>; // Write the data to the disk using the Uuid as a key
}
trait DataInterface { // Necessary data functions
async fn to_bytes(&self) -> Result<&[u8], Error>; // Convert the data into a byte array
}
trait MessageInterface { // Sending & receiving messages
async fn send_message(&self, MessageType, Uuid) -> Result<(), Error>; // Send a message to a recipient
async fn receive_message(&self, Channel) -> Message; // Asynchronously wait for an incoming message, and deal with the first one we get
}
OCAP
TODO
Messages
- postcard for message passing
- Priority Queue for processing multiple messages, while dealing with higher-priority ones first
Messages will be fully modelled so an actor can know exactly what they have to deal with, and what they can send.
Different channels are used to make each one less clogged up, and used only for a specific purpose.
Actors can read from/write to a specific channel, allowing them to ignore the others.
They can then also deal with channels in different ways, maybe deprioritizing the Test
channel.
enum Channel { // Channels for sending/receiving messages on
Graphics, // Low-latency graphics updates
Test, // Designated channel for testing messages
Filesystem, // Batch filesystem operations
Print, // Printing text
Executable, // Executable-related messages
}
enum ProcessCode {
Exit, // Exit the process
Save, // Save data
Clear, // Clear data
Restart, // Restart process
}
enum MessageType {
Ping(String), // Simple test if we can send/recieve a message
FilesystemUpdate(gravitas::FileOperation), // We want to operate on the filesystem
GraphicsUpdate(photon::GraphicsOperation), // Update a graphics window
TextUpdate(String), // Send some text (text mode only)
ProcessUpdate(ProcessCode), // Send some info about an operation to be done on the current process. Usually kernel -> exe
}
struct Message {
id: Uuid, // UUID of the message itself
m_type: MessageType, // Message type & content
priority: u8, // For priority queueing
sender: Uuid, // Who is sending the message
recipient: Uuid, // Who the message is meant for
}
An example message handling loop may look like this:
loop { // Continuously loop through message sending & receiving
actor.send_message(MessageType::Ping("hello!".to_string())).await; // Block and await until we can send the test message.
match actor.receive_message(&self, Channel::Test).await.m_type { // Match on a message type
Ping(s) => println!("We got pinged! {}", s), // Print if we got pinged
_ => {}, // Ignore other states
}
}