Add debug log of resource lock
[platform/core/multimedia/mm-resource-manager.git] / src / lib / mm_resource_manager_priv.c
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
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 #include <stdlib.h>
18 #include <inttypes.h>
19 #include <glib.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <sys/stat.h>
23
24 #include "common/mm_resource_manager_utils.h"
25 #include "lib/mm_resource_manager_priv.h"
26 #include "common/mm_resource_manager_dbus.h"
27
28 static GMutex handles_lock;
29 static GPtrArray *handles;
30
31 #define MM_RESOURCE_MANAGER(x) ((mm_resource_manager_s *) (x))
32 #define MM_RESOURCE_MANAGER_CHECK(x) \
33         MM_RM_UNLOCK_RETVM_IF(!__check_rm_handle(x), handles_lock, \
34                 MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER, \
35                 "Invalid resource manager handle %p", x)
36 #define MM_RESOURCE_MANAGER_RESERVED_RES_ARRAY_SIZE 16   /* preallocated size */
37 #define MM_RESOURCE_MANAGER_RESERVED_HANDLE_ARRAY_SIZE 8 /* preallocated size */
38 #define MM_RESOURCE_MANAGER_RES_NOT_FOUND -1
39
40
41
42 typedef enum {
43         MM_RESOURCE_MANAGER_RES_STATE_FOR_ACQUIRE,   /* uncommitted */
44         MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED,      /* committed   */
45         MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE    /* uncommitted */
46 } mm_resource_manager_res_state_e;
47
48 typedef struct {
49         void *cb;
50         void *user_data;
51 } mm_resource_manager_cb_s;
52
53 typedef struct {
54         mm_resource_manager_res_type_e type;
55         mm_resource_manager_res_volume volume;
56         mm_resource_manager_res_state_e state;
57         gboolean is_acquire_failed;
58 } mm_resource_manager_res_s;
59 typedef mm_resource_manager_res_s *mm_resource_manager_res_p;
60
61 typedef struct {
62         mm_resource_manager_id id;
63
64         GPtrArray *resources;
65
66         mm_resource_manager_cb_s release_cb;
67         mm_resource_manager_cb_s status_cb;
68
69         GMutex resources_lock;
70
71         mm_resource_manager_res_volume __max_resource_volumes
72                 [MM_RESOURCE_MANAGER_RES_TYPE_MAX];
73         mm_resource_manager_res_volume __condition_volumes
74                 [MM_RESOURCE_MANAGER_RES_TYPE_MAX]
75                 [MM_RESOURCE_MANAGER_RES_TYPE_COND_MAX];
76
77         MMResourceManager *dbus_proxy;
78
79         GMainContext *dispatcher_context;
80         GMainLoop *dispatcher_loop;
81         GThread *dispatcher_thread;
82 } mm_resource_manager_s;
83
84 static void __init_lib() __attribute__((constructor));
85 static void __deinit_lib() __attribute__((destructor));
86 static int __check_resource(mm_resource_manager_s *rm,
87                 mm_resource_manager_res_type_e type,
88                 mm_resource_manager_res_volume volume);
89 static int __create_resource(mm_resource_manager_s *rm,
90                 mm_resource_manager_res_type_e type,
91                 mm_resource_manager_res_volume volume, mm_resource_manager_res_p *res);
92 static void __destroy_resource(void *res);
93 static int __get_resource_index(mm_resource_manager_s *rm,
94                 mm_resource_manager_res_p res);
95 static gboolean __mark_resource_for_release(GPtrArray *resources, int index,
96                 mm_resource_manager_res_p resource);  /* FALSE if resource is destroyed */
97 static void __send_release_cb_sync(mm_resource_manager_id id);
98 static void __mm_resource_manager_release_callback(mm_resource_manager_s *handle,
99                 mm_resource_manager_id id,
100                 mm_resource_manager_res_type_e type,
101                 mm_resource_manager_res_volume volume);
102 static void __mm_resource_manager_status_callback(mm_resource_manager_s *handle,
103                 mm_resource_manager_status_e status);
104 static void __mm_resource_handles_lock(void);
105 static void __mm_resource_handles_unlock(void);
106 static void __mm_resources_lock(mm_resource_manager_s *h);
107 static void __mm_resources_unlock(mm_resource_manager_s *h);
108
109
110 static int __dbus_init(mm_resource_manager_s *handle);
111 static int __dbus_deinit(mm_resource_manager_s *handle);
112 static int __dbus_init_conf(mm_resource_manager_s *handle);
113 static gboolean __check_rm_handle(mm_resource_manager_s *handle);
114 static int __dbus_create(mm_resource_manager_s *handle,
115                 mm_resource_manager_app_class_e app_class);
116 static int __dbus_destroy(mm_resource_manager_s *handle);
117 static int __dbus_commit(mm_resource_manager_s *handle);
118 static void __dbus_release_callback(MMResourceManager *object, guint64 arg_id,
119                 gint arg_resource_type, gint arg_volume);
120 static void __dbus_status_callback(MMResourceManager *object, gint arg_status);
121 static gpointer __dispatcher_thread(gpointer user_data);
122 static void __destroy_dispatcher(mm_resource_manager_s *handle);
123
124
125 int _mm_resource_manager_create(mm_resource_manager_app_class_e app_class,
126                 mm_resource_manager_release_cb cb, void *cb_data,
127                 mm_resource_manager_h *rm)
128 {
129         mm_resource_manager_s *handle = *rm = NULL;
130         int ret;
131         GError *error = NULL;
132
133         handle = (mm_resource_manager_s *) calloc(1, sizeof(mm_resource_manager_s));
134         MM_RM_RETVM_IF(NULL == handle, MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION,
135                         "Error allocating memory for Handle");
136
137         handle->dispatcher_context = g_main_context_new();
138         if (!handle->dispatcher_context) {
139                 free(handle);
140                 MM_RM_ERROR("g_main_context_new failed");
141                 return MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION;
142         }
143         MM_RM_INFO("new main context %p", handle->dispatcher_context);
144
145         handle->dispatcher_loop = g_main_loop_new(handle->dispatcher_context, FALSE);
146         if (!handle->dispatcher_loop) {
147                 g_main_context_unref(handle->dispatcher_context);
148                 free(handle);
149                 MM_RM_ERROR("g_main_loop_new failed");
150                 return MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION;
151         }
152         MM_RM_INFO("new main loop %p", handle->dispatcher_loop);
153
154         ret = __dbus_init(handle);
155         if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
156                 __destroy_dispatcher(handle);
157                 free(handle);
158                 MM_RM_ERROR("Error initializing dbus client");
159                 return ret;
160         }
161
162         handle->dispatcher_thread = g_thread_try_new("dispatcher_thread", __dispatcher_thread, handle->dispatcher_loop, &error);
163         if (!handle->dispatcher_thread) {
164                 if (error) {
165                         MM_RM_ERROR("dispatcher_thread creation failed : %s", error->message);
166                         g_error_free(error);
167                 }
168                 __destroy_dispatcher(handle);
169                 free(handle);
170                 return MM_RESOURCE_MANAGER_ERROR_NOT_ENOUGH;
171         }
172         MM_RM_INFO("new dispatcher thread %p", handle->dispatcher_thread);
173
174         ret = __dbus_init_conf(handle);
175         if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
176                 __destroy_dispatcher(handle);
177                 free(handle);
178                 MM_RM_ERROR("Configuration cannot be requested");
179                 return ret;
180         }
181
182         ret = __dbus_create(handle, app_class);
183         if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
184                 __destroy_dispatcher(handle);
185                 free(handle);
186                 MM_RM_ERROR("Dbus create request failed");
187                 return ret;
188         }
189
190         handle->release_cb.cb = cb;
191         handle->release_cb.user_data = cb_data;
192
193         handle->resources = g_ptr_array_new_full(
194                         MM_RESOURCE_MANAGER_RESERVED_RES_ARRAY_SIZE, __destroy_resource);
195
196         g_mutex_init(&handle->resources_lock);
197
198         *rm = (mm_resource_manager_h *) handle;
199         __mm_resource_handles_lock();
200         g_ptr_array_add(handles, handle);
201
202         MM_RM_INFO("Resource manager #%"PRIu64" is created", _mm_rm_hash64(handle->id));
203         __mm_resource_handles_unlock();
204
205         return MM_RESOURCE_MANAGER_ERROR_NONE;
206 }
207
208 int _mm_resource_manager_destroy(mm_resource_manager_h rm)
209 {
210         mm_resource_manager_s *handle = MM_RESOURCE_MANAGER(rm);
211         mm_resource_manager_id id;
212         int ret = MM_RESOURCE_MANAGER_ERROR_NONE;
213
214         __mm_resource_handles_lock();
215         MM_RESOURCE_MANAGER_CHECK(handle);
216         g_ptr_array_remove_fast(handles, handle);
217         __mm_resource_handles_unlock();
218
219         id = handle->id;
220
221         MM_RM_INFO("Resource manager #%"PRIu64" would be destroyed", _mm_rm_hash64(id));
222
223         __mm_resources_lock(handle);
224         ret = __dbus_destroy(handle);
225         if (ret != MM_RESOURCE_MANAGER_ERROR_NONE)
226                 MM_RM_ERROR("Dbus destroy request failed 0x%x", ret);
227
228         __destroy_dispatcher(handle);
229
230         g_ptr_array_free(handle->resources, TRUE);
231         __mm_resources_unlock(handle);
232
233         g_mutex_clear(&handle->resources_lock);
234         free(handle);
235
236         if (ret == MM_RESOURCE_MANAGER_ERROR_NONE)
237                 MM_RM_INFO("Resource manager #%"PRIu64" is destroyed", _mm_rm_hash64(id));
238
239         return ret;
240 }
241
242 int _mm_resource_manager_mark_for_acquire(
243                 mm_resource_manager_h rm, mm_resource_manager_res_type_e type,
244                 mm_resource_manager_res_volume volume,
245                 mm_resource_manager_res_h *resource_h)
246 {
247         int ret = MM_RESOURCE_MANAGER_ERROR_NONE;
248         mm_resource_manager_s *handle = MM_RESOURCE_MANAGER(rm);
249         mm_resource_manager_res_p resource;
250
251         __mm_resource_handles_lock();
252         MM_RESOURCE_MANAGER_CHECK(handle);
253         __mm_resources_lock(handle);
254         __mm_resource_handles_unlock();
255
256         ret = __create_resource(handle, type, volume, &resource);
257         MM_RM_UNLOCK_RETVM_IF(ret != MM_RESOURCE_MANAGER_ERROR_NONE,
258                         handle->resources_lock, ret, "Resource cannot be created");
259         g_ptr_array_add(handle->resources, resource);
260
261         *resource_h = resource;
262
263         MM_RM_INFO("Resource %p of type %d with volume %d is marked for acquire in "
264                         "resource manager #%"PRIu64, *resource_h, type, volume,
265                         _mm_rm_hash64(handle->id));
266         __mm_resources_unlock(handle);
267
268         return ret;
269 }
270
271 int _mm_resource_manager_resize_marked(mm_resource_manager_h rm,
272                 mm_resource_manager_res_h resource_h,
273                 mm_resource_manager_res_volume new_volume)
274 {
275         mm_resource_manager_s *handle = MM_RESOURCE_MANAGER(rm);
276         mm_resource_manager_res_p resource = (mm_resource_manager_res_p) resource_h;
277         mm_resource_manager_res_p tmp_resource;
278         mm_resource_manager_res_volume add_volume;
279         int i;
280         int ret = MM_RESOURCE_MANAGER_ERROR_NONE;
281
282         __mm_resource_handles_lock();
283         MM_RESOURCE_MANAGER_CHECK(handle);
284         __mm_resources_lock(handle);
285         __mm_resource_handles_unlock();
286
287         i = __get_resource_index(handle, resource);
288         MM_RM_UNLOCK_RETVM_IF(i == MM_RESOURCE_MANAGER_RES_NOT_FOUND,
289                         handle->resources_lock,
290                         MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER,
291                         "Invalid resource handle");
292         if (new_volume == resource->volume) {
293                 __mm_resources_unlock(handle);
294                 MM_RM_DEBUG("New volume equals the old. Resize is not needed.");
295                 return ret;
296         }
297
298         add_volume = resource->volume != MM_RESOURCE_MANAGER_RES_VOLUME_FULL &&
299                         new_volume != MM_RESOURCE_MANAGER_RES_VOLUME_FULL &&
300                         resource->volume < new_volume ? (new_volume - resource->volume) : 0;
301
302         switch (resource->state) {
303         case MM_RESOURCE_MANAGER_RES_STATE_FOR_ACQUIRE:
304                 if (add_volume > 0) {
305                         ret = __check_resource(handle, resource->type, add_volume);
306                         MM_RM_UNLOCK_RETVM_IF(ret != MM_RESOURCE_MANAGER_ERROR_NONE,
307                                 handle->resources_lock, ret, "Resource check failed");
308                 }
309                 resource->volume = new_volume;
310                 break;
311
312         case MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE:
313         case MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED:
314                 ret = __create_resource(handle, resource->type, add_volume, &tmp_resource);
315                 MM_RM_UNLOCK_RETVM_IF(ret != MM_RESOURCE_MANAGER_ERROR_NONE,
316                                 handle->resources_lock, ret, "Resource cannot be created");
317
318                 tmp_resource->volume = resource->volume;
319                 tmp_resource->state = MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE;
320
321                 g_ptr_array_add(handle->resources, resource);
322                 resource->state = MM_RESOURCE_MANAGER_RES_STATE_FOR_ACQUIRE;
323                 resource->volume = new_volume;
324
325                 handle->resources->pdata[i] = tmp_resource;
326                 break;
327         }
328
329         MM_RM_INFO("Resource %p is resized for acquire in resource manager #%"PRIu64,
330                         resource_h, _mm_rm_hash64(handle->id));
331         __mm_resources_unlock(handle);
332
333         return ret;
334 }
335
336 int _mm_resource_manager_mark_for_release(mm_resource_manager_h rm,
337                 mm_resource_manager_res_h resource_h)
338 {
339         mm_resource_manager_s *handle = MM_RESOURCE_MANAGER(rm);
340         mm_resource_manager_res_p resource = (mm_resource_manager_res_p) resource_h;
341         int i;
342
343         __mm_resource_handles_lock();
344         MM_RESOURCE_MANAGER_CHECK(handle);
345         __mm_resources_lock(handle);
346         __mm_resource_handles_unlock();
347
348         i = __get_resource_index(handle, resource);
349         MM_RM_UNLOCK_RETVM_IF(i == MM_RESOURCE_MANAGER_RES_NOT_FOUND,
350                         handle->resources_lock,
351                         MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER,
352                         "Invalid resource handle");
353
354         __mark_resource_for_release(handle->resources, i, resource);
355
356         MM_RM_INFO("Resource %p is marked for release in resource manager #%"PRIu64,
357                         resource_h, _mm_rm_hash64(handle->id));
358         __mm_resources_unlock(handle);
359
360         return MM_RESOURCE_MANAGER_ERROR_NONE;
361 }
362
363 int _mm_resource_manager_mark_all_for_release(mm_resource_manager_h rm)
364 {
365         mm_resource_manager_s *handle = MM_RESOURCE_MANAGER(rm);
366         int i;
367
368         __mm_resource_handles_lock();
369         MM_RESOURCE_MANAGER_CHECK(handle);
370         __mm_resources_lock(handle);
371         __mm_resource_handles_unlock();
372
373         for (i = 0; i < handle->resources->len; i++) {
374                 if (!__mark_resource_for_release(handle->resources, i,
375                                 (mm_resource_manager_res_p) handle->resources->pdata[i]))
376                         i--;
377         }
378
379         MM_RM_INFO("All resources are marked for release in resource manager #%"PRIu64,
380                         _mm_rm_hash64(handle->id));
381         __mm_resources_unlock(handle);
382
383         return MM_RESOURCE_MANAGER_ERROR_NONE;
384 }
385
386 int _mm_resource_manager_get_resource_info(mm_resource_manager_h rm,
387                 mm_resource_manager_res_h resource_h,
388                 mm_resource_manager_res_info_s *info)
389 {
390         mm_resource_manager_s *handle = MM_RESOURCE_MANAGER(rm);
391         mm_resource_manager_res_p resource = (mm_resource_manager_res_p) resource_h;
392         int i;
393
394         __mm_resource_handles_lock();
395         MM_RESOURCE_MANAGER_CHECK(handle);
396         __mm_resources_lock(handle);
397         __mm_resource_handles_unlock();
398
399         i = __get_resource_index(handle, resource);
400         MM_RM_UNLOCK_RETVM_IF(i == MM_RESOURCE_MANAGER_RES_NOT_FOUND,
401                         handle->resources_lock,
402                         MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER,
403                         "Invalid resource handle");
404
405         info->type = resource->type;
406         info->volume = resource->volume;
407         info->is_acquire_failed = resource->is_acquire_failed;
408
409         MM_RM_INFO("Info structure of resource %p in resource manager #%"PRIu64" is filled",
410                         resource_h, _mm_rm_hash64(handle->id));
411         __mm_resources_unlock(handle);
412
413         return MM_RESOURCE_MANAGER_ERROR_NONE;
414 }
415
416 int _mm_resource_manager_commit(mm_resource_manager_h rm)
417 {
418         mm_resource_manager_s *handle = MM_RESOURCE_MANAGER(rm);
419         int ret;
420
421         __mm_resource_handles_lock();
422         MM_RESOURCE_MANAGER_CHECK(handle);
423         __mm_resources_lock(handle);
424         __mm_resource_handles_unlock();
425
426         ret = __dbus_commit(handle);
427         if (ret == MM_RESOURCE_MANAGER_ERROR_NONE)
428                 MM_RM_INFO("Changes in resource manager #%"PRIu64" have been committed successfully",
429                                 _mm_rm_hash64(handle->id));
430         else
431                 MM_RM_ERROR("Dbus commit request failed");
432         __mm_resources_unlock(handle);
433
434         return ret;
435 }
436
437 int _mm_resource_manager_set_status_cb(mm_resource_manager_h rm,
438                 mm_resource_manager_status_cb cb, void *user_data)
439 {
440         mm_resource_manager_s *handle = MM_RESOURCE_MANAGER(rm);
441
442         __mm_resource_handles_lock();
443         MM_RESOURCE_MANAGER_CHECK(handle);
444         __mm_resources_lock(handle);
445         __mm_resource_handles_unlock();
446
447         handle->status_cb.cb = cb;
448         handle->status_cb.user_data = user_data;
449         __mm_resources_unlock(handle);
450
451         MM_RM_INFO("Status callback %p in resource manager #%"PRIu64" is set", cb,
452                         _mm_rm_hash64(handle->id));
453
454         return MM_RESOURCE_MANAGER_ERROR_NONE;
455 }
456
457 int _mm_resource_manager_get_res_type_max_volume(mm_resource_manager_h rm,
458                 mm_resource_manager_res_type_e type,
459                 mm_resource_manager_res_volume *max_volume)
460 {
461         mm_resource_manager_s *handle = MM_RESOURCE_MANAGER(rm);
462
463         if (handle->__max_resource_volumes[type] == MM_RESOURCE_MANAGER_NO_RES) {
464                 MM_RM_DEBUG("No resource for the platform");
465                 return MM_RESOURCE_MANAGER_ERROR_NOT_SUPPORTED;
466         } else {
467                 *max_volume = handle->__max_resource_volumes[type];
468                 return MM_RESOURCE_MANAGER_ERROR_NONE;
469         }
470 }
471
472 int _mm_resource_manager_get_res_type_volume(mm_resource_manager_h rm,
473                 mm_resource_manager_res_type_e type,
474                 mm_resource_manager_res_type_cond_e condition,
475                 mm_resource_manager_res_volume *volume)
476 {
477         mm_resource_manager_s *handle = MM_RESOURCE_MANAGER(rm);
478
479         if (handle->__condition_volumes[type][condition] == MM_RESOURCE_MANAGER_NO_RES) {
480                 MM_RM_DEBUG("No pair (resource,condition) for the platform");
481                 return MM_RESOURCE_MANAGER_ERROR_NOT_SUPPORTED;
482         } else {
483                 *volume = handle->__condition_volumes[type][condition];
484                 return MM_RESOURCE_MANAGER_ERROR_NONE;
485         }
486 }
487
488 void __mm_resource_manager_release_callback(mm_resource_manager_s *handle,
489                 mm_resource_manager_id id,
490                 mm_resource_manager_res_type_e type,
491                 mm_resource_manager_res_volume volume)
492 {
493         mm_resource_manager_res_s *resource;
494         mm_resource_manager_id handle_id;
495         gboolean release_all = FALSE;
496         int j;
497
498         MM_RM_DEBUG("Release callback is emitted for %s of volume %d in RM #%"PRIu64,
499                         _mm_resource_manager_get_res_str(type), volume, id);
500
501         handle_id = handle->id;
502         MM_RM_HASH64(handle_id);
503         if (handle_id == id) {
504                 __mm_resources_lock(handle);
505                 __mm_resource_handles_unlock();
506                 for (j = 0; j < handle->resources->len; j++) {
507                         resource = (mm_resource_manager_res_s*)handle->resources->pdata[j];
508                         if (resource->type == type && resource->volume == volume) {
509
510                                 release_all = ((mm_resource_manager_release_cb)
511                                                 handle->release_cb.cb)(
512                                                 handle, resource, handle->release_cb.user_data);
513
514                                 __send_release_cb_sync(handle->id);
515
516                                 g_ptr_array_remove_index_fast(handle->resources, j);
517                                 break;
518                         }
519                 }
520                 __mm_resources_unlock(handle);
521
522                 if (release_all) {
523                         if (_mm_resource_manager_mark_all_for_release(handle) == MM_RESOURCE_MANAGER_ERROR_NONE
524                                 && _mm_resource_manager_commit(handle) == MM_RESOURCE_MANAGER_ERROR_NONE) {
525                                 MM_RM_DEBUG("All resources are released after release cb");
526                         } else {
527                                 MM_RM_ERROR("Resources cannot be released after release cb");
528                         }
529                 }
530         } else {
531                 __mm_resource_handles_unlock();
532         }
533 }
534
535 void __mm_resource_manager_status_callback(mm_resource_manager_s *handle,
536                 mm_resource_manager_status_e status)
537 {
538         __mm_resources_lock(handle);
539         __mm_resource_handles_unlock();
540         if (handle->status_cb.cb) {
541                 ((mm_resource_manager_status_cb)handle->status_cb.cb)(handle, status,
542                                 handle->status_cb.user_data);
543         }
544         __mm_resources_unlock(handle);
545 }
546
547 static void __mm_resource_handles_lock(void)
548 {
549         MM_RM_INFO("handles lock");
550         g_mutex_lock(&handles_lock);
551 }
552
553 static void __mm_resource_handles_unlock(void)
554 {
555         g_mutex_unlock(&handles_lock);
556         MM_RM_INFO("handles unlocked");
557 }
558
559 static void __mm_resources_lock(mm_resource_manager_s *h)
560 {
561         MM_RM_RETM_IF(!h, "handle is NULL");
562         MM_RM_INFO("[handle %p]resources lock", h);
563         g_mutex_lock(&h->resources_lock);
564 }
565
566 static void __mm_resources_unlock(mm_resource_manager_s *h)
567 {
568         MM_RM_RETM_IF(!h, "handle is NULL");
569         g_mutex_unlock(&h->resources_lock);
570         MM_RM_INFO("[handle %p]resources unlocked", h);
571 }
572
573 static int __check_resource(mm_resource_manager_s *rm,
574                 mm_resource_manager_res_type_e type,
575                 mm_resource_manager_res_volume volume)
576 {
577         mm_resource_manager_res_volume remaining_local_volume =
578                         rm->__max_resource_volumes[type];
579         mm_resource_manager_res_p i_res;
580         int i;
581
582         MM_RM_RETVM_IF(remaining_local_volume == MM_RESOURCE_MANAGER_NO_RES,
583                         MM_RESOURCE_MANAGER_ERROR_NOT_SUPPORTED,
584                         "No resource for the platform");
585
586         if (volume > 0) {
587                 MM_RM_RETVM_IF(remaining_local_volume < volume,
588                                 MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION,
589                                 "Requested volume %d exceeds max value %d", volume,
590                                 remaining_local_volume);
591                 for (i = 0; i < rm->resources->len; i++) {
592                         i_res = (mm_resource_manager_res_p) rm->resources->pdata[i];
593                         if (i_res->type == type &&
594                                         i_res->state != MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE &&
595                                         (i_res->volume == MM_RESOURCE_MANAGER_RES_VOLUME_FULL ||
596                                                         (remaining_local_volume -= i_res->volume) < volume)) {
597                                 MM_RM_ERROR("Requested volume %d exceeds remaining local volume %d",
598                                                 volume,
599                                                 i_res->volume == MM_RESOURCE_MANAGER_RES_VOLUME_FULL ?
600                                                 0 : remaining_local_volume);
601                                 return MM_RESOURCE_MANAGER_ERROR_NOT_ENOUGH;
602                         }
603                 }
604         }
605
606         return MM_RESOURCE_MANAGER_ERROR_NONE;
607 }
608
609 static int __create_resource(mm_resource_manager_s *rm,
610                 mm_resource_manager_res_type_e type,
611                 mm_resource_manager_res_volume volume, mm_resource_manager_res_p *res)
612 {
613         int ret;
614
615         MM_RM_RETVM_IF(res == NULL, MM_RESOURCE_MANAGER_ERROR_INVALID_PARAMETER,
616                         "NULL pointer");
617         ret = __check_resource(rm, type, volume);
618         if (ret != MM_RESOURCE_MANAGER_ERROR_NONE)
619                 return ret;
620
621         *res = g_new0(mm_resource_manager_res_s, 1);
622         (*res)->type = type;
623         (*res)->volume = volume;
624
625         return MM_RESOURCE_MANAGER_ERROR_NONE;
626 }
627
628 static void __destroy_resource(void *res)
629 {
630         g_free(res);
631 }
632
633 static int __get_resource_index(mm_resource_manager_s *rm,
634                 mm_resource_manager_res_p res)
635 {
636         int i;
637
638         for (i = 0; i < rm->resources->len; i++)
639                 if (rm->resources->pdata[i] == (gpointer) res)
640                         return i;
641
642         return MM_RESOURCE_MANAGER_RES_NOT_FOUND;
643 }
644
645 static gboolean __mark_resource_for_release(GPtrArray *resources, int index,
646                 mm_resource_manager_res_p resource)
647 {
648         switch (resource->state) {
649         case MM_RESOURCE_MANAGER_RES_STATE_FOR_ACQUIRE:
650                 g_ptr_array_remove_index_fast(resources, index);
651                 MM_RM_DEBUG("Resource %p is removed implicitly", resource);
652                 return FALSE;
653         case MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED:
654                 resource->state = MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE;
655                 break;
656         case MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE:
657                 MM_RM_DEBUG("Resource %p is already marked", resource);
658                 break;
659         }
660         return TRUE;
661 }
662
663
664 static gboolean __check_rm_handle(mm_resource_manager_s *handle)
665 {
666         int i;
667
668         for (i = 0; i < handles->len; i++)
669                 if (g_ptr_array_index(handles, i) == handle)
670                         return TRUE;
671         return FALSE;
672 }
673
674 static void __send_release_cb_sync(mm_resource_manager_id id)
675 {
676         int sync_fd;
677
678         sync_fd = open(RELEASE_CB_SYNC_PATH, O_WRONLY);
679         MM_RM_RETM_IF(sync_fd == -1, "Sync FIFO cannot be opened");
680
681         if (write(sync_fd, &id, sizeof(id)) == sizeof(id))
682                 MM_RM_DEBUG("Sync message is sent successfully");
683         else
684                 MM_RM_ERROR("Sync message cannot be sent");
685
686         close(sync_fd);
687 }
688
689 static void __init_lib()
690 {
691         handles = g_ptr_array_sized_new(
692                         MM_RESOURCE_MANAGER_RESERVED_HANDLE_ARRAY_SIZE);
693         MM_RM_RETM_IF(handles == NULL, "API lib cannot be initialized");
694
695         MM_RM_INFO("API lib is loaded");
696 }
697
698 static void __deinit_lib()
699 {
700         if (handles->len > 0) {
701                 MM_RM_DEBUG("Handles array [%d] is not empty. It will be cleaned now.", handles->len);
702                 while (handles->len > 0)
703                         _mm_resource_manager_destroy(handles->pdata[0]);
704         }
705
706         g_ptr_array_free(handles, TRUE);
707
708         MM_RM_INFO("API lib is unloaded");
709 }
710
711 static int __dbus_init(mm_resource_manager_s *handle)
712 {
713         GError *error = NULL;
714
715         MM_RM_RETVM_IF(handle->dbus_proxy != NULL,
716                         MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION,
717                         "Dbus proxy is not NULL");
718
719         g_main_context_push_thread_default(handle->dispatcher_context);
720         handle->dbus_proxy = mmresource_manager_proxy_new_for_bus_sync(
721                         G_BUS_TYPE_SYSTEM, 0, RMD_GDBUS_NAME, RMD_GDBUS_PATH, NULL, &error);
722         g_main_context_pop_thread_default(handle->dispatcher_context);
723         MM_RM_RET_IF_GERR(error, "Dbus proxy cannot be created");
724
725         if (g_signal_connect(handle->dbus_proxy, "release_callback",
726                         (GCallback)__dbus_release_callback, NULL) < 1 ||
727                         g_signal_connect(handle->dbus_proxy, "status_callback",
728                         (GCallback)__dbus_status_callback, NULL) < 1) {
729
730                 g_object_unref(handle->dbus_proxy);
731                 handle->dbus_proxy = NULL;
732                 MM_RM_ERROR("Release or status callback signals cannot be connected");
733
734                 return MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION;
735         }
736
737         return MM_RESOURCE_MANAGER_ERROR_NONE;
738 }
739
740 static int __dbus_deinit(mm_resource_manager_s *handle)
741 {
742         MM_RM_RETVM_IF(handle->dbus_proxy == NULL,
743                         MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION, "Dbus proxy is NULL");
744         g_object_unref(handle->dbus_proxy);
745         handle->dbus_proxy = NULL;
746         return MM_RESOURCE_MANAGER_ERROR_NONE;
747 }
748
749 static int __dbus_init_conf(mm_resource_manager_s *handle)
750 {
751         int i, j;
752         GError *error = NULL;
753         int rm_error = MM_RESOURCE_MANAGER_ERROR_NONE;
754         GVariant *max_volume = NULL;
755         GVariant *cond_volume = NULL;
756         GVariant *tmp;
757         GVariantIter volume_iter;
758         GVariantIter cond_volume_iter;
759
760         mmresource_manager_call_conf_sync(handle->dbus_proxy, &rm_error,
761                         &max_volume, &cond_volume, NULL, &error);
762         MM_RM_RET_IF_GERR(error, "DBus conf msg cannot be sent");
763
764         MM_RM_RETVM_IF(max_volume == NULL || cond_volume == NULL,
765                         MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION,
766                         "Variant data are empty");
767
768         if (g_variant_iter_init(&volume_iter, max_volume) ==
769                         MM_RESOURCE_MANAGER_RES_TYPE_MAX) {
770                 for (i = 0; g_variant_iter_next(&volume_iter, "i",
771                                 &handle->__max_resource_volumes[i]); i++);
772                 g_variant_unref(max_volume);
773         } else {
774                 g_variant_unref(max_volume);
775                 g_variant_unref(cond_volume);
776                 MM_RM_ERROR("Wrong max volume array size");
777                 return MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION;
778         }
779
780         if (g_variant_iter_init(&volume_iter, cond_volume) ==
781                         MM_RESOURCE_MANAGER_RES_TYPE_MAX) {
782                 for (i = 0; (tmp = g_variant_iter_next_value(&volume_iter)) != NULL;
783                                 i++) {
784                         if (g_variant_iter_init(&cond_volume_iter, tmp) ==
785                                         MM_RESOURCE_MANAGER_RES_TYPE_COND_MAX) {
786                                 for (j = 0; g_variant_iter_next(&cond_volume_iter, "i",
787                                                 &handle->__condition_volumes[i][j]); j++);
788                                 g_variant_unref(tmp);
789                         } else {
790                                 g_variant_unref(tmp);
791                                 MM_RM_ERROR("Wrong condition volume array size");
792                                 rm_error = MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION;
793                                 break;
794                         }
795                 }
796                 g_variant_unref(cond_volume);
797         } else {
798                 g_variant_unref(cond_volume);
799                 MM_RM_ERROR("Wrong condition volume array size");
800                 return MM_RESOURCE_MANAGER_ERROR_INVALID_OPERATION;
801         }
802
803         return rm_error;
804 }
805
806 static int __dbus_create(mm_resource_manager_s *handle,
807                 mm_resource_manager_app_class_e app_class)
808 {
809         GError *error = NULL;
810         int rm_error = MM_RESOURCE_MANAGER_ERROR_NONE;
811
812         mmresource_manager_call_create_sync(handle->dbus_proxy, app_class, &handle->id,
813                         &rm_error, NULL, &error);
814         MM_RM_RET_IF_GERR(error, "DBus create msg cannot be sent");
815
816         MM_RM_DEBUG("Create returned id - #%"PRIu64", error - %d",
817                         _mm_rm_hash64(handle->id), rm_error);
818
819         return rm_error;
820 }
821
822 static int __dbus_destroy(mm_resource_manager_s *handle)
823 {
824         GError *error = NULL;
825         int rm_error = MM_RESOURCE_MANAGER_ERROR_NONE;
826
827         mmresource_manager_call_destroy_sync(handle->dbus_proxy, handle->id, &rm_error,
828                         NULL, &error);
829         MM_RM_RET_IF_GERR(error, "DBus destroy msg cannot be sent");
830
831         MM_RM_DEBUG("Destroy for id - #%"PRIu64" returned error - %d", _mm_rm_hash64(handle->id), rm_error);
832
833         return MM_RESOURCE_MANAGER_ERROR_NONE;
834 }
835
836 static int __dbus_commit(mm_resource_manager_s *handle)
837 {
838         GVariant *release;
839         GVariant *acquire;
840         GVariantBuilder *release_builder;
841         GVariantBuilder *acquire_builder;
842         GVariant *flags_variant;
843         GVariantIter flags_iter;
844         mm_resource_manager_res_p resource;
845         int i;
846         int release_num = 0;
847         int acquire_num = 0;
848         GError *error = NULL;
849         int rm_error = MM_RESOURCE_MANAGER_ERROR_NONE;
850
851         release_builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY);
852         acquire_builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY);
853         for (i = 0; i < handle->resources->len; i++) {
854                 resource = (mm_resource_manager_res_p) handle->resources->pdata[i];
855
856                 switch (resource->state) {
857                 case MM_RESOURCE_MANAGER_RES_STATE_FOR_ACQUIRE:
858                         g_variant_builder_add_value(acquire_builder, g_variant_new("(ii)",
859                                         resource->type, resource->volume));
860                         acquire_num++;
861                         break;
862                 case MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE:
863                         g_variant_builder_add_value(release_builder, g_variant_new("(ii)",
864                                         resource->type, resource->volume));
865                         release_num++;
866                         break;
867                 default:
868                         break;
869                 }
870         }
871
872         if (release_num + acquire_num == 0) {
873                 g_variant_builder_unref(release_builder);
874                 g_variant_builder_unref(acquire_builder);
875                 MM_RM_DEBUG("There is nothing to commit - dbus request is not sent");
876                 return rm_error;
877         }
878
879         /*
880          * Acquire and release arrays are ended with special element, because
881          * g_variant_builder_end crashes without at least one element
882          */
883         g_variant_builder_add_value(acquire_builder, g_variant_new("(ii)",
884                         MM_RESOURCE_MANAGER_NO_RES, 0));
885         acquire = g_variant_builder_end(acquire_builder);
886         g_variant_builder_unref(acquire_builder);
887
888         g_variant_builder_add_value(release_builder, g_variant_new("(ii)",
889                         MM_RESOURCE_MANAGER_NO_RES, 0));
890         release = g_variant_builder_end(release_builder);
891         g_variant_builder_unref(release_builder);
892
893         mmresource_manager_call_commit_sync(handle->dbus_proxy, handle->id, release,
894                         acquire, &rm_error, &flags_variant, NULL, &error);
895         if (error != NULL)
896                 MM_RM_RET_IF_GERR(error, "DBus commit msg cannot be sent");
897
898         MM_RM_DEBUG("Commit for id - #%"PRIu64" returned error - %d",
899                         _mm_rm_hash64(handle->id), rm_error);
900
901         if (rm_error == MM_RESOURCE_MANAGER_ERROR_NONE) {
902                 for (i = 0; i < handle->resources->len; i++) {
903                         resource = (mm_resource_manager_res_p) handle->resources->pdata[i];
904
905                         switch (resource->state) {
906                         case MM_RESOURCE_MANAGER_RES_STATE_FOR_ACQUIRE:
907                                 resource->state = MM_RESOURCE_MANAGER_RES_STATE_ACQUIRED;
908                                 resource->is_acquire_failed = FALSE;
909                                 break;
910                         case MM_RESOURCE_MANAGER_RES_STATE_FOR_RELEASE:
911                                 g_ptr_array_remove_index_fast(handle->resources, i--);
912                                 break;
913                         default:
914                                 ;
915                         }
916                 }
917         } else if (rm_error == MM_RESOURCE_MANAGER_ERROR_LOW_PRIORITY) {
918                 g_variant_iter_init(&flags_iter, flags_variant);
919
920                 for (i = 0; i < handle->resources->len; i++) {
921                         resource = (mm_resource_manager_res_p) handle->resources->pdata[i];
922
923                         if (resource->state == MM_RESOURCE_MANAGER_RES_STATE_FOR_ACQUIRE)
924                                 g_variant_iter_next(&flags_iter, "b", &resource->is_acquire_failed);
925                 }
926         }
927
928         g_variant_unref(flags_variant);
929
930         return rm_error;
931 }
932
933 static void __dbus_release_callback(MMResourceManager *object, guint64 arg_id,
934                 gint arg_resource_type, gint arg_volume)
935 {
936         mm_resource_manager_s *handle;
937         gboolean unlock = TRUE;
938         int i;
939
940         __mm_resource_handles_lock();
941         for (i = 0; i < handles->len; i++) {
942                 handle = (mm_resource_manager_s*)handles->pdata[i];
943                 if (handle->dbus_proxy == object) {
944                         __mm_resource_manager_release_callback(handle, arg_id,
945                                         arg_resource_type, arg_volume);
946                         unlock = FALSE;
947                         break;
948                 }
949         }
950
951         if (unlock)
952                 __mm_resource_handles_unlock();
953 }
954
955 static void __dbus_status_callback(MMResourceManager *object, gint arg_status)
956 {
957         mm_resource_manager_s *handle;
958         gboolean unlock = TRUE;
959         int i;
960
961         __mm_resource_handles_lock();
962         for (i = 0; i < handles->len; i++) {
963                 handle = (mm_resource_manager_s*)handles->pdata[i];
964                 if (handle->dbus_proxy == object) {
965                         __mm_resource_manager_status_callback(handle, arg_status);
966                         unlock = FALSE;
967                         break;
968                 }
969         }
970
971         if (unlock)
972                 __mm_resource_handles_unlock();
973 }
974
975 static gpointer __dispatcher_thread(gpointer user_data)
976 {
977         GMainLoop *ml = (GMainLoop *) user_data;
978
979         if (ml) {
980                 g_main_loop_run(ml);
981                 MM_RM_INFO("main loop %p quit", ml);
982         }
983
984         return NULL;
985 }
986
987 static void __destroy_dispatcher(mm_resource_manager_s *handle)
988 {
989         if (__dbus_deinit(handle) != MM_RESOURCE_MANAGER_ERROR_NONE)
990                 MM_RM_ERROR("Error while dbus deinitializing");
991
992         if (handle->dispatcher_loop) {
993                 if (g_main_loop_is_running(handle->dispatcher_loop)) {
994                         MM_RM_INFO("mainloop %p is running", handle->dispatcher_loop);
995                         g_main_loop_quit(handle->dispatcher_loop);
996                 }
997                 g_main_loop_unref(handle->dispatcher_loop);
998                 handle->dispatcher_loop = NULL;
999         }
1000
1001         if (handle->dispatcher_thread) {
1002                 g_thread_join(handle->dispatcher_thread);
1003                 MM_RM_INFO("dispatcher thread join %p", handle->dispatcher_thread);
1004                 handle->dispatcher_thread = NULL;
1005         }
1006
1007         if (handle->dispatcher_context) {
1008                 g_main_context_unref(handle->dispatcher_context);
1009                 handle->dispatcher_context = NULL;
1010         }
1011 }