4143e3667bdf723f2a41c7307b0abbafb040320a
[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         /* check only swap usage also */
68         MEM_LIMIT_NONE,
69         /* register OOM event and control application in kernel side */
70         MEM_LIMIT_OOM,
71         /* register threshold event and control application in resourced*/
72         MEM_LIMIT_TRHESHOLD,
73 };
74
75 static enum mem_limit_type mem_limit;
76
77
78 static GHashTable *memory_limit_hash;
79 static char *registerpath;
80 static unsigned int mem_service_limit;
81 static unsigned int mem_widget_limit;
82 static unsigned int mem_guiapp_limit;
83 static unsigned int mem_bgapp_limit;
84
85 static int mem_service_action = PROC_ACTION_KILL;
86 static int mem_widget_action = PROC_ACTION_KILL;
87 static int mem_guiapp_action = PROC_ACTION_KILL;
88 static int mem_bgapp_action = PROC_ACTION_KILL;
89
90 struct memory_info {
91         pid_t pid;
92         unsigned int size;
93 };
94
95 struct memory_limit_log {
96         pid_t pid;
97         char *appname;
98         char *cgdir;
99 };
100
101 static int get_pid_use_max_memory(GArray *pids_array, unsigned int *max_mem)
102 {
103         int i, ret;
104         struct memory_info mi = {0, 0};
105
106         if (!pids_array->len)
107                 return RESOURCED_ERROR_NO_DATA;
108
109         for (i = 0; i < pids_array->len; i++) {
110                 pid_t mpid = 0;
111                 unsigned int size;
112
113                 mpid = g_array_index(pids_array, pid_t, i);
114                 if (!mpid)
115                         continue;
116
117                 ret = proc_get_mem_usage(mpid, &size);
118                 if (ret != RESOURCED_ERROR_NONE)
119                         continue;
120
121                 _D("%d used %d memory", mpid, size);
122
123                 if (size > mi.size) {
124                         mi.pid = mpid;
125                         mi.size = size;
126                 }
127         }
128         *max_mem = mi.size;
129         _D("%d used max %d memory", mi.pid, mi.size);
130         return mi.pid;
131 }
132
133 static pid_t get_main_pid(const char *dir, unsigned int *max_mem)
134 {
135         GArray *pids_array = NULL;
136         _cleanup_free_ char *path = NULL;
137         int ret;
138         pid_t main_pid;
139
140         ret = asprintf(&path, "%s/", dir);
141         if (ret < 0)
142                 return -ENOMEM;
143
144         ret = cgroup_get_pids(path, &pids_array);
145         if (ret < 0 || !pids_array->len) {
146                 g_array_free(pids_array, true);
147                 return RESOURCED_ERROR_NO_DATA;
148         }
149
150         main_pid = get_pid_use_max_memory(pids_array, max_mem);
151         g_array_free(pids_array, true);
152         return main_pid;
153 }
154
155 static void memory_limit_hash_destroy(gpointer data)
156 {
157         struct memory_limit_event *mle = (struct memory_limit_event *)data;
158         if (!mle) {
159                 _E("[DEBUG] Memory limit event structure is NULL");
160                 return;
161         }
162
163         if (mle->fd > 0)
164                 close(mle->fd);
165
166         if (mle->path)
167                 free(mle->path);
168
169         /* NB: `mle->fdh` is NOT cleaned up here. This is because the removal
170          * of `memory_limit_event` only happens in two cases:
171          *
172          *  a) at the end of `lowmem_limit_cb`, which returns `false`
173          *     on removal, which results in `fd_handler` getting cleaned
174          *     up independently (see `channel_changed` in `fd-handler.c`)
175          *
176          *  b) at exit, by which time GIO channels should've already been
177          *     cleaned up (due to `g_main_loop_quit` et al.). */
178
179         free(mle);
180 }
181
182 static int lowmem_limit_broadcast(pid_t pid)
183 {
184         int ret;
185         char appname[PROC_NAME_MAX];
186         struct proc_app_info *pai = NULL;
187         char *appid;
188
189         pai = find_app_info(pid);
190         if (pai) {
191                 appid = pai->appid;
192         } else {
193                 ret = proc_get_cmdline(pid, appname, sizeof appname);
194                 if (ret < 0) {
195                         _E("[DEBUG] Failed to get cmdline basename of pid(%d)", pid);
196                         return ret;
197                 }
198                 appid = appname;
199         }
200
201         ret = d_bus_broadcast_signal_gvariant(RESOURCED_PATH_OOM,
202                         RESOURCED_INTERFACE_OOM, SIGNAL_OOM_MEMLIMIT_EVENT,
203                         g_variant_new("(is)", pid, appid));
204         if (ret < 0)
205                 _E("[DEBUG] Fail to broadcast dbus signal with pid(%d), appname(%s)", pid, appname);
206
207         return ret;
208 }
209
210 static gboolean liveness_check_cb(gpointer data)
211 {
212         struct memory_limit_event *mle = (struct memory_limit_event *)data;
213
214         if (!mle) {
215                 _E("[DEBUG] memory limit event structure is NULL");
216                 goto timer_out;
217         }
218
219         if (mle->pid <= 0) {
220                 _E("[DEBUG] pid should be larger than 0");
221                 goto mle_timer_init;
222         }
223
224         if (kill(mle->pid, 0) == 0) {
225                 safe_kill(mle->pid, SIGKILL);
226         }
227
228 mle_timer_init:
229         mle->pid = -1;
230 timer_out:
231         return G_SOURCE_REMOVE;
232 }
233
234 static bool memory_action_cb(int fd, void *data)
235 {
236         int result;
237         pid_t main_pid;
238         uint32_t usage, max_mem;
239         uint64_t dummy_efd;
240         char *cg_dir = (char *)data;
241         struct memory_limit_event *mle;
242         _cleanup_free_ struct cgroup_memory_stat *mem_stat = NULL;
243
244         mle = g_hash_table_lookup(memory_limit_hash, cg_dir);
245         if (!mle) {
246                 _E("invalid event\n");
247                 return false;
248         }
249
250         result = read(fd, &dummy_efd, sizeof(dummy_efd));
251         if (result < 0) {
252                 _E("[DEBUG] wrong eventfd %s\n", cg_dir);
253                 goto remove_mle;
254         }
255
256         if (access(cg_dir, F_OK) == -1) {
257                 _D("[DEBUG] there is no (%s) cgroup any longer, removing it", cg_dir);
258                 goto remove_mle;
259         }
260
261         result = cgroup_read_node_uint32(cg_dir, MEMCG_SWAP_USAGE, &usage);
262         if (result < 0) {
263                 result = cgroup_read_node_uint32(cg_dir, MEMCG_USAGE, &usage);
264                 if (result < 0) {
265                         _D("[DEBUG] there is no (%s) cgroup any longer, removed it", cg_dir);
266                         goto remove_mle;
267                 }
268         }
269
270         if (usage < mle->threshold) {
271                 _D("[DEBUG] (%s) cgroup escaped low memory status. usage(%d), threshold(%d)",
272                         cg_dir, usage, mle->threshold);
273                 return true;
274         }
275
276         result = memcg_get_memory_stat(cg_dir, &mem_stat);
277         if (result < 0) {
278                 _E("[DEBUG] Failed to get memory status : %s", cg_dir);
279                 goto remove_mle;
280         }
281
282 /*      anon_usage = mem_stat->value[CGROUP_MEMORY_STAT_RSS] +
283                 mem_stat->value[CGROUP_MEMORY_STAT_SWAP];
284         if (anon_usage < mle->threshold) {
285                 _D("[DEBUG] (%s) cgroup escaped low memory status. usage(%d), anon usage (%d), threshold(%d)",
286                                 cg_dir, usage, anon_usage, mle->threshold);
287                 return true;
288         }*/
289
290         switch (mle->action) {
291                 case PROC_ACTION_BROADCAST:
292                         main_pid = get_main_pid(cg_dir, &max_mem);
293                         if (main_pid <= 0) {
294                                 _D("[DEBUG] there is no victim, removed cgroup : %s", cg_dir);
295                                 goto remove_mle;
296                         }
297
298                         if (lowmem_limit_broadcast(main_pid)) {
299                                 _E("[DEBUG] Failed to broadcast of process (%s)", cg_dir);
300                                 return false;
301                         }
302                         break;
303                 case PROC_ACTION_RECLAIM:
304                         lowmem_trigger_swap(0, cg_dir, false);
305                         break;
306                 case PROC_ACTION_KILL:
307                         main_pid = get_main_pid(cg_dir, &max_mem);
308                         if (main_pid <= 0) {
309                                 _D("[DEBUG] there is no victim, removed cgroup : %s", cg_dir);
310                                 goto remove_mle;
311                         }
312                         safe_kill(main_pid, SIGTERM);
313
314                         if (mle->pid == -1) {
315                                 mle->pid = main_pid;
316                                 g_timeout_add_seconds(2, liveness_check_cb, mle);
317                         }
318
319                         break;
320                 default:
321                         _E("[DEBUG] Unkown action of memory threshold");
322         }
323
324         return true;
325
326 remove_mle:
327         g_hash_table_remove(memory_limit_hash, cg_dir);
328         return false;
329 }
330
331 int lowmem_reassign_limit(const char *dir,
332                 unsigned int limit, enum proc_action action)
333 {
334         int fd;
335         fd_handler_h fdh = NULL;
336         gpointer hash_entry;
337         struct memory_limit_event *mle = NULL;
338         char buf[MAX_DEC_SIZE(int)] = {0};
339
340         if (memory_limit_hash) {
341                 /* TO DO: currently concurrent processes with same app name are located
342                  * in the same directory.
343                  * Fix to distinguish processes with the same app name
344                  */
345                 hash_entry = g_hash_table_lookup(memory_limit_hash, dir);
346                 if (hash_entry) {
347                         mle = (struct memory_limit_event *)hash_entry;
348                         if (mle->threshold == limit) {
349                                 return RESOURCED_ERROR_NONE;
350                         }
351                 }
352         }
353         else {
354                 memory_limit_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
355                                 NULL, memory_limit_hash_destroy);
356                 if (!memory_limit_hash) {
357                         _E("[DEBUG] Failed to create hash table");
358                         return RESOURCED_ERROR_FAIL;
359                 }
360         }
361
362         check_oom_and_set_limit(dir, limit * 1.2);
363         snprintf(buf, sizeof(buf), "%d", limit);
364
365         if (mle) {
366                 mle->threshold = limit;
367                 memcg_init_eventfd(mle->fd, dir, registerpath, buf);
368                 return RESOURCED_ERROR_NONE;
369         }
370
371         fd = memcg_set_eventfd(dir, registerpath, buf);
372         if (fd > 0) {
373                 mle = calloc(1, sizeof(struct memory_limit_event));
374                 if (!mle) {
375                         _E("[DEBUG] out of memory");
376                         return RESOURCED_ERROR_OUT_OF_MEMORY;
377                 }
378                 mle->fd = fd;
379                 mle->path = (char *)strdup(dir);
380                 if (!mle->path) {
381                         _E("[DEBUG] out of memory");
382                         free(mle);
383                         return RESOURCED_ERROR_OUT_OF_MEMORY;
384                 }
385                 mle->action = action;
386                 mle->threshold = limit;
387                 mle->pid = -1;
388                 add_fd_read_handler(fd, memory_action_cb, mle->path, NULL, &fdh);
389                 mle->fdh = fdh;
390                 g_hash_table_insert(memory_limit_hash, (gpointer)mle->path,
391                                 (gpointer)mle);
392
393                 return RESOURCED_ERROR_NONE;
394         }
395
396         return fd;
397 }
398
399 int lowmem_limit_move_cgroup(struct proc_app_info *pai)
400 {
401         GSList *iter = NULL;
402         _cleanup_free_ char *path = NULL;
403         int ret;
404
405         if (!pai)
406                 return RESOURCED_ERROR_NO_DATA;
407
408         if (!pai->memory.use_mem_limit)
409                 return RESOURCED_ERROR_NO_DATA;
410
411         ret = asprintf(&path, "%s/%s", MEMCG_HIGH_PP_PATH, pai->appid);
412         if (ret < 0) {
413                 _E("not enough memory");
414                 return RESOURCED_ERROR_OUT_OF_MEMORY;
415         }
416         cgroup_write_pid_fullpath(path, pai->main_pid);
417         if (pai->childs) {
418                 gslist_for_each_item(iter, pai->childs)
419                         cgroup_write_pid_fullpath(path, GPOINTER_TO_PID(iter->data));
420         }
421         return RESOURCED_ERROR_NONE;
422 }
423
424 /*static int memlimit_config_parse(struct parse_result *result, void *user_data)
425 {
426         if (!result->section)
427                 return RESOURCED_ERROR_NONE;
428
429         if (strncmp(result->section, MEMLIMIT_CONFIG_SECTION,
430                                 sizeof(MEMLIMIT_CONFIG_SECTION)))
431                 return RESOURCED_ERROR_NONE;
432
433         if (!result->name || !result->value)
434                 return RESOURCED_ERROR_NONE;
435
436         if (!strncmp(result->name, MEMLIMIT_CONFIG_LIM_PFX, sizeof(MEMLIMIT_CONFIG_LIM_PFX)-1)) {
437                 const int limit = atoi(result->value);
438                 if (!strcmp(result->name, MEMLIMIT_CONFIG_SERVICE))
439                         mem_service_limit = limit;
440                 else if (!strcmp(result->name, MEMLIMIT_CONFIG_GUIAPP))
441                         mem_guiapp_limit = limit;
442                 else if (!strcmp(result->name, MEMLIMIT_CONFIG_WIDGET))
443                         mem_widget_limit = limit;
444                 else if (!strcmp(result->name, MEMLIMIT_CONFIG_BGAPP))
445                         mem_bgapp_limit = limit;
446         }
447
448         return RESOURCED_ERROR_NONE;
449 }*/
450
451 void lowmem_limit_set_system_service(pid_t pid, unsigned int limit,
452                 const char *name, enum proc_action action)
453 {
454         _cleanup_free_ char *path = NULL;
455         int result;
456         unsigned int totalram = lowmem_get_totalram();
457
458         if (limit < MIN_LIMIT_VALUE || limit > totalram) {
459                 _E("[DEBUG] It's meaningless to set memory limit with size (%d)", limit);
460                 return;
461         }
462
463         if (!name) {
464                 _E("[DEBUG] service name is NULL");
465                 return;
466         }
467
468         result = asprintf(&path, "%s/%s", MEMCG_HIGH_PP_PATH, name);
469         if (result < 0) {
470                 _E("[DEBUG] not enough memory");
471                 return;
472         }
473
474         result = cgroup_make_subdir(MEMCG_HIGH_PP_PATH, name, NULL);
475         if (result < 0) {
476                 _E("[DEBUG] Failed to create cgroup subdir '%s/%s'",
477                                 MEMCG_HIGH_PP_PATH, name);
478                 return;
479         }
480
481         result = lowmem_reassign_limit(path, limit, action);
482         if (result < 0) {
483                 _W("Failed to reassign limit for %s", path);
484                 return;
485         }
486
487         result = cgroup_write_node_uint32(path, MEMCG_MOVE_CHARGE, 3U);
488         if (result < 0)
489                 _W("[DEBUG] Failed to set immigrate mode for %s (non-crucial, continuing)", path);
490
491         cgroup_write_pid_fullpath(path, pid);
492 }
493
494 int lowmem_limit_set_app(unsigned int limit, struct proc_app_info *pai,
495                 enum proc_action action)
496 {
497         _cleanup_free_ char *path = NULL;
498         GSList *iter = NULL;
499         int result;
500         unsigned int totalram = lowmem_get_totalram();
501
502         if (limit < MIN_LIMIT_VALUE || limit > totalram) {
503                 _E("[DEBUG] It's meaningless to set memory limit with size (%d)", limit);
504                 return RESOURCED_ERROR_INVALID_PARAMETER;
505         }
506
507         if (!pai) {
508                 _E("process app information is NULL");
509                 return RESOURCED_ERROR_INVALID_PARAMETER;
510         }
511
512         result = asprintf(&path, "%s/%s", MEMCG_HIGH_PP_PATH, pai->appid);
513         if (result < 0) {
514                 _E("not enough memory");
515                 return RESOURCED_ERROR_OUT_OF_MEMORY;
516         }
517
518         result = cgroup_make_subdir(MEMCG_HIGH_PP_PATH, pai->appid, NULL);
519         if (result < 0) {
520                 _E("Failed to create cgroup subdir '%s/%s'",
521                                 MEMCG_HIGH_PP_PATH, pai->appid);
522                 return result;
523         }
524
525         result = lowmem_reassign_limit(path, limit, action);
526         if (result < 0) {
527                 _W("Failed to reassign limit for %s", path);
528                 return result;
529         }
530
531         result = cgroup_write_node_uint32(path, MEMCG_MOVE_CHARGE, 3U);
532         if (result < 0)
533                 _W("Failed to set immigrate mode for %s (non-crucial, continuing)", path);
534
535         cgroup_write_pid_fullpath(path, pai->main_pid);
536         if (pai->childs) {
537                 gslist_for_each_item(iter, pai->childs)
538                         cgroup_write_pid_fullpath(path, GPOINTER_TO_PID(iter->data));
539         }
540
541         pai->memory.use_mem_limit = true;
542
543         return RESOURCED_ERROR_NONE;
544 }
545
546 static int lowmem_limit_app(void *data)
547 {
548         int error;
549
550         assert(data);
551
552         struct proc_limit_status *pls = (struct proc_limit_status *)data;
553
554         error = lowmem_limit_set_app(pls->limit, pls->ps.pai, pls->action);
555         if (!error)
556                 pls->ps.pai->memory.memlimit_update_exclude = true;
557         return RESOURCED_ERROR_NONE;
558 }
559
560 static int lowmem_limit_system_service(void *data)
561 {
562         assert(data);
563
564         struct proc_limit_status *pls = (struct proc_limit_status *)data;
565
566         lowmem_limit_set_system_service(pls->ps.pid, pls->limit, pls->ps.pci->name, pls->action);
567         return RESOURCED_ERROR_NONE;
568 }
569
570 static int lowmem_limit_service(void *data)
571 {
572         assert(data);
573
574         struct proc_status *ps = (struct proc_status *)data;
575
576         if (ps->pai && ps->pai->memory.memlimit_update_exclude)
577                 return RESOURCED_ERROR_NONE;
578
579         if (mem_service_limit) {
580                 lowmem_limit_set_app(mem_service_limit, ps->pai, mem_service_action);
581         }
582         return RESOURCED_ERROR_NONE;
583 }
584
585 static int lowmem_limit_appwidget(void *data)
586 {
587         assert(data);
588
589         struct proc_status *ps = (struct proc_status *)data;
590
591         if (ps->pai && ps->pai->memory.memlimit_update_exclude)
592                 return RESOURCED_ERROR_NONE;
593
594         if (mem_guiapp_limit && ps->pai->type == PROC_TYPE_GUI) {
595                 lowmem_limit_set_app(mem_guiapp_limit, ps->pai, mem_guiapp_action);
596         }
597         if (mem_widget_limit && ps->pai->type == PROC_TYPE_WIDGET) {
598                 lowmem_limit_set_app(mem_widget_limit, ps->pai, mem_widget_action);
599         }
600
601         return RESOURCED_ERROR_NONE;
602 }
603
604 static int lowmem_limit_bgapp(void *data)
605 {
606         assert(data);
607
608         struct proc_status *ps = (struct proc_status *)data;
609
610         if (ps->pai && ps->pai->memory.memlimit_update_exclude)
611                 return RESOURCED_ERROR_NONE;
612
613         lowmem_limit_set_app(mem_bgapp_limit, ps->pai, mem_bgapp_action);
614
615         return RESOURCED_ERROR_NONE;
616 }
617
618 static int lowmem_limit_fgapp(void *data)
619 {
620         assert(data);
621
622         struct proc_status *ps = (struct proc_status *)data;
623
624         if ((mem_guiapp_limit && ps->pai->type == PROC_TYPE_GUI) ||
625             (mem_widget_limit && ps->pai->type == PROC_TYPE_WIDGET))
626                 return lowmem_limit_appwidget(data);
627
628         _E("[DEBUG] Unable to set foreground app limit - app type not supported");
629
630         return RESOURCED_ERROR_NONE;
631 }
632
633 void lowmem_memory_init(unsigned int service_limit, unsigned int widget_limit,
634                 unsigned int guiapp_limit, unsigned int bgapp_limit)
635 {
636         mem_service_limit = service_limit;
637         mem_widget_limit = widget_limit;
638         mem_guiapp_limit = guiapp_limit;
639         mem_bgapp_limit = bgapp_limit;
640 }
641
642 void lowmem_action_init(int service_action, int widget_action,
643                 int guiapp_action, int bgapp_action)
644 {
645         if (service_action > 0)
646                 mem_service_action = service_action;
647         
648         if (widget_action > 0)
649                 mem_widget_action = widget_action;
650         
651         if (guiapp_action > 0)
652                 mem_guiapp_action = guiapp_action;
653
654         if (bgapp_action > 0)
655                 mem_bgapp_action = bgapp_action;
656 }
657
658 void lowmem_limit_init(void)
659 {
660         int result;
661         unsigned int usage;
662
663         mem_limit = MEM_LIMIT_TRHESHOLD;
664         result = cgroup_read_node_uint32(MEMCG_PATH, MEMCG_SWAP_USAGE, &usage);
665         if (result == RESOURCED_ERROR_NONE)
666                 registerpath = MEMCG_SWAP_USAGE;
667         else
668                 registerpath = MEMCG_USAGE;
669
670         register_notifier(RESOURCED_NOTIFIER_LIMIT_SYSTEM_SERVICE, lowmem_limit_system_service);
671         register_notifier(RESOURCED_NOTIFIER_LIMIT_APP, lowmem_limit_app);
672         if (mem_limit == MEM_LIMIT_NONE)
673                 return;
674
675         if (mem_service_limit)
676                 register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, lowmem_limit_service);
677         if (mem_guiapp_limit || mem_widget_limit)
678                 register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, lowmem_limit_appwidget);
679         if (mem_bgapp_limit) {
680                 if (!mem_guiapp_limit || !mem_widget_limit) {
681                         _W("Background app limit requires that both GUIApp and Widget limits to be set to work properly. Ignoring.");
682                 } else {
683                         register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, lowmem_limit_bgapp);
684                         register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, lowmem_limit_fgapp);
685                         register_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, lowmem_limit_fgapp);
686                         register_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, lowmem_limit_bgapp);
687                 }
688         }
689 }
690
691 void lowmem_limit_exit(void)
692 {
693         if (memory_limit_hash) {
694                 g_hash_table_destroy(memory_limit_hash);
695 #ifdef _UNIT_TEST
696                 memory_limit_hash = NULL;
697 #endif
698         }
699
700         unregister_notifier(RESOURCED_NOTIFIER_LIMIT_SYSTEM_SERVICE, lowmem_limit_system_service);
701         unregister_notifier(RESOURCED_NOTIFIER_LIMIT_APP, lowmem_limit_app);
702         unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, lowmem_limit_service);
703         unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, lowmem_limit_appwidget);
704         unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, lowmem_limit_bgapp);
705         unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, lowmem_limit_fgapp);
706         unregister_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, lowmem_limit_fgapp);
707         unregister_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, lowmem_limit_bgapp);
708 }