diff --git a/code/nel/tools/pipeline/service/build_task_queue.cpp b/code/nel/tools/pipeline/service/build_task_queue.cpp index fb103cb9c..31e20ef85 100644 --- a/code/nel/tools/pipeline/service/build_task_queue.cpp +++ b/code/nel/tools/pipeline/service/build_task_queue.cpp @@ -227,17 +227,18 @@ uint CBuildTaskQueue::countWorkingTasks() return nb; } -uint CBuildTaskQueue::countRemainingBuildableTasksAndWorkingTasks() +void CBuildTaskQueue::countRemainingBuildableTasksAndWorkingTasks(uint &buildable, uint &working) { m_Mutex.lock(); std::vector availableTasks; createBuildableTaskList(availableTasks, m_BypassDependencyError); - uint nb = availableTasks.size(); + buildable = availableTasks.size(); + uint nb = 0; for (std::vector::iterator it = m_Tasks.begin(), end = m_Tasks.end(); it != end; ++it) if ((*it)->State == TASK_WORKING) ++nb; + working = nb; m_Mutex.unlock(); - return nb; } void CBuildTaskQueue::listTaskQueueByMostDependents(std::vector &result) diff --git a/code/nel/tools/pipeline/service/build_task_queue.h b/code/nel/tools/pipeline/service/build_task_queue.h index 78be24a06..f90628e83 100644 --- a/code/nel/tools/pipeline/service/build_task_queue.h +++ b/code/nel/tools/pipeline/service/build_task_queue.h @@ -113,7 +113,7 @@ public: uint countRemainingBuildableTasks(); uint countWorkingTasks(); // when next are 0 the build should stop - uint countRemainingBuildableTasksAndWorkingTasks(); + void countRemainingBuildableTasksAndWorkingTasks(uint &buildable, uint &working); // informational listing for sending initial task listing to terminals void listTaskQueueByMostDependents(std::vector &result); diff --git a/code/nel/tools/pipeline/service/module_pipeline_master.cpp b/code/nel/tools/pipeline/service/module_pipeline_master.cpp index 43a0de496..8a6cab7d3 100644 --- a/code/nel/tools/pipeline/service/module_pipeline_master.cpp +++ b/code/nel/tools/pipeline/service/module_pipeline_master.cpp @@ -40,6 +40,7 @@ #include "module_pipeline_slave_itf.h" #include "pipeline_service.h" #include "database_status.h" +#include "build_task_queue.h" using namespace std; using namespace NLMISC; @@ -47,10 +48,14 @@ using namespace NLNET; namespace PIPELINE { +// temporary flags #define PIPELINE_INFO_MASTER_RELOAD_SHEETS "M_RELOAD_SHEETS" #define PIPELINE_INFO_MASTER_UPDATE_DATABASE_FOR_SLAVE "M_UPD_DB_FOR_S" +// permanent flags #define PIPELINE_INFO_CODE_ERROR_UNMACRO "CODE_ERROR_UNMACRO" +#define PIPELINE_INFO_SLAVE_REJECTED "SLAVE_REJECT" +#define PIPELINE_INFO_SLAVE_CRASHED "SLAVE_CRASH" /** * \brief CModulePipelineMaster @@ -70,7 +75,8 @@ class CModulePipelineMaster : Proxy(moduleProxy), ActiveTaskId(0), SheetsOk(true), - SaneBehaviour(3) { } + SaneBehaviour(3), + BuildReadyState(0) { } CModulePipelineMaster *Master; CModulePipelineSlaveProxy Proxy; std::vector Vector; @@ -78,6 +84,7 @@ class CModulePipelineMaster : bool SheetsOk; std::vector PluginsAvailable; sint SaneBehaviour; + uint BuildReadyState; ~CSlave() { @@ -96,7 +103,7 @@ class CModulePipelineMaster : bool canAcceptTask() { - return SheetsOk && (ActiveTaskId == 0) && SaneBehaviour > 0; + return SheetsOk && (ActiveTaskId == 0) && SaneBehaviour > 0 && BuildReadyState == 2; } }; @@ -104,9 +111,15 @@ protected: typedef std::map TSlaveMap; TSlaveMap m_Slaves; mutable boost::mutex m_SlavesMutex; + CBuildTaskQueue m_BuildTaskQueue; + bool m_BuildWorking; + + // build command + bool m_BypassErrors; + bool m_VerifyOnly; public: - CModulePipelineMaster() + CModulePipelineMaster() : m_BuildWorking(false) { g_IsMaster = true; } @@ -163,7 +176,9 @@ public: if (slave->ActiveTaskId) { - // ... + // if it goes down while busy on a task it crashed (or was poorly stopped by user...) + CInfoFlags::getInstance()->addFlag(PIPELINE_INFO_SLAVE_CRASHED); + // ... TODO ... } m_Slaves.erase(slaveIt); @@ -176,16 +191,83 @@ public: virtual void onModuleUpdate() { // if state build, iterate trough all slaves to see if any is free, and check if there's any waiting tasks + if (m_BuildWorking) + { + m_SlavesMutex.lock(); + // iterate trough all slaves to tell them the enter build_ready state. + for (TSlaveMap::iterator it = m_Slaves.begin(), end = m_Slaves.end(); it != end; ++it) + { + if (it->second->BuildReadyState == 0 && it->second->SaneBehaviour > 0 && it->second->SheetsOk) + { + it->second->BuildReadyState = 1; + it->second->Proxy.enterBuildReadyState(this); + } + // wait for confirmation, set BuildReadyState = 2 in that callback! + } + m_SlavesMutex.unlock(); + + uint nbBuildable, nbWorking; + m_BuildTaskQueue.countRemainingBuildableTasksAndWorkingTasks(nbBuildable, nbWorking); + if (nbBuildable > 0) + { + m_SlavesMutex.lock(); + // Iterate trough all slaves to see if any is free for a building task. + for (TSlaveMap::iterator it = m_Slaves.begin(), end = m_Slaves.end(); it != end; ++it) + { + if (it->second->canAcceptTask()) + { + CBuildTaskInfo *taskInfo = m_BuildTaskQueue.getTaskForSlave(it->second->PluginsAvailable); + if (taskInfo != NULL) + { + it->second->ActiveTaskId = taskInfo->Id.Global; + it->second->Proxy.startBuildTask(this, taskInfo->ProjectName, taskInfo->ProcessPluginId); + // the slave may either reject; or not answer until it's finished with this task + } + } + } + m_SlavesMutex.unlock(); + } + else if (nbWorking == 0) + { + // done (or stuck) + + m_BuildWorking = false; + + m_SlavesMutex.lock(); + // Iterate trough all slaves to tell them to end build_ready state. + for (TSlaveMap::iterator it = m_Slaves.begin(), end = m_Slaves.end(); it != end; ++it) + { + it->second->BuildReadyState = 0; + it->second->Proxy.leaveBuildReadyState(this); + nlassert(it->second->ActiveTaskId == 0); + } + m_SlavesMutex.unlock(); + + PIPELINE::endedBuildReady(); + } + } + } + + virtual void slaveFinishedBuildTask(NLNET::IModuleProxy *sender, uint8 errorLevel) + { + // TODO } - virtual void slaveFinishedBuildTask(NLNET::IModuleProxy *sender, uint32 taskId, uint8 errorLevel) + virtual void slaveAbortedBuildTask(NLNET::IModuleProxy *sender) { // TODO } - virtual void slaveRefusedBuildTask(NLNET::IModuleProxy *sender, uint32 taskId) + // in fact slaves are not allowed to refuse tasks, but they may do this if the user is toying around with the slave service + virtual void slaveRefusedBuildTask(NLNET::IModuleProxy *sender) { // TODO + CSlave *slave = m_Slaves[sender]; + m_BuildTaskQueue.rejectedTask(slave->ActiveTaskId); + slave->ActiveTaskId = 0; + --slave->SaneBehaviour; + CInfoFlags::getInstance()->addFlag(PIPELINE_INFO_SLAVE_REJECTED); + // TODO } virtual void slaveReloadedSheets(NLNET::IModuleProxy *sender) @@ -194,6 +276,20 @@ public: slave->SheetsOk = true; CInfoFlags::getInstance()->removeFlag(PIPELINE_INFO_MASTER_RELOAD_SHEETS); } + + virtual void slaveBuildReadySuccess(NLNET::IModuleProxy *sender) + { + CSlave *slave = m_Slaves[sender]; + slave->BuildReadyState = 2; + } + + virtual void slaveBuildReadyFail(NLNET::IModuleProxy *sender) + { + CSlave *slave = m_Slaves[sender]; + slave->BuildReadyState = 0; + --slave->SaneBehaviour; + CInfoFlags::getInstance()->addFlag(PIPELINE_INFO_SLAVE_REJECTED); + } virtual void vectorPushString(NLNET::IModuleProxy *sender, const std::string &str) { @@ -232,10 +328,42 @@ public: CSlave *slave = m_Slaves[sender]; slave->PluginsAvailable = pluginsAvailable; } + + bool build(bool bypassEros, bool verifyOnly) + { + if (PIPELINE::tryBuildReady()) + { + m_BuildWorking = true; + m_BypassErrors = bypassEros; + m_VerifyOnly = verifyOnly; + m_BuildTaskQueue.resetQueue(); + m_BuildTaskQueue.loadQueue(g_PipelineWorkspace, bypassEros); + return true; + } + return false; + } + + bool abort() + { + m_BuildTaskQueue.abortQueue(); + + m_SlavesMutex.lock(); + // Abort all slaves. + for (TSlaveMap::iterator it = m_Slaves.begin(), end = m_Slaves.end(); it != end; ++it) + { + if (it->second->ActiveTaskId != 0) + it->second->Proxy.abortBuildTask(this); + } + m_SlavesMutex.unlock(); + + return true; + } protected: NLMISC_COMMAND_HANDLER_TABLE_EXTEND_BEGIN(CModulePipelineMaster, CModuleBase) NLMISC_COMMAND_HANDLER_ADD(CModulePipelineMaster, reloadSheets, "Reload sheets across all services", "") + NLMISC_COMMAND_HANDLER_ADD(CModulePipelineMaster, build, "Build", "") + NLMISC_COMMAND_HANDLER_ADD(CModulePipelineMaster, abort, "Abort", "") NLMISC_COMMAND_HANDLER_TABLE_END NLMISC_CLASS_COMMAND_DECL(reloadSheets) @@ -268,6 +396,49 @@ protected: } } + NLMISC_CLASS_COMMAND_DECL(build) + { + bool bypassErrors = false; + bool verifyOnly = false; + std::vector unknownVec; + std::vector &workingVec = unknownVec; + + for (std::vector::const_iterator it = args.begin(), end = args.end(); it != end; ++it) + { + if ((*it)[0] == '-') + { + workingVec = unknownVec; + if (((*it) == "--bypassErrors") || ((*it) == "-be")) + bypassErrors = true; + else if (((*it) == "--verifyOnly") || ((*it) == "-vo")) + bypassErrors = true; + else + unknownVec.push_back(*it); + } + else + { + workingVec.push_back(*it); + } + } + + if (unknownVec.size() > 0) + { + for (std::vector::iterator it = unknownVec.begin(), end = unknownVec.end(); it != end; ++it) + log.displayNL("Unknown build parameter: %s", (*it).c_str()); + return false; + } + else + { + return build(bypassErrors, verifyOnly); + } + } + + NLMISC_CLASS_COMMAND_DECL(abort) + { + if (args.size() != 0) return false; + return this->abort(); + } + }; /* class CModulePipelineMaster */ void module_pipeline_master_forceLink() { } diff --git a/code/nel/tools/pipeline/service/module_pipeline_master_itf.cpp b/code/nel/tools/pipeline/service/module_pipeline_master_itf.cpp index 3de223215..436860af4 100644 Binary files a/code/nel/tools/pipeline/service/module_pipeline_master_itf.cpp and b/code/nel/tools/pipeline/service/module_pipeline_master_itf.cpp differ diff --git a/code/nel/tools/pipeline/service/module_pipeline_master_itf.h b/code/nel/tools/pipeline/service/module_pipeline_master_itf.h index 7dd4bd265..951e7b0c6 100644 Binary files a/code/nel/tools/pipeline/service/module_pipeline_master_itf.h and b/code/nel/tools/pipeline/service/module_pipeline_master_itf.h differ diff --git a/code/nel/tools/pipeline/service/module_pipeline_master_itf.xml b/code/nel/tools/pipeline/service/module_pipeline_master_itf.xml index 7b3fe75e6..d0f8b6572 100644 --- a/code/nel/tools/pipeline/service/module_pipeline_master_itf.xml +++ b/code/nel/tools/pipeline/service/module_pipeline_master_itf.xml @@ -7,20 +7,29 @@ - - + + - + + + + + + + + + + diff --git a/code/nel/tools/pipeline/service/module_pipeline_slave.cpp b/code/nel/tools/pipeline/service/module_pipeline_slave.cpp index f4b36eb25..df1cfaa10 100644 --- a/code/nel/tools/pipeline/service/module_pipeline_slave.cpp +++ b/code/nel/tools/pipeline/service/module_pipeline_slave.cpp @@ -146,7 +146,7 @@ public: //this->queueModuleTask CModulePipelineMasterProxy master(sender); - master.slaveRefusedBuildTask(this, 999999999); // NO MORE TASK ID + master.slaveRefusedBuildTask(this); // NO MORE TASK ID } virtual void abortBuildTask(NLNET::IModuleProxy *sender) @@ -172,6 +172,23 @@ public: if (PIPELINE::reloadSheets()) m_ReloadSheetsState = REQUEST_WORKING; else m_ReloadSheetsState = REQUEST_MADE; } + + virtual void enterBuildReadyState(NLNET::IModuleProxy *sender) + { + if (PIPELINE::tryBuildReady()) + { + m_Master->slaveBuildReadySuccess(this); + } + else + { + m_Master->slaveBuildReadyFail(this); + } + } + + virtual void leaveBuildReadyState(NLNET::IModuleProxy *sender) + { + PIPELINE::endedBuildReady(); + } void sendMasterAvailablePlugins() { diff --git a/code/nel/tools/pipeline/service/module_pipeline_slave_itf.cpp b/code/nel/tools/pipeline/service/module_pipeline_slave_itf.cpp index d69fef5f9..e7c621b2b 100644 Binary files a/code/nel/tools/pipeline/service/module_pipeline_slave_itf.cpp and b/code/nel/tools/pipeline/service/module_pipeline_slave_itf.cpp differ diff --git a/code/nel/tools/pipeline/service/module_pipeline_slave_itf.h b/code/nel/tools/pipeline/service/module_pipeline_slave_itf.h index 33adb6402..b9698c4f3 100644 Binary files a/code/nel/tools/pipeline/service/module_pipeline_slave_itf.h and b/code/nel/tools/pipeline/service/module_pipeline_slave_itf.h differ diff --git a/code/nel/tools/pipeline/service/module_pipeline_slave_itf.xml b/code/nel/tools/pipeline/service/module_pipeline_slave_itf.xml index 6c9d36b36..8e7a40a7b 100644 --- a/code/nel/tools/pipeline/service/module_pipeline_slave_itf.xml +++ b/code/nel/tools/pipeline/service/module_pipeline_slave_itf.xml @@ -22,6 +22,14 @@ + + + + + + + + diff --git a/code/nel/tools/pipeline/service/pipeline_service.cpp b/code/nel/tools/pipeline/service/pipeline_service.cpp index 00845293b..dd7622b16 100644 --- a/code/nel/tools/pipeline/service/pipeline_service.cpp +++ b/code/nel/tools/pipeline/service/pipeline_service.cpp @@ -142,6 +142,8 @@ enum EState STATE_RUNNABLE_TASK, STATE_BUSY_TEST, STATE_DIRECT_CODE, + STATE_BUILD_READY, + STATE_BUILD_PROCESS, }; /// Data @@ -152,6 +154,7 @@ CPipelineProcessImpl *s_PipelineProcessImpl = NULL; EState s_State = STATE_IDLE; std::string s_StateRunnableTaskName = ""; CMutex s_StateMutex; +uint s_BuildReadyRecursive = 0; std::vector s_LoadedLibraries; @@ -258,6 +261,63 @@ void endedDirectTask() endedRunnableTask(STATE_DIRECT_CODE); } +bool tryBuildReady() +{ + bool result = false; + s_StateMutex.enter(); + result = (s_State == STATE_IDLE) + || (s_State == STATE_BUILD_READY); + if (result) + { + s_State = STATE_BUILD_READY; + ++s_BuildReadyRecursive; + } + s_StateMutex.leave(); + if (!result) return false; + + nlassert(s_State == STATE_BUILD_READY && s_BuildReadyRecursive > 0); + + return true; +} + +void endedBuildReady() +{ + nlassert(s_State == STATE_BUILD_READY && s_BuildReadyRecursive > 0); + + s_StateMutex.enter(); + --s_BuildReadyRecursive; + if (s_BuildReadyRecursive == 0) + s_State = STATE_IDLE; + s_StateMutex.leave(); +} + +bool tryBuildProcess(const std::string &stateName) +{ + bool result = false; + s_StateMutex.enter(); + result = (s_State == STATE_BUILD_READY); + if (result) + { + s_State = STATE_BUILD_PROCESS; + s_StateRunnableTaskName = stateName; + } + s_StateMutex.leave(); + if (!result) return false; + + nlassert(s_State == STATE_BUILD_PROCESS); + + return true; +} + +void endedBuildProcess() +{ + nlassert(s_State == STATE_BUILD_PROCESS); + + s_StateMutex.enter(); + s_State = STATE_BUILD_READY; + s_StateMutex.leave(); +} + // ****************************************************************** namespace { @@ -472,14 +532,14 @@ public: s_InfoFlags->addFlag("RELEASE"); - NLNET::IModuleManager::releaseInstance(); - while (NLMISC::CAsyncFileManager::getInstance().getNumWaitingTasks() > 0) { nlSleep(10); } NLMISC::CAsyncFileManager::terminate(); - + + NLNET::IModuleManager::releaseInstance(); + for (std::vector::iterator it = s_LoadedLibraries.begin(), end = s_LoadedLibraries.end(); it != end; ++it) { (*it)->freeLibrary(); @@ -540,6 +600,17 @@ NLMISC_DYNVARIABLE(std::string, pipelineServiceState, "State of the pipeline ser case PIPELINE::STATE_BUSY_TEST: *pointer = "BUSY_TEST"; break; + case PIPELINE::STATE_BUILD_READY: + if (PIPELINE::s_BuildReadyRecursive == 1) + *pointer = "BUILD_READY (S)"; + else if (PIPELINE::s_BuildReadyRecursive == 2) + *pointer = "BUILD_READY (M)"; + else + *pointer = "BUILD_READY (???)"; + break; + case PIPELINE::STATE_BUILD_PROCESS: + *pointer = "BP: " + PIPELINE::s_StateRunnableTaskName; + break; } } } diff --git a/code/nel/tools/pipeline/service/pipeline_service.h b/code/nel/tools/pipeline/service/pipeline_service.h index 5ce51339e..17eae011b 100644 --- a/code/nel/tools/pipeline/service/pipeline_service.h +++ b/code/nel/tools/pipeline/service/pipeline_service.h @@ -73,6 +73,12 @@ void endedRunnableTask(); bool tryDirectTask(const std::string &stateName); void endedDirectTask(); +bool tryBuildReady(); +void endedBuildReady(); + +bool tryBuildProcess(const std::string &stateName); +void endedBuildProcess(); + /// Only use for informational purposes, service may not be idle after calling this. Not for thread safe usage. bool isServiceStateIdle(); diff --git a/code/nel/tools/pipeline/service/pipeline_workspace.cpp b/code/nel/tools/pipeline/service/pipeline_workspace.cpp index b85172615..7a52fbf88 100644 --- a/code/nel/tools/pipeline/service/pipeline_workspace.cpp +++ b/code/nel/tools/pipeline/service/pipeline_workspace.cpp @@ -149,7 +149,7 @@ void CPipelineWorkspace::getProcessPlugins(std::vector &resu processPlugin.Id.Sub.Handler = i; resultAppend.push_back(processPlugin); - nldebug("Found '%s': '%s', '%s'", process.c_str(), processPlugin.Handler.c_str(), processPlugin.Info.c_str()); + // nldebug("Found '%s': '%s', '%s'", process.c_str(), processPlugin.Handler.c_str(), processPlugin.Info.c_str()); } else { diff --git a/code/ryzom/common/data_leveldesign/leveldesign/DFN/pipeline/pipeline_plugin_process.dfn b/code/ryzom/common/data_leveldesign/leveldesign/DFN/pipeline/pipeline_plugin_process.dfn index ff766ec0c..8228affc4 100644 --- a/code/ryzom/common/data_leveldesign/leveldesign/DFN/pipeline/pipeline_plugin_process.dfn +++ b/code/ryzom/common/data_leveldesign/leveldesign/DFN/pipeline/pipeline_plugin_process.dfn @@ -2,6 +2,7 @@ + @@ -9,5 +10,6 @@ Sat Feb 18 13:34:33 2012 (Kaetemi) Dfn Structure = Sat Feb 18 13:34:44 2012 (Kaetemi) Dfn Structure = Sat Feb 18 13:44:42 2012 (Kaetemi) Dfn Structure = -Sat Mar 03 11:06:35 2012 (Kaetemi) Dfn Structure = +Sat Mar 03 11:06:35 2012 (Kaetemi) Dfn Structure = +Fri Mar 09 19:46:05 2012 (Kaetemi) Dfn Structure = diff --git a/code/ryzom/common/data_leveldesign/leveldesign/pipeline/common_interface.pipeline_project b/code/ryzom/common/data_leveldesign/leveldesign/pipeline/common_interface.pipeline_project index e519e0021..6f0724043 100644 --- a/code/ryzom/common/data_leveldesign/leveldesign/pipeline/common_interface.pipeline_project +++ b/code/ryzom/common/data_leveldesign/leveldesign/pipeline/common_interface.pipeline_project @@ -14,6 +14,7 @@ + @@ -92,5 +93,7 @@ Sat Mar 03 13:34:02 2012 (Kaetemi) .Interface.Atlas[2].DstFile = [&DstInterf Sat Mar 03 13:34:02 2012 (Kaetemi) .Macros[1].Name = DstInterfaceAtlasDxtc Sat Mar 03 13:34:02 2012 (Kaetemi) .Macros[1].Value = [!OutputDirectory]/interface_atlas_dxtc/ Sat Mar 03 13:34:02 2012 (Kaetemi) formName Resized = 2 -Sat Mar 03 13:34:32 2012 (Kaetemi) .Interface.AtlasDxtc[0].DstFile = [&DstInterfaceAtlasDxtc]/texture_interfaces_dxtc.tga +Sat Mar 03 13:34:32 2012 (Kaetemi) .Interface.AtlasDxtc[0].DstFile = [&DstInterfaceAtlasDxtc]/texture_interfaces_dxtc.tga +Fri Mar 09 19:49:23 2012 (Kaetemi) .Processes[1] = Dummy +Fri Mar 09 19:49:23 2012 (Kaetemi) formName Resized = 2 diff --git a/code/ryzom/common/data_leveldesign/leveldesign/pipeline/plugin_nel.pipeline_plugin b/code/ryzom/common/data_leveldesign/leveldesign/pipeline/plugin_nel.pipeline_plugin index f411c3f99..2da04f246 100644 --- a/code/ryzom/common/data_leveldesign/leveldesign/pipeline/plugin_nel.pipeline_plugin +++ b/code/ryzom/common/data_leveldesign/leveldesign/pipeline/plugin_nel.pipeline_plugin @@ -4,7 +4,17 @@ + + + + + + + + + + @@ -22,5 +32,15 @@ Sat Mar 03 10:55:24 2012 (Kaetemi) formName Resized = 1 Sat Mar 03 10:56:08 2012 (Kaetemi) .ProcessHandlers[0].Handler = CProcessInterface Sat Mar 03 10:56:08 2012 (Kaetemi) .ProcessHandlers[0].Process = Interface Sat Mar 03 11:13:55 2012 (Kaetemi) .ProcessHandlers[0].Info = CProcessInterfaceInfo -Sat Mar 03 12:10:28 2012 (Kaetemi) .Description = Pipeline Plugin NeL +Sat Mar 03 12:10:28 2012 (Kaetemi) .Description = Pipeline Plugin NeL +Fri Mar 09 19:48:26 2012 (Kaetemi) .ProcessHandlers[0].Description = Invalid process for testing +Fri Mar 09 19:48:26 2012 (Kaetemi) .ProcessHandlers[0].Handler = CProcessDummy +Fri Mar 09 19:48:26 2012 (Kaetemi) .ProcessHandlers[0].Info = CProcessDummy +Fri Mar 09 19:48:26 2012 (Kaetemi) .ProcessHandlers[0].Process = Dummy +Fri Mar 09 19:48:26 2012 (Kaetemi) .ProcessHandlers[1].Description = Builds interface textures +Fri Mar 09 19:48:26 2012 (Kaetemi) Array Insert = 0 +Fri Mar 09 19:48:26 2012 (Kaetemi) formName Deleted = +Fri Mar 09 19:48:26 2012 (Kaetemi) formName Resized = 2 +Fri Mar 09 19:48:39 2012 (Kaetemi) .ProcessHandlers[1].ProcessDependencies[0] = Dummy +Fri Mar 09 19:48:39 2012 (Kaetemi) formName Resized = 1