utils/actions.cpp
Go to the documentation of this file.00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/utils/actions.cpp $ 00003 version : $LastChangedRevision: 1315 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2010-07-17 18:08:53 +0200 (Sat, 17 Jul 2010) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2010 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
Documentation generated for frePPLe by
