[NUI] Change Registry as concurrent collection for protection code (#113)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / Registry.cs
1 /*
2  * Copyright(c) 2018 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 using System;
19 using System.Collections.Concurrent;
20 using System.Threading;
21
22 namespace Tizen.NUI
23 {
24     /// <summary>
25     /// This is used to store a mapping between C++ base handle objects and it's C# instances.
26     ///
27     /// </summary>
28     internal sealed class Registry
29     {
30         private static readonly Registry registry = new Registry();
31
32         /// <summary>
33         /// static initialization singleton
34         /// </summary>
35         internal static Registry Instance
36         {
37             get { return registry; }
38         }
39
40         /// <summary>
41         /// Given a C++ object, the dictionary allows us to find which C# object it belongs to.
42         /// By keeping the weak reference only, it will allow the object to be garbage collected.
43         /// </summary>
44         private ConcurrentDictionary<IntPtr, WeakReference> _controlMap;
45
46         private Registry()
47         {
48             _controlMap = new ConcurrentDictionary<IntPtr, WeakReference>();
49         }
50
51
52         /// <summary>
53         /// Stores the mapping between this instance of BaseHandle (C# base class) and native part.
54         /// </summary>
55         /// <param name="baseHandle">The instance of BaseHandle (C# base class).</param>
56         internal static void Register(BaseHandle baseHandle)
57         {
58             // We store a pointer to the RefObject for the control
59             RefObject refObj = baseHandle.GetObjectPtr();
60             IntPtr refCptr = (IntPtr)RefObject.getCPtr(refObj);
61
62             if(baseHandle == null)
63             {
64                 NUILog.Debug($"Register: baseHandle == null ! means native object is null!");
65             }
66             else
67             {
68                 NUILog.Debug($"Register: type={baseHandle?.GetTypeName()} refCptr=0x{refCptr.ToInt64():X}");
69             }
70
71             RegistryCurrentThreadCheck();
72
73             if(Instance._controlMap.TryAdd(refCptr, new WeakReference(baseHandle, false)) != true)
74             {
75                 NUILog.Debug("refCptr is already exist! OR something wrong!");
76             }
77
78             return;
79         }
80
81         /// <summary>
82         /// Removes this instance of BaseHandle (C# base class) and native part from the mapping table.
83         /// </summary>
84         /// <param name="baseHandle"> The instance of BaseHandle (C# base class)</param>
85         internal static void Unregister(BaseHandle baseHandle)
86         {
87             RefObject refObj = baseHandle.GetObjectPtr();
88             IntPtr refCptr = (IntPtr)RefObject.getCPtr(refObj);
89
90             if (baseHandle == null)
91             {
92                 NUILog.Debug($"Unregister: baseHandle == null ! means native object is null!");
93             }
94             else
95             {
96                 NUILog.Debug($"Unregister: type={baseHandle?.GetTypeName()} refCptr=0x{refCptr.ToInt64():X}");
97             }
98
99             RegistryCurrentThreadCheck();
100
101             WeakReference removeTarget;
102             if(Instance._controlMap.TryRemove(refCptr, out removeTarget) != true)
103             {
104                 NUILog.Debug("something wrong when removing refCptr!");
105             }
106
107             return;
108         }
109
110         internal static BaseHandle GetManagedBaseHandleFromNativePtr(BaseHandle baseHandle)
111         {
112             RefObject refObj = baseHandle.GetObjectPtr();
113             IntPtr refObjectPtr = (IntPtr)RefObject.getCPtr(refObj);
114
115             // we store a dictionary of ref-obects (C++ land) to managed obects (C# land)
116             return GetManagedBaseHandleFromRefObject(refObjectPtr);
117         }
118
119         internal static BaseHandle GetManagedBaseHandleFromNativePtr(IntPtr cPtr)
120         {
121             IntPtr refObjectPtr = NDalicPINVOKE.GetRefObjectPtr(cPtr);
122
123             // we store a dictionary of ref-obects (C++ land) to managed obects (C# land)
124             return GetManagedBaseHandleFromRefObject(refObjectPtr);
125         }
126
127         internal static BaseHandle GetManagedBaseHandleFromRefObject(IntPtr refObjectPtr)
128         {
129             if (refObjectPtr == global::System.IntPtr.Zero)
130             {
131                 NUILog.Debug("Registry refObjectPtr is NULL! This means bind native object is NULL!");
132                 return null;
133             }
134             else
135             {
136                 NUILog.Debug($"refObjectPtr=0x{refObjectPtr.ToInt64():X}");
137             }
138
139             RegistryCurrentThreadCheck();
140
141             // we store a dictionary of ref-obects (C++ land) to managed obects (C# land)
142             WeakReference weakReference;
143
144             if (Instance._controlMap.TryGetValue(refObjectPtr, out weakReference))
145             {
146                 if(weakReference == null)
147                 {
148                     throw new System.InvalidOperationException("Error! NUI Registry weakReference should not be NULL!");
149                 }
150
151                 BaseHandle ret = weakReference.Target as BaseHandle;
152                 return ret;
153             }
154             else
155             {
156                 return null;
157             }
158         }
159
160         private static Thread savedApplicationThread;
161         internal Thread SavedApplicationThread
162         {
163             get
164             {
165                 return savedApplicationThread;
166             }
167             set
168             {
169                 savedApplicationThread = value;
170             }
171         }
172
173         private static void RegistryCurrentThreadCheck()
174         {
175             int currentId = Thread.CurrentThread.ManagedThreadId;
176             int mainThreadId = savedApplicationThread.ManagedThreadId;
177
178             if(currentId != mainThreadId)
179             {
180                 Tizen.Log.Fatal("NUI", $"Error! current thread({currentId}) which is NOT main thread({mainThreadId}) utilizes NUI object!");
181             }
182         }
183
184
185     }
186 }