r589: Added threaded rasterizer
[ctsim.git] / src / backgroundsupr.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          BackgroundSupr.cpp
5 **   Purpose:       Background Supervisor classes
6 **   Programmer:    Kevin Rosenberg
7 **   Date Started:  February 2001
8 **
9 **  This is part of the CTSim program
10 **  Copyright (C) 1983-2001 Kevin Rosenberg
11 **
12 **  $Id: backgroundsupr.cpp,v 1.9 2001/02/27 03:59:30 kevin Exp $
13 **
14 **  This program is free software; you can redistribute it and/or modify
15 **  it under the terms of the GNU General Public License (version 2) as
16 **  published by the Free Software Foundation.
17 **
18 **  This program is distributed in the hope that it will be useful,
19 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 **  GNU General Public License for more details.
22 **
23 **  You should have received a copy of the GNU General Public License
24 **  along with this program; if not, write to the Free Software
25 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 ******************************************************************************/
27
28 #include "wx/wxprec.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/wx.h"
32 #endif
33
34 #include "ct.h"
35 #include "ctsim.h"
36 #include "docs.h"
37 #include "views.h"
38 #include "backgroundsupr.h"
39 #include "backgroundmgr.h"
40
41 #if defined(HAVE_CONFIG_H)
42 #include "config.h"
43 #endif
44
45
46 ////////////////////////////////////////////////////////////////////////////
47 //
48 // Class BackgroundSupervisor -- An event handler run by a SupervisorThread
49 //
50 ////////////////////////////////////////////////////////////////////////////
51
52 IMPLEMENT_DYNAMIC_CLASS(BackgroundSupervisor, wxEvtHandler)
53 BEGIN_EVENT_TABLE(BackgroundSupervisor, BackgroundSupervisor)
54 EVT_MENU(MSG_BACKGROUND_SUPERVISOR_CANCEL, BackgroundSupervisor::OnCancel)
55 EVT_MENU(MSG_WORKER_THREAD_FAIL, BackgroundSupervisor::OnWorkerFail)
56 EVT_MENU(MSG_WORKER_THREAD_DONE, BackgroundSupervisor::OnWorkerDone)
57 EVT_MENU(MSG_WORKER_THREAD_UNIT_TICK, BackgroundSupervisor::OnWorkerUnitTick)
58 EVT_MENU(MSG_DOCUMENT_ACK_REMOVE, BackgroundSupervisor::OnAckDocumentRemove)
59 END_EVENT_TABLE()
60
61 // Static function
62 void 
63 BackgroundSupervisor::cancelSupervisor (BackgroundSupervisor* pSupervisor)
64 {
65   wxCommandEvent cancelEvent (wxEVT_COMMAND_MENU_SELECTED, MSG_BACKGROUND_SUPERVISOR_CANCEL);
66   wxPostEvent (pSupervisor, cancelEvent);
67 }
68
69
70 BackgroundSupervisor::BackgroundSupervisor (SupervisorThread* pMyThread, wxFrame* pParentFrame, wxDocument* pDocument, const char* const pszProcessTitle, int iTotalUnits)
71     : m_pMyThread(pMyThread), m_pParentFrame(pParentFrame), m_pDocument(pDocument), m_pDialogProgress(NULL), m_strProcessTitle(pszProcessTitle), 
72     m_iTotalUnits(iTotalUnits), m_iNumThreads(0), m_bDone(false), m_bFail(false), m_bCancelled(false), 
73     m_pTimer(NULL), m_bBackgroundTaskAdded(false), m_bWorkersDeleted(false),
74     wxEvtHandler()
75 {
76   m_iNumThreads = theApp->getNumberCPU();
77     ++m_iNumThreads;
78
79   m_vecpThreads.reserve (m_iNumThreads);
80   for (int iThread = 0; iThread < m_iNumThreads; iThread++)
81     m_vecpThreads[iThread] = NULL;
82
83 }
84
85 BackgroundSupervisor::~BackgroundSupervisor()
86 {
87   if (m_bBackgroundTaskAdded) {
88     wxCommandEvent doneEvent (wxEVT_COMMAND_MENU_SELECTED, MSG_BACKGROUND_SUPERVISOR_REMOVE);
89     doneEvent.SetClientData (this);
90     wxPostEvent (theApp->getBackgroundManager(), doneEvent);
91     wxPostEvent (m_pDocument, doneEvent);
92   }
93
94   while (m_bBackgroundTaskAdded) {
95     m_pMyThread->Sleep(50);
96     ProcessPendingEvents();
97   }
98
99   delete m_pTimer;
100   delete m_pDialogProgress;
101 }
102
103 void
104 BackgroundSupervisor::deleteWorkers()
105 {
106   wxCriticalSectionLocker lock (m_critsectThreads);
107   if (m_bWorkersDeleted)
108     return;
109
110   for (int i = 0; i < m_iNumThreads; i++) 
111     if (m_vecpThreads[i]) {
112       m_vecpThreads[i]->Delete(); // sends Destroy message to workers
113   }
114
115   while (m_iRunning > 0) {
116     m_pMyThread->Sleep(50);
117     ProcessPendingEvents();
118   }
119   m_iRunning = 0;
120   m_bWorkersDeleted = true;
121 }
122
123 bool
124 BackgroundSupervisor::start()
125 {
126   int iBaseUnits = m_iTotalUnits / m_iNumThreads;
127   int iExtraUnits = m_iTotalUnits % m_iNumThreads;
128   int iStartUnit = 0;
129   for (int iThread = 0; iThread < m_iNumThreads; iThread++) {
130     int iNumUnits = iBaseUnits;
131     if (iThread < iExtraUnits)
132       ++iNumUnits;
133     m_vecpThreads[iThread] = createWorker (iThread, iStartUnit, iNumUnits);
134     if (! m_vecpThreads[iThread]) {
135       m_bFail = true;
136       m_strFailMessage = "createWorker returned NULL [BackgroundSupervisor]";
137       break;
138     }
139     if (m_vecpThreads[iThread]->Create () != wxTHREAD_NO_ERROR) {
140       m_bFail = true;
141       m_strFailMessage = "Thread creation failed [BackgroundSupervisor]";
142       break;
143     }
144    m_vecpThreads[iThread]->SetPriority (40);
145    iStartUnit += iNumUnits;
146   }
147   if (m_bFail)
148     return false;
149
150   m_pTimer = new Timer;
151   
152   if (! theApp->getUseBackgroundTasks())
153     m_pDialogProgress = new wxProgressDialog (_T("Filtered Backprojection"), _T("Reconstruction Progress"), 
154     m_iTotalUnits, m_pParentFrame, wxPD_CAN_ABORT | wxPD_AUTO_HIDE);
155   else {
156     std::string strLabel (m_strProcessTitle);
157     strLabel += " ";
158     strLabel += m_pParentFrame->GetTitle();
159     wxCommandEvent addTaskEvent (wxEVT_COMMAND_MENU_SELECTED, MSG_BACKGROUND_SUPERVISOR_ADD);
160     addTaskEvent.SetString (strLabel.c_str());
161     addTaskEvent.SetInt (m_iTotalUnits);
162     addTaskEvent.SetClientData (this);
163     wxPostEvent (theApp->getBackgroundManager(), addTaskEvent);
164     wxPostEvent (m_pDocument, addTaskEvent);
165     m_bBackgroundTaskAdded = true;
166   }
167   
168   m_iRunning = m_iNumThreads;
169   m_iUnitsDone = 0;
170
171   for (int i = 0; i < m_iNumThreads; i++)
172     m_vecpThreads[i]->Run();
173     
174   return true;
175 }
176
177 void
178 BackgroundSupervisor::OnCancel(wxCommandEvent& event)
179 {
180   m_bCancelled = true;
181   m_bDone = true;
182   deleteWorkers();
183 }
184
185 void
186 BackgroundSupervisor::OnAckDocumentRemove(wxCommandEvent& event)
187 {
188   m_bBackgroundTaskAdded = false;
189 }
190
191 void
192 BackgroundSupervisor::OnWorkerUnitTick (wxCommandEvent& event)
193 {
194     ++m_iUnitsDone;
195     
196 #ifdef DEBUG
197     if (theApp->getVerboseLogging())
198       *theApp->getLog() << "Units done: " << static_cast<int>(m_iUnitsDone) <<"\n";
199 #endif
200     
201     if (m_pDialogProgress) {
202       if (! m_pDialogProgress->Update (m_iUnitsDone - 1)) {
203         wxCommandEvent dummy;
204         OnCancel (dummy);
205       }
206     } else {
207       wxCommandEvent addTaskEvent (wxEVT_COMMAND_MENU_SELECTED, MSG_BACKGROUND_SUPERVISOR_UNIT_TICK);
208       addTaskEvent.SetInt (m_iUnitsDone - 1);
209       addTaskEvent.SetClientData (this);
210       wxPostEvent (theApp->getBackgroundManager(), addTaskEvent);
211     }
212 }
213
214 void
215 BackgroundSupervisor::OnWorkerDone (wxCommandEvent& event)
216 {
217   m_iRunning--;
218   wxASSERT (m_iRunning >= 0);
219
220 #ifdef DEBUG
221   if (theApp->getVerboseLogging()) {
222     wxString msg;
223     msg.Printf("Background Supervisor: Thread finished. Remaining threads: %d\n", m_iRunning);  
224     wxCommandEvent eventLog (wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
225     eventLog.SetString( msg );
226     wxPostEvent( theApp->getMainFrame(), eventLog ); // send log event
227   }
228 #endif
229   if (m_iRunning <= 0 && ! m_bCancelled) {
230     deleteWorkers();
231     onDone();
232   }
233 }
234
235 void
236 BackgroundSupervisor::OnWorkerFail (wxCommandEvent& event)
237 {
238   m_iRunning--;
239   wxCommandEvent eventLog( wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
240   eventLog.SetString( event.GetString() );
241   wxPostEvent( theApp->getMainFrame(), eventLog ); // send log event
242
243   wxCommandEvent dummy;
244   OnCancel(dummy);
245 }
246