Creating Asynchronous Virtual Devices
Sometimes Virtual Devices need significant time to respond to an input event. Other times, Virtual Devices need to generate events in response to something other than input events. When Virtual Devices have any of these requirements, we create an asynchronous Virtual Device.
In the Creating Virtual Devices section, we created a synchronous Virtual Device which executed system commands. One of its faults was that if its command blocked, the system would block. As an example, if you ran a "sleep 2" command (supported on *nix systems), the command would block for 2 seconds before returning, suspending all other events in the system for those 2 seconds. We are going to turn the command_runner Virtual Device into an asynchronous Virtual Device.
The "generate_event" Method
If you wish to generate an event from within a Virtual Device, you call the generate_event method. The method takes a single hash parameter, and the hash contains output events. The hash is the same type of hash as the Virtual Device input hash and return value hash, with the keys being terminal names, and values being event values. To see a a full definition of these hashes, see the Virtual Device Programs section.
To create an event for the "out" terminal with a value of "hello", one would write:
Making the "command_runner" Virtual Device Asynchronous
We are going the alter the command_runner program, so it always returns quickly, fixing its blocking problem. To fix it, we'll create a new thread to execute the system command, and return from the system call immediately. We'll let our new thread create an event when the command is done.
That fixes the blocking problem. When you try this program out, you may need to refresh your view in order to see your result. The thread we've created sometimes finishes after the command is 'done'.
Virtual Device Errors
So far, we've seen 2 actions a Virtual Device can take - returning events and returning no events. Generally, that's about all you want a system Device to do. However, what if there is a problem which you want to let the user or you, the developer, know about?
If you want to signal an error, you can raise a RuntimeError. Anytime the system encounters a RuntimeError, it will trap the exception and write its value to the Log. So, if we wanted to let the user know about a Device problem, we could raise a RuntimeError exception. If you choose to raise a RuntimeError exception, make sure you will only do them rarely. Flooding the Log with errors may mask Log entries of interest from other parts of the system.
Adding Error Logging to "command_runner"
The last command_runner implementation won't block, but it can run commands on top of each other (you can run another command before the previous one is finished). Perhaps you don't want that. You could check to see if a command is done before starting the next. You could also let the user to know about the problem. We'll log an error to tell the user that. We'll also log an error if the command is not formatted correctly.
Try running this Virtual Device with an unquoted command. Try running "sleep 20" and then "uptime" before the 20 seconds are up. Check the Log for errors.