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

Documentation generated for frePPLe by  doxygen