utils/actions.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.8.0/src/utils/actions.cpp $
00003   version : $LastChangedRevision: 1145 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2010-01-04 19:24:24 +0100 (Mon, 04 Jan 2010) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007 by Johan De Taeye                                    *
00010  *                                                                         *
00011  * This library is free software; you can redistribute it and/or modify it *
00012  * under the terms of the GNU Lesser General Public License as published   *
00013  * by the Free Software Foundation; either version 2.1 of the License, or  *
00014  * (at your option) any later version.                                     *
00015  *                                                                         *
00016  * This library is distributed in the hope that it will be useful,         *
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser *
00019  * General Public License for more details.                                *
00020  *                                                                         *
00021  * You should have received a copy of the GNU Lesser General Public        *
00022  * License along with this library; if not, write to the Free Software     *
00023  * Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 *
00024  * USA                                                                     *
00025  *                                                                         *
00026  ***************************************************************************/
00027 
00028 #define FREPPLE_CORE
00029 #include "frepple/utils.h"
00030 
00031 
00032 // These headers are required for the loading of dynamic libraries
00033 #ifdef WIN32
00034   #include <windows.h>
00035 #else
00036   #include <dlfcn.h>
00037 #endif
00038 
00039 
00040 namespace frepple
00041 {
00042 namespace utils
00043 {
00044 
00045 
00046 DECLARE_EXPORT bool Command::getVerbose() const
00047 {
00048   if (verbose==INHERIT)
00049     // Note: a command gets the level INHERIT by default. In case the command
00050     // was never added to a commandlist, the owner field will be NULL. In such
00051     // case the value INHERIT is interpreted as SILENT.
00052     return owner ? owner->getVerbose() : false;
00053   else
00054     return verbose==YES;
00055 }
00056 
00057 
00058 //
00059 // COMMAND LIST
00060 //
00061 
00062 
00063 DECLARE_EXPORT bool CommandList::getAbortOnError() const
00064 {
00065   if (abortOnError==INHERIT)
00066   {
00067     // A command list can be nested in another command list. In this case we
00068     // inherit this field from the owning command list
00069     CommandList *owning_list = dynamic_cast<CommandList*>(owner);
00070     return owning_list ? owning_list->getAbortOnError() : true;
00071   }
00072   else
00073     return abortOnError==YES;
00074 }
00075 
00076 
00077 DECLARE_EXPORT void CommandList::add(Command* c)
00078 {
00079   // Validity check
00080   if (!c) throw LogicException("Adding NULL command to a command list");
00081   if (curCommand)
00082     throw RuntimeException("Can't add a command to the list during execution");
00083 
00084   // Set the owner of the command
00085   c->owner = this;
00086 
00087   // Maintenance of the linked list of child commands
00088   c->prev = lastCommand;
00089   if (lastCommand)
00090     // Let the last command in the chain point to this new extra command
00091     lastCommand->next = c;
00092   else
00093     // This is the first command in this command list
00094     firstCommand = c;
00095   lastCommand = c;
00096 
00097   // Update the undoable field
00098   if (!c->undoable()) can_undo = false;
00099 }
00100 
00101 
00102 DECLARE_EXPORT void CommandList::undo(Command *c)
00103 {
00104   // Check validity of argument
00105   if (c && c->owner != this)
00106     throw LogicException("Invalid call to CommandList::undo(Command*)");
00107 
00108   // Don't even try to undo a list which can't be undone.
00109   if (!c && !undoable(c))
00110     throw RuntimeException("Trying to undo a CommandList which " \
00111         "contains non-undoable actions or is executed in parallel");
00112 
00113   // Undo all commands and delete them.
00114   // Note that undoing an operation that hasn't been executed yet or has been
00115   // undone already is expected to be harmless, so we don't need to worry
00116   // about that...
00117   for (Command *i = lastCommand; i != c; )
00118   {
00119     Command *t = i;  // Temporarily store the pointer to be deleted
00120     i = i->prev;
00121     delete t; // The delete is expected to also undo the command!
00122   }
00123 
00124   // Maintain the linked list of commands still present
00125   if (c)
00126   {
00127     // Partially undo
00128     c->next = NULL;
00129     lastCommand = c;
00130   }
00131   else
00132   {
00133     // Completely erase the list
00134     firstCommand = NULL;
00135     lastCommand = NULL;
00136   }
00137 }
00138 
00139 
00140 DECLARE_EXPORT bool CommandList::undoable(const Command *c) const
00141 {
00142   // Check validity of argument
00143   if (c && c->owner!=this)
00144     throw LogicException("Invalid call to CommandList::undoable(Command*)");
00145 
00146   // Parallel commands can't be undone
00147   if (maxparallel > 1) return false;
00148 
00149   // Easy cases
00150   if (!c || can_undo) return can_undo;
00151 
00152   // Step over the remaining commands and check whether they can be undone
00153   for (; c; c = c->next) if (!c->undoable()) return false;
00154   return true;
00155 }
00156 
00157 
00158 DECLARE_EXPORT Command* CommandList::selectCommand()
00159 {
00160   ScopeMutexLock l(lock );
00161   Command *c = curCommand;
00162   if (curCommand) curCommand = curCommand->next;
00163   return c;
00164 }
00165 
00166 
00167 DECLARE_EXPORT void CommandList::execute()
00168 {
00169   // Execute the actions
00170   // This field is set asap in this method since it is used a flag to
00171   // recognize that execution is in progress.
00172   curCommand = firstCommand;
00173 
00174   // Message
00175   if (getVerbose())
00176     logger << "Start executing command list at " << Date::now() << endl;
00177   Timer t;
00178 
00179 #ifndef MT
00180   // Compile 1: No multithreading
00181   if (maxparallel>1) maxparallel = 1;
00182 #else
00183   if (maxparallel>1)
00184   {
00185     // MODE 1: Parallel execution of the commands
00186     int numthreads = getNumberOfCommands();
00187     // Limit the number of threads to the maximum allowed
00188     if (numthreads>maxparallel) numthreads = maxparallel;
00189     if (numthreads == 1)
00190       // Only a single command in the list: no need for threads
00191       wrapper(curCommand);
00192     else if (numthreads > 1)
00193     {
00194       int worker = 0;
00195 #ifdef HAVE_PTHREAD_H
00196       // Create a thread for every command list. The main thread will then
00197       // wait for all of them to finish.
00198       pthread_t threads[numthreads];     // holds thread info
00199       int errcode;                       // holds pthread error code
00200 
00201       // Create the threads
00202       for (; worker<numthreads; ++worker)
00203       {
00204         if ((errcode=pthread_create(&threads[worker],  // thread struct
00205             NULL,                  // default thread attributes
00206             wrapper,               // start routine
00207             this)))                // arg to routine
00208         {
00209           if (!worker)
00210           {
00211             ostringstream ch;
00212             ch << "Can't create any threads, error " << errcode;
00213             throw RuntimeException(ch.str());
00214           }
00215           // Some threads could be created.
00216           // Let these threads run and do all the work.
00217           logger << "Warning: Could create only " << worker
00218             << " threads, error " << errcode << endl;
00219         }
00220       }
00221 
00222       // Wait for the threads as they exit
00223       for (--worker; worker>=0; --worker)
00224         // Wait for thread to terminate.
00225         // The second arg is NULL, since we don't care about the return status
00226         // of the finished threads.
00227         if ((errcode=pthread_join(threads[worker],NULL)))
00228         {
00229           ostringstream ch;
00230           ch << "Can't join with thread " << worker << ", error " << errcode;
00231           throw RuntimeException(ch.str());
00232         }
00233 #else
00234       // Create a thread for every command list. The main thread will then
00235       // wait for all of them to finish.
00236       HANDLE* threads = new HANDLE[numthreads];
00237       unsigned int * m_id = new unsigned int[numthreads];
00238 
00239       // Create the threads
00240       for (; worker<numthreads; ++worker)
00241       {
00242         threads[worker] =  reinterpret_cast<HANDLE>(
00243           _beginthreadex(0,  // Security atrtributes
00244           0,                 // Stack size
00245           &wrapper,          // Thread function
00246           this,              // Argument list
00247           0,                 // Initial state is 0, "running"
00248           &m_id[worker]));   // Address to receive the thread identifier
00249         if (!threads[worker])
00250         {
00251           if (!worker)
00252           {
00253             // No threads could be created at all.
00254             delete threads;
00255             delete m_id;
00256             throw RuntimeException("Can't create any threads, error " + errno);
00257           }
00258           // Some threads could be created.
00259           // Let these threads run and do all the work.
00260           logger << "Warning: Could create only " << worker
00261             << " threads, error " << errno << endl;
00262           break; // Step out of the thread creation loop
00263         }
00264       }
00265 
00266       // Wait for the threads as they exit
00267       int res = WaitForMultipleObjects(worker, threads, true, INFINITE);
00268       if (res == WAIT_FAILED)
00269       {
00270         char error[256];
00271         FormatMessage(
00272           FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
00273           NULL,
00274           GetLastError(),
00275           0,
00276           error,
00277           256,
00278           NULL );
00279         delete threads;
00280         delete m_id;
00281         throw RuntimeException(string("Can't join threads: ") + error);
00282       }
00283 
00284       // Cleanup
00285       for (--worker; worker>=0; --worker)
00286         CloseHandle(threads[worker]);
00287       delete threads;
00288       delete m_id;
00289 #endif
00290     }  // End: else if (numthreads>1)
00291   }
00292   else // Else: sequential
00293 #endif
00294   if (getAbortOnError())
00295   {
00296     // MODE 2: Sequential execution, and a single command failure aborts the
00297     // whole sequence.
00298     try
00299     {
00300       for (; curCommand; curCommand = curCommand->next) curCommand->execute();
00301     }
00302     catch (...)
00303     {
00304       logger << "Error: Caught an exception while executing command:" << endl;
00305       try {throw;}
00306       catch (exception& e) {logger << "  " << e.what() << endl;}
00307       catch (...) {logger << "  Unknown type" << endl;}
00308       // Undo all commands executed so far
00309       if (undoable()) undo();
00310     }
00311   }
00312   else
00313     // MODE 3: Sequential execution, and when a command in the sequence fails
00314     // the rest continues
00315     wrapper(this);
00316 
00317   // Clean it up after executing ALL actions.
00318   for (Command *i=lastCommand; i; )
00319   {
00320     Command *t = i;
00321     i = i->prev;
00322     delete t;
00323   }
00324   firstCommand = NULL;
00325   lastCommand = NULL;
00326 
00327   // Log
00328   if (getVerbose())
00329     logger << "Finished executing command list at " << Date::now()
00330     << " : " << t << endl;
00331 }
00332 
00333 
00334 #if defined(HAVE_PTHREAD_H) || !defined(MT)
00335 void* CommandList::wrapper(void *arg)
00336 #else
00337 unsigned __stdcall CommandList::wrapper(void *arg)
00338 #endif
00339 {
00340   // Each OS-level thread needs to initialize a Python thread state.
00341   CommandList *l = static_cast<CommandList*>(arg);
00342   bool threaded = l->getMaxParallel() > 1 && l->getNumberOfCommands() > 1;
00343   if (threaded) PythonInterpreter::addThread();
00344 
00345   // Execute the commands
00346   for (Command *c = l->selectCommand(); c; c = l->selectCommand())
00347   {
00348 #if defined(HAVE_PTHREAD_H) || !defined(MT)
00349     // Verfiy whether there has been a cancellation request in the meantime
00350     pthread_testcancel();
00351 #endif
00352     try {c->execute();}
00353     catch (...)
00354     {
00355       // Error message
00356       logger << "Error: Caught an exception while executing command:" << endl;
00357       try {throw;}
00358       catch (exception& e) {logger << "  " << e.what() << endl;}
00359       catch (...) {logger << "  Unknown type" << endl;}
00360     }
00361   }
00362 
00363   // Finalize the Python thread state
00364   if (threaded) PythonInterpreter::deleteThread();
00365   return 0;
00366 }
00367 
00368 
00369 DECLARE_EXPORT CommandList::~CommandList()
00370 {
00371   if (!firstCommand) return;
00372   logger << "Warning: Deleting an action list with actions that have"
00373     << " not been committed or undone" << endl;
00374   for (Command *i = lastCommand; i; )
00375   {
00376     Command *t = i;  // Temporary storage for the object to delete
00377     i = i->prev;
00378     delete t;
00379   }
00380 }
00381 
00382 
00383 //
00384 // LOADLIBRARY COMMAND
00385 //
00386 
00387 
00388 DECLARE_EXPORT void CommandLoadLibrary::execute()
00389 {
00390   // Type definition of the initialization function
00391   typedef const char* (*func)(const ParameterList&);
00392 
00393   // Log
00394   if (getVerbose())
00395     logger << "Start loading library '" << lib << "' at " << Date::now() << endl;
00396   Timer t;
00397 
00398   // Validate
00399   if (lib.empty())
00400     throw DataException("Error: No library name specified for loading");
00401 
00402 #ifdef WIN32
00403   // Load the library - The windows way
00404 
00405   // Change the error mode: we handle errors now, not the operating system
00406   UINT em = SetErrorMode(SEM_FAILCRITICALERRORS);
00407   HINSTANCE handle = LoadLibraryEx(lib.c_str(),NULL,LOAD_WITH_ALTERED_SEARCH_PATH);
00408   if (!handle) handle = LoadLibraryEx(lib.c_str(), NULL, 0);
00409   if (!handle)
00410   {
00411     // Get the error description
00412     char error[256];
00413     FormatMessage(
00414       FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
00415       NULL,
00416       GetLastError(),
00417       0,
00418       error,
00419       256,
00420       NULL );
00421     throw RuntimeException(error);
00422   }
00423   SetErrorMode(em);  // Restore the previous error mode
00424 
00425   // Find the initialization routine
00426   func inithandle =
00427     reinterpret_cast<func>(GetProcAddress(HMODULE(handle), "initialize"));
00428   if (!inithandle)
00429   {
00430     // Get the error description
00431     char error[256];
00432     FormatMessage(
00433       FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
00434       NULL,
00435       GetLastError(),
00436       0,
00437       error,
00438       256,
00439       NULL );
00440     throw RuntimeException(error);
00441   }
00442 
00443 #else
00444   // Load the library - The UNIX way
00445 
00446   // Search the frePPLe directories for the library
00447   string fullpath = Environment::searchFile(lib);
00448   if (fullpath.empty())
00449   throw RuntimeException("Module '" + lib + "' not found");
00450   dlerror(); // Clear the previous error
00451   void *handle = dlopen(fullpath.c_str(), RTLD_NOW | RTLD_GLOBAL);
00452   const char *err = dlerror();  // Pick up the error string
00453   if (err)
00454   {
00455      // Search the normal path for the library
00456      dlerror(); // Clear the previous error
00457      handle = dlopen(lib.c_str(), RTLD_NOW | RTLD_GLOBAL);
00458      err = dlerror();  // Pick up the error string
00459      if (err) throw RuntimeException(err);
00460   }
00461 
00462   // Find the initialization routine
00463   func inithandle = (func)(dlsym(handle, "initialize"));
00464   err = dlerror(); // Pick up the error string
00465   if (err) throw RuntimeException(err);
00466 #endif
00467 
00468   // Call the initialization routine with the parameter list
00469   string x = (inithandle)(parameters);
00470   if (x.empty()) throw DataException("Invalid module name returned");
00471 
00472   // Insert the new module in the registry
00473   registry.insert(x);
00474 
00475   // Log
00476   if (getVerbose())
00477     logger << "Finished loading module '" << x << "' from library '" << lib
00478     << "' at " << Date::now() << " : " << t << endl;
00479 }
00480 
00481 
00482 DECLARE_EXPORT PyObject* CommandLoadLibrary::executePython
00483   (PyObject* self, PyObject* args, PyObject* kwds)
00484 {
00485 
00486   // Create the command
00487   char *data = NULL;
00488   int ok = PyArg_ParseTuple(args, "s:loadmodule", &data);
00489   if (!ok) return NULL;
00490   CommandLoadLibrary cmd(data);
00491 
00492   // Load parameters for the module
00493   if (kwds)
00494   {
00495     PyObject *key, *value;
00496     Py_ssize_t pos = 0;
00497     while (PyDict_Next(kwds, &pos, &key, &value))
00498       cmd.addParameter(
00499         PythonObject(key).getString(),
00500         PythonObject(value).getString()
00501         );
00502   }
00503 
00504   // Free Python interpreter for other threads.
00505   // This is important since the module may also need access to Python
00506   // during its initialization...
00507   Py_BEGIN_ALLOW_THREADS
00508   try {
00509     // Load the library
00510     cmd.execute();
00511   }
00512   catch(...)
00513   {
00514     Py_BLOCK_THREADS;
00515     PythonType::evalException();
00516     return NULL;
00517   }
00518   Py_END_ALLOW_THREADS   // Reclaim Python interpreter
00519   return Py_BuildValue("");
00520 }
00521 
00522 
00523 DECLARE_EXPORT void CommandLoadLibrary::printModules()
00524 {
00525   logger << "Loaded modules:" << endl;
00526   for (set<string>::const_iterator i=registry.begin(); i!=registry.end(); ++i)
00527     logger << "   " << *i << endl;
00528   logger << endl;
00529 }
00530 
00531 
00532 
00533 
00534 } // end namespace
00535 } // end namespace