1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
#include "multithreading.h"
#include <wx/utils.h>
//#include "windows.h"
//MessageBox(0, "hi", "", 0);
/*choreography:
------------- ---------------
|main thread| |worker thread|
------------- ---------------
1. Instantiation (Constructor)
-------------------------------
create thread
enter waiting state
2. waitUntilReady
-------------------------------
wait until thread is ready
3. Call execute
-------------------------------
send signal to start
start processing
update UI while thread works
finish processing
wait until main thread is ready to receive signal
receive signal
enter waiting state
4. Termination (Destructor)
-------------------------------
wait until thread is in wait state
set exit flag
signal thread to continue (and exit)
*/
class WorkerThread : public wxThread
{
friend class UpdateWhileExecuting;
public:
WorkerThread(UpdateWhileExecuting* handler) :
readyToBeginProcessing(),
beginProcessing(readyToBeginProcessing),
threadIsInitialized(false),
threadExitIsRequested(false),
threadHandler(handler)
{ }
~WorkerThread() {}
ExitCode Entry()
{
readyToBeginProcessing.Lock(); //this lock needs to be called IN the thread => calling it from constructor(Main thread) would be useless
sharedData.Enter();
threadIsInitialized = true;
sharedData.Leave();
while (true)
{
beginProcessing.Wait();
//no mutex needed in this context
if (threadExitIsRequested)
return 0;
//actual (long running) work is done in this method
threadHandler->longRunner();
threadHandler->readyToReceiveResult.Lock();
threadHandler->receivingResult.Signal();
threadHandler->readyToReceiveResult.Unlock();
}
return 0;
}
private:
wxMutex readyToBeginProcessing;
wxCondition beginProcessing;
//shared data
wxCriticalSection sharedData;
bool threadIsInitialized;
bool threadExitIsRequested;
UpdateWhileExecuting* threadHandler;
};
UpdateWhileExecuting::UpdateWhileExecuting() :
readyToReceiveResult(),
receivingResult(readyToReceiveResult)
{
//mutex needs to be initially locked for condition receivingResult to work properly
readyToReceiveResult.Lock();
theWorkerThread = new WorkerThread(this);
theWorkerThread->Create();
theWorkerThread->Run();
//wait until the thread has locked readyToBeginProcessing
bool threadInitialized = false;
while (!threadInitialized)
{
theWorkerThread->sharedData.Enter();
threadInitialized = theWorkerThread->threadIsInitialized;
theWorkerThread->sharedData.Leave();
wxMilliSleep(5);
} //-> it's not nice, but works and is no issue
}
UpdateWhileExecuting::~UpdateWhileExecuting()
{
//wait until thread is ready, then start and exit immediately
readyToReceiveResult.Unlock(); //avoid possible deadlock, when thread might be waiting to send the signal
theWorkerThread->readyToBeginProcessing.Lock();
//set flag to exit thread
theWorkerThread->threadExitIsRequested = true;
theWorkerThread->beginProcessing.Signal();
theWorkerThread->readyToBeginProcessing.Unlock();
//theWorkerThread deletes itself!
}
void UpdateWhileExecuting::waitUntilReady()
{
readyToReceiveResult.Unlock(); //avoid possible deadlock, when thread might be waiting to send the signal (if abort was pressed)
theWorkerThread->readyToBeginProcessing.Lock();
}
// /|\ \|/ must be called directly after each other
void UpdateWhileExecuting::execute(StatusUpdater* statusUpdater)
{
readyToReceiveResult.Lock();
theWorkerThread->beginProcessing.Signal();
theWorkerThread->readyToBeginProcessing.Unlock();
while (receivingResult.WaitTimeout(UI_UPDATE_INTERVAL) == wxCOND_TIMEOUT)
statusUpdater->triggerUI_Refresh(true); //ATTENTION: Exception "AbortThisProcess" may be thrown here!!!
}
|