summaryrefslogtreecommitdiff
path: root/library/multithreading.cpp
blob: 899a426bbd01740c1e39f550afbf1094a96068c2 (plain)
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#include "multithreading.h"
#include "statusHandler.h"
#include <wx/utils.h>

//#include <wx/msw/wrapwin.h> //includes "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) :
            wxThread(wxTHREAD_DETACHED),
            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(); // kind of a double notice that work is completed
            threadHandler->workDone = true;          // Workaround for wxWidgets: bug in wxCondition (wxWidgets v2.8.9, signal might geht lost)
            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),
        workDone(false)
{
    //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();

    workDone = false; //no mutex needed here (worker thread that changes this variable is in waiting state)
}
//          /|\  \|/   must be called directly after each other

void UpdateWhileExecuting::execute(StatusHandler* statusUpdater)
{
    readyToReceiveResult.Lock();

    theWorkerThread->beginProcessing.Signal();
    theWorkerThread->readyToBeginProcessing.Unlock();

    while (receivingResult.WaitTimeout(UI_UPDATE_INTERVAL) == wxCOND_TIMEOUT)
    {
        statusUpdater->requestUiRefresh(false); //don't allow throwing exception within this call

        if (workDone) //workaround for a bug in wxWidgets v2.8.9 class wxCondition: signals might get lost
            break;    //no mutex for workDone needed here: it is changed only when mainthread is in WaitTimeout()
    }
}



//    ------------------------------------------------------
//    |Pattern: workload queue and multiple worker threads |
//    ------------------------------------------------------
//typedef std::vector<DirectoryDescrType*> Workload;
//
//class ThreadSorting : public wxThread
//{
//public:
//    ThreadSorting(wxCriticalSection& syncWorkload, Workload& workload) :
//            wxThread(wxTHREAD_JOINABLE),
//            syncWorkload_(syncWorkload),
//            workload_(workload)
//    {
//        if (Create() != wxTHREAD_NO_ERROR)
//            throw RuntimeException(wxString(wxT("Error creating thread for sorting!")));
//    }
//
//    ~ThreadSorting() {}
//
//
//    ExitCode Entry()
//    {
//        while (true)
//        {
//            DirectoryDescrType* descr = NULL;
//            {  //see if there is work to do...
//                wxCriticalSectionLocker dummy(syncWorkload_);
//                if (workload_.empty())
//                    return 0;
//                else
//                {
//                    descr = workload_.back();
//                    workload_.pop_back();
//                }
//            }
//            //do work
//            std::sort(descr->begin(), descr->end());
//        }
//    }
//
//private:
//    wxCriticalSection& syncWorkload_;
//    Workload& workload_;
//};
//
//
//void DirectoryDescrBuffer::preFillBuffers(const std::vector<FolderPairCfg>& fpConfigFormatted)
//{
//    //assemble workload
//	...
//
//    //we use binary search when comparing the directory structures: so sort() first
//    const int CPUCount = wxThread::GetCPUCount();
//    if (CPUCount >= 2) //do it the multithreaded way:
//    {
//        wxCriticalSection syncWorkload;
//
//        typedef std::vector<boost::shared_ptr<ThreadSorting> > ThreadContainer;
//        ThreadContainer sortThreads;
//        sortThreads.reserve(CPUCount);
//
//        //start CPUCount worker threads
//        for (size_t i = 0; i < std::min(static_cast<size_t>(CPUCount), workload.size()); ++i)
//        {
//            boost::shared_ptr<ThreadSorting> newWorker(new ThreadSorting(syncWorkload, workload));
//
//            if (newWorker->Run() != wxTHREAD_NO_ERROR)
//                throw RuntimeException(wxString(wxT("Error starting thread for sorting!")));
//
//            sortThreads.push_back(newWorker);
//        }
//
//        //wait until all worker are finished
//        for (ThreadContainer::iterator i = sortThreads.begin(); i != sortThreads.end(); ++i)
//        {
//            if ((*i)->Wait() != 0)
//                throw RuntimeException(wxString(wxT("Error waiting for thread (sorting)!")));
//        }
//    }
//    else //single threaded
//    {
//        for (Workload::iterator i = workload.begin(); i != workload.end(); ++i)
//            std::sort((*i)->begin(), (*i)->end());
//    }
//}
bgstack15