Code: Select all
error_t service_open(Connection *id, const char *URL);
Code: Select all
error_t service_msg_lookup(MessageType *msg, Namespace namespace, const char *method_string);
Code: Select all
error_t service_msg_send(Message *msgid, Connection fd, MessageType msg, ...);
Code: Select all
error_t service_msg_wait(Message *msgid, Connection fd);
Code: Select all
error_t service_msg_wait_sel(Connection *cid, Message *msgid, Connection fd[]);
Code: Select all
error_t service_msg_wait_specific(Connection fd, Message msgid);
Code: Select all
error_t service_msg_perform_closure(Message *msg, Connection fd);
Code: Select all
error_t service_msg_resp(Connection fd, Message msg, ...);
Code: Select all
error_t service_msg_resp_clear(Connection fd, Message msgid, ...);
Code: Select all
error_t service_msg_clear(Connection fd, Message msgid);
Usage notes:
Every service runs in user space.
Connections have associated send and receive queues for the client.
Before sending a message, the client must obtain a selector for the desired op.
In the global namespace, predefined selectors exist for most operations (read, write, sync, etc...)
Selectors are reusable for the lifetime of a service connection, or for global selectors, always.
Messages are placed on the send queue.
When a service handles a message, it places that message and any return on the receive queue.
Messages may be of a variable, but strictly limited, size.
Messages involving large amounts of data may use "message blocks", a built in shared memory type.
Services may define kernel side closures.
The closure is defined by giving the kernel a block of (currently) LISP code, and data types/bound variable info.
The LISP code is executed in kernel space, in context of the process sending the message, when a wait state is allowed (called to service_msg_wait_*)
Closures can not enter wait states.
Clients may request any active (non-waited) closure to run, by using service_msg_perform_closure().
This design specifically allows clients to send multiple messages without need to worry about blocking. However, the send and receive queue sizes are limited, so a client must continue clearing responses, or risk being blocked from sending new messages or receiving responses. A client will not be allowed to send a message if the maximum response size for that message is not available in the receive queue (the message will stay in the send queue but not be visible to the service).
There are no guarantees on ordering of message responses - strictly ordered messages would have to be done by waiting for each response between message send commands.
Example interaction, this code has some issues in it's queue management (makes invalid assumptions, no error checking, etc...)
Code: Select all
Connection fd;
MessageType setxy;
Message setxy_msg, write_msg;
char *buffer = "Hello World';
size_t buf_size = strlen(buffer);
int bytes_written;
/* setxy is a closure, execute immediately */
service_open(&fd, "^tty://console");
service_msg_lookup( &setxy, service_namespace(fd), "setxy");
service_msg_send( &setxy_msg, fd, setxy, 15, 15 );
service_msg_perform_closure (&setxy_msg, fd);
service_msg_clear (setxy_msg);
/* Perform blocking write */
service_msg_send( &write_msg, fd, MSG_WRITE, buffer, buf_size);
service_wait_specific (fd, write_msg);
service_msg_resp (fd, write_msg, &bytes_written);
service_msg_clear (fd, write_msg);