Add unit(in variable) & fix bugs
[platform/core/system/resourced.git] / src / resource-limiter / memory / lowmem-limit.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18 */
19
20
21 #ifndef _GNU_SOURCE
22         /* For asprintf(3). Only affects this one file
23          * because tests dislike GNU_SOURCE since it
24          * makes mockability difficult. */
25         #define _GNU_SOURCE
26         #include <stdio.h>
27         #undef _GNU_SOURCE
28 #else
29         #include <stdio.h>
30 #endif
31
32 #include <sys/types.h>
33 #include <signal.h>
34 #include <unistd.h>
35
36 #include "trace.h"
37 #include "macro.h"
38 #include "module.h"
39 #include "module-data.h"
40 #include "lowmem-handler.h"
41 #include "notifier.h"
42 #include "procfs.h"
43 #include "cgroup.h"
44 #include "const.h"
45 #include "proc-common.h"
46 #include "memory-cgroup.h"
47 #include "util.h"
48 #include "smaps.h"
49 #include "config-parser.h"
50 #include "resourced-helper-worker.h"
51 #include "fd-handler.h"
52 #include "dbus-handler.h"
53 #include "safe-kill.h"
54
55 #define MEM_CONF_FILE                   RD_CONFIG_FILE(limiter)
56 #define MEMLIMIT_CONFIG_SECTION "MemLimit"
57 #define MEMLIMIT_CONFIG_TRIGGER "MemLimitTrigger"
58
59 #define MEMLIMIT_CONFIG_LIM_PFX MEMLIMIT_CONFIG_SECTION
60 #define MEMLIMIT_CONFIG_SERVICE MEMLIMIT_CONFIG_LIM_PFX "Service"
61 #define MEMLIMIT_CONFIG_GUIAPP  MEMLIMIT_CONFIG_LIM_PFX "GUIApp"
62 #define MEMLIMIT_CONFIG_WIDGET  MEMLIMIT_CONFIG_LIM_PFX "Widget"
63 #define MEMLIMIT_CONFIG_BGAPP   MEMLIMIT_CONFIG_LIM_PFX "BgApp"
64 #define MIN_LIMIT_VALUE         MBYTE_TO_BYTE(1) /* Byte */
65
66 /*enum mem_limit_type {
67         MEM_LIMIT_NONE,
68         MEM_LIMIT_OOM,
69         MEM_LIMIT_TRHESHOLD,
70 };
71
72 static enum mem_limit_type mem_limit;*/
73
74
75 static GHashTable *memory_limit_hash;
76 static char *registerpath;
77 static unsigned long long mem_service_limit_bytes;
78 static unsigned long long mem_widget_limit_bytes;
79 static unsigned long long mem_guiapp_limit_bytes;
80 static unsigned long long mem_bgapp_limit_bytes;
81
82 static int mem_service_action = PROC_ACTION_IGNORE;
83 static int mem_widget_action = PROC_ACTION_IGNORE;
84 static int mem_guiapp_action = PROC_ACTION_IGNORE;
85 static int mem_bgapp_action = PROC_ACTION_IGNORE;
86
87 static void memory_limit_hash_destroy(gpointer data)
88 {
89         struct memory_limit_event *mle = (struct memory_limit_event *)data;
90         if (!mle) {
91                 _E("[MEMORY-LIMIT] Memory limit event structure is NULL");
92                 return;
93         }
94
95         if (mle->fd > 0)
96                 close(mle->fd);
97
98         if (mle->path)
99                 free(mle->path);
100
101         /* NB: `mle->fdh` is NOT cleaned up here. This is because the removal
102          * of `memory_limit_event` only happens in two cases:
103          *
104          *  a) at the end of `lowmem_limit_cb`, which returns `false`
105          *     on removal, which results in `fd_handler` getting cleaned
106          *     up independently (see `channel_changed` in `fd-handler.c`)
107          *
108          *  b) at exit, by which time GIO channels should've already been
109          *     cleaned up (due to `g_main_loop_quit` et al.). */
110
111         free(mle);
112 }
113
114 static int lowmem_limit_broadcast(pid_t pid)
115 {
116         int ret;
117         char appname[PROC_NAME_MAX];
118         struct proc_app_info *pai = NULL;
119         char *appid;
120
121         pai = find_app_info(pid);
122         if (pai) {
123                 appid = pai->appid;
124         } else {
125                 ret = proc_get_cmdline(pid, appname, sizeof appname);
126                 if (ret < 0) {
127                         _E("[MEMORY-LIMIT] Failed to get cmdline basename of pid(%d)", pid);
128                         return ret;
129                 }
130                 appid = appname;
131         }
132
133         ret = d_bus_broadcast_signal_gvariant(RESOURCED_PATH_OOM,
134                         RESOURCED_INTERFACE_OOM, SIGNAL_OOM_MEMLIMIT_EVENT,
135                         g_variant_new("(is)", pid, appid));
136         if (ret < 0)
137                 _E("[MEMORY-LIMIT] Fail to broadcast dbus signal with pid(%d), appname(%s)", pid, appname);
138
139         return ret;
140 }
141
142 static gboolean liveness_check_cb(gpointer data)
143 {
144         struct memory_limit_event *mle = (struct memory_limit_event *)data;
145
146         if (!mle) {
147                 _E("[MEMORY-LIMIT] memory limit event structure is NULL");
148                 goto timer_out;
149         }
150
151         if (mle->pids_array == NULL) {
152                 _E("[MEMORY-LIMIT] pids array should not be NULL");
153                 goto timer_out;
154         }
155
156         for (int i = 0; i < mle->pids_array->len; i++) {
157                 pid_t pid = g_array_index(mle->pids_array, pid_t, i);
158                 if (!pid)
159                         continue;
160
161                 if (kill(pid, 0) == 0) {
162                         _I("[MEMORY-LIMIT] dir %s's pid (%d) is still avlie, so kill forcely", mle->path, pid);
163                         safe_kill(pid, SIGKILL);
164                 }
165         }
166
167         g_array_free(mle->pids_array, true);
168
169         mle->pids_array = NULL;
170 timer_out:
171         return G_SOURCE_REMOVE;
172 }
173
174 static bool memory_action_cb(int fd, void *data)
175 {
176         int result;
177         pid_t pid;
178         GArray *pids_array = NULL;
179         unsigned long long usage_bytes;
180         unsigned long long dummy_efd;
181         char *cg_dir = (char *)data;
182         struct memory_limit_event *mle;
183
184         mle = g_hash_table_lookup(memory_limit_hash, cg_dir);
185         if (!mle) {
186                 _E("[MEMORY-LIMIT] invalid event\n");
187                 return false;
188         }
189
190         result = read(fd, &dummy_efd, sizeof(dummy_efd));
191         if (result < 0) {
192                 _E("[MEMORY-LIMIT] wrong eventfd %s\n", cg_dir);
193                 goto remove_mle;
194         }
195
196         if (access(cg_dir, F_OK) == -1) {
197                 _D("[MEMORY-LIMIT] there is no (%s) cgroup any longer, removing it", cg_dir);
198                 goto remove_mle;
199         }
200
201         result = cgroup_read_node_ulonglong(cg_dir, MEMCG_SWAP_USAGE, &usage_bytes);
202         if (result < 0) {
203                 result = cgroup_read_node_ulonglong(cg_dir, MEMCG_USAGE, &usage_bytes);
204                 if (result < 0) {
205                         _D("[MEMORY-LIMIT] there is no (%s) cgroup any longer, removed it", cg_dir);
206                         goto remove_mle;
207                 }
208         }
209
210         if (usage_bytes < mle->threshold_bytes) {
211                 _D("[MEMORY-LIMIT] (%s) cgroup escaped low memory status. usage(%llu) bytes, threshold(%llu) bytes",
212                         cg_dir, usage_bytes, mle->threshold_bytes);
213                 return true;
214         }
215
216         switch (mle->action) {
217                 case PROC_ACTION_BROADCAST:
218                         result = cgroup_get_pids(cg_dir, &pids_array);
219                         if (result < 0 || !pids_array->len) {
220                                 g_array_free(pids_array, true);
221                                 goto remove_mle;
222                         }
223
224                         for (int i = 0; i < pids_array->len; i++) {
225                                 pid = g_array_index(pids_array, pid_t, i);
226                                 if (!pid)
227                                         continue;
228
229                                 if (lowmem_limit_broadcast(pid)) {
230                                         _E("[MEMORY-LIMIT] Failed to broadcast of process (%s)", cg_dir);
231                                         safe_kill(pid, SIGTERM);
232                                 }
233                         }
234
235                         g_array_free(pids_array, true);
236                         break;
237                 case PROC_ACTION_RECLAIM:
238                         lowmem_trigger_swap(0, cg_dir, false);
239                         break;
240                 case PROC_ACTION_KILL:
241                         result = cgroup_get_pids(cg_dir, &pids_array);
242                         if (result < 0 || !pids_array->len) {
243                                 g_array_free(pids_array, true);
244                                 goto remove_mle;
245                         }
246
247                         for (int i = 0; i < pids_array->len; i++) {
248                                 pid = g_array_index(pids_array, pid_t, i);
249                                 if (!pid)
250                                         continue;
251
252                                 safe_kill(pid, SIGTERM);
253                         }
254
255                         if (mle->pids_array == NULL) {
256                                 mle->pids_array = pids_array;
257                                 g_timeout_add_seconds(2, liveness_check_cb, mle);
258                         }
259                         else
260                                 g_array_free(pids_array, true);
261
262                         break;
263                 default:
264                         _E("[MEMORY-LIMIT] Unkown action of memory threshold");
265         }
266
267         return true;
268
269 remove_mle:
270         g_hash_table_remove(memory_limit_hash, cg_dir);
271         return false;
272 }
273
274 int lowmem_reassign_limit(const char *dir,
275                 unsigned long long limit_bytes, enum proc_action action)
276 {
277         int fd;
278         fd_handler_h fdh = NULL;
279         gpointer hash_entry;
280         struct memory_limit_event *mle = NULL;
281         char buf[MAX_DEC_SIZE(int)] = {0};
282         unsigned long long max_limit_bytes;
283
284         if (memory_limit_hash) {
285                 /* TO DO: currently concurrent processes with same app name are located
286                  * in the same directory.
287                  * Fix to distinguish processes with the same app name
288                  */
289                 hash_entry = g_hash_table_lookup(memory_limit_hash, dir);
290                 if (hash_entry) {
291                         mle = (struct memory_limit_event *)hash_entry;
292                         if (mle->threshold_bytes == limit_bytes) {
293                                 return RESOURCED_ERROR_NONE;
294                         }
295                 }
296         }
297         else {
298                 memory_limit_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
299                                 NULL, memory_limit_hash_destroy);
300                 if (!memory_limit_hash) {
301                         _E("[MEMORY-LIMIT] Failed to create hash table");
302                         return RESOURCED_ERROR_FAIL;
303                 }
304         }
305
306         if (limit_bytes > lowmem_get_totalram()) {
307                 max_limit_bytes = lowmem_get_totalram();
308                 limit_bytes = lowmem_get_totalram();
309         }
310         else if (limit_bytes * 1.2 > lowmem_get_totalram()) {
311                 max_limit_bytes = lowmem_get_totalram();
312         }
313         else
314                 max_limit_bytes = limit_bytes * 1.2;
315
316         check_oom_and_set_limit(dir, max_limit_bytes);
317
318         snprintf(buf, sizeof(buf), "%llu", limit_bytes);
319
320         if (mle) {
321                 mle->threshold_bytes = limit_bytes;
322                 memcg_init_eventfd(mle->fd, dir, registerpath, buf);
323                 return RESOURCED_ERROR_NONE;
324         }
325
326         fd = memcg_set_eventfd(dir, registerpath, buf);
327         if (fd > 0) {
328                 mle = calloc(1, sizeof(struct memory_limit_event));
329                 if (!mle) {
330                         _E("[MEMORY-LIMIT] out of memory");
331                         return RESOURCED_ERROR_OUT_OF_MEMORY;
332                 }
333                 mle->fd = fd;
334                 mle->path = (char *)strdup(dir);
335                 if (!mle->path) {
336                         _E("[MEMORY-LIMIT] out of memory");
337                         free(mle);
338                         return RESOURCED_ERROR_OUT_OF_MEMORY;
339                 }
340                 mle->action = action;
341                 mle->threshold_bytes = limit_bytes;
342                 mle->pids_array = NULL;
343                 add_fd_read_handler(fd, memory_action_cb, mle->path, NULL, &fdh);
344                 mle->fdh = fdh;
345                 g_hash_table_insert(memory_limit_hash, (gpointer)mle->path,
346                                 (gpointer)mle);
347
348                 return RESOURCED_ERROR_NONE;
349         }
350
351         return fd;
352 }
353
354 int lowmem_limit_move_cgroup(struct proc_app_info *pai)
355 {
356         GSList *iter = NULL;
357         _cleanup_free_ char *path = NULL;
358         int ret;
359
360         if (!pai)
361                 return RESOURCED_ERROR_NO_DATA;
362
363         if (!pai->memory.use_mem_limit)
364                 return RESOURCED_ERROR_NO_DATA;
365
366         ret = asprintf(&path, "%s/%s", MEMCG_HIGH_PP_PATH, pai->appid);
367         if (ret < 0) {
368                 _E("[MEMORY-LIMIT] not enough memory");
369                 return RESOURCED_ERROR_OUT_OF_MEMORY;
370         }
371         cgroup_write_pid_fullpath(path, pai->main_pid);
372         if (pai->childs) {
373                 gslist_for_each_item(iter, pai->childs)
374                         cgroup_write_pid_fullpath(path, GPOINTER_TO_PID(iter->data));
375         }
376         return RESOURCED_ERROR_NONE;
377 }
378
379 int lowmem_limit_set_system_service(pid_t pid, unsigned long long limit_bytes,
380                 const char *name, enum proc_action action)
381 {
382         _cleanup_free_ char *path = NULL;
383         int result;
384         unsigned long long totalram_bytes = lowmem_get_totalram();
385
386         if (limit_bytes < MIN_LIMIT_VALUE || limit_bytes > totalram_bytes) {
387                 _E("[MEMORY-LIMIT] It's meaningless to set memory limit with size (%llu) bytes", limit_bytes);
388                 return RESOURCED_ERROR_INVALID_PARAMETER;
389         }
390
391         if (action == PROC_ACTION_IGNORE)
392                 return RESOURCED_ERROR_NONE;
393
394         if (!name) {
395                 _E("[MEMORY-LIMIT] service name is NULL");
396                 return RESOURCED_ERROR_FAIL;
397         }
398
399         result = asprintf(&path, "%s/%s", MEMCG_HIGH_PP_PATH, name);
400         if (result < 0) {
401                 _E("[MEMORY-LIMIT] not enough memory");
402                 return RESOURCED_ERROR_OUT_OF_MEMORY;
403         }
404
405         result = cgroup_make_subdir(MEMCG_HIGH_PP_PATH, name, NULL);
406         if (result < 0) {
407                 _E("[MEMORY-LIMIT] Failed to create cgroup subdir '%s/%s'",
408                                 MEMCG_HIGH_PP_PATH, name);
409                 return result;
410         }
411
412         result = lowmem_reassign_limit(path, limit_bytes, action);
413         if (result < 0) {
414                 _W("[MEMORY-LIMIT] Failed to reassign limit for %s", path);
415                 return result;
416         }
417
418         result = cgroup_write_node_uint32(path, MEMCG_MOVE_CHARGE, 3U);
419         if (result < 0) {
420                 _W("[MEMORY-LIMIT] Failed to set immigrate mode for %s (non-crucial, continuing)", path);
421                 return result;
422         }
423
424         cgroup_write_pid_fullpath(path, pid);
425
426         return RESOURCED_ERROR_NONE;
427 }
428
429 int lowmem_limit_set_app(unsigned long long limit_bytes, struct proc_app_info *pai,
430                 enum proc_action action)
431 {
432         _cleanup_free_ char *path = NULL;
433         GSList *iter = NULL;
434         int result;
435         unsigned long long totalram_bytes = lowmem_get_totalram();
436
437         if (limit_bytes < MIN_LIMIT_VALUE || limit_bytes > totalram_bytes) {
438                 _E("[MEMORY-LIMIT] It's meaningless to set memory limit with size (%llu) bytes", limit_bytes);
439                 return RESOURCED_ERROR_INVALID_PARAMETER;
440         }
441
442         if (action == PROC_ACTION_IGNORE)
443                 return RESOURCED_ERROR_NONE;
444
445         if (!pai) {
446                 _E("[MEMORY-LIMIT] process app information is NULL");
447                 return RESOURCED_ERROR_INVALID_PARAMETER;
448         }
449
450         result = asprintf(&path, "%s/%s", MEMCG_HIGH_PP_PATH, pai->appid);
451         if (result < 0) {
452                 _E("[MEMORY-LIMIT] not enough memory");
453                 return RESOURCED_ERROR_OUT_OF_MEMORY;
454         }
455
456         result = cgroup_make_subdir(MEMCG_HIGH_PP_PATH, pai->appid, NULL);
457         if (result < 0) {
458                 _E("[MEMORY-LIMIT] Failed to create cgroup subdir '%s/%s'",
459                                 MEMCG_HIGH_PP_PATH, pai->appid);
460                 return result;
461         }
462
463         result = lowmem_reassign_limit(path, limit_bytes, action);
464         if (result < 0) {
465                 _W("[MEMORY-LIMIT] Failed to reassign limit for %s", path);
466                 return result;
467         }
468
469         result = cgroup_write_node_uint32(path, MEMCG_MOVE_CHARGE, 3U);
470         if (result < 0)
471                 _W("[MEMORY-LIMIT] Failed to set immigrate mode for %s (non-crucial, continuing)", path);
472
473         cgroup_write_pid_fullpath(path, pai->main_pid);
474         if (pai->childs) {
475                 gslist_for_each_item(iter, pai->childs)
476                         cgroup_write_pid_fullpath(path, GPOINTER_TO_PID(iter->data));
477         }
478
479         pai->memory.use_mem_limit = true;
480
481         return RESOURCED_ERROR_NONE;
482 }
483
484 static int lowmem_limit_app(void *data)
485 {
486         int error;
487
488         assert(data);
489
490         struct proc_limit_status *pls = (struct proc_limit_status *)data;
491
492         error = lowmem_limit_set_app(pls->limit_bytes, pls->ps.pai, pls->action);
493         if (!error)
494                 pls->ps.pai->memory.memlimit_update_exclude = true;
495
496         return error;
497 }
498
499 static int lowmem_limit_system_service(void *data)
500 {
501         int error;
502
503         assert(data);
504
505         struct proc_limit_status *pls = (struct proc_limit_status *)data;
506
507         error = lowmem_limit_set_system_service(pls->ps.pid, pls->limit_bytes,
508                         pls->ps.pci->name, pls->action);
509
510         return error;
511 }
512
513 static int lowmem_limit_service(void *data)
514 {
515         assert(data);
516
517         struct proc_status *ps = (struct proc_status *)data;
518
519         if (ps->pai && ps->pai->memory.memlimit_update_exclude)
520                 return RESOURCED_ERROR_NONE;
521
522         if (mem_service_limit_bytes && mem_service_action != PROC_ACTION_IGNORE) {
523                 lowmem_limit_set_app(mem_service_limit_bytes, ps->pai, mem_service_action);
524         }
525         return RESOURCED_ERROR_NONE;
526 }
527
528 static int lowmem_limit_appwidget(void *data)
529 {
530         assert(data);
531
532         struct proc_status *ps = (struct proc_status *)data;
533
534         if (ps->pai && ps->pai->memory.memlimit_update_exclude)
535                 return RESOURCED_ERROR_NONE;
536
537         if (mem_guiapp_limit_bytes && mem_guiapp_action != PROC_ACTION_IGNORE &&
538             ps->pai->type == PROC_TYPE_GUI) {
539                 lowmem_limit_set_app(mem_guiapp_limit_bytes, ps->pai, mem_guiapp_action);
540         }
541
542         if (mem_widget_limit_bytes && mem_widget_action != PROC_ACTION_IGNORE &&
543             ps->pai->type == PROC_TYPE_WIDGET) {
544                 lowmem_limit_set_app(mem_widget_limit_bytes, ps->pai, mem_widget_action);
545         }
546
547         return RESOURCED_ERROR_NONE;
548 }
549
550 static int lowmem_limit_bgapp(void *data)
551 {
552         assert(data);
553
554         struct proc_status *ps = (struct proc_status *)data;
555
556         if (ps->pai && ps->pai->memory.memlimit_update_exclude)
557                 return RESOURCED_ERROR_NONE;
558
559         lowmem_limit_set_app(mem_bgapp_limit_bytes, ps->pai, mem_bgapp_action);
560
561         return RESOURCED_ERROR_NONE;
562 }
563
564 static int lowmem_limit_fgapp(void *data)
565 {
566         assert(data);
567
568         struct proc_status *ps = (struct proc_status *)data;
569
570         if ((mem_guiapp_limit_bytes && ps->pai->type == PROC_TYPE_GUI) ||
571             (mem_widget_limit_bytes && ps->pai->type == PROC_TYPE_WIDGET))
572                 return lowmem_limit_appwidget(data);
573
574         _E("[MEMORY-LIMIT] Unable to set foreground app limit - app type not supported");
575
576         return RESOURCED_ERROR_NONE;
577 }
578
579 void lowmem_memory_init(unsigned long long service_limit_bytes, unsigned long long widget_limit_bytes,
580                 unsigned long long guiapp_limit_bytes, unsigned long long bgapp_limit_bytes)
581 {
582         mem_service_limit_bytes = service_limit_bytes;
583         mem_widget_limit_bytes = widget_limit_bytes;
584         mem_guiapp_limit_bytes = guiapp_limit_bytes;
585         mem_bgapp_limit_bytes = bgapp_limit_bytes;
586
587         _I("[MEMORY-LIMIT] service = %llu bytes, widget = %llu bytes, guiapp = %llu bytes, bgapp = %llu",
588                         mem_service_limit_bytes, mem_widget_limit_bytes, mem_guiapp_limit_bytes, mem_bgapp_limit_bytes);
589 }
590
591 void lowmem_action_init(int service_action, int widget_action,
592                 int guiapp_action, int bgapp_action)
593 {
594         if (service_action > 0)
595                 mem_service_action = service_action;
596
597         if (widget_action > 0)
598                 mem_widget_action = widget_action;
599
600         if (guiapp_action > 0)
601                 mem_guiapp_action = guiapp_action;
602
603         if (bgapp_action > 0)
604                 mem_bgapp_action = bgapp_action;
605 }
606
607 void lowmem_limit_init(void)
608 {
609         int result;
610         unsigned long long usage_bytes;
611
612         result = cgroup_read_node_ulonglong(MEMCG_PATH, MEMCG_SWAP_USAGE, &usage_bytes);
613         if (result == RESOURCED_ERROR_NONE)
614                 registerpath = MEMCG_SWAP_USAGE;
615         else
616                 registerpath = MEMCG_USAGE;
617
618         register_notifier(RESOURCED_NOTIFIER_LIMIT_SYSTEM_SERVICE, lowmem_limit_system_service);
619         register_notifier(RESOURCED_NOTIFIER_LIMIT_APP, lowmem_limit_app);
620
621         if (mem_service_limit_bytes && mem_service_action != PROC_ACTION_IGNORE)
622                 register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, lowmem_limit_service);
623         if ((mem_guiapp_limit_bytes && mem_guiapp_action != PROC_ACTION_IGNORE) ||
624             (mem_widget_limit_bytes && mem_widget_action != PROC_ACTION_IGNORE))
625                 register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, lowmem_limit_appwidget);
626         if (mem_bgapp_limit_bytes && mem_bgapp_action != PROC_ACTION_IGNORE) {
627                 if (!(mem_guiapp_limit_bytes && mem_guiapp_action != PROC_ACTION_IGNORE) ||
628                     !(mem_widget_limit_bytes && mem_widget_action != PROC_ACTION_IGNORE)) {
629                         _W("[MEMORY-LIMIT] Background app limit requires that both GUIApp and Widget limits to be set to work properly. Ignoring.");
630                 } else {
631                         register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, lowmem_limit_bgapp);
632                         register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, lowmem_limit_fgapp);
633                         register_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, lowmem_limit_fgapp);
634                         register_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, lowmem_limit_bgapp);
635                 }
636         }
637 }
638
639 void lowmem_limit_exit(void)
640 {
641         if (memory_limit_hash) {
642                 g_hash_table_destroy(memory_limit_hash);
643 #ifdef _UNIT_TEST
644                 memory_limit_hash = NULL;
645 #endif
646         }
647
648         unregister_notifier(RESOURCED_NOTIFIER_LIMIT_SYSTEM_SERVICE, lowmem_limit_system_service);
649         unregister_notifier(RESOURCED_NOTIFIER_LIMIT_APP, lowmem_limit_app);
650         unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, lowmem_limit_service);
651         unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, lowmem_limit_appwidget);
652         unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, lowmem_limit_bgapp);
653         unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, lowmem_limit_fgapp);
654         unregister_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, lowmem_limit_fgapp);
655         unregister_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, lowmem_limit_bgapp);
656 }