HLT sample applications
[u/mrichter/AliRoot.git] / HLT / BASE / AliHLTSystem.cxx
1 // $Id$
2
3 /**************************************************************************
4  * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
5  *                                                                        *
6  * Authors: Matthias Richter <Matthias.Richter@ift.uib.no>                *
7  *          for The ALICE Off-line Project.                               *
8  *                                                                        *
9  * Permission to use, copy, modify and distribute this software and its   *
10  * documentation strictly for non-commercial purposes is hereby granted   *
11  * without fee, provided that the above copyright notice appears in all   *
12  * copies and that both the copyright notice and this permission notice   *
13  * appear in the supporting documentation. The authors make no claims     *
14  * about the suitability of this software for any purpose. It is          *
15  * provided "as is" without express or implied warranty.                  *
16  **************************************************************************/
17
18 /** @file   AliHLTSystem.cxx
19     @author Matthias Richter
20     @date   
21     @brief  Implementation of HLT module management.
22 */
23
24 #if __GNUC__>= 3
25 using namespace std;
26 #endif
27
28 #include "AliHLTStdIncludes.h"
29 #include "AliHLTSystem.h"
30 #include "AliHLTComponentHandler.h"
31 #include "AliHLTComponent.h"
32 #include "AliHLTConfiguration.h"
33 #include "AliHLTConfigurationHandler.h"
34 #include "AliHLTTask.h"
35 #include "AliHLTModuleAgent.h"
36 #include "AliHLTOfflineInterface.h"
37 #include <TObjArray.h>
38 #include <TObjString.h>
39 #include <TString.h>
40 #include <TStopwatch.h>
41
42 /** ROOT macro for the implementation of ROOT specific class methods */
43 ClassImp(AliHLTSystem)
44
45 AliHLTSystem::AliHLTSystem()
46   :
47   fpComponentHandler(new AliHLTComponentHandler()),
48   fpConfigurationHandler(new AliHLTConfigurationHandler()),
49   fTaskList(),
50   fState(0)
51 {
52   // see header file for class documentation
53   // or
54   // refer to README to build package
55   // or
56   // visit http://web.ift.uib.no/~kjeks/doc/alice-hlt
57
58   if (fgNofInstances++>0)
59     HLTWarning("multiple instances of AliHLTSystem, you should not use more than one at a time");
60
61   SetGlobalLoggingLevel(kHLTLogDefault);
62   if (fpComponentHandler) {
63     AliHLTComponentEnvironment env;
64     memset(&env, 0, sizeof(AliHLTComponentEnvironment));
65     env.fAllocMemoryFunc=AliHLTSystem::AllocMemory;
66     env.fLoggingFunc=NULL;
67     fpComponentHandler->SetEnvironment(&env);
68   } else {
69     HLTFatal("can not create Component Handler");
70   }
71   if (fpConfigurationHandler) {
72     AliHLTConfiguration::GlobalInit(fpConfigurationHandler);
73   } else {
74     HLTFatal("can not create Configuration Handler");
75   }
76 }
77
78 AliHLTSystem::AliHLTSystem(const AliHLTSystem&)
79   :
80   AliHLTLogging(),
81   fpComponentHandler(NULL),
82   fpConfigurationHandler(NULL),
83   fTaskList(),
84   fState(0)
85 {
86   // see header file for class documentation
87   if (fgNofInstances++>0)
88     HLTWarning("multiple instances of AliHLTSystem, you should not use more than one at a time");
89
90   HLTFatal("copy constructor untested");
91 }
92
93 AliHLTSystem& AliHLTSystem::operator=(const AliHLTSystem&)
94
95   // see header file for class documentation
96   HLTFatal("assignment operator untested");
97   return *this;
98 }
99
100 AliHLTSystem::~AliHLTSystem()
101 {
102   // see header file for class documentation
103   fgNofInstances--;
104   CleanTaskList();
105   AliHLTConfiguration::GlobalDeinit(fpConfigurationHandler);
106   if (fpConfigurationHandler) {
107     delete fpConfigurationHandler;
108   }
109   fpConfigurationHandler=NULL;
110   
111   if (fpComponentHandler) {
112     delete fpComponentHandler;
113   }
114   fpComponentHandler=NULL;
115 }
116
117 int AliHLTSystem::fgNofInstances=0;
118
119 int AliHLTSystem::AddConfiguration(AliHLTConfiguration* pConf)
120 {
121   // see header file for class documentation
122   int iResult=0;
123   if (pConf) {
124   } else {
125     iResult=-EINVAL;
126   }
127   return iResult;
128 }
129
130 int AliHLTSystem::InsertConfiguration(AliHLTConfiguration* pConf, AliHLTConfiguration* pPrec)
131 {
132   // see header file for class documentation
133   int iResult=0;
134   if (pConf) {
135     if (pPrec) {
136       // find the position
137     }
138   } else {
139     iResult=-EINVAL;
140   }
141   return iResult;
142 }
143
144 int AliHLTSystem::DeleteConfiguration(AliHLTConfiguration* pConf)
145 {
146   // see header file for class documentation
147   int iResult=0;
148   if (pConf) {
149   } else {
150     iResult=-EINVAL;
151   }
152   return iResult;
153 }
154
155 int AliHLTSystem::BuildTaskList(AliHLTConfiguration* pConf)
156 {
157   // see header file for class documentation
158   int iResult=0;
159   if (pConf) {
160     AliHLTTask* pTask=NULL;
161     if ((pTask=FindTask(pConf->GetName()))!=NULL) {
162       if (pTask->GetConf()!=pConf) {
163         HLTError("configuration missmatch, there is already a task with configuration name \"%s\", but it is different. Most likely configuration %p is not registered properly", pConf->GetName(), pConf);
164         iResult=-EEXIST;
165         pTask=NULL;
166       }
167     } else if (pConf->SourcesResolved(1)!=1) {
168         HLTError("configuration \"%s\" has unresolved sources, aborting ...", pConf->GetName());
169         iResult=-ENOLINK;
170     } else {
171       pTask=new AliHLTTask(pConf);
172       if (pTask==NULL) {
173         iResult=-ENOMEM;
174       }
175     }
176     if (pTask) {
177       // check for circular dependencies
178       if ((iResult=pConf->FollowDependency(pConf->GetName()))>0) {
179         HLTError("detected circular dependency for configuration \"%s\"", pTask->GetName());
180         pTask->PrintDependencyTree(pTask->GetName(), 1/*use the configuration list*/);
181         HLTError("aborted ...");
182         iResult=-ELOOP;
183       }
184       if (iResult>=0) {
185         // check whether all dependencies are already in the task list
186         // create the missing ones
187         // this step is an iterative process which calls this function again for the missing
188         // configurations, in order to avoid the currently processed task to be created
189         // again it is added to the list temporarily and removed afterwards
190         // This is of high importance to preserve the order of the tasks. Furthermore, the
191         // InsertTask method has to be used in order to set all the cross links right 
192         fTaskList.Add(pTask);
193         AliHLTConfiguration* pDep=pConf->GetFirstSource();
194         while (pDep!=NULL && iResult>=0) {
195           if (FindTask(pDep->GetName())==NULL) {
196             iResult=BuildTaskList(pDep);
197           }
198           pDep=pConf->GetNextSource();
199         }
200         // remove the temporarily added task
201         fTaskList.Remove(pTask);
202
203         // insert the task and set the cross-links
204         if (iResult>=0) {
205           iResult=InsertTask(pTask);
206         }
207       } else {
208         delete pTask;
209         pTask=NULL;
210       }
211     }
212   } else {
213     iResult=-EINVAL;
214   }
215   return iResult;
216 }
217
218 int AliHLTSystem::CleanTaskList()
219 {
220   // see header file for class documentation
221   int iResult=0;
222   TObjLink* lnk=NULL;
223   while ((lnk=fTaskList.FirstLink())!=NULL) {
224     fTaskList.Remove(lnk);
225     delete (lnk->GetObject());
226   }
227   return iResult;
228 }
229
230 int AliHLTSystem::InsertTask(AliHLTTask* pTask)
231 {
232   // see header file for class documentation
233   int iResult=0;
234   TObjLink *lnk = NULL;
235   if ((iResult=pTask->CheckDependencies())>0)
236     lnk=fTaskList.FirstLink();
237   while (lnk && iResult>0) {
238     AliHLTTask* pCurr = (AliHLTTask*)lnk->GetObject();
239     //HLTDebug("checking  \"%s\"", pCurr->GetName());
240     iResult=pTask->Depends(pCurr);
241     if (iResult>0) {
242       iResult=pTask->SetDependency(pCurr);
243       pCurr->SetTarget(pTask);
244       HLTDebug("set dependency  \"%s\" for configuration \"%s\"", pCurr->GetName(), pTask->GetName());
245     }
246     if (pCurr->Depends(pTask)) {
247       // circular dependency
248       HLTError("circular dependency: can not resolve dependencies for configuration \"%s\"", pTask->GetName());
249       iResult=-ELOOP;
250     } else if ((iResult=pTask->CheckDependencies())>0) {
251       lnk = lnk->Next();
252     }
253   }
254   if (iResult==0) {
255       if (lnk) {
256         fTaskList.AddAfter(lnk, pTask);
257       } else {
258         fTaskList.AddFirst(pTask);
259       }
260       HLTDebug("task \"%s\" inserted", pTask->GetName());
261   } else if (iResult>0) {
262     HLTError("can not resolve dependencies for configuration \"%s\" (%d unresolved)", pTask->GetName(), iResult);
263     iResult=-ENOLINK;
264   }
265   return iResult;
266 }
267
268 AliHLTTask* AliHLTSystem::FindTask(const char* id)
269 {
270   // see header file for class documentation
271   AliHLTTask* pTask=NULL;
272   if (id) {
273     pTask=(AliHLTTask*)fTaskList.FindObject(id); 
274   }
275   return pTask;
276 }
277
278 void AliHLTSystem::PrintTaskList()
279 {
280   // see header file for class documentation
281   HLTLogKeyword("task list");
282   TObjLink *lnk = NULL;
283   HLTMessage("Task List");
284   lnk=fTaskList.FirstLink();
285   while (lnk) {
286     TObject* obj=lnk->GetObject();
287     if (obj) {
288       HLTMessage("  %s - status:", obj->GetName());
289       AliHLTTask* pTask=(AliHLTTask*)obj;
290       pTask->PrintStatus();
291     } else {
292     }
293     lnk = lnk->Next();
294   }
295 }
296
297 int AliHLTSystem::Run(Int_t iNofEvents) 
298 {
299   // see header file for class documentation
300   int iResult=0;
301   int iCount=0;
302   SetStatusFlags(kRunning);
303   TStopwatch StopwatchBase; StopwatchBase.Reset();
304   TStopwatch StopwatchDA; StopwatchDA.Reset();
305   TStopwatch StopwatchInput; StopwatchInput.Reset();
306   TStopwatch StopwatchOutput; StopwatchOutput.Reset();
307   TObjArray Stopwatches;
308   Stopwatches.AddAt(&StopwatchBase, (int)AliHLTComponent::kSWBase);
309   Stopwatches.AddAt(&StopwatchDA, (int)AliHLTComponent::kSWDA);
310   Stopwatches.AddAt(&StopwatchInput, (int)AliHLTComponent::kSWInput);
311   Stopwatches.AddAt(&StopwatchOutput, (int)AliHLTComponent::kSWOutput);
312   if ((iResult=InitTasks())>=0 && (iResult=InitBenchmarking(&Stopwatches))>=0) {
313     if ((iResult=StartTasks())>=0) {
314       for (int i=0; i<iNofEvents && iResult>=0; i++) {
315         iResult=ProcessTasks(i);
316         if (iResult>=0) {
317           HLTInfo("Event %d successfully finished (%d)", i, iResult);
318           iResult=0;
319           iCount++;
320         } else {
321           HLTError("Processing of event %d failed (%d)", i, iResult);
322           // TODO: define different running modes to either ignore errors in
323           // event processing or not
324           // currently ignored 
325           //iResult=0;
326         }
327       }
328       StopTasks();
329     } else {
330       HLTError("can not start task list");
331     }
332     DeinitTasks();
333   } else if (iResult!=-ENOENT) {
334     HLTError("can not initialize task list");
335   }
336   if (iResult>=0) {
337     iResult=iCount;
338     HLTInfo("HLT statistics:\n"
339             "    base:              R:%.3fs C:%.3fs\n"
340             "    input:             R:%.3fs C:%.3fs\n"
341             "    output:            R:%.3fs C:%.3fs\n"
342             "    event processing : R:%.3fs C:%.3fs"
343             , StopwatchBase.RealTime(),StopwatchBase.CpuTime()
344             , StopwatchInput.RealTime(),StopwatchInput.CpuTime()
345             , StopwatchOutput.RealTime(),StopwatchOutput.CpuTime()
346             , StopwatchDA.RealTime(),StopwatchDA.CpuTime());
347   }
348   ClearStatusFlags(kRunning);
349   return iResult;
350 }
351
352 int AliHLTSystem::InitTasks()
353 {
354   // see header file for class documentation
355   int iResult=0;
356   TObjLink *lnk=fTaskList.FirstLink();
357   if (lnk==NULL) {
358     HLTWarning("Task list is empty, aborting ...");
359     return -ENOENT;
360   }
361   while (lnk && iResult>=0) {
362     TObject* obj=lnk->GetObject();
363     if (obj) {
364       AliHLTTask* pTask=(AliHLTTask*)obj;
365       iResult=pTask->Init(NULL, fpComponentHandler);
366     } else {
367     }
368     lnk = lnk->Next();
369   }
370   if (iResult<0) {
371   }
372   return iResult;
373 }
374
375 int AliHLTSystem::InitBenchmarking(TObjArray* pStopwatches)
376 {
377   // see header file for class documentation
378   if (pStopwatches==NULL) return -EINVAL;
379
380   int iResult=0;
381   TObjLink *lnk=fTaskList.FirstLink();
382   while (lnk && iResult>=0) {
383     TObject* obj=lnk->GetObject();
384     if (obj) {
385       AliHLTTask* pTask=(AliHLTTask*)obj;
386       AliHLTComponent* pComp=NULL;
387       if (iResult>=0 && (pComp=pTask->GetComponent())!=NULL) {
388         switch (pComp->GetComponentType()) {
389         case AliHLTComponent::kProcessor:
390           pComp->SetStopwatches(pStopwatches);
391           break;
392         case AliHLTComponent::kSource:
393           {
394             // this switch determines whether the time consumption of the
395             // AliHLTComponent base methods should be counted to the input
396             // stopwatch or base stopwatch.
397             //int inputBase=(int)AliHLTComponent::kSWBase;
398             int inputBase=(int)AliHLTComponent::kSWInput;
399             pComp->SetStopwatch(pStopwatches->At(inputBase), AliHLTComponent::kSWBase);
400             pComp->SetStopwatch(pStopwatches->At((int)AliHLTComponent::kSWInput), AliHLTComponent::kSWDA);
401           }
402           break;
403         case AliHLTComponent::kSink:
404           {
405             // this switch determines whether the time consumption of the
406             // AliHLTComponent base methods should be counted to the output
407             // stopwatch or base stopwatch.
408             //int outputBase=(int)AliHLTComponent::kSWBase;
409             int outputBase=(int)AliHLTComponent::kSWOutput;
410             pComp->SetStopwatch(pStopwatches->At(outputBase), AliHLTComponent::kSWBase);
411             pComp->SetStopwatch(pStopwatches->At((int)AliHLTComponent::kSWOutput), AliHLTComponent::kSWDA);
412           }
413           break;
414         }
415       }
416     } else {
417     }
418     lnk = lnk->Next();
419   }
420   return iResult;
421 }
422
423 int AliHLTSystem::StartTasks()
424 {
425   // see header file for class documentation
426   int iResult=0;
427   TObjLink *lnk=fTaskList.FirstLink();
428   while (lnk && iResult>=0) {
429     TObject* obj=lnk->GetObject();
430     if (obj) {
431       AliHLTTask* pTask=(AliHLTTask*)obj;
432       iResult=pTask->StartRun();
433     } else {
434     }
435     lnk = lnk->Next();
436   }
437   if (iResult<0) {
438   }
439   return iResult;
440 }
441
442 int AliHLTSystem::ProcessTasks(Int_t eventNo)
443 {
444   // see header file for class documentation
445   int iResult=0;
446   HLTDebug("processing event no %d", eventNo);
447   TObjLink *lnk=fTaskList.FirstLink();
448   while (lnk && iResult>=0) {
449     TObject* obj=lnk->GetObject();
450     if (obj) {
451       AliHLTTask* pTask=(AliHLTTask*)obj;
452       iResult=pTask->ProcessTask(eventNo);
453       HLTDebug("task %s finnished (%d)", pTask->GetName(), iResult);
454     } else {
455     }
456     lnk = lnk->Next();
457   }
458   return iResult;
459 }
460
461 int AliHLTSystem::StopTasks()
462 {
463   // see header file for class documentation
464   int iResult=0;
465   TObjLink *lnk=fTaskList.FirstLink();
466   while (lnk && iResult>=0) {
467     TObject* obj=lnk->GetObject();
468     if (obj) {
469       AliHLTTask* pTask=(AliHLTTask*)obj;
470       iResult=pTask->EndRun();
471     } else {
472     }
473     lnk = lnk->Next();
474   }
475   return iResult;
476 }
477
478 int AliHLTSystem::DeinitTasks()
479 {
480   // see header file for class documentation
481   int iResult=0;
482   TObjLink *lnk=fTaskList.FirstLink();
483   while (lnk && iResult>=0) {
484     TObject* obj=lnk->GetObject();
485     if (obj) {
486       AliHLTTask* pTask=(AliHLTTask*)obj;
487       iResult=pTask->Deinit();
488     } else {
489     }
490     lnk = lnk->Next();
491   }
492   return iResult;
493 }
494
495 void* AliHLTSystem::AllocMemory( void* param, unsigned long size )
496 {
497   // see header file for class documentation
498   if (param==NULL) {
499     // get rid of 'unused parameter' warning
500   }
501   return (void*)new char[size];
502 }
503
504 int AliHLTSystem::Reconstruct(int nofEvents, AliRunLoader* runLoader, 
505                               AliRawReader* rawReader)
506 {
507   // see header file for class documentation
508   int iResult=0;
509   if (runLoader) {
510     HLTInfo("Run Loader %p, Raw Reader %p , %d events", runLoader, rawReader, nofEvents);
511     if (CheckStatus(kReady)) {
512       if ((iResult=AliHLTOfflineInterface::SetParamsToComponents(runLoader, rawReader))>=0) {
513         iResult=Run(nofEvents);
514       }
515     } else {
516       HLTError("wrong state %#x, required flags %#x", GetStatusFlags(), kReady);
517     }
518   } else {
519     HLTError("missing run loader instance");
520     iResult=-EINVAL;
521   }
522   return iResult;
523 }
524
525 int AliHLTSystem::FillESD(int eventNo, AliRunLoader* runLoader, AliESD* esd)
526 {
527   // see header file for class documentation
528   int iResult=0;
529   if (runLoader) {
530     HLTInfo("Event %d: Run Loader %p, ESD %p", eventNo, runLoader, esd);
531     iResult=AliHLTOfflineInterface::FillComponentESDs(eventNo, runLoader, esd);
532   } else {
533     HLTError("missing run loader/ESD instance(s)");
534     iResult=-EINVAL;
535   }
536   return iResult;
537 }
538
539 int AliHLTSystem::LoadComponentLibraries(const char* libraries)
540 {
541   // see header file for class documentation
542   int iResult=0;
543   if (libraries) {
544     if (fpComponentHandler) {
545       TString libs(libraries);
546       TObjArray* pTokens=libs.Tokenize(" ");
547       if (pTokens) {
548         int iEntries=pTokens->GetEntries();
549         for (int i=0; i<iEntries && iResult>=0; i++) {
550           iResult=fpComponentHandler->LoadLibrary((((TObjString*)pTokens->At(i))->GetString()).Data());
551         }
552         delete pTokens;
553       }
554       if (iResult>=0) {
555         SetStatusFlags(kLibrariesLoaded);
556       } else {
557         // lets see if we need this, probably not
558         //fpComponentHandler->UnloadLibraries();
559         ClearStatusFlags(kLibrariesLoaded);
560       }
561     } else {
562       iResult=-EFAULT;
563       HLTFatal("no component handler available");
564     }
565   } else {
566     iResult=-EINVAL;
567   }
568   return iResult;
569 }
570
571 int AliHLTSystem::Configure(AliRunLoader* runloader)
572 {
573   // see header file for class documentation
574   int iResult=0;
575   if (CheckStatus(kRunning)) {
576     HLTError("HLT system in running state, can not configure");
577     return -EBUSY;
578   }
579   if (CheckFilter(kHLTLogDebug))
580     AliHLTModuleAgent::PrintStatus();
581   ClearStatusFlags(kConfigurationLoaded|kTaskListCreated);
582   iResult=LoadConfigurations(runloader);
583   if (iResult>=0) {
584     SetStatusFlags(kConfigurationLoaded);
585     iResult=BuildTaskListsFromTopConfigurations(runloader);
586     if (iResult>=0) {
587       SetStatusFlags(kTaskListCreated);
588     }
589   }
590   if (iResult<0) SetStatusFlags(kError);
591   
592   return iResult;
593 }
594
595 int AliHLTSystem::Reset(int bForce)
596 {
597   // see header file for class documentation
598   int iResult=0;
599   if (!bForce && CheckStatus(kRunning)) {
600     HLTError("HLT system in running state, can not configure");
601     return -EBUSY;
602   }
603   CleanTaskList();
604   ClearStatusFlags(~kUninitialized);
605   return iResult;
606 }
607
608 int AliHLTSystem::LoadConfigurations(AliRunLoader* runloader)
609 {
610   // see header file for class documentation
611   if (CheckStatus(kRunning)) {
612     HLTError("HLT system in running state, can not configure");
613     return -EBUSY;
614   }
615   int iResult=0;
616   AliHLTModuleAgent* pAgent=AliHLTModuleAgent::GetFirstAgent();
617   while (pAgent && iResult>=0) {
618     const char* deplibs=pAgent->GetRequiredComponentLibraries();
619     if (deplibs) {
620       HLTDebug("load libraries \'%s\' for agent %s (%p)", deplibs, pAgent->GetName(), pAgent);
621       iResult=LoadComponentLibraries(deplibs);
622     }
623     if (iResult>=0) {
624       HLTDebug("load configurations for agent %s (%p)", pAgent->GetName(), pAgent);
625       pAgent->CreateConfigurations(fpConfigurationHandler, runloader);
626       pAgent=AliHLTModuleAgent::GetNextAgent();
627     }
628   }
629   return iResult;
630 }
631
632 int AliHLTSystem::BuildTaskListsFromTopConfigurations(AliRunLoader* runloader)
633 {
634   // see header file for class documentation
635   if (CheckStatus(kRunning)) {
636     HLTError("HLT system in running state, can not configure");
637     return -EBUSY;
638   }
639   if (!CheckStatus(kConfigurationLoaded)) {
640     HLTWarning("configurations not yet loaded");
641     return 0;
642   }
643
644   int iResult=0;
645   AliHLTModuleAgent* pAgent=AliHLTModuleAgent::GetFirstAgent();
646   while (pAgent && iResult>=0) {
647     TString tops=pAgent->GetLocalRecConfigurations(runloader);
648     HLTDebug("top configurations for agent %s (%p): %s", pAgent->GetName(), pAgent, tops.Data());
649     TObjArray* pTokens=tops.Tokenize(" ");
650     if (pTokens) {
651       int iEntries=pTokens->GetEntries();
652       for (int i=0; i<iEntries && iResult>=0; i++) {
653         const char* pCID=((TObjString*)pTokens->At(i))->GetString().Data();
654         AliHLTConfiguration* pConf=fpConfigurationHandler->FindConfiguration(pCID);
655         if (pConf) {
656           iResult=BuildTaskList(pConf);
657         } else {
658           HLTWarning("can not find top configuration %s", pCID);
659         }
660       }
661       delete pTokens;
662     }
663     
664     pAgent=AliHLTModuleAgent::GetNextAgent();
665   }
666   if (iResult>=0) SetStatusFlags(kTaskListCreated);
667
668   return iResult;
669 }
670
671 int AliHLTSystem::CheckStatus(int flag)
672 {
673   // see header file for class documentation
674   if (flag==kUninitialized && flag==fState) return 1;
675   if ((fState&flag)==flag) return 1;
676   return 0;
677 }
678
679 int AliHLTSystem::GetStatusFlags()
680 {
681   // see header file for class documentation
682   return fState;
683 }
684
685 int AliHLTSystem::SetStatusFlags(int flags)
686 {
687   // see header file for class documentation
688   fState|=flags;
689   return fState;
690 }
691
692 int AliHLTSystem::ClearStatusFlags(int flags)
693 {
694   // see header file for class documentation
695   fState&=~flags;
696   return fState;
697 }