C H A P T E R 2 |
User API |
This chapter describes the User API which consists of functions the user can deploy in the application code. This API consists of three main parts:
Additional information is provided in:
The Late-Binding API provides primitives for the synchronization of distributed threads, communication, and memory allocation. This API is treated specially by the tejacc compiler, and is generated dynamically based on contextual information. See the Sun Netra Data Plane Software Suite 2.1 Update 1 User Guide for an overview of this API.
Acquires a mutual exclusion lock. If the mutex is already locked, this function does not return until the mutex becomes available and the lock is acquired for the calling thread. Once the lock is held by the thread, what occurs if this function is called a second time is undefined. If an error is returned, the caller can assume the lock was not acquired.
int teja_mutex_lock(teja_mutex_t mutex);
int - If successful, this function returns 0. In case of an error, this function
returns -1.
Attempts to lock the given mutex without blocking. If the mutex is already locked, this function exits immediately returning -1, otherwise the function locks the mutex and returns 0. Once the lock is held by the thread, what occurs if this function is called a second time is undefined.
int teja_mutex_trylock(teja_mutex_t mutex);
int - If successful, this function returns 0. In case of an error, this function returns
-1.
Unlocks the given mutex. If the mutex was not locked by the current thread the result is undefined. Avoid such behavior.
int teja_mutex_unlock(teja_mutex_t mutex);
int - If successful, this function returns 0. In case of an error, this function returns
-1.
See the examples in teja_mutex_lock and teja_mutex_trylock.
The first word of the node that is enqueued is permitted to be overwritten by the queue implementation.
Enqueues a node into a queue. The queue implementation is permitted to overwrite the first word of the node. If -1 is returned, the queue might be full or some other error has occurred.
int teja_queue_enqueue(teja_queue_t queue, void *node);
node - Pointer to node to enqueue.
int - If successful, this function returns 0. In case of an error, this function returns -1.
void * node; node = teja_malloc (16); if (node) { if (teja_queue_enqueue (queue, node) < 0) { printf (“Error while attempting to enqueue a node”); } } |
Dequeues a pointer to a node from the queue. The first word of the returned node might have been overwritten by the queue implementation.
void *teja_queue_dequeue(teja_queue_t queue);
queue - Queue to dequeue from.
void * - NULL if the queue was empty or pointer to the dequeued node otherwise.
void * node; node = teja_queue_dequeue (queue); if (node) { printf (“Dequeued node %x\n”, node); } else { printf (“Queue was empty\n”); } |
Tests to see if the queue is empty.
int teja_queue_is_empty(teja_queue_t queue);
int - 0 if the queue is not empty, 1 if the queue is empty.
if (teja_queue_is_empty (queue)) { printf ("Queue is empty\n"); } else { printf ("Queue is not empty\n"); } |
Returns the number of elements in the queue. The function returns a value that is a snapshot in time of the depth of the queue. Not all custom implementations support this function. This function is to be used for debug purposes only, because its implementation (when available) is computation intensive and not meant for fast path operation.
int teja_queue_get_size(teja_queue_t queue);
queue - Queue to obtain size for.
int - Value is -1 if implementation is not provided for this custom implementation, or the number of elements currently in the queue.
Returns a pointer to a newly allocated fixed-sized node from the given memory pool.
void *teja_memory_pool_get_node(teja_memory_pool_t memory-pool);
memory-pool - Memory pool to allocate from.
void * - NULL if the memory pool is empty or the pointer to the newly allocated node.
Frees a node back to a memory pool.
int teja_memory_pool_put_node(teja_memory_pool_t memory-pool, void *node);
memory-pool - Memory pool to return the node to.
node - Pointer to node to free.
int - If successful, this function returns 0. In case of an error, this function returns -1.
See the example in teja_memory_pool_get_node.
Memory pool nodes are contiguous in memory and have a sequential index number. This function returns the node that corresponds to the given index. The effect of this function is not equivalent to a teja_memory_pool_get_node call because the node is not actually extracted from the pool. For this reason, the node must be allocated and not free in the memory pool when used by the application. For performance reasons, a range check is not performed, so the index value must be valid or a programming flaw might occur.
void *teja_memory_pool_get_node_from_index(teja_memory_pool_t memory-pool, int index);
memory-pool - Memory pool from which the node belongs.
void * - Pointer to the node specified by index.
void * node; node = teja_memory_pool_get_node_from_index (pool, 3); if (teja_memory_pool_get_index_from_node (pool, node) != 3) { printf ("Impossible!\n"); } |
Memory pool nodes are contiguous in memory and have a sequential index number. This function returns the index that corresponds to the given node pointer. For performance reasons, a range check is not performed, so the node value must be valid or a programming flaw might occur.
int teja_memory_pool_get_index_from_node(teja_memory_pool_t memory-pool, void *node);
memory-pool - Memory pool from which the node belongs.
node - Pointer to a node for which the index is requested.
int - Index of the given node.
See the example in teja_memory_pool_get_node_from_index.
Returns 1 if the connection is open, 0 if the connection is closed.
int teja_channel_is_connection_open(teja_channel_t channel);
int - 1 if the connection is open, 0 if the connection is closed.
See the example in teja_channel_send.
Establishes a connection on the given channel (if the channel requires the connection to be established at runtime).
int teja_channel_make_connection(teja_channel_t channel);
channel - Channel to operate on.
int - 0 if operation was successful, -1 otherwise.
See the example in teja_channel_send.
Breaks an existing connection on the given channel.
int teja_channel_break_connection(teja_channel_t channel);
channel - Channel to operate on.
int - 0 if operation was successful, -1 otherwise.
See the example in teja_channel_send.
Sends message-size bytes of data into the channel for the user. This function optionally enables users to send an event value that can be used at the receiver to discriminate the data type of the received data. This functionality is useful if multiple data types are sent. The event logic can be disabled by passing TEJA_NO_EVENT. Depending upon the channel implementation, the user might also be signaled at the time the message is sent.
int teja_channel_send(teja_channel_t channel, short int event, void *message, int message-size);
channel - Channel to send data on.
event - Optional value that is sent on the channel with the data. This value can be used at the receiver to discriminate the data type of the received data. This parameter is optional. Passing the constant TEJA_NO_EVENT causes event logic to be skipped in the code generation.
message - Pointer to the data to send.
message-size - Size of the message being sent (in bytes).
int - Number of bytes sent or -1 in case of error.
This example shows how to send data using a channel.
See also the example in teja_wait, which shows how to receive data from the channel.
The teja_wait() call enables users to wait for a timeout to expire or for data to arrive on a list of channels, whichever happens first. This function’s semantics are similar to the select() call on UNIX (or Linux) systems. For targets on which the TEJA_IS_RAW_OS constant is not defined, the teja_wait() call can also be interrupted by Sun Netra DPS signals and by registered file descriptors.
Waits for a timeout, for data arriving on one of the channels, or for any registered signals or file descriptor to be triggered, whichever happens first. For more information on signal and file descriptor registration. Channels are checked once before starting the timeout wait.
int teja_wait(int seconds, int nanoseconds, int poll-seconds, int poll-nanoseconds, short int *event, void *buffer, int buffer-size, ...);
seconds - Number of seconds to wait. Passing TEJA_INFINITE_WAIT causes the function to wait indefinitely.
nanoseconds - Number of nanoseconds to wait. This value must be from 0 to 999999999.
poll-seconds - Number of seconds to wait before polling channels. Passing TEJA_INFINITE_WAIT causes the function not to poll channels.
poll-nanoseconds - Number of nanoseconds to wait before polling channels. This value must be from 0 to 999999999.
event - Pointer to a variable in which the event value is copied. Passing NULL causes event logic to be skipped.
buffer - Pointer to buffer in which received data is copied.
buffer-size - Size of the buffer.
... - List of channels to read from. The list must be NULL terminated.
int - Returns -1 if error, 0 if timeout expires, or the number of bytes read from channels and copied into the buffer.
The seconds and nanoseconds parameters identify the timeout. If TEJA_INFINITE_WAIT is passed to seconds, then no timing logic is generated and the function waits indefinitely until some data arrives on the channels. The poll-seconds and poll-nanoseconds identify the amount of time to wait between channel polls, while waiting.
event is an optional parameter. If a non-NULL value is passed the event value coming from the sender is copied in the variable pointed by the event parameter. Typically event is used to discriminate among a set of possible types for the received data so the event can determine what data type to cast the received data to. In case event is not needed (for example, if only one data type is sent on the channel) then the code generator can be instructed to skip event management logic by using TEJA_NO_EVENT at the sender and NULL at the receiver.
Buffer and buffer-size identify the buffer in which received data is copied and its size.
The final variable argument list consists of a NULL-terminated channel list. The order in which channels are listed is the same that is used to poll channels. If no channels are listed, then only timing logic is generated.
Note - This function may not be invoked at initialization time. |
This example shows how to receive data from a channel using teja_wait().
See also the example in teja_channel_send, which shows how to send data.
The Sun Netra DPS Runtime API consists of portable abstractions over various operating system facilities such as threads, nonmemory pool-based memory management, thread management, socket communication, and signal registration and handling. Unlike late-binding APIs, Sun Netra DPS Runtime APIs are not treated specially by the compiler and are implemented in precompiled libraries. See the Sun Netra Data Plane Software Suite 2.1 Update 1 User Guide for an overview of this API.
The memory management functions offer malloc and free functionality. These functions are computation expensive and only used in initialization code or non-relative critical code. On bare hardware targets the free() function is an empty operation, so use malloc() only to obtain memory that is not meant to be released. For all other purposes, use the memory pool API.
Frees memory buffer. On bare hardware targets this operation is empty.
ptr - Pointer to buffer to free.
Allocates memory buffer of specified size. On bare hardware targets the teja_free() operation is empty, so use teja_malloc()only to obtain memory that is not meant to be released. For all other purposes, use the memory pool API.
void *teja_malloc(size_t size);
size - Size in bytes of memory to allocate.
void * - Value to be used as pointer to allocated buffer.
Extends the memory buffer to become as big as the specified size. The new block might be allocated at a new address if there was not enough space for size bytes at the original location.
void *teja_realloc(void *ptr, size_t size);
ptr - Pointer to memory to reallocate.
size - Size in bytes of memory to allocate.
void * - Pointer to newly allocated memory or NULL if the operation failed. In case of failure the original block is left untouched.
This API offers thread management functionality. The teja_thread_t type implements thread IDs and the type can be assigned thread identifiers defined in the software architecture. Indicate these thread identifiers as strings in the software architecture using teja_thread_create(). In the user application, these identifiers are used as C identifiers (not as strings), which are defined by the compiler.
Two data types can be used to identify threads:
Returns the thread ID of the current thread. The thread ID can be compared against thread identifiers defined in the software architecture.
teja_thread_t teja_get_thread_id(void);
teja_thread_t - Thread ID of the current thread.
Returns the name of the given thread.
char *teja_get_thread_name_for_id(teja_thread_t thread-id);
thread-id - ID of the thread to operate on.
char * - Name of the given thread.
Returns the ID of the given thread.
teja_thread_t teja_get_id_for_thread_name(char *name);
name - Name of the thread to operate on.
teja_thread_t - ID of the given thread.
Starts a new thread dynamically, executing the given function. This function is available only on OS-based targets or targets for which the TEJA_IS_RAW_OS constant is not defined.
int teja_thread_handle_start(teja_thread_handle_t *thread, teja_thread_function_t function, void *arg, int stack-size, int priority);
thread - Pointer to an uninitialized TejaThread instance. Upon successful execution, the thread contains a properly set up TejaThread handler.
function - Main function of the thread.
arg - Argument that is passed to the thread main function.
stack-size - Size of the stack for the thread. This functionality is not available on all systems. A predefined value is TEJA_DEFAULT_STACK_SIZE.
priority - Priority of the thread. This functionality is not available on all systems. predefined value is TEJA_DEFAULT_PRIORITY.
int - 0 if execution was successful, -1 if an error occurred.
Ends a thread that was started with teja_thread_handle_start(). Do not use this function on threads defined in the software architecture. For software architecture threads use teja_thread_shutdown(). This function is available only on OS-based targets or targets for which the TEJA_IS_RAW_OS constant is not defined.
void teja_thread_handle_end(void);
Returns the thread handle pointer for the given thread ID. This function is available only on OS-based targets or targets for which the TEJA_IS_RAW_OS constant is not defined.
teja_thread_handle_t *teja_thread_handle_get_for_thread_id(int thread-id);
thread-id - ID of the thread to operate on.
teja_thread_handle_t * - Handle pointer for the given thread ID.
Shuts down the current Sun Netra DPS thread.
Note - This function may not be invoked at initialization time. |
void teja_thread_shutdown(void);
Returns the current time in seconds and nanoseconds. The precision depends on the granularity of the underlying system clock.
int teja_get_time(int *seconds, int *nanoseconds);
seconds - User-provided variable that contains the current seconds after the call.
nanoseconds - User-provided variable that contains the current nanoseconds after the call.
int - 0 on success, -1 on error.
Causes the current thread to sleep the specified time. The actual sleep time varies, depending upon the granularity of the underlying system clock and the system overhead involved in rescheduling the thread.
Note - This function is implemented as a macro on top of teja_wait(). This function may not be invoked at initialization time. |
int teja_wait_time(int seconds, int nanoseconds);
seconds - Number of seconds to wait. Passing TEJA_INFINITE_WAIT causes the function to wait indefinitely
nanoseconds - Number of nanoseconds to wait. This value must be between 0 and 999999999.
int - 0 on success, -1 on error.
Causes the current thread to sleep the specified time. The actual sleep time varies depending upon the granularity of the underlying system clock and the system overhead to reschedule the thread. Unlike teja_wait_time() this function is not implemented on top of teja_wait().
int teja_os_wait(int seconds, int nanoseconds);
seconds - Number of seconds to wait. Passing TEJA_INFINITE_WAIT causes the function to wait indefinitely.
nanoseconds - Number of nanoseconds to wait. This value must be contained between 0 and 999999999.
int - 0 on success, -1 on error.
Returns the number of arguments passed to the program on the command line.
Returns an array of strings containing the arguments passed to the program on the command line.
This macro-based API can be used to implement efficient state machine logic within a Sun Netra DPS application. States are computational elements and transitions are program flow elements that connect states.
These functions are available in different versions:
State machines come with a user-defined context. The first field of the context must be a void * pointer and is reserved for the system. The user can freely add other fields.
The multiple-context version of the API invokes a user-provided scheduler to switch in a new context at the end of each transition. This is an efficient way to implement parallel execution on single-threaded systems. For example, while a context waits, the state machine could switch in a new context and continue computation, thus increasing the CPU utilization.
The single-context version of the API uses a simple pointer scheduler and does not perform any switching. This version is useful on architectures that support multithreading in hardware.
The user might choose an implementation based on computed gotos, versus function pointers. Computed gotos might perform faster, but not all target compilers support them.
Note - State machines need to be declared outside of functions. |
Declares a state machine with the given name. This function must be used in the global scope outside functions.
#define teja_fsm_declare(name)
name - Name of the state machine.
Starts the definition of a state machine of the given name. This function must be used after teja_fsm_declare and must be used in the global scope outside functions. No semi-colon (;) is required at the end of this call.
#define teja_fsm_begin(name initial-state-name context-scheduler context-iterator)
name - Name of the state machine.
initial-state-name - Name of the initial state.
context-scheduler - If using single-context mode, this is the pointer to the context. If using multi-context mode this is the name of a user-defined function (of signature void * f (void)) returning the next context.
context-iterator - Name of a user-defined function (of signature void * f (void)) that returns a pointer to the next context until there are no more contexts, in which case the function returns NULL. The system uses this function to iterate over the contexts in the beginning in order to initialize them. This function is not used in single-context mode.
Ends the definition of a state machine. This function must be used after teja_fsm_begin and must be used in the global scope outside functions. No semi-colon (;) is required at the end of this call.
Starts execution of a state machine with the given name. This function must be used inside a function.
name - Name of the state machine.
Declares a state with the given name. This function must be used inside a state machine declaration, immediately after teja_fsm_begin.
#define teja_fsm_state_declare(name)
Starts the definition of a state of the given name. This function must be used inside a state machine after all teja_fsm_declare calls. The user can add regular C code immediately after this macro up to the teja_fsm_state_end macro. No semi-colon (;) is required at the end of this call.
#define teja_fsm_state_begin(name)
Ends the definition of a state. This function must be used after teja_fsm_state_begin. The user can add regular C code immediately before this macro. No semi-colon (;) is required at the end of this call.
Performs a jump to the given state. This function must be used inside a state definition (that is between teja_fsm_state_begin and teja_fsm_state_end). This macro can be invoked No semi-colon (;) is required at the end of this call.
#define teja_fsm_goto_state(name)
name - Name of the state to jump to.
EXAMPLE 2-1 implements a simple state machine depicted in FIGURE 2-1.
FIGURE 2-1 Finite State Machine Example
Returns the value of the clock tick register.
uint64_t teja_os_get_timer(void);
int - Returns the value of the clock tick register.
Sun Netra DPS programs running on bare hardware CMT can use the following standard C library functions:
Copyright © 2010, Oracle and/or its affiliates. All rights reserved.