Messages

Cheap Threads provides a facility for passing messages among threads. A message not only conveys information -- it also awakens the recipient, if it is sleeping, and enqueues it for execution at the highest priority.

Sending an Event to a Single Thread

The ct_send_msg() function sends a message to a designated thread. You must supply a message type and a thread handle identifying the recipient, along with the length and contents of the message.

Message types are defined by the application. Each type must be represented by a positive integer. Message type zero is reserved to denote the absence of a message. Message type -1 (cast to a Ct_msgtype) is reserved to denote a timeout.

The thread handle must have been obtained from a successful call to ct_create_thread(), ct_create_sleeping_thread(), or ct_self(). If the thread handle is invalid because the thread has expired, the message will be silently discarded. If the thread handle is invalid for any other reason, the results are undefined.

Optionally, the sender may include arbitrary data with the message by supplying a non-null pointer to the data, together with a non-zero length (for Embedded Cheap Threads this length must not exceed a maximum fixed at compile time). The ct_send_msg() function makes a copy of the data to be delivered later to the recipient. The format of the data is defined by the application. For example, it may contain text, binary fields, pointers, or a mixture of data types.

Short message data will be packaged along with the rest of the message. For data longer than CT_MSG_BUF_LEN bytes (defined in ct.h as 16), ct_send_msg() will dynamically allocate memory as needed and deliver a pointer to that memory. For performance reasons, then, it pays to keep your messages short.

Distributed Messages

It may be enough to send a message to one thread a time, using ct_send_msg(). However, the sender would have to keep track of the thread handles of all the threads it sends messages to. This requirement is burdensome when there are a large number of recipients, especially when they are created and destroyed dynamically.

To address this problem, the ct_distribute_msg() function sends a copy of a message to all the threads that have previously subscribed to messages of that type. The parameters are similar to those of the ct_send_msg() function, except that you don't have to supply a thread handle.

A thread may subscribe to a given message type by calling the ct_subscribe() function. It may be subscribed to more than one message type at the same time. It may cancel a subscription by calling ct_unsubscribe(), or cancel all subscriptions by calling ct_unsubscribe_all().

If a thread is still subscribed to any message types when it expires, Cheap Thread will automatically cancel all the subscriptions.

Broadcast Messages

The ct_broadcast_msg() function sends a message to all threads, including the sender.

Broadcasting a message is like distributing a message type to which all threads have subscribed. It is slightly more efficient, however, because it avoids the overhead involved in keeping track of subscriptions.

Dispatching Messages

The message-sending functions post a message event temporarily to a global event queue.

When the thread returns control to the scheduler, the scheduler dispatches all the events in the global queue. For message events, the scheduler delivers to each recipient a pointer to a shared copy of the message (this copy is deallocated when the last recipient discards it). Finally, the scheduler enqueues each recipient for execution at the highest priority.

There is one exception. If the recipient is already enqueued because of a previous event, the scheduler leaves it where it is in the run queue. Otherwise a succession of events might move the thread repeatedly to the end of the queue, never giving it a chance to run. This exception will make more sense once you have read about the internals of the scheduler.

Receiving a Message

When a thread receives control, it may call the ct_query_msg() function, which returns a struct of type Ct_msgheader. This struct contains the type and length of the message at the head of the queue. A message type of zero means that the queue is empty.

Note that ct_query_msg() does not remove anything from the message queue. It is up to the thread to call either of the following functions:

In either case, the message will be removed and discarded. If it carries more than CT_MSG_BUF_LEN bytes of user data, the associated memory will be deallocated automatically when it is no longer needed.

If there are multiple messages in a thread's input queue, the thread will receive them in the sequence in which they were sent. If a thread returns without removing all of its messages from the input queue, it will be enqueued for execution again at the highest priority, even if it has called ct_wait() to put itself to sleep. As a result, in most cases the thread doesn't need to loop explicitly through its input messages. It can process one message at a time, and let the scheduler handle the looping.

If any messages remain in its input queue when a thread expires, Cheap Threads will automatically destroy them.

Timing Considerations

Whenever a thread sends one or more events, the scheduler guarantees that all of the recipients will run before the sender can run again. Even if the thread sends an event to itself, all other recipients will run before the sender runs again.

This is one of the few guarantees that Cheap Threads can make about the timing of threads, and it may be useful in some cases.

For example, instead of sending a long message, a thread might send a pointer to the message data. Next time the sender runs, it knows that each recipient has received the message, so it can deallocate or overwrite the original. This approach may use less memory and less CPU time than sending a copy of the full message.


Home