utils/actions.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.9.1/src/utils/actions.cpp $
00003   version : $LastChangedRevision: 1656 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2012-03-27 19:05:34 +0200 (Tue, 27 Mar 2012) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007-2012 by Johan De Taeye, frePPLe bvba                 *
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 namespace frepple
00033 {
00034 namespace utils
00035 {
00036 
00037 
00038 //
00039 // COMMAND LIST
00040 //
00041 
00042 
00043 DECLARE_EXPORT void CommandList::add(Command* c)
00044 {
00045   // Validity check
00046   if (!c) throw LogicException("Adding NULL command to a command list");
00047 
00048   // Set the owner of the command
00049   c->owner = this;
00050 
00051   // Maintenance of the linked list of child commands
00052   c->prev = lastCommand;
00053   if (lastCommand)
00054     // Let the last command in the chain point to this new extra command
00055     lastCommand->next = c;
00056   else
00057     // This is the first command in this command list
00058     firstCommand = c;
00059   lastCommand = c;
00060 }
00061 
00062 
00063 DECLARE_EXPORT void CommandList::rollback()
00064 {
00065   // Undo all commands and delete them.
00066   // Note that undoing an operation that hasn't been executed yet or has been
00067   // undone already is expected to be harmless, so we don't need to worry
00068   // about that...
00069   for (Command *i = lastCommand; i; )
00070   {
00071     Command *t = i;  // Temporarily store the pointer to be deleted
00072     i = i->prev;
00073     delete t; // The delete is expected to also revert the change!
00074   }
00075 
00076   // Reset the list
00077   firstCommand = NULL;
00078   lastCommand = NULL;
00079 }
00080 
00081 
00082 DECLARE_EXPORT void CommandList::undo()
00083 {
00084   // Undo all commands and delete them.
00085   // Note that undoing an operation that hasn't been executed yet or has been
00086   // undone already is expected to be harmless, so we don't need to worry
00087   // about that...
00088   for (Command *i = lastCommand; i; i = i->prev)
00089     i->undo();
00090 }
00091 
00092 
00093 DECLARE_EXPORT void CommandList::commit()
00094 {
00095   // Commit the commands
00096   for (Command *i = firstCommand; i;)
00097   {
00098     Command *t = i;  // Temporarily store the pointer to be deleted
00099     i->commit();
00100     i = i->next;
00101     delete t;
00102   }
00103 
00104   // Reset the list
00105   firstCommand = NULL;
00106   lastCommand = NULL;
00107 }
00108 
00109 
00110 DECLARE_EXPORT void CommandList::redo()
00111 {
00112   // Redo the commands
00113   for (Command* c = firstCommand; c; c = c->next)
00114     c->redo();
00115 }
00116 
00117 
00118 DECLARE_EXPORT CommandList::~CommandList()
00119 {
00120   if (firstCommand)
00121   {
00122     logger << "Warning: Deleting a command list with commands that have"
00123         << " not been committed or rolled back" << endl;
00124     rollback();
00125   }
00126 }
00127 
00128 
00129 //
00130 // COMMAND MANAGER
00131 //
00132 
00133 
00134 DECLARE_EXPORT CommandManager::Bookmark* CommandManager::setBookmark()
00135 {
00136   Bookmark* n = new Bookmark(currentBookmark);
00137   lastBookmark->nextBookmark = n;
00138   n->prevBookmark = lastBookmark;
00139   lastBookmark = n;
00140   currentBookmark = n;
00141   return n;
00142 }
00143 
00144 
00145 DECLARE_EXPORT void CommandManager::undoBookmark(CommandManager::Bookmark* b)
00146 {
00147   if (!b) throw LogicException("Can't undo NULL bookmark");
00148 
00149   Bookmark* i = lastBookmark;
00150   for (; i && i != b; i = i->prevBookmark)
00151   {
00152     if (i->isChildOf(b) && i->active)
00153     {
00154       i->undo();
00155       i->active = false;
00156     }
00157   }
00158   if (!i) throw LogicException("Can't find bookmark to undo");
00159   currentBookmark = b->parent;
00160 }
00161 
00162 
00163 DECLARE_EXPORT void CommandManager::redoBookmark(CommandManager::Bookmark* b)
00164 {
00165   if (!b) throw LogicException("Can't redo NULL bookmark");
00166 
00167   for (Bookmark* i = b; i; i = i->nextBookmark)
00168   {
00169     if (i->isChildOf(b) && !i->active)
00170     {
00171       i->redo();
00172       i->active = true;
00173     }
00174   }
00175   currentBookmark = b;
00176 }
00177 
00178 
00179 DECLARE_EXPORT void CommandManager::rollback(CommandManager::Bookmark* b)
00180 {
00181   if (!b)
00182     throw LogicException("Can't rollback NULL bookmark");
00183   if (b == &firstBookmark)
00184     throw LogicException("Can't rollback default bookmark");
00185 
00186   // Remove all later child bookmarks
00187   Bookmark* i = lastBookmark;
00188   while (i && i != b)
00189   {
00190     if (i->isChildOf(b))
00191     {
00192       // Remove from bookmark list
00193       if (i->prevBookmark)
00194         i->prevBookmark->nextBookmark = i->nextBookmark;
00195       if (i->nextBookmark)
00196         i->nextBookmark->prevBookmark = i->prevBookmark;
00197       else
00198         lastBookmark = i->prevBookmark;
00199       i->rollback();
00200       if (currentBookmark == i)
00201         currentBookmark = b;
00202       Bookmark* tmp = i;
00203       i = i->prevBookmark;
00204       delete tmp;
00205     }
00206     else
00207       // Bookmark has a different parent
00208       i = i->prevBookmark;
00209   }
00210   if (!i) throw LogicException("Can't find bookmark to rollback");
00211   b->rollback();
00212 }
00213 
00214 
00215 DECLARE_EXPORT void CommandManager::commit()
00216 {
00217   if (firstBookmark.active) firstBookmark.commit();
00218   for (Bookmark* i = firstBookmark.nextBookmark; i; )
00219   {
00220     if (i->active) i->commit();
00221     Bookmark *tmp = i;
00222     i = i->nextBookmark;
00223     delete tmp;
00224   }
00225   firstBookmark.nextBookmark = NULL;
00226   currentBookmark = &firstBookmark;
00227   lastBookmark = &firstBookmark;
00228 }
00229 
00230 
00231 DECLARE_EXPORT void CommandManager::rollback()
00232 {
00233   for (Bookmark* i = lastBookmark; i != &firstBookmark;)
00234   {
00235     i->rollback();
00236     Bookmark *tmp = i;
00237     i = i->prevBookmark;
00238     delete tmp;
00239   }
00240   firstBookmark.rollback();
00241   firstBookmark.nextBookmark = NULL;
00242   currentBookmark = &firstBookmark;
00243   lastBookmark = &firstBookmark;
00244 }
00245 
00246 
00247 //
00248 // THREAD GROUP
00249 //
00250 
00251 
00252 DECLARE_EXPORT void ThreadGroup::execute()
00253 {
00254 #ifndef MT
00255   // CASE 1: Sequential execution when compiled without multithreading
00256   wrapper(this);
00257 #else
00258   // CASE 2: No need to create worker threads when either a) only a single
00259   // worker is allowed or b) only a single function needs to be called.
00260   if (maxParallel<=1 || countCallables<=1)
00261   {
00262     wrapper(this);
00263     return;
00264   }
00265 
00266   // CASE 3: Parallel execution in worker threads
00267   int numthreads = countCallables;
00268   // Limit the number of threads to the maximum allowed
00269   if (numthreads > maxParallel) numthreads = maxParallel;
00270   int worker = 0;
00271 #ifdef HAVE_PTHREAD_H
00272   // Create a thread for every command list. The main thread will then
00273   // wait for all of them to finish.
00274   pthread_t threads[numthreads];     // holds thread info
00275   int errcode;                       // holds pthread error code
00276 
00277   // Create the threads
00278   for (; worker<numthreads; ++worker)
00279   {
00280     if ((errcode=pthread_create(&threads[worker],  // thread struct
00281         NULL,                  // default thread attributes
00282         wrapper,               // start routine
00283         this)))                // arg to routine
00284     {
00285       if (!worker)
00286       {
00287         ostringstream ch;
00288         ch << "Can't create any threads, error " << errcode;
00289         throw RuntimeException(ch.str());
00290       }
00291       // Some threads could be created.
00292       // Let these threads run and do all the work.
00293       logger << "Warning: Could create only " << worker
00294           << " threads, error " << errcode << endl;
00295     }
00296   }
00297 
00298   // Wait for the threads as they exit
00299   for (--worker; worker>=0; --worker)
00300     // Wait for thread to terminate.
00301     // The second arg is NULL, since we don't care about the return status
00302     // of the finished threads.
00303     if ((errcode=pthread_join(threads[worker],NULL)))
00304     {
00305       ostringstream ch;
00306       ch << "Can't join with thread " << worker << ", error " << errcode;
00307       throw RuntimeException(ch.str());
00308     }
00309 #else
00310   // Create a thread for every command list. The main thread will then
00311   // wait for all of them to finish.
00312   HANDLE* threads = new HANDLE[numthreads];
00313   unsigned int * m_id = new unsigned int[numthreads];
00314 
00315   // Create the threads
00316   for (; worker<numthreads; ++worker)
00317   {
00318     threads[worker] =  reinterpret_cast<HANDLE>(
00319         _beginthreadex(0,  // Security atrtributes
00320             0,                 // Stack size
00321             &wrapper,          // Thread function
00322             this,              // Argument list
00323             0,                 // Initial state is 0, "running"
00324             &m_id[worker]));   // Address to receive the thread identifier
00325     if (!threads[worker])
00326     {
00327       if (!worker)
00328       {
00329         // No threads could be created at all.
00330         delete threads;
00331         delete m_id;
00332         throw RuntimeException("Can't create any threads, error " + errno);
00333       }
00334       // Some threads could be created.
00335       // Let these threads run and do all the work.
00336       logger << "Warning: Could create only " << worker
00337           << " threads, error " << errno << endl;
00338       break; // Step out of the thread creation loop
00339     }
00340   }
00341 
00342   // Wait for the threads as they exit
00343   int res = WaitForMultipleObjects(worker, threads, true, INFINITE);
00344   if (res == WAIT_FAILED)
00345   {
00346     char error[256];
00347     FormatMessage(
00348       FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
00349       NULL,
00350       GetLastError(),
00351       0,
00352       error,
00353       256,
00354       NULL );
00355     delete threads;
00356     delete m_id;
00357     throw RuntimeException(string("Can't join threads: ") + error);
00358   }
00359 
00360   // Cleanup
00361   for (--worker; worker>=0; --worker)
00362     CloseHandle(threads[worker]);
00363   delete threads;
00364   delete m_id;
00365 #endif    // End of #ifdef ifHAVE_PTHREAD_H
00366 #endif    // End of #ifndef MT
00367 }
00368 
00369 
00370 DECLARE_EXPORT ThreadGroup::callableWithArgument ThreadGroup::selectNextCallable()
00371 {
00372   ScopeMutexLock l(lock );
00373   if (callables.empty())
00374   {
00375     // No more functions
00376     assert( countCallables == 0 );
00377     return callableWithArgument(static_cast<callable>(NULL),static_cast<void*>(NULL));
00378   }
00379   callableWithArgument c = callables.top();
00380   callables.pop();
00381   --countCallables;
00382   return c;
00383 }
00384 
00385 
00386 #if defined(HAVE_PTHREAD_H) || !defined(MT)
00387 void* ThreadGroup::wrapper(void *arg)
00388 #else
00389 unsigned __stdcall ThreadGroup::wrapper(void *arg)
00390 #endif
00391 {
00392   // Each OS-level thread needs to initialize a Python thread state.
00393   ThreadGroup *l = static_cast<ThreadGroup*>(arg);
00394   bool threaded = l->maxParallel > 1 && l->countCallables > 1;
00395   if (threaded) PythonInterpreter::addThread();
00396 
00397   for (callableWithArgument nextfunc = l->selectNextCallable();
00398       nextfunc.first;
00399       nextfunc = l->selectNextCallable())
00400   {
00401 #if defined(HAVE_PTHREAD_H) && defined(MT)
00402     // Verify whether there has been a cancellation request in the meantime
00403     pthread_testcancel();
00404 #endif
00405     try {nextfunc.first(nextfunc.second);}
00406     catch (...)
00407     {
00408       // Error message
00409       logger << "Error: Caught an exception while executing command:" << endl;
00410       try {throw;}
00411       catch (const exception& e) {logger << "  " << e.what() << endl;}
00412       catch (...) {logger << "  Unknown type" << endl;}
00413     }
00414   };
00415 
00416   // Finalize the Python thread state
00417   if (threaded) PythonInterpreter::deleteThread();
00418   return 0;
00419 }
00420 
00421 
00422 //
00423 // LOADMODULE FUNCTION
00424 //
00425 
00426 
00427 DECLARE_EXPORT PyObject* loadModule
00428 (PyObject* self, PyObject* args, PyObject* kwds)
00429 {
00430 
00431   // Create the command
00432   char *data = NULL;
00433   int ok = PyArg_ParseTuple(args, "s:loadmodule", &data);
00434   if (!ok) return NULL;
00435 
00436   // Load parameters for the module
00437   Environment::ParameterList params;
00438   if (kwds)
00439   {
00440     PyObject *key, *value;
00441     Py_ssize_t pos = 0;
00442     while (PyDict_Next(kwds, &pos, &key, &value))
00443       params[PythonObject(key).getString()] = PythonObject(value).getString();
00444   }
00445 
00446   // Free Python interpreter for other threads.
00447   // This is important since the module may also need access to Python
00448   // during its initialization...
00449   Py_BEGIN_ALLOW_THREADS
00450   try
00451   {
00452     // Load the library
00453     Environment::loadModule(data, params);
00454   }
00455   catch(...)
00456   {
00457     Py_BLOCK_THREADS;
00458     PythonType::evalException();
00459     return NULL;
00460   }
00461   Py_END_ALLOW_THREADS   // Reclaim Python interpreter
00462   return Py_BuildValue("");
00463 }
00464 
00465 
00466 DECLARE_EXPORT void Environment::printModules()
00467 {
00468   logger << "Loaded modules:" << endl;
00469   for (set<string>::const_iterator i=moduleRegistry.begin(); i!=moduleRegistry.end(); ++i)
00470     logger << "   " << *i << endl;
00471   logger << endl;
00472 }
00473 
00474 
00475 
00476 
00477 } // end namespace
00478 } // end namespace

Documentation generated for frePPLe by  doxygen