]> git.lyx.org Git - lyx.git/blob - 3rdparty/nod/nod.hpp
* es/Intro from Dan.
[lyx.git] / 3rdparty / nod / nod.hpp
1 #ifndef IG_NOD_INCLUDE_NOD_HPP
2 #define IG_NOD_INCLUDE_NOD_HPP
3
4 #include <vector>       // std::vector
5 #include <functional>   // std::function
6 #include <mutex>        // std::mutex, std::lock_guard
7 #include <memory>       // std::shared_ptr, std::weak_ptr
8 #include <algorithm>    // std::find_if()
9 #include <cassert>      // assert()
10 #include <thread>       // std::this_thread::yield()
11 #include <type_traits>  // std::is_same
12 #include <iterator>     // std::back_inserter
13
14 namespace nod {
15         // implementational details
16         namespace detail {
17                 /// Interface for type erasure when disconnecting slots
18                 struct disconnector {
19                         virtual void operator()( std::size_t index ) const = 0;
20                 };
21                 /// Deleter that doesn't delete
22                 inline void no_delete(disconnector*){
23                 };
24         } // namespace detail
25
26         /// Base template for the signal class
27         template <class P, class T>
28         class signal_type;
29
30
31         /// Connection class.
32         ///
33         /// This is used to be able to disconnect slots after they have been connected.
34         /// Used as return type for the connect method of the signals.
35         ///
36         /// Connections are default constructible.
37         /// Connections are not copy constructible or copy assignable.
38         /// Connections are move constructible and move assignable.
39         ///
40         class connection {
41                 public:
42                         /// Default constructor
43                         connection() :
44                                 _index()
45                         {}
46
47                         // Connection are not copy constructible or copy assignable
48                         connection( connection const& ) = delete;
49                         connection& operator=( connection const& ) = delete;
50
51                         /// Move constructor
52                         /// @param other   The instance to move from.
53                         connection( connection&& other ) :
54                                 _weak_disconnector( std::move(other._weak_disconnector) ),
55                                 _index( other._index )
56                         {}
57
58                         /// Move assign operator.
59                         /// @param other   The instance to move from.
60                         connection& operator=( connection&& other ) {
61                                 _weak_disconnector = std::move( other._weak_disconnector );
62                                 _index = other._index;
63                                 return *this;
64                         }
65
66                         /// @returns `true` if the connection is connected to a signal object,
67                         ///          and `false` otherwise.
68                         bool connected() const {
69                                 return !_weak_disconnector.expired();
70                         }
71
72                         /// Disconnect the slot from the connection.
73                         ///
74                         /// If the connection represents a slot that is connected to a signal object, calling
75                         /// this method will disconnect the slot from that object. The result of this operation
76                         /// is that the slot will stop receiving calls when the signal is invoked.
77                         void disconnect();
78
79                 private:
80                         /// The signal template is a friend of the connection, since it is the
81                         /// only one allowed to create instances using the meaningful constructor.
82                         template<class P,class T> friend class signal_type;
83
84                         /// Create a connection.
85                         /// @param shared_disconnector   Disconnector instance that will be used to disconnect
86                         ///                              the connection when the time comes. A weak pointer
87                         ///                              to the disconnector will be held within the connection
88                         ///                              object.
89                         /// @param index                 The slot index of the connection.
90                         connection( std::shared_ptr<detail::disconnector> const& shared_disconnector, std::size_t index ) :
91                                 _weak_disconnector( shared_disconnector ),
92                                 _index( index )
93                         {}
94
95                         /// Weak pointer to the current disconnector functor.
96                         std::weak_ptr<detail::disconnector> _weak_disconnector;
97                         /// Slot index of the connected slot.
98                         std::size_t _index;
99         };
100
101         /// Scoped connection class.
102         ///
103         /// This type of connection is automatically disconnected when
104         /// the connection object is destructed.
105         ///
106         class scoped_connection
107         {
108                 public:
109                         /// Scoped are default constructible
110                         scoped_connection() = default;
111                         /// Scoped connections are not copy constructible
112                         scoped_connection( scoped_connection const& ) = delete;
113                         /// Scoped connections are not copy assingable
114                         scoped_connection& operator=( scoped_connection const& ) = delete;
115
116                         /// Move constructor
117                         scoped_connection( scoped_connection&& other ) :
118                                 _connection( std::move(other._connection) )
119                         {}
120
121                         /// Move assign operator.
122                         /// @param other   The instance to move from.
123                         scoped_connection& operator=( scoped_connection&& other ) {
124                                 reset( std::move( other._connection ) );
125                                 return *this;
126                         }
127
128                         /// Construct a scoped connection from a connection object
129                         /// @param connection   The connection object to manage
130                         scoped_connection( connection&& c ) :
131                                 _connection( std::forward<connection>(c) )
132                         {}
133
134                         /// destructor
135                         ~scoped_connection() {
136                                 disconnect();
137                         }
138
139                         /// Assignment operator moving a new connection into the instance.
140                         /// @note If the scoped_connection instance already contains a
141                         ///       connection, that connection will be disconnected as if
142                         ///       the scoped_connection was destroyed.
143                         /// @param c   New connection to manage
144                         scoped_connection& operator=( connection&& c ) {
145                                 reset( std::forward<connection>(c) );
146                                 return *this;
147                         }
148
149                         /// Reset the underlying connection to another connection.
150                         /// @note The connection currently managed by the scoped_connection
151                         ///       instance will be disconnected when resetting.
152                         /// @param c   New connection to manage
153                         void reset( connection&& c = {} ) {
154                                 disconnect();
155                                 _connection = std::move(c);
156                         }
157
158                         /// Release the underlying connection, without disconnecting it.
159                         /// @returns The newly released connection instance is returned.
160                         connection release() {
161                                 connection c = std::move(_connection);
162                                 _connection = connection{};
163                                 return c;
164                         }
165
166                         ///
167                         /// @returns `true` if the connection is connected to a signal object,
168                         ///          and `false` otherwise.
169                         bool connected() const {
170                                 return _connection.connected();
171                         }
172
173                         /// Disconnect the slot from the connection.
174                         ///
175                         /// If the connection represents a slot that is connected to a signal object, calling
176                         /// this method will disconnect the slot from that object. The result of this operation
177                         /// is that the slot will stop receiving calls when the signal is invoked.
178                         void disconnect() {
179                                 _connection.disconnect();
180                         }
181
182                 private:
183                         /// Underlying connection object
184                         connection _connection;
185         };
186
187         /// Policy for multi threaded use of signals.
188         ///
189         /// This policy provides mutex and lock types for use in
190         /// a multithreaded environment, where signals and slots
191         /// may exists in different threads.
192         ///
193         /// This policy is used in the `nod::signal` type provided
194         /// by the library.
195         struct multithread_policy
196         {
197                 using mutex_type = std::mutex;
198                 using mutex_lock_type = std::unique_lock<mutex_type>;
199                 /// Function that yields the current thread, allowing
200                 /// the OS to reschedule.
201                 static void yield_thread() {
202                         std::this_thread::yield();
203                 }
204                 /// Function that defers a lock to a lock function that prevents deadlock
205                 static mutex_lock_type defer_lock(mutex_type & m){
206                         return mutex_lock_type{m, std::defer_lock};
207                 }
208                 /// Function that locks two mutexes and prevents deadlock
209                 static void lock(mutex_lock_type & a,mutex_lock_type & b) {
210                         std::lock(a,b);
211                 }
212         };
213
214         /// Policy for single threaded use of signals.
215         ///
216         /// This policy provides dummy implementations for mutex
217         /// and lock types, resulting in that no synchronization
218         /// will take place.
219         ///
220         /// This policy is used in the `nod::unsafe_signal` type
221         /// provided by the library.
222         struct singlethread_policy
223         {
224                 /// Dummy mutex type that doesn't do anything
225                 struct mutex_type{};
226                 /// Dummy lock type, that doesn't do any locking.
227                 struct mutex_lock_type
228                 {
229                         /// A lock type must be constructible from a
230                         /// mutex type from the same thread policy.
231                         explicit mutex_lock_type( mutex_type const& ) {
232                         }
233                 };
234                 /// Dummy implementation of thread yielding, that
235                 /// doesn't do any actual yielding.
236                 static void yield_thread() {
237                 }
238                 /// Dummy implemention of defer_lock that doesn't
239                 /// do anything
240                 static mutex_lock_type defer_lock(mutex_type &m){
241                         return mutex_lock_type{m};
242                 }
243                 /// Dummy implemention of lock that doesn't
244                 /// do anything
245                 static void lock(mutex_lock_type &,mutex_lock_type &) {
246                 }
247         };
248
249         /// Signal accumulator class template.
250         ///
251         /// This acts sort of as a proxy for triggering a signal and
252         /// accumulating the slot return values.
253         ///
254         /// This class is not really intended to instantiate by client code.
255         /// Instances are aquired as return values of the method `accumulate()`
256         /// called on signals.
257         ///
258         /// @tparam S      Type of signal. The signal_accumulator acts
259         ///                as a type of proxy for a signal instance of
260         ///                this type.
261         /// @tparam T      Type of initial value of the accumulate algorithm.
262         ///                This type must meet the requirements of `CopyAssignable`
263         ///                and `CopyConstructible`
264         /// @tparam F      Type of accumulation function.
265         /// @tparam A...   Argument types of the underlying signal type.
266         ///
267         template <class S, class T, class F, class...A>
268         class signal_accumulator
269         {
270                 public:
271                         /// Result type when calling the accumulating function operator.
272 #if __cplusplus >= 201703L
273                         using result_type = typename std::invoke_result<F, T, typename S::slot_type::result_type>::type;
274 #else
275                         using result_type = typename std::result_of<F(T, typename S::slot_type::result_type)>::type;
276 #endif
277
278                         /// Construct a signal_accumulator as a proxy to a given signal
279                         //
280                         /// @param signal   Signal instance.
281                         /// @param init     Initial value of the accumulate algorithm.
282                         /// @param func     Binary operation function object that will be
283                         ///                 applied to all slot return values.
284                         ///                 The signature of the function should be
285                         ///                 equivalent of the following:
286                         ///                   `R func( T1 const& a, T2 const& b )`
287                         ///                  - The signature does not need to have `const&`.
288                         ///                  - The initial value, type `T`, must be implicitly
289                         ///                    convertible to `R`
290                         ///                  - The return type `R` must be implicitly convertible
291                         ///                    to type `T1`.
292                         ///                  - The type `R` must be `CopyAssignable`.
293                         ///                  - The type `S::slot_type::result_type` (return type of
294                         ///                    the signals slots) must be implicitly convertible to
295                         ///                    type `T2`.
296                         signal_accumulator( S const& signal, T init, F func ) :
297                                 _signal( signal ),
298                                 _init( init ),
299                                 _func( func )
300                         {}
301
302                         /// Function call operator.
303                         ///
304                         /// Calling this will trigger the underlying signal and accumulate
305                         /// all of the connected slots return values with the current
306                         /// initial value and accumulator function.
307                         ///
308                         /// When called, this will invoke the accumulator function will
309                         /// be called for each return value of the slots. The semantics
310                         /// are similar to the `std::accumulate` algorithm.
311                         ///
312                         /// @param args   Arguments to propagate to the slots of the
313                         ///               underlying when triggering the signal.
314                         result_type operator()( A const& ... args ) const {
315                                 return _signal.trigger_with_accumulator( _init, _func, args... );
316                         }
317
318                 private:
319
320                         /// Reference to the underlying signal to proxy.
321                         S const& _signal;
322                         /// Initial value of the accumulate algorithm.
323                         T _init;
324                         /// Accumulator function.
325                         F _func;
326
327         };
328
329         /// Signal template specialization.
330         ///
331         /// This is the main signal implementation, and it is used to
332         /// implement the observer pattern whithout the overhead
333         /// boilerplate code that typically comes with it.
334         ///
335         /// Any function or function object is considered a slot, and
336         /// can be connected to a signal instance, as long as the signature
337         /// of the slot matches the signature of the signal.
338         ///
339         /// @tparam P      Threading policy for the signal.
340         ///                A threading policy must provide two type definitions:
341         ///                 - P::mutex_type, this type will be used as a mutex
342         ///                   in the signal_type class template.
343         ///                 - P::mutex_lock_type, this type must implement a
344         ///                   constructor that takes a P::mutex_type as a parameter,
345         ///                   and it must have the semantics of a scoped mutex lock
346         ///                   like std::lock_guard, i.e. locking in the constructor
347         ///                   and unlocking in the destructor.
348         ///
349         /// @tparam R      Return value type of the slots connected to the signal.
350         /// @tparam A...   Argument types of the slots connected to the signal.
351         template <class P, class R, class... A >
352         class signal_type<P,R(A...)>
353         {
354                 public:
355                         /// signals are not copy constructible
356                         signal_type( signal_type const& ) = delete;
357                         /// signals are not copy assignable
358                         signal_type& operator=( signal_type const& ) = delete;
359                         /// signals are move constructible
360                         signal_type(signal_type&& other)
361                         {
362                                 mutex_lock_type lock{other._mutex};
363                                 _slot_count = std::move(other._slot_count);
364                                 _slots = std::move(other._slots);
365                                 if(other._shared_disconnector != nullptr)
366                                 {
367                                         _disconnector = disconnector{ this };
368                                         _shared_disconnector = std::move(other._shared_disconnector);
369                                         // replace the disconnector with our own disconnector
370                                         *static_cast<disconnector*>(_shared_disconnector.get()) = _disconnector;
371                                 }
372                         }
373                         /// signals are move assignable
374                         signal_type& operator=(signal_type&& other)
375                         {
376                                 auto lock = thread_policy::defer_lock(_mutex);
377                                 auto other_lock = thread_policy::defer_lock(other._mutex);
378                                 thread_policy::lock(lock,other_lock);
379
380                                 _slot_count = std::move(other._slot_count);
381                                 _slots = std::move(other._slots);
382                                 if(other._shared_disconnector != nullptr)
383                                 {
384                                         _disconnector = disconnector{ this };
385                                         _shared_disconnector = std::move(other._shared_disconnector);
386                                         // replace the disconnector with our own disconnector
387                                         *static_cast<disconnector*>(_shared_disconnector.get()) = _disconnector;
388                                 }
389                                 return *this;
390                         }
391
392                         /// signals are default constructible
393                         signal_type() :
394                                 _slot_count(0)
395                         {}
396
397                         // Destruct the signal object.
398                         ~signal_type() {
399                                 invalidate_disconnector();
400                         }
401
402                         /// Type that will be used to store the slots for this signal type.
403                         using slot_type = std::function<R(A...)>;
404                         /// Type that is used for counting the slots connected to this signal.
405                         using size_type = typename std::vector<slot_type>::size_type;
406
407
408                         /// Connect a new slot to the signal.
409                         ///
410                         /// The connected slot will be called every time the signal
411                         /// is triggered.
412                         /// @param slot   The slot to connect. This must be a callable with
413                         ///               the same signature as the signal itself.
414                         /// @return       A connection object is returned, and can be used to
415                         ///               disconnect the slot.
416                         template <class T>
417                         connection connect( T&& slot ) {
418                                 mutex_lock_type lock{ _mutex };
419                                 _slots.push_back( std::forward<T>(slot) );
420                                 std::size_t index = _slots.size()-1;
421                                 if( _shared_disconnector == nullptr ) {
422                                         _disconnector = disconnector{ this };
423                                         _shared_disconnector = std::shared_ptr<detail::disconnector>{&_disconnector, detail::no_delete};
424                                 }
425                                 ++_slot_count;
426                                 return connection{ _shared_disconnector, index };
427                         }
428
429                         /// Function call operator.
430                         ///
431                         /// Calling this is how the signal is triggered and the
432                         /// connected slots are called.
433                         ///
434                         /// @note The slots will be called in the order they were
435                         ///       connected to the signal.
436                         ///
437                         /// @param args   Arguments that will be propagated to the
438                         ///               connected slots when they are called.
439                         void operator()( A const&... args ) const {
440                                 for( auto const& slot : copy_slots() ) {
441                                         if( slot ) {
442                                                 slot( args... );
443                                         }
444                                 }
445                         }
446
447                         /// Construct a accumulator proxy object for the signal.
448                         ///
449                         /// The intended purpose of this function is to create a function
450                         /// object that can be used to trigger the signal and accumulate
451                         /// all the slot return values.
452                         ///
453                         /// The algorithm used to accumulate slot return values is similar
454                         /// to `std::accumulate`. A given binary function is called for
455                         /// each return value with the parameters consisting of the
456                         /// return value of the accumulator function applied to the
457                         /// previous slots return value, and the current slots return value.
458                         /// A initial value must be provided for the first slot return type.
459                         ///
460                         /// @note This can only be used on signals that have slots with
461                         ///       non-void return types, since we can't accumulate void
462                         ///       values.
463                         ///
464                         /// @tparam T      The type of the initial value given to the accumulator.
465                         /// @tparam F      The accumulator function type.
466                         /// @param init    Initial value given to the accumulator.
467                         /// @param op      Binary operator function object to apply by the accumulator.
468                         ///                The signature of the function should be
469                         ///                equivalent of the following:
470                         ///                  `R func( T1 const& a, T2 const& b )`
471                         ///                 - The signature does not need to have `const&`.
472                         ///                 - The initial value, type `T`, must be implicitly
473                         ///                   convertible to `R`
474                         ///                 - The return type `R` must be implicitly convertible
475                         ///                   to type `T1`.
476                         ///                 - The type `R` must be `CopyAssignable`.
477                         ///                 - The type `S::slot_type::result_type` (return type of
478                         ///                   the signals slots) must be implicitly convertible to
479                         ///                   type `T2`.
480                         template <class T, class F>
481                         signal_accumulator<signal_type, T, F, A...> accumulate( T init, F op ) const {
482                                 static_assert( std::is_same<R,void>::value == false, "Unable to accumulate slot return values with 'void' as return type." );
483                                 return { *this, init, op };
484                         }
485
486
487                         /// Trigger the signal, calling the slots and aggregate all
488                         /// the slot return values into a container.
489                         ///
490                         /// @tparam C     The type of container. This type must be
491                         ///               `DefaultConstructible`, and usable with
492                         ///               `std::back_insert_iterator`. Additionally it
493                         ///               must be either copyable or moveable.
494                         /// @param args   The arguments to propagate to the slots.
495                         template <class C>
496                         C aggregate( A const&... args ) const {
497                                 static_assert( std::is_same<R,void>::value == false, "Unable to aggregate slot return values with 'void' as return type." );
498                                 C container;
499                                 auto iterator = std::back_inserter( container );
500                                 for( auto const& slot : copy_slots() ) {
501                                         if( slot ) {
502                                                 (*iterator) = slot( args... );
503                                         }
504                                 }
505                                 return container;
506                         }
507
508                         /// Count the number of slots connected to this signal
509                         /// @returns   The number of connected slots
510                         size_type slot_count() const {
511                                 return _slot_count;
512                         }
513
514                         /// Determine if the signal is empty, i.e. no slots are connected
515                         /// to it.
516                         /// @returns   `true` is returned if the signal has no connected
517                         ///            slots, and `false` otherwise.
518                         bool empty() const {
519                                 return slot_count() == 0;
520                         }
521
522                         /// Disconnects all slots
523                         /// @note This operation invalidates all scoped_connection objects
524                         void disconnect_all_slots() {
525                                 mutex_lock_type lock{ _mutex };
526                                 _slots.clear();
527                                 _slot_count = 0;
528                                 invalidate_disconnector();
529                         }
530
531                 private:
532                         template<class, class, class, class...> friend class signal_accumulator;
533                         /// Thread policy currently in use
534                         using thread_policy = P;
535                         /// Type of mutex, provided by threading policy
536                         using mutex_type = typename thread_policy::mutex_type;
537                         /// Type of mutex lock, provided by threading policy
538                         using mutex_lock_type = typename thread_policy::mutex_lock_type;
539
540                         /// Invalidate the internal disconnector object in a way
541                         /// that is safe according to the current thread policy.
542                         ///
543                         /// This will effectively make all current connection objects to
544                         /// to this signal incapable of disconnecting, since they keep a
545                         /// weak pointer to the shared disconnector object.
546                         void invalidate_disconnector() {
547                                 // If we are unlucky, some of the connected slots
548                                 // might be in the process of disconnecting from other threads.
549                                 // If this happens, we are risking to destruct the disconnector
550                                 // object managed by our shared pointer before they are done
551                                 // disconnecting. This would be bad. To solve this problem, we
552                                 // discard the shared pointer (that is pointing to the disconnector
553                                 // object within our own instance), but keep a weak pointer to that
554                                 // instance. We then stall the destruction until all other weak
555                                 // pointers have released their "lock" (indicated by the fact that
556                                 // we will get a nullptr when locking our weak pointer).
557                                 std::weak_ptr<detail::disconnector> weak{_shared_disconnector};
558                                 _shared_disconnector.reset();
559                                 while( weak.lock() != nullptr ) {
560                                         // we just yield here, allowing the OS to reschedule. We do
561                                         // this until all threads has released the disconnector object.
562                                         thread_policy::yield_thread();
563                                 }
564                         }
565
566                         /// Retrieve a copy of the current slots
567                         ///
568                         /// It's useful and necessary to copy the slots so we don't need
569                         /// to hold the lock while calling the slots. If we hold the lock
570                         /// we prevent the called slots from modifying the slots vector.
571                         /// This simple "double buffering" will allow slots to disconnect
572                         /// themself or other slots and connect new slots.
573                         std::vector<slot_type> copy_slots() const
574                         {
575                                 mutex_lock_type lock{ _mutex };
576                                 return _slots;
577                         }
578
579                         /// Implementation of the signal accumulator function call
580                         template <class T, class F>
581                         typename signal_accumulator<signal_type, T, F, A...>::result_type trigger_with_accumulator( T value, F& func, A const&... args ) const {
582                                 for( auto const& slot : copy_slots() ) {
583                                         if( slot ) {
584                                                 value = func( value, slot( args... ) );
585                                         }
586                                 }
587                                 return value;
588                         }
589
590                         /// Implementation of the disconnection operation.
591                         ///
592                         /// This is private, and only called by the connection
593                         /// objects created when connecting slots to this signal.
594                         /// @param index   The slot index of the slot that should
595                         ///                be disconnected.
596                         void disconnect( std::size_t index ) {
597                                 mutex_lock_type lock( _mutex );
598                                 assert( _slots.size() > index );
599                                 if( _slots[ index ] != nullptr ) {
600                                         --_slot_count;
601                                 }
602                                 _slots[ index ] = slot_type{};
603                                 while( _slots.size()>0 && !_slots.back() ) {
604                                         _slots.pop_back();
605                                 }
606                         }
607
608                         /// Implementation of the shared disconnection state
609                         /// used by all connection created by signal instances.
610                         ///
611                         /// This inherits the @ref detail::disconnector interface
612                         /// for type erasure.
613                         struct disconnector :
614                                 detail::disconnector
615                         {
616                                 /// Default constructor, resulting in a no-op disconnector.
617                                 disconnector() :
618                                         _ptr(nullptr)
619                                 {}
620
621                                 /// Create a disconnector that works with a given signal instance.
622                                 /// @param ptr   Pointer to the signal instance that the disconnector
623                                 ///              should work with.
624                                 disconnector( signal_type<P,R(A...)>* ptr ) :
625                                         _ptr( ptr )
626                                 {}
627
628                                 /// Disconnect a given slot on the current signal instance.
629                                 /// @note If the instance is default constructed, or created
630                                 ///       with `nullptr` as signal pointer this operation will
631                                 ///       effectively be a no-op.
632                                 /// @param index   The index of the slot to disconnect.
633                                 void operator()( std::size_t index ) const override {
634                                         if( _ptr ) {
635                                                 _ptr->disconnect( index );
636                                         }
637                                 }
638
639                                 /// Pointer to the current signal.
640                                 signal_type<P,R(A...)>* _ptr;
641                         };
642
643                         /// Mutex to synchronize access to the slot vector
644                         mutable mutex_type _mutex;
645                         /// Vector of all connected slots
646                         std::vector<slot_type> _slots;
647                         /// Number of connected slots
648                         size_type _slot_count;
649                         /// Disconnector operation, used for executing disconnection in a
650                         /// type erased manner.
651                         disconnector _disconnector;
652                         /// Shared pointer to the disconnector. All connection objects has a
653                         /// weak pointer to this pointer for performing disconnections.
654                         std::shared_ptr<detail::disconnector> _shared_disconnector;
655         };
656
657         // Implementation of the disconnect operation of the connection class
658         inline void connection::disconnect() {
659                 auto ptr = _weak_disconnector.lock();
660                 if( ptr ) {
661                         (*ptr)( _index );
662                 }
663                 _weak_disconnector.reset();
664         }
665
666         /// Signal type that is safe to use in multithreaded environments,
667         /// where the signal and slots exists in different threads.
668         /// The multithreaded policy provides mutexes and locks to synchronize
669         /// access to the signals internals.
670         ///
671         /// This is the recommended signal type, even for single threaded
672         /// environments.
673         template <class T> using signal = signal_type<multithread_policy, T>;
674
675         /// Signal type that is unsafe in multithreaded environments.
676         /// No synchronizations are provided to the signal_type for accessing
677         /// the internals.
678         ///
679         /// Only use this signal type if you are sure that your environment is
680         /// single threaded and performance is of importance.
681         template <class T> using unsafe_signal = signal_type<singlethread_policy, T>;
682 } // namespace nod
683
684 #endif // IG_NOD_INCLUDE_NOD_HPP