int Create(int priority, void (*function)())allocates and initializes a task descriptor, using the given priority, and the given function pointer as a pointer to the entry point of executable code, essentially a function with no arguments and no return value. When Create returns, the task descriptor has all the state needed to run the task, the task's stack has been suitably initialized, and the task has been entered into its ready queue so that it will run the next time it is scheduled.
Return Value
tid | the positive integer task id of the newly created task. The task id must be unique, in the sense that no other active task has the same task id. |
-1 | invalid priority. |
-2 | kernel is out of task descriptors. |
int MyTid()returns the task id of the calling task.
Return Value
tid | the positive integer task id of the task that calls it. |
int MyParentTid( )returns the task id of the task that created the calling task. This will be problematic only if the parent task has exited or been destroyed, in which case the return value is implementation-dependent.
Return Value
tid | the task id of the task that created the calling task. |
void Yield()causes a task to pause executing. The task is moved to the end of its priority queue, and will resume executing when next scheduled.
Comment: Like every entry to the kernel, Yield() reschedules.
void Exit()causes a task to cease execution permanently. It is removed from all priority queues, send queues, receive queues and event queues. Resources owned by the task, primarily its memory and task descriptor, may be reclaimed.
Comment: Exit does not return.
int Send(int tid, const char *msg, int msglen, char *reply, int rplen)sends a message to another task and receives a reply. The message, in a buffer in the sending task's memory, is copied to the memory of the task to which it is sent by the kernel. Send() supplies a buffer into which the reply is to be copied, and the size of the reply buffer, so that the kernel can detect overflow. When Send() returns without error it is guaranteed that the message has been received, and that a reply has been sent, not necessarily by the same task. The kernel will not overflow the reply buffer. If the size of the reply set exceeds rplen, the reply is truncated and the buffer contains the first rplen characters of the reply. The caller is expected to check the return value and to act accordingly. There is no guarantee that Send() will return. If, for example, the task to which the message is directed never calls Receive(), Send() never returns and the sending task remains blocked forever. Send() has a passing resemblance, and no more, to a remote procedure call.
Return Value
>=0 | the size of the message returned by the replying task. The actual reply is less than or equal to the size of the reply buffer provided for it. Longer replies are truncated. |
-1 | tid is not the task id of an existing task. |
-2 | send-receive-reply transaction could not be completed. |
int Receive(int *tid, char *msg, int msglen)If there are no unreceived Sends for this caller, Receive blocks until a message is Sent to the caller. Receive returns with the sent message in its message buffer and tid set to the task id of the task that sent the message. The argument msg must point to a buffer at least as large as msglen. The kernel will not overflow the message buffer. If the size of the incoming message set exceeds msglen, the message is truncated and the buffer contains the first msglen characters of the message sent. The caller is expected to check the return value and to act accordingly.
Return Value
>=0 | the size of the message sent by the sender (stored in tid). The actual message is less than or equal to the size of the message buffer supplied. Longer messages are truncated. |
int Reply(int tid, const char *reply, int rplen)sends a reply to a task that previously sent a message. When it returns without error, the reply has been copied into the sender's memory. The calling task and the sender return at the same logical time, so whichever is of higher priority runs first.
Return Value
>=0 | the size of the reply message transmitted to the original sender task. If this is less than the size of the reply message, the message has been truncated. |
-1 | tid is not the task id of an existing task. |
-2 | tid is not the task id of a reply-blocked task. |
int AwaitEvent(int eventid)blocks until the event identified by eventid occurs then returns with event-specific data, if any.
Return Value
>=0 | event-specific data, in the form of a positive integer. |
-1 | invalid event. |
These interface functions can be implemented as wrappers covering a Send to the name server.
int RegisterAs(const char *name)registers the task id of the caller under the given name. On return without error it is guaranteed that all WhoIs() calls by any task will return the task id of the caller until the registration is overwritten. If another task has already registered with the given name, its registration is overwritten.
Return Value
0 | success. |
-1 | unable to reach name server |
int WhoIs(const char *name)asks the name server for the task id of the task that is registered under the given name. Whether WhoIs() blocks waiting for a registration or returns with an error, if no task is registered under the given name, is implementation-dependent. There is guaranteed to be a unique task id associated with each registered name, but the registered task may change at any time after a call to WhoIs().
Return Value
tid | task id of the registered task. |
-1 | unable to reach name server |
These interface functions can be implemented as wrappers covering Sends to a time server (specified by tid).
int Time(int tid)returns the number of ticks since the clock server was created and initialized. With a 10 millisecond tick and a 32-bit unsigned int for the time wraparound is almost 12,000 hours, plenty of time for your demo. Time is actually a wrapper for a send to the clock server. The argument is the tid of the clock server.
Return Value
>=0 | time in ticks since the clock server initialized. |
-1 | tid is not a valid clock server task. |
int Delay(int tid, int ticks)Blocks the caller until at least until the specified number of ticks has elapsed (since the Delay call), according to the time server identified by tid. The actual wait time is not guaranteed because the caller may have to wait on higher priority tasks.
Return Value
>=0 | success. The current time returned (as in Time()) |
-1 | tid is not reachable or is not a time server |
-2 | negative delay. |
int DelayUntil(int tid, int ticks)Blocks the caller at least until the specified number of ticks has elapsed since the initialization of time server tid. How long after is not guaranteed because the caller may have to wait on higher priority tasks. DelayUntil(tid, Time(tid) + ticks)) is similar to Delay(tid, ticks)).
Return Value
>=0 | success. The current time returned (as in Time()) |
-1 | tid is not reachable or is not a time server |
-2 | negative delay. |
int Getc(int tid, int channel)returns the next un-returned character from the given channel. The first argument is the task id of the appropriate I/O server. How communication errors are handled is implementation-dependent. Getc() is actually a wrapper for a send to the appropriate server.
Return Value
>=0 | new character from the given UART. |
-1 | tid is not a valid uart server task. |
int Putc(int tid, int channel, unsigned char ch)queues the given character for transmission by the given UART. On return the only guarantee is that the character has been queued. Whether it has been transmitted or received is not guaranteed. How communication errors are handled is implementation-dependent. Putc() is actually a wrapper for a send to the appropriate server.
Return Value
0 | success. |
-1 | tid is not a valid uart server task. |
AwaitEvent() requires a table of event ids, which is, in essence a catalogue of hardware events to which the kernel and servers are able to provide a response. This is an awkward intrusion of hardware configuration into an otherwise clean operating system abstraction. There are many different ways of handling this issue within operating system design. Fortunately for you, the hardware environment in which you work is circumscribed and well-defined, so that you can create a small registry of events, provide a reasonably consistent set of responses, and not think too much about the general problem.
You will, no doubt, find that early design of a set of message structures, combined with discipline in your task structure, is important in defending you from bugs that can take a long time to find.