sigslot.cpp 5.45 KB
Newer Older
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
// ================================================================================================
// 
// sigslot.h/sigslot.cpp - Signal/Slot classes:
// 
// Original siglsot implementation written by Sarah Thompson (sarah@telergy.com) 2002 and 
// published under public domain. <http://sigslot.sourceforge.net/>
// 
// This version of the sigslot library is heavily modified, C++ compliant, inherently thread-safe,
// and offers a manager class that allows to queue and asynchronously dispatch signals.
// 
// Copyright (C) 2012-2014, all rights reserved,
//      Christian Schulte zu Berge <christian.szb@in.tum.de>
//      Chair for Computer Aided Medical Procedures
//      Technische Universität München
//      Boltzmannstr. 3, 85748 Garching b. München, Germany
// 
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 
// except in compliance with the License. You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software distributed under the 
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
// either express or implied. See the License for the specific language governing permissions 
// and limitations under the License.
// 
// ================================================================================================

29
#include <memory>
30
31
#include <new>

32
33
34
35
#include "sigslot.h"

namespace sigslot {

36
37
38
    signal_manager::signal_manager() 
        : _handlingMode(DEFAULT)
    {
39
40
41
42

    }

    signal_manager::~signal_manager() {
43
        _signalPool.recycle();
44
45
    }

46
    void signal_manager::triggerSignalImpl(_signal_handle_base* signal) {
47
        if (_handlingMode == FORCE_QUEUE) {
48
            queueSignalImpl(signal);
49
50
51
            return;
        }

52
        signal->processSignal();
53
54
55
        delete signal;
    }

56
    bool signal_manager::queueSignalImpl(_signal_handle_base* signal) {
57
        if (_handlingMode == FORCE_DIRECT) {
58
            triggerSignalImpl(signal);
59
60
61
            return true;
        }

62
        cgtAssert(signal != 0, "Signal must not be 0.");
63
64
65
66
67
68
69
70
71
        if (signal == 0)
            return false;

        _signalQueue.push(signal);
        _evaluationCondition.notify_all();
        return true;
    }

    void signal_manager::run() {
72
        _this_thread_id = std::this_thread::get_id();
73
        std::unique_lock<std::mutex> lock(_ecMutex);
74
75
76

        while (! _stopExecution) {
            // try pop the next event from the event queue
77
            _signal_handle_base* signal = nullptr;
78
            if (_signalQueue.try_pop(signal)) {
79
                signal->processSignal();
80
81
82
83
                delete signal;
            }
            else {
                // there currently is no event in this queue -> go sleep
84
                _evaluationCondition.wait(lock);
85
86
87
88
            }
        }
    }

89
90
91
92
    bool signal_manager::isCurrentThreadSignalManagerThread() const {
        return std::this_thread::get_id() == _this_thread_id;
    }

93
94
95
96
97
98
99
100
    signal_manager::SignalHandlingMode signal_manager::getSignalHandlingMode() const {
        return _handlingMode;
    }

    void signal_manager::setSignalHandlingMode(SignalHandlingMode mode) {
        _handlingMode = mode;
    }

101
102
103
104
    void signal_manager::waitForSignalQueueFlushed() {
        // nothing to wait for if current thread is signal_manager's thread.
        if (isCurrentThreadSignalManagerThread())
            return;
Christian Schulte zu Berge's avatar
Christian Schulte zu Berge committed
105
106
        
        // signal used to detect that the signal queue has been flushed
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
        class SIGSLOT_API flushed_signal : public _signal_handle_base {
        public:
            flushed_signal(bool* ptr)
                : _ptr(ptr)
            {}

            ~flushed_signal() {}

            virtual void processSignal() const {
                *_ptr = true;
            }

        private:
            bool* _ptr;
        };

        bool signalVariable = false;
        this->queueSignalImpl(new flushed_signal(&signalVariable));
Christian Schulte zu Berge's avatar
Christian Schulte zu Berge committed
125
126
127
128
129
130
131

        while (! signalVariable) {
            // so we do some busy waiting here - hopefully not too long...
            std::this_thread::yield();
            // make sure that the signal_manager thread is actually running, otherwise we wait forever.
            _evaluationCondition.notify_all();
        }
132
    }
133
134
135

    const std::string signal_manager::loggerCat_;

136
137
138
139
140
141
142
143
144
145
146
147
148

    // Implementation inspired by http://stackoverflow.com/questions/7194127/how-should-i-write-iso-c-standard-conformant-custom-new-and-delete-operators/7194149#7194149
    void* _signal_handle_base::operator new(std::size_t size) throw(std::bad_alloc) {
        if (size == 0)
            size = 1;

        while (true) {
            void* toReturn = signal_manager::getRef()._signalPool.malloc(size);

            if (toReturn != nullptr)
                return toReturn;

            //allocation was unsuccessful; find out what the current new-handling function is (see below)
149
            std::new_handler globalHandler = std::set_new_handler(0);
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
            std::set_new_handler(globalHandler);

            if (globalHandler)             //If new_hander is registered call it
                (*globalHandler)();
            else 
                throw std::bad_alloc();   //No handler is registered throw an exception
        }
    }

    void _signal_handle_base::operator delete(void* rawMemory, std::size_t size) throw() {
        if (rawMemory == nullptr)
            return;

        signal_manager::getRef()._signalPool.free(rawMemory);
        return;
    }

167
}