packetC utilizes a try-catch-throw exception-handling system similar to many modern languages, such as C++ and Java. In packetC, all exceptions must be caught whether they are system-defined or application-defined. That means that all applications must be wrapped within an error-handler to accommodate catching any errors that may be thrown by the functionality utilized. Any possible errors thrown by an application that are not caught will be flagged by the compiler and compilation will not be allowed to proceed.
Identifiers used by the exception-handling system are named-constants that are either pre-defined by the system or defined by the application to catch exceptions thrown by a portion of the application. Further study on this method of error-handling will show how this can either dominate the architecture of an application or help to keep error-handling out of the primary processing flow, depending upon the amount of up-front planning performed. Additionally, just like other parts of packetC are heavily influenced by scope, so is exception-handling. Ensuring that the scope of caught exceptions is being handled where they are thrown will help to avoid the common application-created exceptions that are trying to be handled at too global of a scope.
For every opcode that produces a possible exception, a location to catch the exception must be present. This leads to the requirement described above whereby the compiler ensures that a program has caught every possible exception to successfully compile. Performance of this approach is enhanced as the opcode itself has the location of the exception-handler and it is not up to additional or subjectively-handled code to determine whether an exception has occurred and whether to handle it. This also ensures more secure code as undetected errors can lead to assumptions about input criteria that may not be visible in a simple audit of a control flow.
The following section describes the control flow within a packetC application. It is important to note that some aspects of the control-flow and exceptions may generate conditions where the application cannot handle the response. This may include the failure of an opcode to perform its expected task. As such, some hypervisor errors will generate exceptions handled by a control plane. While this is out of the scope of packetC, it is important to understand the possibility of that interaction. For example, should the underlying system be unable to receive packets due to an operating system issue, this is out of the scope of a packetC application to handle and as such the exception will not be presented to packetC in the data plane but rather a control plane processor for an exception handler.
try compound_statement catch_list
catch ( identifier ) compound_statement
catch ( … ) compound_statement
packetC uses try-catch-throw error-handling for cleaner code and more advanced error-handling possibilities. Ignoring the fact that not handling errors is very bad programming practice, several methods for error handling we explored and the migration to this mechanism similar to C++ provided the cleanest control flow and deterministic handling. Other possible methods for error handling ranged from return-code testing to On Error handling, both of which did not join well with the flow of packetC applications nor the performance attributes. Try-catch in packetC provides benefits in speed and size, with the added bonus of providing a less error-prone way to handle errors. Given that try-catch-throw is the error-handling method that most modern-day programming languages use, advancing packetC away from the primitive return-code handling of C meets many of the exception-handling demands of a real-time system.
try {
pkt.replicate();
...
// other statements
}
catch( ERR_PKT_NOREPLICATE ) // catch just this error
{…}
catch( ... ) // catch all other errors
{…}
If an exception is thrown in the code sequence of a try block, subsequent statements in the sequence are skipped and the code in the catch block handling the exception is executed.
packetC provides try, catch, and throw constructs as the basis for user error handling. The try construct effectively connects a try block (which may consist of a single packetC statement or a compound statement) with a related group of catch constructs—a catch block. If the execution of any construct in the try block triggers a recognized packetC system error (see table below) or triggers a throw statement, the associated catch block must handle that error.
catch constructs either respond to a single error (identified by a named int constant within parentheses) or handle all errors, indicated by … (an ellipsis) in parentheses. catch statements cannot appear by themselves, they must be associated with a matching try construct.
try {
pkt.replicate();
…
// other statements
}
catch( ERR_PKT_NOREPLICATE ) // catch just this error
{…}
catch( ... ) // catch all other errors
{…}
Each catch clause is associated with one or more statements within curly braces, which take user-specified actions in response to the named error condition. Catch clauses with only one statement must place that statement within curly braces. packetC and C++ both have this requirement.
An implementation may optionally analyze the position of catch-all clauses and issue warnings about catch clauses or catch-all clauses that can never be executed.
// legal – explicit catch clause handles relevant error
try { pkt.replicate(); }
catch ( ERR_PKT_NOREPLICATE ) {…}
// legal – catch-all clause is present
try { pkt.replicate(); }
catch ( ... ) {…}
// ERROR: no catch clause for replicate construct's ERR_PKT_NOREPLICATE
try { pkt.replicate(); }
catch ( ERR_PKT_NOTREQUEUED ) {…}
Some packetC constructs are capable of implicitly throwing a predefined error when they appear within a try statement or block of statements (see Table 13-1). It is a compilation error if any system error that could be thrown by a packetC construct is not handled by an associated catch clause. This requires the construct that could throw the error to appear within a try block.
a = b;
pkt.replicate(); // ERROR: ERR_PKT_NOREPLICATE not handled
b = c;
pkt.replicate();
catch ( ERR_PKT_NOREPLICATE ) {…}
// Error: catch cannot appear stand-alone;
// must be associated with a try construct.
A throw statement explicitly throws the solitary error indicated by its lone argument. That error is a unique constant of type int and is either a predefined system error (as listed in the table above and in file cloudshield.ph) or an error defined by the user. It is a compilation error for the thrown error not to be caught by an appropriate catch statement.
The placement and behavior of throw statements are governed by these rules:
// Assume SOME_ERROR, SOME_OTHER_ERROR are defined "const ints"
try {
…
throw SOME_ERROR;
}
catch ( SOME_OTHER_ERROR ) {…}
catch (…) {…} // Catch all other errors (SOME_ERROR in this case)
In the following example, both of the throw statements are handled by the same catch clause.
try {
try {
...
}
catch( ERR_DB_FULL ) {
...
throw MY_ERROR; // outer try is the enclosing try block
}
catch( ... ) {
...
}
...
throw MY_ERROR;
...
}
catch( ERR_PKT_NOREPLICATE ) {
...
}
catch( MY_ERROR ) {
...
}
In the following example, the throw statement throws an error outside of any try-block, creating an unhandled error.
try {
...
}
catch ( AN_ERROR ) {
...
throw SOME_ERROR; // thrown outside of catch's associated try-block
}
catch ( OTHER_ERROR ) {}
catch (…) {…}
// COMPILER ERROR: SOME_ERROR throw statement does not appear
// within any enclosing try block
A packetC implementation lists all the error numbers it uses for predefined packetC errors (i.e., those implicitly thrown by packetC commands) in the include file “cloudshield.ph”. It is recommended that the user use the ERR_LAST_DEFINED, which is defined as the highest integer value used for system-defined errors. User-provided error numbers can calculate their values in terms of ERR_LAST_DEFINED to ensure unique values.
const int USER_ERROR1 = ERR_LAST_DEFINED + 1;
const int USER_ERROR2 = ERR_LAST_DEFINED + 2;
const int USER_ERROR3 = ERR_LAST_DEFINED + 3;
…
This section lists the conditions for which a packetC implementation should describe system-defined responses. While packetC does not prescribe specific behavior for these various states, it does not ascribe “undefined behavior” for them, as C99 does.
packetC uses the following declarations, available in the file cloudshield.ph for the user to reference.
/* The pre-defined packetC errors and their associated values are shown below:
const int ERR_DB_FULL = 1;
const int ERR_DB_READ = 2;
const int ERR_DB_NOMATCH = 3;
const int ERR_PKT_INSERT = 4;
const int ERR_PKT_DELETE = 5;
const int ERR_PKT_NOREPLICATE = 6;
const int ERR_SET_NOMATCH = 7;
const int ERR_SET_NOTFOUND = 9;
const int ERR_PKT_NOTREQUEUED = 10;
*/
const int ERR_LAST_DEFINED = 64;
packet module searchpayload;
#include <cloudshield.ph>;
#include <protocols.ph>;
regex searchset regexSet[2][4] = {"GET", "POST"};
%pragma datatype regexSet ( regex1 );
void main( $PACKET pkt, $PIB pib, $SYS sys )
{
searchResult rslt;
try {
rslt = regexSet.find( pkt[pib.payloadOffset:end] );
pib.action = FORWARD_PACKET;
}
catch ( ERR_SET_NOTFOUND )
{
pib.action = DROP_PACKET;
}
}
3.135.198.174