Xcos MVC: use std::atomic to be thread safe
[scilab.git] / scilab / modules / scicos / src / cpp / Controller.cpp
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2014-2014 - Scilab Enterprises - Clement DAVID
4  *
5  *  This file must be used under the terms of the CeCILL.
6  *  This source file is licensed as described in the file COPYING, which
7  *  you should have received as part of this distribution.  The terms
8  *  are also available at
9  *  http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
10  *
11  */
12
13 #include <atomic>
14 #include <string>
15 #include <vector>
16 #include <map>
17 #include <utility>
18 #include <algorithm>
19 #include <iterator>
20
21 #include "utilities.hxx"
22
23 #define REF_DEBUG 0
24 #if REF_DEBUG
25 #include "scilabWrite.hxx"
26 #define REF_PRINT(uid, refCount) \
27         do { \
28         std::stringstream print; \
29         print << "referenceObject( " << uid << " ) : " << refCount << std::endl; \
30         scilabForcedWrite(print.str().data()); \
31         } while(0)
32 #define UNREF_PRINT(uid, count) \
33         do { \
34         std::stringstream print; \
35         print << "unreferenceObject( " << uid << " ) : " << refCount << std::endl; \
36         scilabForcedWrite(print.str().data()); \
37         } while(0)
38 #define CLONE_PRINT(uid, clone) \
39         do { \
40         std::stringstream print; \
41         print << "cloneObject( " << uid << " ) : " << clone << std::endl; \
42         scilabForcedWrite(print.str().data()); \
43         } while(0)
44 #else
45 #define REF_PRINT(uid, refCount)
46 #define UNREF_PRINT(uid, refCount)
47 #define CLONE_PRINT(uid, clone)
48 #endif
49
50 #include "Controller.hxx"
51 #include "model/BaseObject.hxx"
52
53 #include "LoggerView.hxx"
54
55 namespace org_scilab_modules_scicos
56 {
57
58 static inline void lock(std::atomic_flag* m)
59 {
60     while (m->test_and_set(std::memory_order_acquire))  // acquire lock
61         ; // spin
62 }
63
64 static inline void unlock(std::atomic_flag* m)
65 {
66     m->clear(std::memory_order_release);
67 }
68
69 /*
70  * Implement SharedData methods
71  */
72 Controller::SharedData::SharedData() :
73     onModelStructuralModification(), model(),
74     onViewsStructuralModification(), allNamedViews(), allViews()
75 {
76     onModelStructuralModification.clear();
77     onViewsStructuralModification.clear();
78 }
79
80 Controller::SharedData::~SharedData()
81 {
82     lock(&onViewsStructuralModification);
83     for (view_set_t::iterator iter = allViews.begin(); iter != allViews.end(); ++iter)
84     {
85         delete *iter;
86     }
87     unlock(&onViewsStructuralModification);
88 }
89
90 Controller::SharedData Controller::m_instance;
91
92 View* Controller::register_view(const std::string& name, View* v)
93 {
94     lock(&m_instance.onViewsStructuralModification);
95
96     m_instance.allNamedViews.push_back(name);
97     m_instance.allViews.push_back(v);
98
99     unlock(&m_instance.onViewsStructuralModification);
100     return v;
101 }
102
103 void Controller::unregister_view(View* v)
104 {
105     lock(&m_instance.onViewsStructuralModification);
106
107     view_set_t::iterator it = std::find(m_instance.allViews.begin(), m_instance.allViews.end(), v);
108     if (it != m_instance.allViews.end())
109     {
110         size_t d = std::distance(m_instance.allViews.begin(), it);
111         m_instance.allNamedViews.erase(m_instance.allNamedViews.begin() + d);
112         m_instance.allViews.erase(m_instance.allViews.begin() + d);
113     }
114
115     unlock(&m_instance.onViewsStructuralModification);
116 }
117
118 View* Controller::unregister_view(const std::string& name)
119 {
120     View* view = nullptr;
121
122     lock(&m_instance.onViewsStructuralModification);
123
124     view_name_set_t::iterator it = std::find(m_instance.allNamedViews.begin(), m_instance.allNamedViews.end(), name);
125     if (it != m_instance.allNamedViews.end())
126     {
127         size_t d = std::distance(m_instance.allNamedViews.begin(), it);
128         view = *(m_instance.allViews.begin() + d);
129         m_instance.allNamedViews.erase(m_instance.allNamedViews.begin() + d);
130         m_instance.allViews.erase(m_instance.allViews.begin() + d);
131     }
132     unlock(&m_instance.onViewsStructuralModification);
133
134     return view;
135 }
136
137 View* Controller::look_for_view(const std::string& name)
138 {
139     View* view = nullptr;
140
141     lock(&m_instance.onViewsStructuralModification);
142     view_name_set_t::iterator it = std::find(m_instance.allNamedViews.begin(), m_instance.allNamedViews.end(), name);
143     if (it != m_instance.allNamedViews.end())
144     {
145         size_t d = std::distance(m_instance.allNamedViews.begin(), it);
146         view = *(m_instance.allViews.begin() + d);
147     }
148     unlock(&m_instance.onViewsStructuralModification);
149
150     return view;
151 }
152
153 Controller::Controller()
154 {
155 }
156
157 Controller::~Controller()
158 {
159 }
160
161 ScicosID Controller::createObject(kind_t k)
162 {
163     lock(&m_instance.onModelStructuralModification);
164     ScicosID uid = m_instance.model.createObject(k);
165     unlock(&m_instance.onModelStructuralModification);
166
167     lock(&m_instance.onViewsStructuralModification);
168     for (view_set_t::iterator iter = m_instance.allViews.begin(); iter != m_instance.allViews.end(); ++iter)
169     {
170         (*iter)->objectCreated(uid, k);
171     }
172     unlock(&m_instance.onViewsStructuralModification);
173
174     return uid;
175 }
176
177 unsigned Controller::referenceObject(const ScicosID uid) const
178 {
179     lock(&m_instance.onModelStructuralModification);
180
181     unsigned refCount = m_instance.model.referenceObject(uid);
182     REF_PRINT(uid, refCount);
183
184     auto o = m_instance.model.getObject(uid);
185     unlock(&m_instance.onModelStructuralModification);
186
187     lock(&m_instance.onViewsStructuralModification);
188     for (view_set_t::iterator iter = m_instance.allViews.begin(); iter != m_instance.allViews.end(); ++iter)
189     {
190         (*iter)->objectReferenced(uid, o->kind(), refCount);
191     }
192     unlock(&m_instance.onViewsStructuralModification);
193
194     return refCount;
195 }
196
197 void Controller::deleteObject(ScicosID uid)
198 {
199     // if this object is the empty uid, ignore it : is is not stored in the model
200     if (uid == 0)
201     {
202         return;
203     }
204
205     lock(&m_instance.onModelStructuralModification);
206
207     auto initial = m_instance.model.getObject(uid);
208     if (initial == nullptr)
209     {
210         // defensive programming
211         return;
212     }
213     const kind_t k = initial->kind();
214
215     // if this object has been referenced somewhere else do not delete it but decrement the reference counter
216     unsigned& refCount = m_instance.model.referenceCount(uid);
217     unlock(&m_instance.onModelStructuralModification);
218     if (refCount > 0)
219     {
220         --refCount;
221         UNREF_PRINT(uid, refCount);
222
223         lock(&m_instance.onViewsStructuralModification);
224         for (view_set_t::iterator iter = m_instance.allViews.begin(); iter != m_instance.allViews.end(); ++iter)
225         {
226             (*iter)->objectUnreferenced(uid, k, refCount);
227         }
228         unlock(&m_instance.onViewsStructuralModification);
229         return;
230     }
231
232     // We need to delete this object and cleanup all the referenced model object
233
234     // disconnect / remove references of weak connected objects and decrement the reference count of all strongly connected objects.
235     if (k == ANNOTATION)
236     {
237         unlinkVector(uid, k, PARENT_DIAGRAM, CHILDREN);
238         unlinkVector(uid, k, PARENT_BLOCK, CHILDREN);
239         // RELATED_TO is not referenced back
240     }
241     else if (k == BLOCK)
242     {
243         unlinkVector(uid, k, PARENT_DIAGRAM, CHILDREN);
244         unlinkVector(uid, k, PARENT_BLOCK, CHILDREN);
245
246         deleteVector(uid, k, INPUTS);
247         deleteVector(uid, k, OUTPUTS);
248         deleteVector(uid, k, EVENT_INPUTS);
249         deleteVector(uid, k, EVENT_OUTPUTS);
250
251         unlink(uid, k, CHILDREN, PARENT_BLOCK);
252         deleteVector(uid, k, CHILDREN);
253         // FIXME what about REFERENCED_PORT ?
254     }
255     else if (k == DIAGRAM)
256     {
257         unlink(uid, k, CHILDREN, PARENT_DIAGRAM);
258         deleteVector(uid, k, CHILDREN);
259     }
260     else if (k == LINK)
261     {
262         unlinkVector(uid, k, PARENT_DIAGRAM, CHILDREN);
263         unlinkVector(uid, k, PARENT_BLOCK, CHILDREN);
264
265         unlinkVector(uid, k, SOURCE_PORT, CONNECTED_SIGNALS);
266         unlinkVector(uid, k, DESTINATION_PORT, CONNECTED_SIGNALS);
267     }
268     else if (k == PORT)
269     {
270         unlinkVector(uid, k, SOURCE_BLOCK, INPUTS);
271         unlinkVector(uid, k, SOURCE_BLOCK, OUTPUTS);
272         unlinkVector(uid, k, SOURCE_BLOCK, EVENT_INPUTS);
273         unlinkVector(uid, k, SOURCE_BLOCK, EVENT_OUTPUTS);
274
275         unlink(uid, k, CONNECTED_SIGNALS, SOURCE_PORT);
276         unlink(uid, k, CONNECTED_SIGNALS, DESTINATION_PORT);
277     }
278
279     // delete the object
280     lock(&m_instance.onModelStructuralModification);
281     m_instance.model.deleteObject(uid);
282     unlock(&m_instance.onModelStructuralModification);
283
284     lock(&m_instance.onViewsStructuralModification);
285     for (view_set_t::iterator iter = m_instance.allViews.begin(); iter != m_instance.allViews.end(); ++iter)
286     {
287         (*iter)->objectDeleted(uid, k);
288     }
289     unlock(&m_instance.onViewsStructuralModification);
290 }
291
292 void Controller::unlinkVector(ScicosID uid, kind_t k, object_properties_t uid_prop, object_properties_t ref_prop)
293 {
294     ScicosID v;
295     m_instance.model.getObjectProperty(uid, k, uid_prop, v);
296     if (v != 0)
297     {
298         auto o = m_instance.model.getObject(v);
299         if (o == nullptr)
300         {
301             return;
302         }
303
304         std::vector<ScicosID> children;
305         m_instance.model.getObjectProperty(o->id(), o->kind(), ref_prop, children);
306
307         std::vector<ScicosID>::iterator it = std::find(children.begin(), children.end(), uid);
308         if (it != children.end())
309         {
310             children.erase(it);
311         }
312
313         m_instance.model.setObjectProperty(o->id(), o->kind(), ref_prop, children);
314     }
315 }
316
317 void Controller::unlink(ScicosID uid, kind_t k, object_properties_t uid_prop, object_properties_t ref_prop)
318 {
319     std::vector<ScicosID> v;
320     m_instance.model.getObjectProperty(uid, k, uid_prop, v);
321     for (const ScicosID id : v)
322     {
323         if (id != 0)
324         {
325             auto o = m_instance.model.getObject(id);
326             if (o == nullptr)
327             {
328                 continue;
329             }
330
331             // Find which end of the link is connected to the port
332             ScicosID oppositeRef;
333             m_instance.model.getObjectProperty(o->id(), o->kind(), ref_prop, oppositeRef);
334             if (oppositeRef == uid)
335             {
336                 m_instance.model.setObjectProperty(o->id(), o->kind(), ref_prop, ScicosID());
337             }
338         }
339     }
340 }
341
342 void Controller::deleteVector(ScicosID uid, kind_t k, object_properties_t uid_prop)
343 {
344     std::vector<ScicosID> children;
345     m_instance.model.getObjectProperty(uid, k, uid_prop, children);
346
347     for (ScicosID id : children)
348     {
349         m_instance.model.deleteObject(id);
350     }
351 }
352
353 template<typename T>
354 void Controller::cloneProperties(model::BaseObject* initial, ScicosID clone)
355 {
356     for (int i = 0; i < MAX_OBJECT_PROPERTIES; ++i)
357     {
358         enum object_properties_t p = static_cast<enum object_properties_t>(i);
359
360         T value;
361         bool status = m_instance.model.getObjectProperty(initial->id(), initial->kind(), p, value);
362         if (status)
363         {
364             m_instance.model.setObjectProperty(clone, initial->kind(), p, value);
365         }
366     }
367 }
368
369 ScicosID Controller::cloneObject(std::map<ScicosID, ScicosID>& mapped, ScicosID uid, bool cloneChildren, bool clonePorts)
370 {
371     auto initial = m_instance.model.getObject(uid);
372     const kind_t k = initial->kind();
373     ScicosID o = m_instance.model.createObject(k);
374     mapped.insert(std::make_pair(uid, o));
375
376     // Get then set all properties per type that do not manage ScicosID
377     cloneProperties<double>(initial, o);
378     cloneProperties<int>(initial, o);
379     cloneProperties<bool>(initial, o);
380     cloneProperties<std::string>(initial, o);
381     cloneProperties<std::vector<double> >(initial, o);
382     cloneProperties<std::vector<int> >(initial, o);
383     cloneProperties<std::vector<std::string> >(initial, o);
384
385     // deep copy children, manage ScicosID and std::vector<ScicosID>
386     if (k == ANNOTATION)
387     {
388         deepClone(mapped, uid, o, k, PARENT_DIAGRAM, false);
389         deepClone(mapped, uid, o, k, PARENT_BLOCK, false);
390         deepClone(mapped, uid, o, k, RELATED_TO, true);
391     }
392     else if (k == BLOCK)
393     {
394         deepClone(mapped, uid, o, k, PARENT_DIAGRAM, false);
395         if (clonePorts)
396         {
397             // Only copy the block, without any port
398             deepCloneVector(mapped, uid, o, k, INPUTS, true);
399             deepCloneVector(mapped, uid, o, k, OUTPUTS, true);
400             deepCloneVector(mapped, uid, o, k, EVENT_INPUTS, true);
401             deepCloneVector(mapped, uid, o, k, EVENT_OUTPUTS, true);
402         }
403
404         deepClone(mapped, uid, o, k, PARENT_BLOCK, false);
405         if (cloneChildren)
406         {
407             deepCloneVector(mapped, uid, o, k, CHILDREN, true);
408         }
409         // FIXME what about REFERENCED_PORT ?
410     }
411     else if (k == DIAGRAM)
412     {
413         if (cloneChildren)
414         {
415             deepCloneVector(mapped, uid, o, k, CHILDREN, true);
416         }
417     }
418     else if (k == LINK)
419     {
420         deepClone(mapped, uid, o, k, PARENT_DIAGRAM, false);
421         deepClone(mapped, uid, o, k, PARENT_BLOCK, false);
422         deepClone(mapped, uid, o, k, SOURCE_PORT, false);
423         deepClone(mapped, uid, o, k, DESTINATION_PORT, false);
424     }
425     else if (k == PORT)
426     {
427         deepClone(mapped, uid, o, k, SOURCE_BLOCK, false);
428         deepCloneVector(mapped, uid, o, k, CONNECTED_SIGNALS, false);
429     }
430     return o;
431 }
432
433 void Controller::deepClone(std::map<ScicosID, ScicosID>& mapped, ScicosID uid, ScicosID clone, kind_t k, object_properties_t p, bool cloneIfNotFound)
434 {
435     ScicosID v;
436     m_instance.model.getObjectProperty(uid, k, p, v);
437
438     ScicosID cloned = 0;
439
440     std::map<ScicosID, ScicosID>::iterator it = mapped.find(v);
441     if (it != mapped.end())
442     {
443         cloned = it->second;
444     }
445     else
446     {
447         if (cloneIfNotFound)
448         {
449             if (v != 0)
450             {
451                 cloned = cloneObject(mapped, v, true, true);
452             }
453             else
454             {
455                 cloned = 0;
456             }
457         }
458         else
459         {
460             cloned = 0;
461         }
462     }
463
464     m_instance.model.setObjectProperty(clone, k, p, cloned);
465 }
466
467 void Controller::deepCloneVector(std::map<ScicosID, ScicosID>& mapped, ScicosID uid, ScicosID clone, kind_t k, object_properties_t p, bool cloneIfNotFound)
468 {
469     std::vector<ScicosID> v;
470     m_instance.model.getObjectProperty(uid, k, p, v);
471
472     std::vector<ScicosID> cloned;
473     cloned.reserve(v.size());
474
475     for (const ScicosID & id : v)
476     {
477         if (id == 0)
478         {
479             // Deleted Block, the cloning is done at Adapter level
480             cloned.push_back(id);
481             continue;
482         }
483
484         std::map<ScicosID, ScicosID>::iterator it = mapped.find(id);
485         if (it != mapped.end())
486         {
487             cloned.push_back(it->second);
488         }
489         else
490         {
491             if (cloneIfNotFound)
492             {
493                 if (id != 0)
494                 {
495                     cloned.push_back(cloneObject(mapped, id, true, true));
496                 }
497                 else
498                 {
499                     cloned.push_back(ScicosID());
500                 }
501             }
502             else
503             {
504                 cloned.push_back(ScicosID());
505             }
506         }
507     }
508
509     m_instance.model.setObjectProperty(clone, k, p, cloned);
510 }
511
512 ScicosID Controller::cloneObject(ScicosID uid, bool cloneChildren, bool clonePorts)
513 {
514     lock(&m_instance.onModelStructuralModification);
515
516     std::map<ScicosID, ScicosID> mapped;
517     ScicosID clone = cloneObject(mapped, uid, cloneChildren, clonePorts);
518     CLONE_PRINT(uid, clone);
519
520     unlock(&m_instance.onModelStructuralModification);
521     return clone;
522 }
523
524 kind_t Controller::getKind(ScicosID uid) const
525 {
526     lock(&m_instance.onModelStructuralModification);
527
528     kind_t kind = m_instance.model.getKind(uid);
529
530     unlock(&m_instance.onModelStructuralModification);
531     return kind;
532 }
533
534 std::vector<ScicosID> Controller::getAll(kind_t k) const
535 {
536     lock(&m_instance.onModelStructuralModification);
537
538     auto vec = m_instance.model.getAll(k);
539
540     unlock(&m_instance.onModelStructuralModification);
541     return vec;
542 }
543
544 void Controller::sortAndFillKind(std::vector<ScicosID>& uids, std::vector<int>& kinds)
545 {
546     lock(&m_instance.onModelStructuralModification);
547
548     // create a container of pair
549     struct local_pair
550     {
551         ScicosID first;
552         int second;
553     };
554     std::vector<local_pair> container(uids.size());
555
556     // fill it
557     for (size_t i = 0; i < uids.size(); ++i)
558     {
559         container[i] = { uids[i], m_instance.model.getKind(uids[i]) };
560     }
561
562     // sort according to the kinds
563     std::stable_sort(container.begin(), container.end(), [] (const local_pair & a, const local_pair & b)
564     {
565         return a.second < b.second;
566     });
567
568     // move things back
569     uids.clear();
570     kinds.reserve(uids.capacity());
571     for (const auto & v : container)
572     {
573         uids.push_back(v.first);
574         kinds.push_back(v.second);
575     }
576
577     unlock(&m_instance.onModelStructuralModification);
578 }
579
580
581
582 model::BaseObject* Controller::getObject(ScicosID uid) const
583 {
584     lock(&m_instance.onModelStructuralModification);
585     model::BaseObject* o = m_instance.model.getObject(uid);
586     unlock(&m_instance.onModelStructuralModification);
587     return o;
588 }
589
590 } /* namespace org_scilab_modules_scicos */