multithreading
 All Classes Functions Variables Pages
value_tree_clone.h
1 /*
2  ==============================================================================
3 
4  value_tree_clone.h
5  Created: 4 Aug 2014 2:58:12pm
6  Author: Jim Credland
7 
8  ==============================================================================
9 */
10 
11 #ifndef VALUE_TREE_CLONE_H_INCLUDED
12 #define VALUE_TREE_CLONE_H_INCLUDED
13 
14 
15 
20 {
21 public:
22  typedef ReferenceCountedObjectPtr<ValueTreeCopy> Ptr;
23  ValueTreeCopy (ValueTree copyFrom)
24  {
25  t = copyFrom.createCopy();
26  }
27  ~ValueTreeCopy() { }
28  ValueTree& getReference() { return t; }
29 private:
30  ValueTree t;
31 };
32 
47  public ValueTree::Listener
48 {
49 private:
51  class ValueTreeLinkCache
52  {
53  public:
54  ValueTree operator[] (const ValueTree& t)
55  {
56  for (auto& s : updateMap)
57  {
58  if (s.main == t) return s.copy;
59  }
60 
61  return ValueTree::invalid;
62  }
63 
64  void clear()
65  {
66  updateMap.clear();
67  }
69  void add (const ValueTree& src, const ValueTree& copy)
70  {
71  MapEntry x;
72  x.main = src;
73  x.copy = copy;
74  updateMap.push_back (x);
75  }
77  struct MapEntry
78  {
79  ValueTree main, copy;
80  };
81  std::vector<MapEntry> updateMap;
82  };
83 
84 public:
90  typename ValueTreeCopy::Ptr readonly;
91 
99  CriticalThreadValueTree (ValueTree source, LockFreeCallQueue& q) :
100  jobsForCriticalThread (q)
101  {
102  readonly = new ValueTreeCopy (ValueTree ("null"));
103  setSource (source);
104  }
105 
107  {}
108 
111  void setSource (ValueTree source)
112  {
113  sourceTree.removeListener (this);
114  sourceTree = source;
115  sourceTree.addListener (this);
116  syncAll();
117  };
118 
120  void valueTreeChildAdded (ValueTree& parentTree, ValueTree& childWhichHasBeenAdded)
121  {
122  syncAll();
123  }
125  void valueTreeChildRemoved (ValueTree& parentTree, ValueTree& childWhichHasBeenRemoved)
126  {
127  syncAll();
128  }
130  void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved)
131  {
132  syncAll();
133  }
135  void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {}
136 
139  void syncAll()
140  {
141  /* Create a deep copy of the value tree. Pass it to the other thread. */
142  ValueTreeCopy* t = new ValueTreeCopy (sourceTree);
143  /* Create tree to tree mapping so property updates can happen quickly. */
144  updatePropertymap (sourceTree, t->getReference());
145  /* Send it over. */
146  sendReplaceValueTree (t);
147  }
148 
149 
151  void valueTreePropertyChanged (ValueTree& tree, const Identifier& property)
152  {
153  /* Send an update property message. Property removals are handled by sending
154  a null var() object.
155 
156  A property addition however requires that we replace the entire value tree
157  to avoid calling malloc on the audio thread.
158  */
159  if (! isPropertyChangedOperationLockFree (tree, property))
160  {
161  syncAll();
162  return;
163  }
164 
165  /* Create a new ValueTree that points to the same underlying data. */
166  ValueTree target = linkCache[tree];
167  /* Create a var object. Note: var manages the reference counting. */
168  var v = tree[property];
169  /* If it's an object, check that the GarbageCollector knows about it.
170  Otherwise it may end up being deleted on the critical thread.
171 
172  If you hit this assert you probably aren't using a GarbageCollectedObject
173  and instead you've got a standard ReferenceCountedObject here.
174  */
175  jassert (
176  (! v.isObject()) ||
177  (GarbageCollector::getInstance()->isInList (v.getObject()))
178  );
179  /* Pass these objects to the critical thread to update the critical thread's
180  own ValueTree. */
181  sendUpdateProperty (target, property, v);
182  }
183 
184 
185 private:
186  bool isPropertyChangedOperationLockFree (ValueTree& tree, const Identifier& property)
187  {
188  if (tree[property].isString()) return false; /* Might be okay? */
189 
190  ValueTree t = linkCache[tree];
191 
192  if (t == ValueTree::invalid)
193  {
194  jassertfalse; /* This shouldn't happen: we should have all the nodes at least. */
195  return false;
196  }
197 
198  return (t.hasProperty (property));
199  }
203  void updatePropertymap (ValueTree& src, ValueTree& copy)
204  {
205  linkCache.clear();
206  recreatePropertyUpdateMap (sourceTree, copy);
207  }
208  /* Can this be converted to pass by const reference? */
209  void recreatePropertyUpdateMap (ValueTree src, ValueTree copy)
210  {
211  for (int i = 0; i < src.getNumChildren(); ++i)
212  {
213  recreatePropertyUpdateMap (src.getChild (i), copy.getChild (i));
214  }
215 
216  linkCache.add (src, copy);
217  }
218 
219  void sendUpdateProperty (ValueTree& target, const Identifier& property, const var& value)
220  {
221  jobsForCriticalThread.callf (std::bind (&CriticalThreadValueTree::updatePropertyOnCriticalThread,
222  this,
223  target, property, value));
224  }
225 
226  void updatePropertyOnCriticalThread (ValueTree target, Identifier property, var value)
227  {
228  target.setProperty (property, value, nullptr);
229  }
230 
232  void sendReplaceValueTree (ValueTreeCopy* t)
233  {
234  ValueTreeCopy::Ptr p = t;
235  jobsForCriticalThread.callf (std::bind (&CriticalThreadValueTree::replaceValueTree,
236  this,
237  p));
238  }
239 
240  void replaceValueTree (typename ValueTreeCopy::Ptr replacementTree)
241  {
242  /* readonly old one will be deleted on message thread . */
243  DBG ("replacing tree with: " + replacementTree->getReference().toXmlString());
244  readonly = replacementTree;
245  }
246 
247  ValueTreeLinkCache linkCache;
248  LockFreeCallQueue& jobsForCriticalThread;
249  ValueTree sourceTree;
250 };
251 
252 
253 
254 #endif // VALUE_TREE_CLONE_H_INCLUDED
void setSource(ValueTree source)
set the source tree to copy. This is set initally by the constructor, so you may not need to call thi...
Definition: value_tree_clone.h:111
A GarbageCollectedObject wrapper around a ValueTree. You shouldn't have to create one of these direct...
Definition: value_tree_clone.h:18
ValueTreeCopy::Ptr readonly
The output - the clone of the ValueTree.
Definition: value_tree_clone.h:90
Allows a function call to be executed on a different thread, in a fast lock-safe and thread-safe mann...
Definition: nonblocking_call_queue.h:37
bool callf(Functor const &f)
Calls a function, via the queue, on a different thread.
Definition: nonblocking_call_queue.h:99
Maintain a clone of a ValueTree on a critical thread.
Definition: value_tree_clone.h:46
CriticalThreadValueTree(ValueTree source, LockFreeCallQueue &q)
Configure the CriticalThreadValueTree.
Definition: value_tree_clone.h:99
Allows objects to be created and destroyed on a non-critical thread but passed over to a critical thr...
Definition: garbage_collected_object.h:139
void syncAll()
Synchronise. You shouldn't have to call this. It should be called automatically from the non-critical...
Definition: value_tree_clone.h:139
Definition: value_tree_clone.h:77