ABORT

Command: Exit from PUB/PRI method using abort status with optional return Value.

((PUBPRI))

  ABORT < Value >


Returns: Either the current RESULT value, or Value if provided.

  • Value is an optional expression whose value is to be returned, with abort status, from the PUB or PRI method.

Explanation

ABORT is one of two commands (ABORT and RETURN) that terminate a PUB or PRI method's execution.

ABORT causes a return from a PUB or PRI method with abort status; meaning it pops the call stack repeatedly until either the call stack is empty or it reaches a caller with an Abort Trap, ( \ ), and delivers a value in the process.

ABORT is useful for cases where a method needs to terminate and indicate an abnormal or elevated status to the immediate caller or one its previous callers. For example, an application may be involved in a complicated chain of events where any one of those events could lead to a different branch of the chain or a final action decision. It may be easier to write that application using small, specialized methods that are called in a nested fashion, each meant to deal with a specific sub-event in the chain. When one of the simple methods determines a course of action, it can issue an abort that completely collapses the nested call chain and prevents all the intermediate methods from continuing.

When ABORT appears without the optional Value, it returns the current value of the PUB/PRI's built-in RESULT variable. If the Value field was entered, however, the PUB or PRI aborts and returns that Value instead.

About the Call Stack

When methods are called simply by referring to them from other methods, there must be some mechanism in place to store where to return to once the called method is completed. This mechanism is a called a "stack" but we'll use the term "call stack" here. It is simply RAM memory used to store return addresses, return values, parameters and intermediate results. As more and more methods are called, the call stack logically gets longer. As more and more methods are returned from (via RETURN or by reaching the end of the method) the call stack gets shorter. This is called "pushing" onto the stack and "popping" off of the stack, respectively.

The RETURN command pops the most recent data off the call stack to facilitate returning to the immediate caller; the one who directly called the method that just returned. The ABORT command, however, repetitively pops data off the call stack until it reaches a caller with an Abort Trap (see below); returning to some higher-level caller that may have just been one call, or many calls, up the nested chain of calls. Any return points along the way between an aborting method and an abort trapping method are ignored and essentially terminated. In this way, ABORT allows code to back way out of a very deep and potentially complicated series of logic to handle a serious issue at a high level.

Using ABORT

Any method can choose to issue an ABORT command. It's up to the higher-level code to check for an abort status and handle it. This higher-level code can be either that which called an aborting method directly, or via some other set of methods. To issue an ABORT command, use something like the following:

if <bad condition>
  abort                          'If bad condition detected, abort 

—or—

if <bad condition>
  abort <value>                  'If bad condition detected, abort with value 

...where <bad condition> is a condition that determines the method should abort and <value> is a value to return upon aborting.

The Abort Trap ( \ )

To trap an ABORT, the call to the method or method chain that could potentially abort must be preceded with the Abort Trap symbol, a backslash ( \ ). For example, if a method named MayAbort could possibly abort, or if it calls other methods that may abort, a calling method could trap this with the following:

if \MayAbort                     'Call MayAbort with abort trap
  abort <value>                  'Process abort 

The type of exit that MayAbort actually used, ABORT or RETURN, is not automatically known by the trapping call; it may have just happened to be the destination of a RETURN command. Therefore, the code must be written in a way to detect which type was used.

Some possibilities are:

  1. Code may be designed such that a high-level method is the only place that traps an abort and other mid-level code processes things normally without allowing RETURNs to propagate higher.
  2. Aborting methods may return a special value that can not occur in any normal circumstance.
  3. A global flag can be set by the aborting method prior to aborting.

Example Use Of Abort

The following is an example of a simple-minded robot application in which the robot is designed to move away from an object it senses with its four sensors (Left, Right, Front and Back). Assume that CheckSensors, Beep, and MotorStuck are methods defined elsewhere.

CON
  #0, None, Left, Right, Front, Back                    'Direction Enumerations 

PUB Main | Direction
  Direction := None
  repeat
    case CheckSensors                                   'Get active sensor
      Left  : Direction := Right                        'Object on left? Let's go right
      Right : Direction := Left                         'Object on right? Let's go left
      Front : Direction := Back                         'Object in front? Let's go back
      Back  : Direction := Front                        'Object in back? Let's go front
      other : Direction := None                         'Otherwise, stay still
    if not \Move(Direction)                             'Move robot
      Beep                                              'We're stuck? Beep 

PUB Move(Direction)
  result := TRUE                                        'Assume success
  if Direction == None
    return                                              'Return if no direction
  repeat 1000
    DriveMotors(Direction)                              'Drive motor 1000 times 

PUB DriveMotors(Direction)
  <code to drive motors>
  if MotorStuck
    abort FALSE                                         'If motor is stuck, abort
  <more code> 

The above example shows three methods of various logical levels, Main ("high-level"), Move ("mid-level") and DriveMotors ("low-level"). The high-level method, Main, is the decision maker of the application; deciding how to respond to events like sensor activations and motor movements. The mid-level method, Move, is responsible for moving the robot a short distance. The low-level method, DriveMotors, handles the details of driving the motors properly and verifying that it is successful.

In an application like this, critical events could occur in low-level code that needs to be addressed by high-level code. The ABORT command can be instrumental in getting the message to the high-level code without requiring complicated message-passing code for all the mid-level code in-between. In this case, we have only one mid-level method but there could be many nested mid-level methods between the high-level and the low-level.

The Main method gets sensor inputs and decides what direction to move the robot via the CASE statement. It then calls Move in a special way, with the Abort Trap symbol, \ , preceding it. The Move method sets its RESULT to TRUE and then calls DriveMotors in a finite loop. If it successfully completes, Move returns TRUE. The DriveMotors method handles the complication of moving the robot's motors to achieve the desired direction, but if it determines the motors are stuck, it cannot move them further and it aborts with a FALSE value. Otherwise it simply returns normally.

If everything is fine, the DriveMotors method returns normally, the Move method carries on normally and eventually returns TRUE, and the Main method continues on normally. If, however, DriveMotors finds a problem, it ABORTs which causes the Propeller to pop the call stack all the way through the Move method and up to the Main method where the Abort Trap was found. The Move method is completely oblivious to this and is now effectively terminated. The Main method checks the value returned by its call to Move (which is now the FALSE value that was actually returned by the aborted DriveMotors method deep down the call stack) and it decides to Beep as a result of the detected failure.

If we had not put the Abort Trap, ( \ ), in front of the call to Move, when DriveMotors aborted, the call stack would have been popped until it was empty and this application would have terminated immediately.

Unless otherwise noted, content on this site is licensed under the
Creative Commons Attribution-ShareAlike 4.0 International License.