NAME Process - Objects that represent generic computational processes SYNOPSIS # Create the process my $object = MyProcess->new( ... ) or die("Invalid configuration format"); # Initialize it $object->prepare or die("Configuration not supportable"); # Execute the process $object->run or die("Error while trying to execute the process"); DESCRIPTION There are a great number of situations in which you may want to model a computational process as an object. An implementation of this sort of object generally looks like the following when somebody uses it. my $object = MyProcessThingy->new( ... ); my $rv = $object->run; if ( $rv ) { print "Thingy ran ok"; } else { print "Failed to run thingy"; } The "Process" family of modules are intended to be used as base and role classes for these types of objects. They are used to help identify process objects, and enforce a common API on these objects. The primary intent is to provide a common base for objects that will be able to be used with various distributed processing systems, without itself actually implementing any form of distributed system. The scope of uses for Process includes solutions to address the following scenarios. A single CPU on a single host Multiple CPUs on a single host Multiple hosts on a single network Hosts distributed across the internet Any processing resource accessible via any mechanism To put it another way, this family of classes is intended to addresses the separation of concerns between the processing of something, and the results of something. The actual ways in which the processes are run, and the handling of the results of the process are outside the scope of these classes. The "Process" class itself is the root of all of these classes. In fact, it is so abstract that it contains no functionality at all, and serves primarily to indicate that an object obeys the general rules of a "Process" class. Most of the basic "Process" modules are similar. They define how your object should behave (an API for a particular concept) without dictating a particular implementation. However, by using them, you are confirming to some processing system that your objects will obey particular rules, and thus can interact sanely with any processing system that follows the API. What You Can and Cannot Do The use of "Process" is mainly about making guarantees. Because this is Perl, it is not necesarily about enforcing those guarantees in a strict way. These sorts of guarantees need to be implemented at a language level to be meaningful in any case (for example, Perl's tainting or Haskell's monads). You may still hang yourself, but it's your own fault if you do. You may not alter the API of the three core methods It's always tempting to say "This acts like Foo, but we added an extra return condition for method bar". You may not do this. "Process" and a few other members of the family dictate all possible return values for "new", "prepare" and "run", and what state the objects should be in before and after these methods are called in the various cases. You may not break these rules, because larger systems will be depending on them to allow your objects to work with them flexibly and flawlessly. You may not store data in the surrounding Perl environment A "Process" object should be entirely self-contained when you are told to be. If your process is involved in creating or transforming information, then your implementation should keep these results inside the "Process" object. Do not store data elsewhere. This means no globals and no class-level variables. You can't leave error messages in a global $errstr variable, as some Perl modules do. This does not mean you can't store data in files. For "Process" objects that generate vast quantities of data it would be unwieldy to limit you to holding all data in memory at once. However any object data that refers to files should use absolute paths. You may not assume the timing of new and prepare. Most uses of "Process" will involve scheduling, transport, or otherwise mean that once a processing system has a "Process" object, it won't get to it immediately. You should not assume that "prepare" will be run immediately after "new" or in the same interpretor. You may however assume that "run" is run immediately after "prepare", and is in the same interpreter. You may not assume an object is unchanged after "run" A "Process" object may be unchanged after "run". Then again, it may also be completely changed, and have accumulated all sorts of data in it. This also means that you should not assume that a "Process" object can be run again after it completes. A specific class Process::Repeatable will be provided at a later date for this case. You may not interrelate with other Process objects In the default case, all "Process" objects are considered to be independent and standalone. They may not have any form of dependency on other "Process" objects, and they should not expect to communicate with any other "Process" objects. If you choose to add this type of functionality to your particular class that is fine, but any processing system will not be aware of this and thus will not be doing anything to help you out. Process::Depends will be provided at a later date for this case. METHODS new my $object = MyClass->new( $configuration ); if ( $object and $object->isa('Process') ) { print "Object created.\n"; } else { print "Configuration not valid.\n"; } The "new" constructor is required for all classes. It takes zero or more arbitrary params, with the specifics of any params outside the scope of this module. The specifics of any params are to be determined by each specific class. A default implementation which ignores any params and creates an empty object of a "HASH" type is provided as a convenience. However it should not be assumed that all objects will be a "HASH" type. For objects that subclass only the base "Process" class, and are not also subclasses of things like Process::Storable, the param-checking done in "new" should be thorough and and objects should be correct, with problems at "run"-time the exception rather than the rule. Returns a new "Process" object on success. For blame-free "Cannot support process in this Perl interpreter" failure, return false. For failure with blame, may return a string or any other value that is not itself a "Process" object. However, if you need to communicate failure, you should consider putting your param-checking in a "prepare" method and attaching the failure messages to the object itself. You should NEVER store errors in the class, as all "Process" classes are forbidden to use class-level data storage. prepare unless ( $object->prepare ) { # Failed die "Failed to prepare process"; } The "prepare" method is used to check object params and bind platform resources. The concept of object creation in "new" is separated from the concept of checking and binding to support storage and transportation in some subclasses. Because many systems that make use of "Process" do so through the desire to push process requests across a network and have them executed on a remote host, "Process" provides the "prepare" method as a means to separate the checking of the params for general correctness from checking of params relative to the system and interpreter the process is being run on. It additionally provides a good way to have the bulk of serious errors remain attached to the object, and have them transported back across to the requesting entity. Execution platforms are required to call "run" immediately or nearly immediately after "prepare". Generally the only tasks done between "prepare" and "run" will be things like starting timers, setting flags and signalling to any requestor that the process is ok, and about to start. As a result you are encouraged to do all locking of files, socket ports, database handles and so on in "prepare", as they will be used immediately. Thus the only execution errors coming from "run" should be due to unexpected changes, race-condition events in the short gap between "prepare" and "run", or some error that could not possibly be detected until part way through the "run". To restate, resource binding and checking should be done in "prepare" if at all possible. A default null implementation is provided for you which does nothing and returns true. Returns true if all params check out ok, and all system resources needed for the execution are bound correctly. Returns false if not, with any errors to be propagated via storage in the object itself, for example in a "->{errstr}" attribute. The object should not return errors via exceptions. If you expect something within your code to potentially result in an exception, you should trap the exception and return false. You should not expect the object to be Storable or in any other way usable once the "Process" object is "prepare"'ed. The object is wound up and poised ready to "run" and that is the only thing you should do with it. run my $rv = $object->run; if ( $rv ) { print "Process completed successfully\n"; } else { print "Process interupted, or unexpected error\n"; } The "run" method is used to execute the process. It should do all appropriate processing and calculation and detach from all relevant resources before returning. If your process has any results, they should be stored inside the "Process" object itself, and retrieved via an additional method of your choice after the "run" call. A default implementation which does nothing and returns true is provided. Returns true if the "Process" was completed fully, regardless of any results from the process. Returns false if the process was interrupted, or an unexpected error occurs. In the default case for "Process", no distinction is made between the process being interrupted legitimately and any other type of unexpected failure. If the process returns false, it should not be assumed that the process can be restarted or rerun. It should be discarded or returned to the requestor to check for specific errors. SUPPORT Bugs should be reported via the CPAN bug tracker at For other issues, contact the author. AUTHOR Adam Kennedy SEE ALSO COPYRIGHT Copyright 2006 - 2011 Adam Kennedy. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of the license can be found in the LICENSE file included with this module.