JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
updatehandler.cpp
Go to the documentation of this file.
1 //------------------------------------------------------------------------
2// Project : SDK Base
3// Version : 1.0
4//
5// Category : Helpers
6// Filename : base/source/updatehandler.cpp
7// Created by : Steinberg, 2008
8// Description :
9//
10//-----------------------------------------------------------------------------
11// LICENSE
12// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
13//-----------------------------------------------------------------------------
14// Redistribution and use in source and binary forms, with or without modification,
15// are permitted provided that the following conditions are met:
16//
17// * Redistributions of source code must retain the above copyright notice,
18// this list of conditions and the following disclaimer.
19// * Redistributions in binary form must reproduce the above copyright notice,
20// this list of conditions and the following disclaimer in the documentation
21// and/or other materials provided with the distribution.
22// * Neither the name of the Steinberg Media Technologies nor the names of its
23// contributors may be used to endorse or promote products derived from this
24// software without specific prior written permission.
25//
26// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
27// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
28// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
34// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35// OF THE POSSIBILITY OF SUCH DAMAGE.
36//-----------------------------------------------------------------------------
37
40#include "base/source/fstring.h"
41
42#if SMTG_CPP11_STDLIBSUPPORT
43#include <unordered_map>
44#else
45#include <map>
46#endif
47#include <deque>
48#include <vector>
49#include <algorithm>
50
51#define NON_EXISTING_DEPENDENCY_CHECK 0 // not yet
52#define CLASS_NAME_TRACKED DEVELOPMENT
53
55
56namespace Steinberg {
57
58DEF_CLASS_IID (IUpdateManager)
59
60namespace Update {
61const uint32 kHashSize = (1 << 8); // must be power of 2 (16 bytes * 256 == 4096)
62const uint32 kMapSize = 1024 * 10;
63
64//------------------------------------------------------------------------
65inline uint32 hashPointer (void* p)
66{
67 return (uint32)((uint64 (p) >> 12) & (kHashSize - 1));
68}
69
70//------------------------------------------------------------------------
71inline IPtr<FUnknown> getUnknownBase (FUnknown* unknown)
72{
73 FUnknown* result = nullptr;
74 if (unknown)
75 unknown->queryInterface (FUnknown::iid, (void**)&result);
76
77 return owned (result);
78}
79
80#if CLASS_NAME_TRACKED
81//------------------------------------------------------------------------
82struct Dependency
83{
84 Dependency (FUnknown* o, IDependent* d)
85 : obj (o), dep (d), objClass (nullptr), depClass (nullptr)
86 {
87 }
88
89 inline bool operator== (const Dependency& d) const { return obj == d.obj; }
90 inline bool operator!= (const Dependency& d) const { return obj != d.obj; }
91 inline bool operator< (const Dependency& d) const { return obj < d.obj; }
92 inline bool operator> (const Dependency& d) const { return obj > d.obj; }
93 FUnknown* obj;
94 IDependent* dep;
95
96 FClassID objClass;
97 FClassID depClass;
98};
99#endif
100
101//------------------------------------------------------------------------
103{
104 DeferedChange (FUnknown* o, int32 m = 0) : obj (o), msg (m) {}
105 ~DeferedChange () {}
106 DeferedChange (const DeferedChange& r) : obj (r.obj), msg (r.msg) {}
107 inline bool operator== (const DeferedChange& d) const { return obj == d.obj; }
108 inline bool operator!= (const DeferedChange& d) const { return obj != d.obj; }
109 FUnknown* obj;
110 int32 msg;
111};
112
113//------------------------------------------------------------------------
115{
116 UpdateData (FUnknown* o, IDependent** d, uint32 c)
117 : obj (o), dependents (d), count (c)
118 {
119 }
120 FUnknown* obj;
121 IDependent** dependents;
122 uint32 count;
123 bool operator== (const UpdateData& d) const
124 {
125 return d.obj == obj && d.dependents == dependents;
126 }
127};
128
129//------------------------------------------------------------------------
131using DeferedChangeListIterConst = DeferedChangeList::const_iterator;
132using DeferedChangeListIter = DeferedChangeList::iterator;
133
135using UpdateDataListIterConst = UpdateDataList::const_iterator;
136
137#if CLASS_NAME_TRACKED
139#else
141#endif
142using DependentListIter = DependentList::iterator;
143using DependentListIterConst = DependentList::const_iterator;
144
145#if SMTG_CPP11_STDLIBSUPPORT
147#else
149#endif
150using DependentMapIter = DependentMap::iterator;
151using DependentMapIterConst = DependentMap::const_iterator;
152
153struct Table
154{
155 DependentMap depMap[kHashSize];
156 DeferedChangeList defered;
157 UpdateDataList updateData;
158};
159
160//------------------------------------------------------------------------
161void updateDone (FUnknown* unknown, int32 message)
162{
163 if (message != IDependent::kDestroyed)
164 {
165 FObject* obj = FObject::unknownToObject (unknown);
166 if (obj)
167 obj->updateDone (message);
168 }
169}
170} // namespace Update
171
172//------------------------------------------------------------------------
173static int32 countEntries (Update::DependentMap& map)
174{
175 int32 total = 0;
176 Update::DependentMapIterConst iterMap = map.begin ();
177 while (iterMap != map.end ())
178 {
179 const Update::DependentList& list = iterMap->second;
180 Update::DependentListIterConst iterList = list.begin ();
181 while (iterList != list.end ())
182 {
183 total++;
184 ++iterList;
185 }
186 ++iterMap;
187 }
188 return total;
189}
190
191//------------------------------------------------------------------------
192UpdateHandler::UpdateHandler ()
193{
194 table = NEW Update::Table;
195 if (FObject::getUpdateHandler () == nullptr)
197}
198
199//------------------------------------------------------------------------
200UpdateHandler::~UpdateHandler ()
201{
202 if (FObject::getUpdateHandler () == this)
204 delete table;
205 table = nullptr;
206}
207
208//------------------------------------------------------------------------
209tresult PLUGIN_API UpdateHandler::addDependent (FUnknown* u, IDependent* _dependent)
210{
211 IPtr<FUnknown> unknown = Update::getUnknownBase (u);
212 if (!unknown || !_dependent)
213 return kResultFalse;
214
215 FGuard guard (lock);
216
217#if CLASS_NAME_TRACKED
218 Update::Dependency dependent (unknown, _dependent);
219
220 FObject* obj = FObject::unknownToObject (unknown);
221 if (obj)
222 dependent.objClass = obj->isA ();
223 obj = FObject::unknownToObject (_dependent);
224 if (obj)
225 dependent.depClass = obj->isA ();
226#else
227 IDependent* dependent = _dependent;
228#endif
229
230 Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
231 Update::DependentMapIter it = map.find (unknown);
232 if (it == map.end ())
233 {
235 list.push_back (dependent);
236 map[unknown] = list;
237 }
238 else
239 {
240 (*it).second.push_back (dependent);
241 }
242
243 return kResultTrue;
244}
245//------------------------------------------------------------------------
246tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* dependent)
247{
248 size_t eraseCount;
249 return removeDependent (u, dependent, eraseCount);
250}
251
252//------------------------------------------------------------------------
253tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* dependent, size_t& eraseCount)
254{
255 eraseCount = 0;
256 IPtr<FUnknown> unknown = Update::getUnknownBase (u);
257 if (unknown == nullptr && dependent == nullptr)
258 return kResultFalse;
259
260 FGuard guard (lock);
261
262 Update::UpdateDataListIterConst iter = table->updateData.begin ();
263 while (iter != table->updateData.end ())
264 {
265 if ((*iter).obj == unknown || unknown == nullptr)
266 {
267 for (uint32 count = 0; count < (*iter).count; count++)
268 {
269 if ((*iter).dependents[count] == dependent)
270 (*iter).dependents[count] = nullptr;
271 }
272 }
273 ++iter;
274 }
275 // Remove all objects for the given dependent
276 if (unknown == nullptr)
277 {
278 for (uint32 j = 0; j < Update::kHashSize; j++)
279 {
280 Update::DependentMap& map = table->depMap[j];
281 Update::DependentMapIter iterMap = map.begin ();
282 while (iterMap != map.end ())
283 {
284 Update::DependentList& list = (*iterMap).second;
285 Update::DependentListIter iterList = list.begin ();
286 bool listIsEmpty = false;
287
288 while (iterList != list.end ())
289 {
290#if CLASS_NAME_TRACKED
291 if ((*iterList).dep == dependent)
292#else
293 if ((*iterList) == dependent)
294#endif
295 {
296 eraseCount = list.size ();
297 if (list.size () == 1u)
298 {
299 listIsEmpty = true;
300 break;
301 }
302 else
303 {
304 iterList = list.erase (iterList);
305 }
306 }
307 else
308 {
309 ++iterList;
310 }
311 }
312
313 if (listIsEmpty)
314 iterMap = map.erase (iterMap);
315 else
316 ++iterMap;
317 }
318 }
319 }
320 else
321 {
322 bool mustFlush = true;
323
324 Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
325 Update::DependentMapIter iterList = map.find (unknown);
326
327#if NON_EXISTING_DEPENDENCY_CHECK
328 SMTG_ASSERT (iterList != map.end () && "ERROR: Trying to remove a non existing dependency!")
329#endif
330 if (iterList != map.end ())
331 {
332 if (dependent == nullptr) // Remove all dependents of object
333 {
334 eraseCount = iterList->second.size ();
335 map.erase (iterList);
336 }
337 else // Remove one dependent
338 {
339 Update::DependentList& dependentlist = (*iterList).second;
340 Update::DependentListIter iterDependentlist = dependentlist.begin ();
341 while (iterDependentlist != dependentlist.end ())
342 {
343#if CLASS_NAME_TRACKED
344 if ((*iterDependentlist).dep == dependent)
345#else
346 if ((*iterDependentlist) == dependent)
347#endif
348 {
349 iterDependentlist = dependentlist.erase (iterDependentlist);
350 eraseCount++;
351 if (dependentlist.empty ())
352 {
353 map.erase (iterList);
354 break;
355 }
356 }
357 else
358 {
359 ++iterDependentlist;
360 mustFlush = false;
361 }
362 }
363 }
364 }
365 if (mustFlush)
366 cancelUpdates (unknown);
367 }
368
369 return kResultTrue;
370}
371
372//------------------------------------------------------------------------
373tresult UpdateHandler::doTriggerUpdates (FUnknown* u, int32 message, bool suppressUpdateDone)
374{
375 IPtr<FUnknown> unknown = Update::getUnknownBase (u);
376 if (!unknown)
377 return kResultFalse;
378
379 // to avoid crashes due to stack overflow, we reduce the amount of memory reserved for the
380 // dependents
381 IDependent* smallDependents[Update::kMapSize / 10]; // 8kB for x64
382 IDependent** dependents = smallDependents;
383 int32 maxDependents = Update::kMapSize / 10;
384 int32 count = 0;
385
386 {
387 FGuard guard (lock); // scope for lock
388
389 Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
390 Update::DependentMapIterConst iterList = map.find (unknown);
391 if (iterList != map.end ())
392 {
393 const Update::DependentList& dependentlist = (*iterList).second;
394 Update::DependentListIterConst iterDependentlist = dependentlist.begin ();
395 while (iterDependentlist != dependentlist.end ())
396 {
397#if CLASS_NAME_TRACKED
398 dependents[count] = (*iterDependentlist).dep;
399#else
400 dependents[count] = *iterDependentlist;
401#endif
402 count++;
403
404 if (count >= maxDependents)
405 {
406 if (dependents == smallDependents)
407 {
408 dependents = NEW IDependent*[Update::kMapSize];
409 memcpy (dependents, smallDependents, count * sizeof (dependents[0]));
410 maxDependents = Update::kMapSize;
411 }
412 else
413 {
414 SMTG_WARNING ("Dependency overflow")
415 break;
416 }
417 }
418 ++iterDependentlist;
419 }
420 }
421
422 // push update data on the stack
423 if (count > 0)
424 {
425 Update::UpdateData data (unknown, dependents, count);
426 table->updateData.push_back (data);
427 }
428 } // end scope
429
430 int32 i = 0;
431 while (i < count)
432 {
433 if (dependents[i])
434 dependents[i]->update (unknown, message);
435 i++;
436 }
437
438 if (dependents != smallDependents)
439 delete[] dependents;
440
441 // remove update data from the stack
442 if (count > 0)
443 {
444 FGuard guard (lock);
445
446 table->updateData.pop_back ();
447 }
448
449 // send update done message
450 if (suppressUpdateDone == false)
451 Update::updateDone (unknown, message);
452
453 return count > 0 ? kResultTrue : kResultFalse; // Object was found and has been updated
454}
455
456//------------------------------------------------------------------------
457tresult PLUGIN_API UpdateHandler::triggerUpdates (FUnknown* u, int32 message)
458{
459 return doTriggerUpdates (u, message, false);
460}
461
462//------------------------------------------------------------------------
463tresult PLUGIN_API UpdateHandler::deferUpdates (FUnknown* u, int32 message)
464{
465 IPtr<FUnknown> unknown = Update::getUnknownBase (u);
466 if (!unknown)
467 return kResultFalse;
468
469 FGuard guard (lock);
470
471 Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
472 Update::DependentMapIterConst iterList = map.find (unknown);
473
474 bool hasDependency = (iterList != map.end ());
475 if (hasDependency == false)
476 {
477 Update::updateDone (unknown, message);
478 return kResultTrue;
479 }
480
481 bool found = false;
482 Update::DeferedChangeListIterConst iter = table->defered.begin ();
483 while (iter != table->defered.end ())
484 {
485 if ((*iter).obj == unknown && (*iter).msg == message)
486 {
487 found = true;
488 break;
489 }
490 ++iter;
491 }
492
493 if (!found)
494 {
495 Update::DeferedChange change (unknown, message);
496 table->defered.push_back (change);
497 }
498
499 return kResultTrue;
500}
501
502//------------------------------------------------------------------------
504{
505 Update::DeferedChangeList deferedAgain;
506 if (!unknown)
507 {
508 while (table->defered.empty () == false)
509 {
510 // Remove first from queue
511 lock.lock ();
512
513 FUnknown* obj = table->defered.front ().obj;
514 int32 msg = table->defered.front ().msg;
515 table->defered.pop_front ();
516
517 // check if this object is currently being updated. in this case, defer update again...
518 bool canSignal = true;
519 Update::UpdateDataListIterConst iter = table->updateData.begin ();
520 while (iter != table->updateData.end ())
521 {
522 if ((*iter).obj == obj)
523 {
524 canSignal = false;
525 break;
526 }
527 ++iter;
528 }
529 lock.unlock ();
530
531 if (canSignal)
532 {
533 triggerUpdates (obj, msg);
534 }
535 else
536 {
537 Update::DeferedChange change (obj, msg);
538 deferedAgain.push_back (change);
539 }
540 }
541 }
542 else
543 {
544 IPtr<FUnknown> object = Update::getUnknownBase (unknown);
545 Update::DeferedChange tmp (object);
546
547 while (true)
548 {
549 lock.lock ();
550 Update::DeferedChangeListIter it =
551 std::find (table->defered.begin (), table->defered.end (), tmp);
552 if (it == table->defered.end ())
553 {
554 lock.unlock ();
555 return kResultTrue;
556 }
557
558 if ((*it).obj != nullptr)
559 {
560 int32 msg = (*it).msg;
561 table->defered.erase (it);
562
563 // check if this object is currently being updated. in this case, defer update
564 // again...
565 bool canSignal = true;
566 Update::UpdateDataListIterConst iter = table->updateData.begin ();
567 while (iter != table->updateData.end ())
568 {
569 if ((*iter).obj == object)
570 {
571 canSignal = false;
572 break;
573 }
574 ++iter;
575 }
576 lock.unlock ();
577
578 if (canSignal)
579 {
580 triggerUpdates (object, msg);
581 }
582 else
583 {
584 Update::DeferedChange change (object, msg);
585 deferedAgain.push_back (change);
586 }
587 }
588 }
589 }
590
591 if (deferedAgain.empty () == false)
592 {
593 FGuard guard (lock);
594
595 Update::DeferedChangeListIterConst iter = deferedAgain.begin ();
596 while (iter != deferedAgain.end ())
597 {
598 table->defered.push_back (*iter);
599 ++iter;
600 }
601 }
602
603 return kResultTrue;
604}
605
606//------------------------------------------------------------------------
608{
609 IPtr<FUnknown> unknown = Update::getUnknownBase (u);
610 if (!unknown)
611 return kResultFalse;
612
613 FGuard guard (lock);
614
615 Update::DeferedChange change (unknown, 0);
616 while (true)
617 {
618 auto iter = std::find (table->defered.begin (), table->defered.end (), change);
619 if (iter != table->defered.end ())
620 table->defered.erase (iter);
621 else
622 break;
623 }
624
625 return kResultTrue;
626}
627
628//------------------------------------------------------------------------
629size_t UpdateHandler::countDependencies (FUnknown* object)
630{
631 FGuard guard (lock);
632 uint32 res = 0;
633
634 IPtr<FUnknown> unknown = Update::getUnknownBase (object);
635 if (unknown)
636 {
637 Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
638 Update::DependentMapIter iterList = map.find (unknown);
639 if (iterList != map.end ())
640 return iterList->second.size ();
641 }
642 else
643 {
644 for (uint32 j = 0; j < Update::kHashSize; j++)
645 {
646 Update::DependentMap& map = table->depMap[j];
647 res += countEntries (map);
648 }
649 }
650 return res;
651}
652
653#if DEVELOPMENT
654//------------------------------------------------------------------------
655bool UpdateHandler::checkDeferred (FUnknown* object)
656{
657 IPtr<FUnknown> unknown = Update::getUnknownBase (object);
658
659 FGuard guard (lock);
660
661 Update::DeferedChange tmp (unknown);
662 Update::DeferedChangeListIterConst it =
663 std::find (table->defered.begin (), table->defered.end (), tmp);
664 if (it != table->defered.end ())
665 return true;
666
667 return false;
668}
669
670//------------------------------------------------------------------------
671bool UpdateHandler::hasDependencies (FUnknown* u)
672{
673 IPtr<FUnknown> unknown = Update::getUnknownBase (u);
674 if (!unknown)
675 return false;
676
677 FGuard guard (lock);
678
679 Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
680 Update::DependentMapIterConst iterList = map.find (unknown);
681 bool hasDependency = (iterList != map.end ());
682
683 return hasDependency;
684}
685
686//------------------------------------------------------------------------
687void UpdateHandler::printForObject (FObject* obj) const
688{
689 IPtr<FUnknown> unknown = Update::getUnknownBase (obj);
690 if (!unknown)
691 return;
692
693 FUnknownPtr<IDependent> dep (obj);
694
695 bool header = false;
696
697 Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
698 Update::DependentMapIterConst iterList = map.begin ();
699 while (iterList != map.end ())
700 {
701 const Update::DependentList& dependentlist = (*iterList).second;
702 Update::DependentListIterConst iterDependentlist = dependentlist.begin ();
703 while (iterDependentlist != dependentlist.end ())
704 {
705#if CLASS_NAME_TRACKED
706 if ((*iterList).first == unknown || (*iterDependentlist).dep == dep.getInterface ())
707 {
708 if (!header)
709 {
710 FDebugPrint ("Dependencies for object %8" FORMAT_INT64A " %s\n", (uint64)obj,
711 obj->isA ());
712 header = true;
713 }
714 FDebugPrint ("%s %8" FORMAT_INT64A "\n <- %s %8" FORMAT_INT64A "\n",
715 (*iterDependentlist).depClass, (uint64) (*iterDependentlist).dep,
716 (*iterDependentlist).objClass, (uint64) ((*iterList).first));
717 }
718#else
719 if ((*iterList).first == unknown || (*iterDependentlist) == dep.getInterface ())
720 {
721 if (!header)
722 {
723 FDebugPrint ("Dependencies for object %8" FORMAT_INT64A " %s\n", (uint64)obj,
724 obj->isA ());
725 header = true;
726 }
727 FDebugPrint ("%8" FORMAT_INT64A "\n <- %8" FORMAT_INT64A "\n",
728 (uint64) (*iterDependentlist), (uint64) ((*iterList).first));
729 }
730#endif
731 ++iterDependentlist;
732 }
733
734 ++iterList;
735 }
736}
737#endif
738
739//------------------------------------------------------------------------
740} // namespace Steinberg
T begin(T... args)
FGuard - automatic object for locks.
Definition flock.h:134
void unlock() SMTG_OVERRIDE
Disables lock.
Definition flock.cpp:114
void lock() SMTG_OVERRIDE
Enables lock.
Definition flock.cpp:98
Implements FUnknown and IDependent.
Definition fobject.h:84
static void setUpdateHandler(IUpdateHandler *handler)
set method for the local attribute
Definition fobject.h:122
static FObject * unknownToObject(FUnknown *unknown)
pointer conversion from FUnknown to FObject
Definition fobject.h:145
static IUpdateHandler * getUpdateHandler()
get method for the local attribute
Definition fobject.h:123
virtual void updateDone(int32)
empty virtual method that should be overridden by derived classes
Definition fobject.h:119
virtual FClassID isA() const
a local alternative to getFClassID ()
Definition fobject.h:99
The basic interface of all interfaces.
Definition funknown.h:375
A dependent will get notified about changes of a model.
virtual void PLUGIN_API update(FUnknown *changedUnknown, int32 message)=0
Inform the dependent, that the passed FUnknown has changed.
IPtr - Smart pointer template class.
tresult PLUGIN_API addDependent(FUnknown *object, IDependent *dependent) SMTG_OVERRIDE
register
tresult PLUGIN_API removeDependent(FUnknown *object, IDependent *dependent, size_t &earseCount)
unregister
tresult PLUGIN_API triggerUpdates(FUnknown *object, int32 message) SMTG_OVERRIDE
send
tresult PLUGIN_API triggerDeferedUpdates(FUnknown *object=nullptr) SMTG_OVERRIDE
send pending messages send by
tresult PLUGIN_API deferUpdates(FUnknown *object, int32 message) SMTG_OVERRIDE
send
tresult PLUGIN_API cancelUpdates(FUnknown *object) SMTG_OVERRIDE
cancel pending messages send by
T count(T... args)
T empty(T... args)
T end(T... args)
T erase(T... args)
#define SMTG_ASSERT(f)
if DEVELOPMENT is not set, these macros will do nothing.
Definition fdebug.h:200
T find(T... args)
T front(T... args)
memcpy
T pop_back(T... args)
T pop_front(T... args)
T push_back(T... args)
IPtr< I > owned(I *p)
Assigning newly created object to an IPtr.