Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: erlang/otp
base: maint
...
head fork: psyeugenic/otp
Checking mergeability… Don't worry, you can still create the pull request.
  • 17 commits
  • 11 files changed
  • 0 commit comments
  • 1 contributor
View
1  erts/emulator/beam/atom.names
@@ -290,6 +290,7 @@ atom last_calls
atom latin1
atom Le='=<'
atom lf
+atom limits
atom line
atom line_length
atom linked_in_driver
View
169 erts/emulator/beam/bif.c
@@ -810,6 +810,7 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
*/
so.flags = SPO_USE_ARGS;
so.min_heap_size = H_MIN_SIZE;
+ so.max_heap_size = H_MAX_SIZE;
so.min_vheap_size = BIN_VH_MIN_SIZE;
so.priority = PRIORITY_NORMAL;
so.max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
@@ -852,6 +853,53 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
} else {
so.min_heap_size = erts_next_heap_size(min_heap_size, 0);
}
+ } else if (arg == am_limits && (is_list(val) || is_nil(val))) {
+ /* on the form: {limits, [{heap_size, value()}]} etc */
+ Eterm limits = val;
+ Eterm limit;
+ Eterm *tpl;
+
+ /* lots or error checks here,
+ * might be dumbed down a bit
+ */
+
+ while (is_list(limits)) {
+ limit = CAR(list_val(limits));
+ if (is_tuple(limit)) {
+ tpl = tuple_val(limit);
+
+ if (*tpl != make_arityval(2))
+ goto error;
+
+ /* reuse vars */
+ arg = tpl[1];
+ val = tpl[2];
+
+
+ /* limit heap size */
+ if (arg == am_heap_size) {
+ Sint heap_size = 0;
+
+ if (is_small(val) && signed_val(val) >= 0) {
+ heap_size = signed_val(val);
+ } else if (val == am_undefined) {
+ heap_size = 0;
+ } else
+ goto error;
+
+ so.max_heap_size = heap_size;
+ } else
+ goto error;
+ } else
+ goto error;
+
+ limits = CDR(list_val(limits));
+ }
+
+ /* error if not proper list */
+ if (is_not_nil(limits)) {
+ goto error;
+ }
} else if (arg == am_min_bin_vheap_size && is_small(val)) {
Sint min_vheap_size = signed_val(val);
if (min_vheap_size < 0) {
@@ -1482,6 +1530,79 @@ static BIF_RETTYPE process_flag_aux(Process *BIF_P,
BIF_ERROR(BIF_P, BADARG);
}
+static int process_flag_limits(Process *p, Eterm limits, Eterm *ret) {
+ Eterm limit, ils, arg, limitval;
+ Eterm *tpl;
+ Uint hsz = 0;
+ Uint *hp;
+ Eterm tup, orig_val;
+
+ ils = limits;
+
+ while (is_list(ils)) {
+ limit = CAR(list_val(ils));
+ if (is_tuple(limit)) {
+ tpl = tuple_val(limit);
+
+ if (*tpl != make_arityval(2))
+ return 0;
+
+ arg = tpl[1];
+ limitval = tpl[2];
+
+ if (arg == am_heap_size) {
+ if ((is_small(limitval) && signed_val(limitval) >= 0) || limitval == am_undefined) {
+
+ hsz += 2 /*cons*/ + 3 /* 2tup */;
+ erts_bld_uint(NULL, &hsz, LIMIT_HEAP_SIZE(p));
+
+ } else { /* not a valid heap_size */
+ return 0;
+ }
+ } else { /* not heap_size */
+ return 0;
+ }
+ } else /* not a is_tuple */
+ return 0;
+
+ ils = CDR(list_val(ils));
+ }
+
+ hp = HAlloc(p, hsz);
+ *ret = NIL;
+ ils = limits;
+
+ while (is_list(ils)) {
+ limit = CAR(list_val(ils));
+ tpl = tuple_val(limit);
+
+ arg = tpl[1];
+ limitval = tpl[2];
+
+ if (arg == am_heap_size) {
+ Sint heap_size = 0;
+
+ if (is_small(limitval))
+ heap_size = signed_val(limitval);
+
+ if (LIMIT_HEAP_SIZE(p) == 0) {
+ orig_val = am_undefined;
+ } else {
+ orig_val = erts_bld_uint(&hp, NULL, LIMIT_HEAP_SIZE(p));
+ }
+ tup = TUPLE2(hp, am_heap_size, orig_val); hp += 3;
+ *ret = CONS(hp, tup, *ret);
+
+ LIMIT_HEAP_SIZE(p) = heap_size;
+
+ }
+
+ ils = CDR(list_val(ils));
+ }
+ return 1;
+}
+
+
BIF_RETTYPE process_flag_2(BIF_ALIST_2)
{
Eterm old_value;
@@ -1591,7 +1712,17 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2)
}
BIF_RET(old_value);
}
- else if (BIF_ARG_1 == am_min_bin_vheap_size) {
+ else if (BIF_ARG_1 == am_limits) {
+ /* lots here can be refactored with spawn_opt
+ * this should be broken out
+ */
+ if (is_list(BIF_ARG_2)) {
+ if(process_flag_limits(BIF_P, BIF_ARG_2, &old_value)) {
+ BIF_RET(old_value);
+ }
+ }
+ goto error;
+ } else if (BIF_ARG_1 == am_min_bin_vheap_size) {
Sint i;
if (!is_small(BIF_ARG_2)) {
goto error;
@@ -4084,6 +4215,11 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
goto error;
}
+ /* egil: Should we calculate erts_next_heap_size before we check? */
+ if (n >= H_MAX_SIZE) {
+ goto error;
+ }
+
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_smp_thr_progress_block();
@@ -4093,6 +4229,37 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
BIF_RET(make_small(oval));
+
+ } else if (BIF_ARG_1 == am_limits) {
+
+ if (is_tuple(BIF_ARG_2)) {
+ Eterm *tp = tuple_val(BIF_ARG_2);
+ if (arityval(tp[0]) == 2) {
+ if (tp[1] == am_heap_size) {
+
+ Sint i = signed_val(tp[2]);
+ Sint oval = H_MAX_SIZE;
+
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_thr_progress_block();
+
+ H_MAX_SIZE = i;
+
+ erts_smp_thr_progress_unblock();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+
+ BIF_RET(make_small(oval));
+ } else {
+ goto error;
+ }
+ }
+ } else if (is_list(BIF_ARG_2)) {
+ /* will this be the real one? */
+ goto error;
+ } else {
+ goto error;
+ }
+
} else if (BIF_ARG_1 == am_min_bin_vheap_size) {
int oval = BIN_VH_MIN_SIZE;
View
30 erts/emulator/beam/erl_bif_info.c
@@ -568,6 +568,7 @@ static Eterm pi_args[] = {
am_min_bin_vheap_size,
am_current_location,
am_current_stacktrace,
+ am_limits,
#ifdef HYBRID
am_message_binary
#endif
@@ -618,8 +619,9 @@ pi_arg2ix(Eterm arg)
case am_min_bin_vheap_size: return 28;
case am_current_location: return 29;
case am_current_stacktrace: return 30;
+ case am_limits: return 31;
#ifdef HYBRID
- case am_message_binary: return 31;
+ case am_message_binary: return 32;
#endif
default: return -1;
}
@@ -643,7 +645,8 @@ static Eterm pi_1_keys[] = {
am_stack_size,
am_reductions,
am_garbage_collection,
- am_suspending
+ am_suspending,
+ am_limits
};
#define ERTS_PI_1_NO_OF_KEYS (sizeof(pi_1_keys)/sizeof(Eterm))
@@ -1466,6 +1469,16 @@ process_info_aux(Process *BIF_P,
break;
}
+ case am_limits: {
+ Eterm t;
+
+ hp = HAlloc(BIF_P, 3+2 + 3); /* last "3" is for outside tuple */
+
+ t = TUPLE2(hp, am_heap_size, make_small(LIMIT_HEAP_SIZE(rp))); hp += 3;
+ res = CONS(hp, t, NIL); hp += 2;
+ break;
+ }
+
case am_garbage_collection: {
DECL_AM(minor_gcs);
Eterm t;
@@ -2147,6 +2160,19 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
hp = HAlloc(BIF_P, 3);
res = TUPLE2(hp, am_sequential_tracer, val);
BIF_RET(res);
+ } else if (BIF_ARG_1 == am_limits){
+ Eterm tup, proc = NIL;
+ hp = HAlloc(BIF_P, 3+2 /* process tuple and cons */ + 3+2);
+
+ /* process limits */
+ tup = TUPLE2(hp, am_heap_size, make_small(H_MAX_SIZE)); hp += 3;
+ proc = CONS(hp, tup, NIL); hp += 2;
+
+ /* resulting limits */
+ tup = TUPLE2(hp, am_process, proc); hp += 3;
+ res = CONS(hp, tup, NIL); hp += 2;
+
+ BIF_RET(res);
} else if (BIF_ARG_1 == am_garbage_collection){
Uint val = (Uint) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
Eterm tup;
View
52 erts/emulator/beam/erl_gc.c
@@ -46,6 +46,8 @@
*/
#define ALENGTH(a) (sizeof(a)/sizeof(a[0]))
+extern BeamInstr beam_exit[];
+
static erts_smp_spinlock_t info_lck;
static Uint garbage_cols; /* no of garbage collections */
static Uint reclaimed; /* no of words reclaimed in GCs */
@@ -442,6 +444,56 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
p->last_old_htop = p->old_htop;
#endif
+ if (ERTS_PROC_HAS_HEAP_SIZE_LIMIT(p) &&
+ (HEAP_SIZE(p) + (OLD_HEAP(p) ? (OLD_HEND(p) - OLD_HEAP(p)) : 0)) > LIMIT_HEAP_SIZE(p)) {
+
+#define LOCAL_HEAP_SIZE (3 + 3)
+ DeclareTmpHeapNoproc(local_heap, LOCAL_HEAP_SIZE);
+ Eterm message;
+ Eterm *hp;
+
+ UseTmpHeapNoproc(LOCAL_HEAP_SIZE);
+ hp = local_heap;
+
+ message = make_small(HEAP_SIZE(p) + (OLD_HEAP(p) ? (OLD_HEND(p) - OLD_HEAP(p)) : 0));
+ message = TUPLE2(hp, am_heap_size, message); hp += 3;
+ message = TUPLE2(hp, am_system_limit, message); hp += 3;
+
+ erts_smp_proc_lock(p, ERTS_PROC_LOCKS_XSIG_SEND);
+ ERTS_LIMIT_EXIT_PROCESS(p, message);
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_XSIG_SEND);
+
+ /* Note #1:
+ * We want to schedule out this process as quickly as possible.
+ * Normally ERTS_VBUMP_ALL_REDS(p) but SWAPIN will not take this
+ * and FCALLS will be overridden.
+ * Hence return the number of fcalls remaining.
+ *
+ * Note #2:
+ * Since we might have SAVED_CALLS enabled we have to take this into
+ * account.
+ *
+ * Note #3:
+ * Some ops don't take gc cost into account which might be a problem
+ * here. Notably bs_append does not add gc cost and perhaps others
+ * as well. This means that a gc of a process which reaches this limit
+ * will not schedule out immediatly and might gc again thus might
+ * increase its heap block size. It will die but not as quickly.
+ * It is a small matter to add the cost but that will also add a runtime
+ * cost for binary append.
+ */
+
+ UnUseTmpHeapNoproc(LOCAL_HEAP_SIZE);
+#undef LOCAL_HEAP_SIZE
+
+ if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) {
+ ASSERT(p->fcalls <= 0);
+ return (int) (CONTEXT_REDS + p->fcalls);
+ }
+ ASSERT(p->fcalls >= 0);
+ return (int) p->fcalls;
+ }
+
/* FIXME: This function should really return an Sint, i.e., a possibly
64 bit wide signed integer, but that requires updating all the code
that calls it. For now, we just return INT_MAX if the result is too
View
16 erts/emulator/beam/erl_init.c
@@ -93,6 +93,7 @@ int erts_use_sender_punish;
Uint display_items; /* no of items to display in traces etc */
int H_MIN_SIZE; /* The minimum heap grain */
+int H_MAX_SIZE; /* The maximum heap grain */
int BIN_VH_MIN_SIZE; /* The minimum binary virtual*/
Uint32 erts_debug_flags; /* Debug flags. */
@@ -490,6 +491,8 @@ void erts_usage(void)
erts_fprintf(stderr, "-hms size set minimum heap size in words (default %d)\n",
H_DEFAULT_SIZE);
+ erts_fprintf(stderr, "-hls size set maximum (limit) heap size in words (default %d)\n",
+ H_DEFAULT_LIMIT_SIZE);
erts_fprintf(stderr, "-hmbs size set minimum binary virtual heap size in words (default %d)\n",
VH_DEFAULT_SIZE);
@@ -618,6 +621,7 @@ early_init(int *argc, char **argv) /*
erts_async_max_threads = 0;
erts_async_thread_suggested_stack_size = ERTS_ASYNC_THREAD_MIN_STACK_SIZE;
H_MIN_SIZE = H_DEFAULT_SIZE;
+ H_MAX_SIZE = H_DEFAULT_LIMIT_SIZE;
BIN_VH_MIN_SIZE = VH_DEFAULT_SIZE;
erts_initialized = 0;
@@ -1075,6 +1079,13 @@ erl_start(int argc, char **argv)
erts_usage();
}
VERBOSE(DEBUG_SYSTEM, ("using minimum heap size %d\n", H_MIN_SIZE));
+ } else if (has_prefix("ls", sub_param)) {
+ arg = get_arg(sub_param+2, argv[i+1], &i);
+ if ((H_MAX_SIZE = atoi(arg)) < 0) {
+ erts_fprintf(stderr, "bad heap size %s\n", arg);
+ erts_usage();
+ }
+ VERBOSE(DEBUG_SYSTEM, ("using maximum (limit) heap size %d\n", H_MAX_SIZE));
} else {
/* backward compatibility */
arg = get_arg(argv[i]+2, argv[i+1], &i);
@@ -1438,6 +1449,11 @@ erl_start(int argc, char **argv)
i++;
}
+ /* make sure min heap size is less than max heap size */
+ if (H_MAX_SIZE && H_MAX_SIZE <= H_MIN_SIZE) {
+ erts_fprintf(stderr, "bad heap size, min heap %d >= max heap %d\n", H_MIN_SIZE, H_MAX_SIZE);
+ erts_usage();
+ }
/* Delayed check of +P flag */
if (erts_max_processes < ERTS_MIN_PROCESSES
|| erts_max_processes > ERTS_MAX_PROCESSES
View
36 erts/emulator/beam/erl_process.c
@@ -5945,6 +5945,18 @@ Process *schedule(Process *p, int calls)
ASSERT(!(p->status_flags & ERTS_PROC_SFLG_PENDADD2SCHEDQ)
|| p->rcount == 0);
}
+#else
+ /* This could seemingly not happen before limits,
+ * so this should only happen when ERTS_LIMIT_EXIT_PROCESS
+ * has been called.
+ */
+ if (ERTS_PROC_IS_EXITING(p)) {
+ ASSERT(p->fvalue != THE_NON_VALUE);
+ p->freason = EXTAG_EXIT;
+ KILL_CATCHES(p);
+ cancel_timer(p);
+ p->i = (BeamInstr *) beam_exit;
+ }
#endif
erts_smp_runq_lock(rq);
@@ -6761,15 +6773,21 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
* noone except us has access to the process.
*/
if (so->flags & SPO_USE_ARGS) {
- p->min_heap_size = so->min_heap_size;
- p->min_vheap_size = so->min_vheap_size;
- p->prio = so->priority;
- p->max_gen_gcs = so->max_gen_gcs;
+ p->min_heap_size = so->min_heap_size;
+ p->min_vheap_size = so->min_vheap_size;
+ p->prio = so->priority;
+ p->max_gen_gcs = so->max_gen_gcs;
+
+ /* limits */
+ LIMIT_HEAP_SIZE(p) = so->max_heap_size;
} else {
- p->min_heap_size = H_MIN_SIZE;
- p->min_vheap_size = BIN_VH_MIN_SIZE;
- p->prio = PRIORITY_NORMAL;
- p->max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
+ p->min_heap_size = H_MIN_SIZE;
+ p->min_vheap_size = BIN_VH_MIN_SIZE;
+ p->prio = PRIORITY_NORMAL;
+ p->max_gen_gcs = (Uint16) erts_smp_atomic32_read_nob(&erts_max_gen_gcs);
+
+ /* limits */
+ LIMIT_HEAP_SIZE(p) = H_MAX_SIZE;
}
p->skipped = 0;
ASSERT(p->min_heap_size == erts_next_heap_size(p->min_heap_size, 0));
@@ -7595,7 +7613,7 @@ send_exit_signal(Process *c_p, /* current process if and only
ASSERT(reason != THE_NON_VALUE);
- if (ERTS_PROC_IS_TRAPPING_EXITS(rp)
+ if (ERTS_PROC_IS_TRAPPING_EXITS(rp) && !(flags & ERTS_XSIG_FLG_IGN_TRAPX)
&& (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) {
if (is_not_nil(token) && token_update)
seq_trace_update_send(token_update);
View
58 erts/emulator/beam/erl_process.h
@@ -474,6 +474,61 @@ extern ErtsAlignedSchedulerData *erts_aligned_scheduler_data;
extern ErtsSchedulerData *erts_scheduler_data;
#endif
+/* All limits within a process */
+
+typedef struct {
+ Uint heap_size;
+} ErlProcessLimits;
+
+#ifdef ERTS_SMP
+#define ERTS_LIMIT_EXIT_PROCESS(p, rsn) \
+ do { \
+ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCKS_XSIG_SEND & \
+ erts_proc_lc_my_proc_locks((p))); \
+ ASSERT(rsn != THE_NON_VALUE); \
+ if (!ERTS_PROC_PENDING_EXIT((p))) { \
+ if (is_immed((rsn))) { \
+ p->pending_exit.reason = (rsn); \
+ } else { \
+ Eterm *hp; \
+ Uint sz = size_object((rsn)); \
+ ErlHeapFragment *bp = new_message_buffer(sz); \
+ hp = &bp->mem[0]; \
+ p->pending_exit.reason = copy_struct((rsn), sz, &hp, &bp->off_heap); \
+ p->pending_exit.bp = bp; \
+ } \
+ ASSERT(ERTS_PROC_PENDING_EXIT((p))); \
+ } \
+ } while (0)
+# else
+#define ERTS_LIMIT_EXIT_PROCESS(p, rsn) \
+ do { \
+ ASSERT((rsn) != THE_NON_VALUE); \
+ if ((p)->status != P_EXITING) { \
+ if ((p)->status == P_RUNNING) { \
+ (p)->status = P_EXITING; \
+ (p)->fvalue = is_immed((rsn)) ? (rsn) : copy_object((rsn), (p)); \
+ } else { \
+ Eterm old_status = (p)->status; \
+ (p)->status = P_EXITING; \
+ (p)->fvalue = is_immed((rsn)) ? (rsn) : copy_object((rsn), (p)); \
+ (p)->freason = EXTAG_EXIT; \
+ KILL_CATCHES((p)); \
+ cancel_timer((p)); \
+ (p)->i = (BeamInstr *) beam_exit; \
+ ACTIVATE((p)); \
+ if (old_status != P_RUNABLE ) \
+ erts_add_to_runq((p)); \
+ } \
+ } \
+ } while (0)
+#endif
+
+/* Define easy access to process limits */
+#define LIMIT_HEAP_SIZE(p) ((p)->limits.heap_size)
+#define ERTS_PROC_HAS_HEAP_SIZE_LIMIT(p) ((p)->limits.heap_size > 0)
+
+
/*
* Process Specific Data.
*
@@ -714,6 +769,7 @@ struct process {
} u;
ErtsRunQueue *bound_runq;
+ ErlProcessLimits limits;
#ifdef ERTS_SMP
erts_proc_lock_t lock;
@@ -838,6 +894,7 @@ typedef struct {
*/
Uint min_heap_size; /* Minimum heap size (must be a valued returned
* from next_heap_size()). */
+ Uint max_heap_size; /* Max heap size */
Uint min_vheap_size; /* Minimum virtual heap size */
int priority; /* Priority for process. */
Uint16 max_gen_gcs; /* Maximum number of gen GCs before fullsweep. */
@@ -1033,6 +1090,7 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags;
/* Option flags to erts_send_exit_signal() */
#define ERTS_XSIG_FLG_IGN_KILL (((Uint32) 1) << 0)
#define ERTS_XSIG_FLG_NO_IGN_NORMAL (((Uint32) 1) << 1)
+#define ERTS_XSIG_FLG_IGN_TRAPX (((Uint32) 1) << 2)
/* Process status values */
View
6 erts/emulator/beam/erl_vm.h
@@ -67,8 +67,9 @@
#define INPUT_REDUCTIONS (2 * CONTEXT_REDS)
-#define H_DEFAULT_SIZE 233 /* default (heap + stack) min size */
-#define VH_DEFAULT_SIZE 32768 /* default virtual (bin) heap min size (words) */
+#define H_DEFAULT_SIZE 233 /* default (heap + stack) min size */
+#define H_DEFAULT_LIMIT_SIZE 0 /* default (heap + stack) max size, 0 = off */
+#define VH_DEFAULT_SIZE 32768 /* default virtual (bin) heap min size (words) */
#ifdef HYBRID
# define SH_DEFAULT_SIZE 2629425 /* default message area min size */
@@ -191,6 +192,7 @@ extern int num_instructions; /* Number of instruction in opc[]. */
#define MAX_PORT_LINK 8 /* Maximum number of links to a port */
extern int H_MIN_SIZE; /* minimum (heap + stack) */
+extern int H_MAX_SIZE; /* maximum (heap + stack) */
extern int BIN_VH_MIN_SIZE; /* minimum virtual (bin) heap */
extern int erts_atom_table_size;/* Atom table size */
View
1  erts/emulator/test/Makefile
@@ -66,6 +66,7 @@ MODULES= \
guard_SUITE \
hash_SUITE \
hibernate_SUITE \
+ limits_SUITE \
list_bif_SUITE \
match_spec_SUITE \
module_info_SUITE \
View
263 erts/emulator/test/limits_SUITE.erl
@@ -0,0 +1,263 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+%% File: limits_SUITE.erl
+%% Author: Björn-Egil Dahlberg
+%% Created: 2012-01-24
+
+-module(limits_SUITE).
+-author('egil@erlang.org').
+
+-export([
+ suite/0,
+ init_per_suite/1,
+ end_per_suite/1,
+ init_per_testcase/2,
+ end_per_testcase/2,
+ groups/0,
+ init_per_group/2,
+ end_per_group/2,
+ all/0
+ ]).
+
+-export([
+ process_heap/1,
+ process_heap_check_size/1,
+ do_not_trap_exit/1,
+ consecutive_limit_sets/1
+ ]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ process_heap,
+ process_heap_check_size,
+ do_not_trap_exit,
+ consecutive_limit_sets
+ ].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+init_per_testcase(Case, Config) when is_list(Config) ->
+ Dog = ?t:timetrap(?t:seconds(30)),
+ [{watchdog, Dog},{testcase, Case}|Config].
+
+end_per_testcase(_Case, Config) when is_list(Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+%% TESTCASES
+
+%% case process_heap
+
+process_heap(_Conf) ->
+ HeapLimit = 60000,
+ process_flag(trap_exit,true),
+ Watcher = watcher_start(),
+ erlang:system_monitor(Watcher, [{large_heap, HeapLimit*2}]),
+ Pid = spawn_opt(fun() -> infinity() end, [
+ {limits, [{heap_size, HeapLimit}]},
+ link
+ ]),
+ Watcher ! {self(), watch, Pid},
+ receive {Watcher, ok} -> ok end,
+ Pid ! {self(), go},
+ recvs(Watcher, Pid).
+
+recvs(Watcher, Pid) ->
+ receive
+ {'EXIT',Pid,{system_limit, {heap_size, Size}}} ->
+ io:format("ok, got system limit on ~w (correct) (size = ~w)~n ", [Pid,Size]),
+ Watcher ! {self(), done},
+ %% flush it
+ flush_watcher_exit(Watcher);
+ {Watcher,large_heap, Pid} ->
+ %% fatal our infinity loop has not caught system limit
+ %% kill it! kill it now!
+ exit(Pid, kill),
+
+ %% Hope and pray that we kill it before VM runs out of memory
+ receive {'EXIT',Pid,killed} -> ok end,
+ %% phew
+
+ %% flush it
+ flush_watcher_exit(Watcher),
+
+ ct:fail("Heap Limit did not take", []);
+ _Msg ->
+ recvs(Watcher, Pid)
+ end.
+
+flush_watcher_exit(Watcher) ->
+ receive
+ {'EXIT',Watcher, normal} -> ok
+ after
+ 1000 ->
+ {comment, "Watcher did not exit"}
+ end.
+
+%% case process_heap_check_size
+
+process_heap_check_size(_Conf) ->
+ HeapLimit = 60000,
+ process_flag(trap_exit,true),
+ Pid = spawn_opt(fun() -> infinity() end, [
+ {limits, [{heap_size, HeapLimit}]},
+ link
+ ]),
+ erlang:trace(Pid, true, [garbage_collection]),
+ Pid ! {self(), go},
+ recvs_check_size(Pid, HeapLimit,0).
+
+recvs_check_size(Pid, HeapLimit,CSz) ->
+ receive
+ {'EXIT',Pid, {system_limit, {heap_size, Size}}} ->
+ io:format("probably ok, got system limit on ~w (correct)~n ", [Pid]),
+ io:format("last known size was ~w, limit set to ~w, terminated at size ~w~n", [CSz, HeapLimit, Size]),
+ ok;
+ {trace,Pid,gc_start, _} ->
+ recvs_check_size(Pid, HeapLimit,CSz);
+ {trace,Pid,gc_end, GI} ->
+ OHBsz = proplists:get_value(old_heap_block_size, GI),
+ HBsz = proplists:get_value(heap_block_size, GI),
+ Sz = OHBsz + HBsz,
+ if
+ Sz < HeapLimit ->
+ %% ok
+ recvs_check_size(Pid, HeapLimit, Sz);
+ true ->
+ % humm, was the process killed?
+ receive
+ {'EXIT',Pid, {system_limit, {heap_size, Size}}} ->
+ io:format("ok, correct heap size limit termination (terminated at size ~w~n", [Size]),
+ % the very correct behaviour
+ ok
+ after 500 ->
+ %% it seems we didn't get an exit
+ %% kill it now now now!
+
+ %% Hope and pray that we kill the process before VM runs out of memory
+ exit(Pid, kill),
+ receive {'EXIT',Pid,killed} -> ok end,
+ %% phew
+ io:format("Heap Limit (~w) reached ~w but process ~w was not terminated.~n", [
+ HeapLimit,
+ Sz,
+ Pid
+ ]),
+ ct:fail("Heap limit reached but process not killed")
+ end
+ end;
+ _Msg ->
+ recvs_check_size(Pid, HeapLimit, CSz)
+ end.
+
+%% case do_not_trap_exit
+
+do_not_trap_exit(_Conf) ->
+ HeapLimit = 200,
+ process_flag(trap_exit,true),
+ Pids = [spawn_link(fun() ->
+ erlang:process_flag(trap_exit, true),
+ erlang:process_flag(limits, [{heap_size, HeapLimit}]),
+ lists:seq(1,1000)
+ end) || _ <- lists:seq(1,1000)],
+ [receive {'EXIT',Pid,{system_limit,{heap_size,_}}} -> ok end || Pid <- Pids],
+ ok.
+
+
+%% case consecutive_limit_sets
+
+consecutive_limit_sets(_Conf) ->
+ process_flag(trap_exit,true),
+ Me = self(),
+ Pids = [spawn_opt(fun() ->
+ [{heap_size, undefined}] = erlang:process_flag(limits, [{heap_size, 50000}]),
+ [{heap_size, 50000}] = erlang:process_flag(limits, [{heap_size, undefined}]),
+ [{heap_size, undefined}] = erlang:process_flag(limits, [{heap_size, 40000}]),
+ [{heap_size, 40000}] = erlang:process_flag(limits, [{heap_size, 500}]),
+ Above500 = lists:seq(1, 500),
+ erlang:garbage_collect(),
+ Me ! {self(), point_of_no_execute, length(Above500)}
+ end, [
+ link,
+ {limits, [{heap_size, undefined}]}
+ ]) || _ <- lists:seq(1,100)],
+ receive_consecutive_limits(Pids).
+
+receive_consecutive_limits([]) -> ok;
+receive_consecutive_limits(Pids) ->
+ receive
+ {'EXIT',Pid, {system_limit, {heap_size, _Size}}} ->
+ % yep, ok
+ receive_consecutive_limits(Pids -- [Pid]);
+ Msg ->
+ % "oh my gosh", to quote skrillex cup stacking
+ % we have an unexptected message, i.e. a failure
+ [exit(Id, kill) || Id <- Pids],
+ ct:fail("No system_limit exit, instead we got: ~p~n", [Msg])
+ end.
+
+
+
+%% aux
+
+
+infinity() -> receive {_, go} -> infinity_loop([<<"hi">>]) end.
+infinity_loop([I|Is]) -> infinity_loop([<<" 42 ", I/binary>>, I|Is]).
+
+
+watcher_start() ->
+ Me = self(),
+ spawn_link(fun() -> watch(Me, none) end).
+
+watch(Parent, Who) ->
+ receive
+ {Parent, done} ->
+ ok;
+ {Parent, watch, Pid} ->
+ Parent ! {self(), ok},
+ watch(Parent, Pid);
+ {monitor, Who, large_heap, Info} ->
+ io:format("Monitored limit process ~w got ~p~n", [Who, Info]),
+ io:format("This should not happen since this process should already be terminated~n"),
+ Parent ! {self(), large_heap, Who};
+ _Other ->
+ watch(Parent, Who)
+ end.
+
+
View
1  erts/etc/common/erlexec.c
@@ -130,6 +130,7 @@ static char *pluss_val_switches[] = {
/* +h arguments with values */
static char *plush_val_switches[] = {
"ms",
+ "ls",
"mbs",
"",
NULL

No commit comments for this range

Something went wrong with that request. Please try again.