Merge "[3.0] Modify doxygen comment of AtomicOperation as ACR review" into devel_3...
[platform/framework/native/appfw.git] / src / io / FIo_FileEventDispatcher.cpp
1 //
2 // Copyright (c) 2013 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  * @file        FIo_FileEventDispatcher.cpp
19  * @brief       This is the implementation file for _FileEventDispatcher class.
20  */
21
22 #include <unique_ptr.h>
23 #include <cstdlib>
24 #include <sys/inotify.h>
25 #include <string.h>
26 #include <errno.h>
27
28 #include <FBaseInteger.h>
29 #include <FBaseColHashMap.h>
30 #include <FBaseColLinkedList.h>
31 #include <FBaseRtMutex.h>
32 #include <FBaseSysLog.h>
33
34 #include "FIo_FileEventManagerImpl.h"
35 #include "FIo_FileEventDispatcher.h"
36 #include "FIo_FileEvent.h"
37
38 #define INOTIFY_BUFFER_LEN      (1024*(sizeof(struct inotify_event) + 16))
39
40 using namespace std;
41 using namespace Tizen::Base;
42 using namespace Tizen::Base::Collection;
43 using namespace Tizen::Base::Runtime;
44
45 namespace Tizen { namespace Io
46 {
47
48 _FileEventDispatcher* _FileEventDispatcher::__pFileEventDispatcherInstance = null;
49
50 _FileEventDispatcher::_FileEventDispatcher(void)
51         : __pWatchList(null)
52         , __inotifyFd(-1)
53         , __pGSource(null)
54         , __pGIOChannel(null)
55 {
56 }
57
58 result
59 _FileEventDispatcher::Construct(void)
60 {
61         unique_ptr< HashMap > pWatchList(new (std::nothrow) HashMap(SingleObjectDeleter));
62         SysTryReturnResult(NID_IO, pWatchList != null, E_OUT_OF_MEMORY, "The memory is insufficient.");
63
64         result r = pWatchList->Construct();
65         SysTryReturnResult(NID_IO, !IsFailed(r), r, "Propagating to caller...");
66
67         __inotifyFd = inotify_init();
68         if (__inotifyFd == -1)
69         {
70                 switch (errno)
71                 {
72                 case EMFILE:
73                         SysLogException(NID_IO, E_MAX_EXCEEDED,
74                                         "The total number of inotify instances has exceeded the maximum limit.");
75                         r = E_MAX_EXCEEDED;
76                         break;
77                 case ENFILE:
78                         SysLogException(NID_IO, E_MAX_EXCEEDED,
79                                         "The total number of file descriptors has exceeded the maximum limit.");
80                         r = E_MAX_EXCEEDED;
81                         break;
82                 case ENOMEM:
83                         SysLogException(NID_IO, E_OUT_OF_MEMORY, "The memory is insufficient.");
84                         r = E_OUT_OF_MEMORY;
85                         break;
86                 default:
87                         SysLogException(NID_IO, E_SYSTEM, "The method cannot proceed due to a severe system error.");
88                         r = E_SYSTEM;
89                         break;
90                 }
91
92                 SysLog(NID_IO, "errno: %d (%s)", errno, strerror(errno));
93                 return r;
94         }
95
96         GMainContext* pGContext = g_main_context_get_thread_default(); //get own gmain context except default thread
97         if (pGContext == null)
98         {
99                 pGContext = g_main_context_default(); //get gmain context from me (default)
100                 SysTryReturnResult(NID_IO, pGContext != null, E_IO, "Failed to get gmain context.");
101         }
102
103         __pGIOChannel = g_io_channel_unix_new(__inotifyFd); //fd wrapping
104         SysTryCatch(NID_IO, __pGIOChannel != null, r = E_IO, E_IO, "[E_IO] Failed to create gio channel.");
105
106         __pGSource = g_io_create_watch(__pGIOChannel, (GIOCondition)(G_IO_IN | G_IO_ERR | G_IO_NVAL | G_IO_HUP));
107         SysTryCatch(NID_IO, __pGSource != null, r = E_IO, E_IO, "[E_IO] Failed to create gsource.");
108
109         g_source_set_callback(__pGSource, (GSourceFunc)OnFileEventOccurred, this, null);
110         g_source_attach(__pGSource, pGContext);
111
112         __pWatchList = pWatchList.release();
113
114         SysLog(NID_IO, "inotify fd: %d, channel: 0x%x", __inotifyFd, __pGIOChannel);
115         return E_SUCCESS;
116
117 CATCH:
118         if (__inotifyFd != -1)
119         {
120                 close(__inotifyFd);
121         }
122
123         if (__pGSource != null)
124         {
125                 g_source_destroy(__pGSource);
126                 g_source_unref(__pGSource);
127                 __pGSource = null;
128         }
129
130         if (__pGIOChannel != null)
131         {
132                 g_io_channel_unref(__pGIOChannel);
133                 __pGIOChannel = null;
134         }
135
136         return r;
137 }
138
139 _FileEventDispatcher::~_FileEventDispatcher(void)
140 {
141         if (__inotifyFd != -1)
142         {
143                 close(__inotifyFd);
144         }
145
146         if (__pGSource != null)
147         {
148                 g_source_destroy(__pGSource);
149                 g_source_unref(__pGSource);
150         }
151
152         if (__pGIOChannel != null)
153         {
154                 g_io_channel_unref(__pGIOChannel);
155         }
156
157         delete __pWatchList;
158 }
159
160 void
161 _FileEventDispatcher::InitSingleton(void)
162 {
163         _FileEventDispatcher* pInst = new (std::nothrow) _FileEventDispatcher();
164         SysTryReturnVoidResult(NID_IO, pInst != null, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] The memory is insufficient.");
165
166         result r = pInst->Construct();
167         SysTryCatch(NID_IO, !IsFailed(r), , r, "[%s] Propagating to caller...", GetErrorMessage(r));
168
169         __pFileEventDispatcherInstance = pInst;
170
171         std::atexit(DestroySingleton);
172         return;
173
174 CATCH:
175         delete pInst;
176 }
177
178 void
179 _FileEventDispatcher::DestroySingleton(void)
180 {
181         delete __pFileEventDispatcherInstance;
182 }
183
184 _FileEventDispatcher*
185 _FileEventDispatcher::GetInstance(void)
186 {
187         static pthread_once_t onceBlock = PTHREAD_ONCE_INIT;
188
189         if (__pFileEventDispatcherInstance == null)
190         {
191                 ClearLastResult();
192                 pthread_once(&onceBlock, InitSingleton);
193                 result r = GetLastResult();
194                 if (IsFailed(r))
195                 {
196                         onceBlock = PTHREAD_ONCE_INIT;
197                         SysPropagate(NID_IO, r);
198                 }
199         }
200
201         return __pFileEventDispatcherInstance;
202 }
203
204 result
205 _FileEventDispatcher::SendEvent(void)
206 {
207         SysTryReturnResult(NID_IO, __inotifyFd != -1, E_IO, "inotify fd is invalid.");
208         SysLog(NID_IO, "inotify fd: %d", __inotifyFd);
209
210         char buffer[INOTIFY_BUFFER_LEN + 1] = { 0, };
211         ssize_t length = read(__inotifyFd, buffer, INOTIFY_BUFFER_LEN);
212         SysTryReturnResult(NID_IO, length >= 0 && length < (long)INOTIFY_BUFFER_LEN, E_IO,
213                         "Failed to read inotify buffer. errno: %d (%s)", errno, strerror(errno));
214
215         unsigned long iter = 0;
216         while (iter < static_cast< unsigned long >(length))
217         {
218                 struct inotify_event* pEvent = (struct inotify_event*)&buffer[iter];
219                 SysTryReturnResult(NID_IO, pEvent != null, E_IO, "inotify event info not found.");
220
221                 Integer wd(pEvent->wd);
222                 _FileEventInfo* pFileEventInfo = GetFileEventInfo(wd);
223                 SysLog(NID_IO, "wd: %d, _FileEventInfo: 0x%x, event mask: 0x%x", pEvent->wd, pFileEventInfo, pEvent->mask);
224                 if (pFileEventInfo == null)
225                 {
226                         SysLog(NID_IO, "There is no watch path.");
227                 }
228                 else
229                 {
230                         unsigned long event = (unsigned long)pEvent->mask;
231                         unsigned int eventId = pEvent->cookie;
232
233                         String path;
234                         if (pEvent->len)
235                         {
236                                 // TODO: Use full path in Tizen 3.0
237                                 path.Append(pEvent->name);
238                         }
239                         else
240                         {
241                                 path.Append(pFileEventInfo->path);
242                         }
243                         // TODO: Resolve path
244                         SysLog(NID_IO, "watch path: %ls", path.GetPointer());
245
246                         _FileEventArg* pEventArg= new (std::nothrow) _FileEventArg(event, path, eventId);
247                         if (pEventArg != null)
248                         {
249                                 _Event* pFileEvent = pFileEventInfo->pFileEventManagerImpl->GetEvent();
250                                 if (pFileEvent != null)
251                                 {
252                                         SysLog(NID_IO, "Fire async event, _FileEventManagerImpl: 0x%x, _Event: 0x%x, event mask: 0x%x, eventId: %d",
253                                                         pFileEventInfo->pFileEventManagerImpl, pFileEvent, event, eventId);
254                                         pFileEvent->FireAsync(*pEventArg);
255                                 }
256                         }
257                 }
258
259                 iter += sizeof(struct inotify_event) + pEvent->len;
260         }
261
262         return E_SUCCESS;
263 }
264
265 gboolean
266 _FileEventDispatcher::OnFileEventOccurred(GIOChannel* source, GIOCondition condition, gpointer data)
267 {
268         SysLog(NID_IO, "channel: 0x%x, condition: %d", source, (int)condition);
269
270         if (condition & G_IO_IN)
271         {
272                 _FileEventDispatcher* pDispatcher = static_cast< _FileEventDispatcher* >(data);
273                 if (pDispatcher != null)
274                 {
275                         pDispatcher->SendEvent();
276                 }
277         }
278
279         return true;
280 }
281
282 result
283 _FileEventDispatcher::AddFileEventInfo(Integer* pWatchDescription, _FileEventInfo* pFileEventInfo)
284 {
285         SysLog(NID_IO, "Add event info, wd: %d, _FileEventInfo: 0x%x, _FileEventManagerImpl: 0x%x",
286                         pWatchDescription->ToInt(), pFileEventInfo, pFileEventInfo->pFileEventManagerImpl);
287         return __pWatchList->Add(pWatchDescription, pFileEventInfo);
288 }
289
290 _FileEventInfo*
291 _FileEventDispatcher::GetFileEventInfo(Integer& watchDescription)
292 {
293         SysLog(NID_IO, "Get event info, wd: %d", watchDescription.ToInt());
294         return dynamic_cast< _FileEventInfo* >(__pWatchList->GetValue(watchDescription));
295 }
296
297 void
298 _FileEventDispatcher::RemoveFileEventInfo(Integer& watchDescription)
299 {
300         SysLog(NID_IO, "Remove event info, wd: %d", watchDescription.ToInt());
301         __pWatchList->Remove(watchDescription);
302 }
303
304 }} // Tizen::Io
305