1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 #ifndef itkLoggerThreadWrapper_hxx
19 #define itkLoggerThreadWrapper_hxx
20 
21 #include <iostream>
22 #include "itkLoggerThreadWrapper.h"
23 #include "itksys/SystemTools.hxx"
24 
25 namespace itk
26 {
27 /** Set the priority level for the current logger. Only messages that have
28  * priorities equal or greater than the one set here will be posted to the
29  * current outputs */
30 template< typename SimpleLoggerType >
SetPriorityLevel(PriorityLevelType level)31 void LoggerThreadWrapper< SimpleLoggerType >::SetPriorityLevel(PriorityLevelType level)
32 {
33   this->m_Mutex.lock();
34   this->m_OperationQ.push(SET_PRIORITY_LEVEL);
35   this->m_LevelQ.push(level);
36   this->m_Mutex.unlock();
37 }
38 
39 /** Get the priority level for the current logger. Only messages that have
40  * priorities equal or greater than the one set here will be posted to the
41  * current outputs */
42 template< typename SimpleLoggerType >
GetPriorityLevel() const43 typename SimpleLoggerType::PriorityLevelType LoggerThreadWrapper< SimpleLoggerType >::GetPriorityLevel() const
44 {
45   this->m_Mutex.lock();
46   PriorityLevelType level = this->m_PriorityLevel;
47   this->m_Mutex.unlock();
48   return level;
49 }
50 
51 template< typename SimpleLoggerType >
SetLevelForFlushing(PriorityLevelType level)52 void LoggerThreadWrapper< SimpleLoggerType >::SetLevelForFlushing(PriorityLevelType level)
53 {
54   this->m_Mutex.lock();
55   this->m_LevelForFlushing = level;
56   this->m_OperationQ.push(SET_LEVEL_FOR_FLUSHING);
57   this->m_LevelQ.push(level);
58   this->m_Mutex.unlock();
59 }
60 
61 template< typename SimpleLoggerType >
GetLevelForFlushing() const62 typename SimpleLoggerType::PriorityLevelType LoggerThreadWrapper< SimpleLoggerType >::GetLevelForFlushing() const
63 {
64   this->m_Mutex.lock();
65   PriorityLevelType level = this->m_LevelForFlushing;
66   this->m_Mutex.unlock();
67   return level;
68 }
69 
70 template< typename SimpleLoggerType >
SetDelay(DelayType delay)71 void LoggerThreadWrapper< SimpleLoggerType >::SetDelay(DelayType delay)
72 {
73   this->m_Mutex.lock();
74   this->m_Delay = delay;
75   this->m_Mutex.unlock();
76 }
77 
78 template< typename SimpleLoggerType >
GetDelay() const79 typename LoggerThreadWrapper< SimpleLoggerType >::DelayType LoggerThreadWrapper< SimpleLoggerType >::GetDelay() const
80 {
81   this->m_Mutex.lock();
82   DelayType delay = this->m_Delay;
83   this->m_Mutex.unlock();
84   return delay;
85 }
86 
87 /** Adds an output stream to the MultipleLogOutput for writing. */
88 template< typename SimpleLoggerType >
AddLogOutput(OutputType * output)89 void LoggerThreadWrapper< SimpleLoggerType >::AddLogOutput(OutputType *output)
90 {
91   this->m_Mutex.lock();
92   this->m_OperationQ.push(ADD_LOG_OUTPUT);
93   this->m_OutputQ.push(output);
94   this->m_Mutex.unlock();
95 }
96 
97 template< typename SimpleLoggerType >
Write(PriorityLevelType level,std::string const & content)98 void LoggerThreadWrapper< SimpleLoggerType >::Write(PriorityLevelType level, std::string const & content)
99 {
100   this->m_Mutex.lock();
101   if ( this->m_PriorityLevel >= level )
102     {
103     this->m_OperationQ.push(WRITE);
104     this->m_MessageQ.push(content);
105     this->m_LevelQ.push(level);
106     }
107   this->m_Mutex.unlock();
108   if ( this->m_LevelForFlushing >= level )
109     {
110     this->Flush();
111     }
112 }
113 
114 template< typename SimpleLoggerType >
Flush()115 void LoggerThreadWrapper< SimpleLoggerType >::Flush()
116 {
117   this->m_Mutex.lock();
118 
119   while ( !this->m_OperationQ.empty() )
120     {
121     switch ( this->m_OperationQ.front() )
122       {
123       case Self::SET_PRIORITY_LEVEL:
124         this->m_PriorityLevel = this->m_LevelQ.front();
125         this->m_LevelQ.pop();
126         break;
127       case Self::SET_LEVEL_FOR_FLUSHING:
128         this->m_LevelForFlushing = this->m_LevelQ.front();
129         this->m_LevelQ.pop();
130         break;
131 
132       case Self::ADD_LOG_OUTPUT:
133         this->m_Output->AddLogOutput( this->m_OutputQ.front() );
134         this->m_OutputQ.pop();
135         break;
136 
137       case Self::WRITE:
138         this->SimpleLoggerType::Write( this->m_LevelQ.front(), this->m_MessageQ.front() );
139         this->m_LevelQ.pop();
140         this->m_MessageQ.pop();
141         break;
142       }
143     this->m_OperationQ.pop();
144     }
145   this->SimpleLoggerType::Flush();
146   this->m_Output->Flush();
147   this->m_Mutex.unlock();
148 }
149 
150 /** Constructor */
151 template< typename SimpleLoggerType >
LoggerThreadWrapper()152 LoggerThreadWrapper< SimpleLoggerType >::LoggerThreadWrapper()
153 {
154   m_Delay = 300; // ms
155   m_TerminationRequested = false;
156   m_Thread = std::thread(&Self::ThreadFunction, this);
157 }
158 
159 /** Destructor */
160 template< typename SimpleLoggerType >
~LoggerThreadWrapper()161 LoggerThreadWrapper< SimpleLoggerType >::~LoggerThreadWrapper()
162 {
163   this->Flush();
164   if( m_Thread.joinable() )
165     {
166     m_TerminationRequested = true;
167     m_Thread.join(); //waits for it to finish if necessary
168     }
169 }
170 
171 template< typename SimpleLoggerType >
172 void
173 LoggerThreadWrapper< SimpleLoggerType >
ThreadFunction()174 ::ThreadFunction()
175 {
176   while ( !m_TerminationRequested )
177     {
178     m_Mutex.lock();
179     while ( !m_OperationQ.empty() )
180       {
181       switch ( m_OperationQ.front() )
182         {
183         case Self::SET_PRIORITY_LEVEL:
184           this->m_PriorityLevel = m_LevelQ.front();
185           m_LevelQ.pop();
186           break;
187 
188         case Self::SET_LEVEL_FOR_FLUSHING:
189           this->m_LevelForFlushing = m_LevelQ.front();
190           m_LevelQ.pop();
191           break;
192 
193         case Self::ADD_LOG_OUTPUT:
194           this->m_Output->AddLogOutput( m_OutputQ.front() );
195           m_OutputQ.pop();
196           break;
197 
198         case Self::WRITE:
199           SimpleLoggerType::Write( m_LevelQ.front(), m_MessageQ.front() );
200           m_LevelQ.pop();
201           m_MessageQ.pop();
202           break;
203         }
204       m_OperationQ.pop();
205       }
206     m_Mutex.unlock();
207     SimpleLoggerType::Flush();
208     itksys::SystemTools::Delay(this->GetDelay());
209     }
210 }
211 
212 /** Print contents of a LoggerThreadWrapper */
213 template< typename SimpleLoggerType >
PrintSelf(std::ostream & os,Indent indent) const214 void LoggerThreadWrapper< SimpleLoggerType >::PrintSelf(std::ostream & os, Indent indent) const
215 {
216   Superclass::PrintSelf(os, indent);
217 
218   os << indent << "Thread ID: " << this->m_Thread.get_id() << std::endl;
219   os << indent << "Low-priority Message Delay: " << this->m_Delay << std::endl;
220   os << indent << "Operation Queue Size: " << this->m_OperationQ.size() << std::endl;
221   os << indent << "Message Queue Size: " << this->m_MessageQ.size() << std::endl;
222   os << indent << "Level Queue Size: " << this->m_LevelQ.size() << std::endl;
223   os << indent << "Output Queue Size: " << this->m_OutputQ.size() << std::endl;
224 }
225 } // namespace itk
226 
227 #endif // itkLoggerThreadWrapper_hxx
228