Loading...
Searching...
No Matches
notice.h
Go to the documentation of this file.
1//
2// Copyright 2016 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7#ifndef PXR_BASE_TF_NOTICE_H
8#define PXR_BASE_TF_NOTICE_H
9
12
13#include "pxr/pxr.h"
14#include "pxr/base/tf/api.h"
18#include "pxr/base/tf/scoped.h"
19#include "pxr/base/tf/type.h"
20#include "pxr/base/tf/weakPtr.h"
22#include "pxr/base/arch/hints.h"
23
24#include <atomic>
25#include <list>
26#include <optional>
27#include <typeinfo>
28
29PXR_NAMESPACE_OPEN_SCOPE
30
31class Tf_NoticeRegistry;
32
80class TfNotice {
81private:
82 class _DelivererBase;
84 typedef std::list<_DelivererBase*> _DelivererList;
85
87 // Per-sender delivery, listener gets sender.
88 template <class LPtr, class L,
89 class Notice, class SPtr, class DeliveredSPtr>
90 static _DelivererBase *
91 _MakeDeliverer(LPtr const &listener,
92 void (L::*method)
93 (const Notice &, DeliveredSPtr const &),
94 SPtr const &sender) {
95 DeliveredSPtr weakSender(sender);
96 return new _DelivererWithSender<
97 LPtr, DeliveredSPtr,
98 void (L::*)(const Notice &, DeliveredSPtr const &),
99 Notice
100 >(listener, method, weakSender);
101 }
102
103 template <class LPtr, class L,
104 class Notice, class SPtr, class DeliveredSPtr>
105 static _DelivererBase *
106 _MakeDeliverer(LPtr const &listener,
107 void (L::*method)
108 (const Notice &, DeliveredSPtr const &) const,
109 SPtr const &sender) {
110 DeliveredSPtr weakSender(sender);
111 return new _DelivererWithSender<
112 LPtr, DeliveredSPtr,
113 void (L::*)(const Notice &, DeliveredSPtr const &) const,
114 Notice
115 >(listener, method, weakSender);
116 }
117
119 // Per-sender delivery, listener does not get sender.
120 template <class LPtr, class L, class SPtr, class Notice>
121 static _DelivererBase *
122 _MakeDeliverer(LPtr const &listener,
123 void (L::*method)(const Notice &),
124 SPtr const &sender) {
125 return new _Deliverer<
126 LPtr, SPtr, void (L::*)(const Notice &), Notice
127 >(listener, method, sender);
128 }
129
130 template <class LPtr, class L, class SPtr, class Notice>
131 static _DelivererBase *
132 _MakeDeliverer(LPtr const &listener,
133 void (L::*method)(const Notice &) const,
134 SPtr const &sender) {
135 return new _Deliverer<
136 LPtr, SPtr, void (L::*)(const Notice &) const, Notice
137 >(listener, method, sender);
138 }
139
141 // Global delivery.
142 template <class LPtr, class L, class Notice>
143 static _DelivererBase *
144 _MakeDeliverer(LPtr const &listener,
145 void (L::*method)(const Notice &)) {
146 return new _Deliverer<
147 LPtr, TfAnyWeakPtr, void (L::*)(const Notice &), Notice
148 >(listener, method);
149 }
150
151 template <class LPtr, class L, class Notice>
152 static _DelivererBase *
153 _MakeDeliverer(LPtr const &listener,
154 void (L::*method)(const Notice &) const) {
155 return new _Deliverer<
156 LPtr, TfAnyWeakPtr, void (L::*)(const Notice &) const, Notice
157 >(listener, method);
158 }
159
161 // Generic (raw) delivery.
162 template <class LPtr, class L>
163 static _DelivererBase *
164 _MakeDeliverer(TfType const &noticeType,
165 LPtr const &listener,
166 void (L::*method)(const TfNotice &,
167 const TfType &,
168 TfWeakBase*, const void *,
169 const std::type_info&),
170 TfAnyWeakPtr const &sender) {
171 return new _RawDeliverer<LPtr,
172 void (L::*)(const TfNotice &, const TfType &,
173 TfWeakBase *, const void *,
174 const std::type_info &)>
175 (listener, method, sender, noticeType);
176 }
177
178 template <class LPtr, class L>
179 static _DelivererBase *
180 _MakeDeliverer(TfType const &noticeType,
181 LPtr const &listener,
182 void (L::*method)(const TfNotice &,
183 const TfType &,
184 TfWeakBase*, const void *,
185 const std::type_info&) const,
186 TfAnyWeakPtr const &sender)
187 {
188 return new _RawDeliverer<LPtr,
189 void (L::*)(const TfNotice &, const TfType &,
190 TfWeakBase *, const void *,
191 const std::type_info &) const>
192 (listener, method, sender, noticeType);
193 }
194
195
196
197public:
198
199 class Probe;
201
205 class Probe : public TfWeakBase {
206 public:
207 TF_API
208 virtual ~Probe() = 0;
209
213 virtual void BeginSend(const TfNotice &notice,
214 const TfWeakBase *sender,
215 const std::type_info &senderType) = 0;
216
219 virtual void EndSend() = 0;
220
225 virtual void BeginDelivery(const TfNotice &notice,
226 const TfWeakBase *sender,
227 const std::type_info &senderType,
228 const TfWeakBase *listener,
229 const std::type_info &listenerType) = 0;
230
234 virtual void EndDelivery() = 0;
235 };
236
243 class Key {
244 public:
245 Key() {}
246
251 bool IsValid() const {
252 return _deliverer && _deliverer->_IsActive();
253 }
254
258 operator bool() const {
259 return IsValid();
260 }
261
262 private:
263 Key(const _DelivererWeakPtr & d) : _deliverer(d) {}
264
265 _DelivererWeakPtr _deliverer;
266
267 friend class Tf_NoticeRegistry;
268 friend class TfNotice;
269 };
270
276 typedef std::vector<Key> Keys;
277
281 TF_API
282 static void InsertProbe(const WeakProbePtr &probe);
283
286 TF_API
287 static void RemoveProbe(const WeakProbePtr &probe);
288
346 template <class LPtr, class MethodPtr>
347 static TfNotice::Key
348 Register(LPtr const &listener, MethodPtr method) {
349 TfAutoMallocTag tag("Tf", "Tf_NoticeRegistry::_Register");
350 return _Register(_MakeDeliverer(listener, method));
351 }
352
353 template <class LPtr, class MethodPtr, class SenderPtr>
354 static TfNotice::Key
355 Register(LPtr const &listener, MethodPtr method, SenderPtr const &sender) {
356 TfAutoMallocTag tag("Tf", "Tf_NoticeRegistry::_Register");
357 return _Register(_MakeDeliverer(listener, method, sender));
358 }
359
360 template <class LPtr, class MethodPtr>
361 static TfNotice::Key
362 Register(LPtr const &listener, MethodPtr method,
363 const TfType &noticeType, const TfAnyWeakPtr &sender) {
364 TfAutoMallocTag tag("Tf", "Tf_NoticeRegistry::_Register");
365 return _Register(_MakeDeliverer(noticeType, listener, method, sender));
366 }
367
376 TF_API
377 static bool Revoke(TfNotice::Key& key);
378
384 TF_API
385 static void Revoke(TfNotice::Keys* keys);
386
396 TF_API
397 static bool RevokeAndWait(TfNotice::Key& key);
398
405 TF_API
406 static void RevokeAndWait(TfNotice::Keys* keys);
407
422 TF_API
423 size_t Send() const;
424
439 template <typename SenderPtr>
440 size_t Send(SenderPtr const &s) const;
441
448 TF_API
449 size_t SendWithWeakBase(const TfWeakBase *senderWeakBase,
450 const void *senderUniqueId,
451 const std::type_info &type) const;
452
453 TF_API
454 virtual ~TfNotice();
455
464 class Block {
465 public:
466 TF_API Block();
467 TF_API ~Block();
468 };
469
470private:
471 // Abstract base class for calling listeners.
472 // A typed-version derives (via templating) off this class.
473 class _DelivererBase : public TfWeakBase {
474 public:
475 _DelivererBase()
476 : _list(0), _active(true), _markedForRemoval(false)
477 {
478 }
479
480 TF_API
481 virtual ~_DelivererBase();
482
483 TF_API
484 void _BeginDelivery(const TfNotice &notice,
485 const TfWeakBase *sender,
486 const std::type_info &senderType,
487 const TfWeakBase *listener,
488 const std::type_info &listenerType,
489 const std::vector<TfNotice::WeakProbePtr> &probes);
490
491 TF_API
492 void _EndDelivery(const std::vector<TfNotice::WeakProbePtr> &probes);
493
494 // The derived class converts n to the proper type and delivers it by
495 // calling the listener's method. The function returns \c true,
496 // unless the listener has expired or been marked in active (i.e. by
497 // TfNotice::Revoke()), in which case the method call is skipped and
498 // \c false is returned.
499 virtual bool
500 _SendToListenerImpl(const TfNotice &n,
501 const TfType &type,
502 const TfWeakBase *s,
503 const void *senderUniqueId,
504 const std::type_info &senderType,
505 const std::vector<TfNotice::WeakProbePtr> &) = 0;
506
507 void _Deactivate() {
508 _active = false;
509 }
510
511 bool _IsActive() const {
512 return _active;
513 }
514
515 void _MarkForRemoval() {
516 _markedForRemoval = true;
517 }
518
519 // True if the entry has been added to the _deadEntries list for
520 // removal. Used to avoid adding it more than once to the list.
521 bool _IsMarkedForRemoval() const {
522 return _markedForRemoval;
523 }
524
525 virtual TfType GetNoticeType() const = 0;
526
527 virtual bool Delivers(TfType const &noticeType,
528 const TfWeakBase *sender) const = 0;
529
530 virtual TfWeakBase const *GetSenderWeakBase() const = 0;
531
532 virtual _DelivererBase *Clone() const = 0;
533
534 // Increment the busy count around the actual delivery.
535 bool _SendToListener(const TfNotice &n,
536 const TfType &type,
537 const TfWeakBase *s,
538 const void *senderUniqueId,
539 const std::type_info &senderType,
540 const std::vector<TfNotice::WeakProbePtr> &probes)
541 {
542 // Increment the number of sends in progress.
543 if (_busy.fetch_add(1, std::memory_order_release) & _waitBit) {
544 // We're waiting to revoke this listener and we haven't
545 // started the real send yet so act like we already revoked.
546 // If we didn't check if we were waiting then it's possible
547 // to enter this function but not yet increment, have wait
548 // see the count is zero and return, then have this function
549 // increment and do the real send after having supposedly
550 // waited for all sends to complete.
551 _busy.fetch_add(-1, std::memory_order_release);
552 return false;
553 }
554
555 // Ensure we decrement the count even if the delivery throws.
556 TfScoped cleanup([this]() {
557 // Decrement the number of sends in progress.
558 _busy.fetch_add(-1, std::memory_order_release);
559 });
560
561 const auto result =
562 _SendToListenerImpl(n, type,
563 s, senderUniqueId, senderType, probes);
564 return result;
565 }
566
567 // Spin wait until no deliveries are in progress. This is used when
568 // revoking a listener so we set the _waitBit in _busy permanently.
569 void _WaitForSendsToFinish()
570 {
571 // Mark this listener as waiting for sends to finish and check if
572 // any send is in progress.
573 if (_busy.fetch_or(_waitBit, std::memory_order_release)) {
574 // At least one send was in progress.
575 _WaitUntilNotSending();
576 }
577 }
578
579 protected:
580
581 template <class ToNoticeType, class FromNoticeType>
582 static inline ToNoticeType const *
583 _CastNotice(FromNoticeType const *from) {
584 // Dynamic casting in deliverers is significant overhead, so only
585 // do error checking in debug builds.
586 if (TF_DEV_BUILD) {
587 if (!dynamic_cast<ToNoticeType const *>(from)) {
588 ToNoticeType const *castNotice =
589 TfSafeDynamic_cast<ToNoticeType const *>(from);
590 // this will abort with a clear error message if
591 // castNotice is NULL
592 TfNotice::_VerifyFailedCast(typeid(ToNoticeType),
593 *from, castNotice);
594 }
595 }
596 return static_cast<ToNoticeType const *>(from);
597 }
598
599 private:
600 // Wait until there are no sends in progress.
601 void _WaitUntilNotSending();
602
603 private:
604 // Linkage to the containing _DelivererList in the Tf_NoticeRegistry
605 _DelivererList *_list;
606 _DelivererList::iterator _listIter;
607
608 bool _active;
609 bool _markedForRemoval;
610 std::atomic<int> _busy{0};
611
612 static constexpr int _waitBit = 0x80000000;
613
614 friend class Tf_NoticeRegistry;
615 };
616
617 template <class Derived>
618 class _StandardDeliverer : public _DelivererBase {
619 public:
620 virtual ~_StandardDeliverer() {}
621
622 virtual TfType GetNoticeType() const {
623 typedef typename Derived::NoticeType NoticeType;
624 TfType ret = TfType::Find<NoticeType>();
625 if (ret.IsUnknown())
626 TF_FATAL_ERROR("notice type " + ArchGetDemangled<NoticeType>() +
627 " undefined in the TfType system");
628 return ret;
629 }
630
631 virtual bool Delivers(TfType const &noticeType,
632 TfWeakBase const *sender) const {
633 Derived const *derived = this->AsDerived();
634 return noticeType.IsA(GetNoticeType()) &&
635 !derived->_sender.IsInvalid() &&
636 sender && derived->_sender.GetWeakBase() == sender;
637 }
638
639 virtual TfWeakBase const *GetSenderWeakBase() const {
640 Derived const *derived = this->AsDerived();
641 return derived->_sender ? derived->_sender.GetWeakBase() : 0;
642 }
643
644 virtual _DelivererBase *Clone() const {
645 Derived const *derived = this->AsDerived();
646 return new Derived(derived->_listener,
647 derived->_method,
648 derived->_sender,
649 GetNoticeType());
650 }
651
652 virtual bool
653 _SendToListenerImpl(const TfNotice &notice,
654 const TfType &noticeType,
655 const TfWeakBase *sender,
656 const void *senderUniqueId,
657 const std::type_info &senderType,
658 const std::vector<TfNotice::WeakProbePtr> &probes)
659 {
660 Derived *derived = this->AsDerived();
661 typedef typename Derived::ListenerType ListenerType;
662 typedef typename Derived::NoticeType NoticeType;
663 ListenerType *listener = get_pointer(derived->_listener);
664
665 if (listener && !derived->_sender.IsInvalid()) {
666
667 std::optional<TfScoped<>> endDeliveryScope;
668 if (ARCH_UNLIKELY(!probes.empty())) {
669 TfWeakBase const *senderWeakBase = GetSenderWeakBase(),
670 *listenerWeakBase = derived->_listener.GetWeakBase();
671 _BeginDelivery(notice, senderWeakBase,
672 senderWeakBase ?
673 senderType : typeid(void),
674 listenerWeakBase,
675 typeid(ListenerType), probes);
676
677 // Ensure _EndDelivery is called even if the listener
678 // throws.
679 endDeliveryScope.emplace([this, &probes]() {
680 _EndDelivery(probes);
681 });
682 }
683
684 derived->
685 _InvokeListenerMethod(listener,
686 *_CastNotice<NoticeType>(&notice),
687 noticeType, sender,
688 senderUniqueId, senderType);
689
690 return true;
691 }
692 return false;
693 }
694
695 private:
696 Derived *AsDerived() {
697 return static_cast<Derived *>(this);
698 }
699
700 Derived const *AsDerived() const {
701 return static_cast<Derived const *>(this);
702 }
703 };
704
705
706 template <typename LPtr, typename SPtr, typename Method, typename Notice>
707 class _Deliverer :
708 public _StandardDeliverer<_Deliverer<LPtr, SPtr, Method, Notice> >
709 {
710 public:
711 typedef Notice NoticeType;
712 typedef typename LPtr::DataType ListenerType;
713 typedef Method MethodPtr;
714
715 _Deliverer(LPtr const &listener,
716 MethodPtr const &methodPtr,
717 SPtr const &sender = SPtr(),
718 TfType const &noticeType = TfType())
719 : _listener(listener)
720 , _sender(sender)
721 , _method(methodPtr)
722 {
723 }
724
725 void _InvokeListenerMethod(ListenerType *listener,
726 const NoticeType &notice,
727 const TfType &noticeType,
728 const TfWeakBase *,
729 const void *,
730 const std::type_info &)
731 {
732 (listener->*_method)(notice);
733 }
734
735 LPtr _listener;
736 SPtr _sender;
737 MethodPtr _method;
738 };
739
740 template <class LPtr, class Method>
741 class _RawDeliverer :
742 public _StandardDeliverer<_RawDeliverer<LPtr, Method> >
743 {
744 public:
745 typedef TfNotice NoticeType;
746 typedef typename LPtr::DataType ListenerType;
747 typedef Method MethodPtr;
748
749 _RawDeliverer(LPtr const &listener,
750 MethodPtr const &methodPtr,
751 TfAnyWeakPtr const &sender,
752 TfType const &noticeType)
753 : _noticeType(noticeType),
754 _listener(listener),
755 _method(methodPtr),
756 _sender(sender)
757 {
758 }
759
760 virtual TfType GetNoticeType() const {
761 return _noticeType;
762 }
763
764 void _InvokeListenerMethod(ListenerType *listener,
765 const NoticeType &notice,
766 const TfType &noticeType,
767 const TfWeakBase *sender,
768 const void *senderUniqueId,
769 const std::type_info &senderType)
770 {
771 (listener->*_method)(notice, noticeType,
772 const_cast<TfWeakBase *>(sender),
773 senderUniqueId, senderType);
774 }
775
776 TfType _noticeType;
777 LPtr _listener;
778 MethodPtr _method;
779 TfAnyWeakPtr _sender;
780 };
781
782 template <class LPtr, class SPtr, class Method, class Notice>
783 class _DelivererWithSender :
784 public _StandardDeliverer<
785 _DelivererWithSender<LPtr, SPtr, Method, Notice>
786 >
787 {
788 public:
789 typedef Notice NoticeType;
790 typedef Method MethodPtr;
791 typedef typename LPtr::DataType ListenerType;
792
793 typedef typename SPtr::DataType SenderType;
794
795 _DelivererWithSender(LPtr const &listener,
796 MethodPtr const &methodPtr,
797 SPtr const &sender,
798 TfType const &noticeType = TfType())
799 : _listener(listener),
800 _sender(sender),
801 _method(methodPtr)
802 {
803 }
804
805 void _InvokeListenerMethod(ListenerType *listener,
806 const NoticeType &notice,
807 const TfType &noticeType,
808 const TfWeakBase *sender,
809 const void *,
810 const std::type_info &)
811 {
812 SenderType *deliveredSender =
813 static_cast<SenderType *>(const_cast<TfWeakBase *>(sender));
814 SPtr deliveredSPtr(deliveredSender);
815 (listener->*_method)(notice, deliveredSPtr);
816 }
817
818 LPtr _listener;
819 SPtr _sender;
820 MethodPtr _method;
821 };
822
823private:
824 // Internal non-templated function to install listeners.
825 TF_API
826 static Key _Register(_DelivererBase*);
827
828 TF_API
829 static void _VerifyFailedCast(const std::type_info& toType,
830 const TfNotice& notice,
831 const TfNotice* castNotice);
832
833 TF_API
834 size_t _Send(const TfWeakBase* sender,
835 const void *senderUniqueId,
836 const std::type_info & senderType) const;
837 TF_API
838 size_t _SendWithType(const TfType & noticeType,
839 const TfWeakBase* sender,
840 const void *senderUniqueId,
841 const std::type_info & senderType) const;
842
843 friend class Tf_NoticeRegistry;
844
845 // Befriend the wrapping so it can access _SendWithType() directly
846 // in order to provide dynamic downcasting of Python notice types.
847 friend class Tf_PyNotice;
848};
849
850template <typename SenderPtr>
851size_t
852TfNotice::Send(SenderPtr const &s) const
853{
854 const TfWeakBase *senderWeakBase = s ? s.GetWeakBase() : NULL;
855 return _Send(senderWeakBase, senderWeakBase ? s.GetUniqueIdentifier() : 0,
856 senderWeakBase ?
857 typeid(typename SenderPtr::DataType) : typeid(void));
858}
859
860PXR_NAMESPACE_CLOSE_SCOPE
861
862#endif // PXR_BASE_TF_NOTICE_H
Type independent WeakPtr holder class.
Low-level utilities for informing users of various internal and external diagnostic conditions.
Provides the ability to hold an arbitrary TfWeakPtr in a non-type-specific manner in order to observe...
Definition: anyWeakPtr.h:37
Scoped (i.e.
Definition: mallocTag.h:249
Blocks sending of all notices in current thread.
Definition: notice.h:464
Handle-object returned by TfNotice::Register().
Definition: notice.h:243
bool IsValid() const
Does this key refer to a valid notification?
Definition: notice.h:251
Probe interface class which may be implemented and then registered via InsertProbe to introspect abou...
Definition: notice.h:205
virtual void EndDelivery()=0
This method is called after the notice in the corresponding BeginDelivery call has finished being pro...
virtual void EndSend()=0
This method is called after the notice in the corresponding BeginSend call has been delivered to all ...
virtual void BeginDelivery(const TfNotice &notice, const TfWeakBase *sender, const std::type_info &senderType, const TfWeakBase *listener, const std::type_info &listenerType)=0
This method is called just before notice is delivered to a listener.
virtual void BeginSend(const TfNotice &notice, const TfWeakBase *sender, const std::type_info &senderType)=0
This method is called just before notice is sent to any listeners.
The base class for objects used to notify interested parties (listeners) when events have occurred.
Definition: notice.h:80
TF_API size_t Send() const
Deliver the notice to interested listeners, returning the number of interested listeners.
static TF_API void RevokeAndWait(TfNotice::Keys *keys)
Revoke interest by listeners.
static TF_API void InsertProbe(const WeakProbePtr &probe)
Register a probe that will be invoked when notices are sent and delivered.
static TF_API void Revoke(TfNotice::Keys *keys)
Revoke interest by listeners.
static TfNotice::Key Register(LPtr const &listener, MethodPtr method)
Register a listener as being interested in a TfNotice.
Definition: notice.h:348
static TF_API void RemoveProbe(const WeakProbePtr &probe)
Remove a probe that was previously registered with InsertProbe.
TF_API size_t SendWithWeakBase(const TfWeakBase *senderWeakBase, const void *senderUniqueId, const std::type_info &type) const
Variant of Send() that takes a specific sender in the form of a TfWeakBase pointer and a typeid.
static TF_API bool Revoke(TfNotice::Key &key)
Revoke interest by a listener.
static TF_API bool RevokeAndWait(TfNotice::Key &key)
Revoke interest by a listener.
std::vector< Key > Keys
A TfNotice::Key container.
Definition: notice.h:276
Execute code on exiting scope.
Definition: scoped.h:34
TfType represents a dynamic runtime type.
Definition: type.h:48
TF_API bool IsA(TfType queryType) const
Return true if this type is the same as or derived from queryType.
bool IsUnknown() const
Return true if this is the unknown type, representing a type unknown to the TfType system.
Definition: type.h:374
Enable a concrete base class for use with TfWeakPtr.
Definition: weakBase.h:124
Pointer storage with deletion detection.
Definition: weakPtr.h:128
Demangle C++ typenames generated by the typeid() facility.
#define TF_FATAL_ERROR(fmt, args)
Issue a fatal error and end the program.
Definition: diagnostic.h:91
Compiler hints.
Pointer storage with deletion detection.