summaryrefslogtreecommitdiff
path: root/library/multithreading.cpp
blob: 19f83b9464918628cbff48eb5a6b602d8ed72feb (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
#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(); // kind of a double notice that work is completed
            threadHandler->workDone = true;          // workaround for wxCondition bug (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(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!!!

        if (workDone == true) //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 maintread is in WaitTimeout()
    }
}

bgstack15