summaryrefslogtreecommitdiffstats
blob: 759672d8cce22a4824b40e475ffc0f4d2b74be6a (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
// Boost.Signals library

// Copyright Douglas Gregor 2001-2004. Use, modification and
// distribution is subject to the Boost Software License, Version
// 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

// For more information, see http://www.boost.org

#define BOOST_SIGNALS_SOURCE

#include <boost/signals/detail/signal_base.hpp>
#include <cassert>

namespace boost {
  namespace BOOST_SIGNALS_NAMESPACE {
    namespace detail {
      signal_base_impl::signal_base_impl(const compare_type& comp,
                                         const any& combiner)
        : call_depth(0),
          slots_(comp),
          combiner_(combiner)
      {
        flags.delayed_disconnect = false;
        flags.clearing = false;
      }

      signal_base_impl::~signal_base_impl()
      {
        // Set the "clearing" flag to ignore extraneous disconnect requests,
        // because all slots will be disconnected on destruction anyway.
        flags.clearing = true;
      }

      void signal_base_impl::disconnect_all_slots()
      {
        // Do nothing if we're already clearing the slot list
        if (flags.clearing)
          return;

        if (call_depth == 0) {
          // Clearing the slot list will disconnect all slots automatically
          temporarily_set_clearing set_clearing(this);
          slots_.clear();
        }
        else {
          // We can't actually remove elements from the slot list because there
          // are still iterators into the slot list that must not be
          // invalidated by this operation. So just disconnect each slot
          // without removing it from the slot list. When the call depth does
          // reach zero, the call list will be cleared.
          flags.delayed_disconnect = true;
          temporarily_set_clearing set_clearing(this);
          for (iterator i = slots_.begin(); i != slots_.end(); ++i) {
            i->first.disconnect();
          }
        }
      }

      connection
      signal_base_impl::
        connect_slot(const any& slot_,
                     const stored_group& name,
                     shared_ptr<slot_base::data_t> data,
                     connect_position at)
      {
        // Transfer the burden of ownership to a local, scoped
        // connection.
        data->watch_bound_objects.set_controlling(false);
        scoped_connection safe_connection(data->watch_bound_objects);

        // Allocate storage for an iterator that will hold the point of
        // insertion of the slot into the list. This is used to later remove
        // the slot when it is disconnected.
        std::auto_ptr<iterator> saved_iter(new iterator);

        // Add the slot to the list.
        iterator pos =
          slots_.insert(name, data->watch_bound_objects, slot_, at);

        // The assignment operation here absolutely must not throw, which
        // intuitively makes sense (because any container's insert method
        // becomes impossible to use in an exception-safe manner without this
        // assumption), but doesn't appear to be mentioned in the standard.
        *saved_iter = pos;

        // Fill out the connection object appropriately. None of these
        // operations can throw
        data->watch_bound_objects.get_connection()->signal = this;
        data->watch_bound_objects.get_connection()->signal_data =
          saved_iter.release();
        data->watch_bound_objects.get_connection()->signal_disconnect =
          &signal_base_impl::slot_disconnected;

        // Make the copy of the connection in the list disconnect when it is
        // destroyed. The local, scoped connection is then released
        // because ownership has been transferred.
        pos->first.set_controlling();
        return safe_connection.release();
      }

      bool signal_base_impl::empty() const
      {
        // Disconnected slots may still be in the list of slots if
        //   a) this is called while slots are being invoked (call_depth > 0)
        //   b) an exception was thrown in remove_disconnected_slots
        for (iterator i = slots_.begin(); i != slots_.end(); ++i) {
          if (i->first.connected())
            return false;
        }

        return true;
      }

      std::size_t signal_base_impl::num_slots() const
      {
        // Disconnected slots may still be in the list of slots if
        //   a) this is called while slots are being invoked (call_depth > 0)
        //   b) an exception was thrown in remove_disconnected_slots
        std::size_t count = 0;
        for (iterator i = slots_.begin(); i != slots_.end(); ++i) {
          if (i->first.connected())
            ++count;
        }
        return count;
      }

      void signal_base_impl::disconnect(const stored_group& group)
      { slots_.disconnect(group); }

      void signal_base_impl::slot_disconnected(void* obj, void* data)
      {
        signal_base_impl* self = reinterpret_cast<signal_base_impl*>(obj);

        // We won't need the slot iterator after this
        std::auto_ptr<iterator> slot(reinterpret_cast<iterator*>(data));

        // If we're flags.clearing, we don't bother updating the list of slots
        if (!self->flags.clearing) {
          // If we're in a call, note the fact that a slot has been deleted so
          // we can come back later to remove the iterator
          if (self->call_depth > 0) {
            self->flags.delayed_disconnect = true;
          }
          else {
            // Just remove the slot now, it's safe
            self->slots_.erase(*slot);
          }
        }
      }

      void signal_base_impl::remove_disconnected_slots() const
      { slots_.remove_disconnected_slots(); }

      call_notification::
        call_notification(const shared_ptr<signal_base_impl>& b) :
          impl(b)
      {
        // A call will be made, so increment the call depth as a notification
        impl->call_depth++;
      }

      call_notification::~call_notification()
      {
        impl->call_depth--;

        // If the call depth is zero and we have some slots that have been
        // disconnected during the calls, remove those slots from the list
        if (impl->call_depth == 0 &&
            impl->flags.delayed_disconnect) {
          impl->remove_disconnected_slots();
          impl->flags.delayed_disconnect = false;
        }
      }

    signal_base::signal_base(const compare_type& comp, const any& combiner)
      : impl()
    {
      impl.reset(new signal_base_impl(comp, combiner));
    }

    signal_base::~signal_base()
    {
    }

    } // namespace detail
  } // namespace BOOST_SIGNALS_NAMESPACE
} // namespace boost