Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow programmatic override of the default runtime options #3342

Merged
merged 5 commits into from
Oct 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ All notable changes to the Pony compiler and standard library will be documented

### Added

- Allow programmatic override of the default runtime options ([PR #3342](https://github.com/ponylang/ponyc/pull/3342))

### Changed

Expand Down
115 changes: 115 additions & 0 deletions packages/builtin/runtime_options.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
struct RuntimeOptions
"""
Pony struct for the Pony runtime options C struct that can be used to
override the Pony runtime defaults via code compiled into the program.

The way this is done is by adding the following function to your `Main` actor:

```
fun @runtime_override_defaults(rto: RuntimeOptions) =>
```

and then overriding the fields of `rto` (the `RuntimeOptions` instance) as
needed.

NOTE: Command line arguments still any values set via
`@runtime_override_defaults`.


The following example overrides the `--ponyhelp` argument to default it to
`true` instead of `false` causing the compiled program to always display
the Pony runtime help:

```
actor Main
new create(env: Env) =>
env.out.print("Hello, world.")

fun @runtime_override_defaults(rto: RuntimeOptions) =>
rto.ponyhelp = true
```
"""

/* NOTE: if you change any of the field docstrings, update `options.h` in
* the runtime to keep them in sync.
*/

var ponymaxthreads: U32 = 0
"""
Use N scheduler threads. Defaults to the number of cores (not hyperthreads)
available.
This can't be larger than the number of cores available.
"""

var ponyminthreads: U32 = 0
"""
Minimum number of active scheduler threads allowed.
Defaults to 0, meaning that all scheduler threads are allowed to be
suspended when no work is available.
This can't be larger than --ponymaxthreads if provided, or the physical
cores available.
"""

var ponynoscale: Bool = false
"""
Don't scale down the scheduler threads.
See --ponymaxthreads on how to specify the number of threads explicitly.
Can't be used with --ponyminthreads.
"""

var ponysuspendthreshold: U32 = 0
"""
Amount of idle time before a scheduler thread suspends itself to minimize
resource consumption (max 1000 ms, min 1 ms).
Defaults to 1 ms.
"""

var ponycdinterval: U32 = 100
"""
Run cycle detection every N ms (max 1000 ms, min 10 ms).
Defaults to 100 ms.
"""

var ponygcinitial: USize = 14
"""
Defer garbage collection until an actor is using at least 2^N bytes.
Defaults to 2^14.
"""

var ponygcfactor: F64 = 2.0
"""
After GC, an actor will next be GC'd at a heap memory usage N times its
current value. This is a floating point value. Defaults to 2.0.
"""

var ponynoyield: Bool = false
"""
Do not yield the CPU when no work is available.
"""

var ponynoblock: Bool = false
"""
Do not send block messages to the cycle detector.
"""

var ponypin: Bool = false
"""
Pin scheduler threads to CPU cores. The ASIO thread can also be pinned if
`--ponypinasio` is set.
"""

var ponypinasio: Bool = false
"""
Pin the ASIO thread to a CPU the way scheduler threads are pinned to CPUs.
Requires `--ponypin` to be set to have any effect.
"""

var ponyversion: Bool = false
"""
Print the version of the compiler and exit.
"""

var ponyhelp: Bool = false
"""
Print the runtime usage options and exit.
"""
1 change: 1 addition & 0 deletions src/libponyc/codegen/genexe.c
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ bool genexe(compile_t* c, ast_t* program)
if(c->opt->verbosity >= VERBOSITY_INFO)
fprintf(stderr, " Reachability\n");
reach(c->reach, main_ast, c->str_create, NULL, c->opt);
reach(c->reach, main_ast, stringtab("runtime_override_defaults"), NULL, c->opt);
reach(c->reach, env_ast, c->str__create, NULL, c->opt);

if(c->opt->limit == PASS_REACH)
Expand Down
38 changes: 38 additions & 0 deletions src/libponyc/pass/sugar.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,28 @@
#include <string.h>


static ast_t* make_runtime_override_defaults(ast_t* ast)
{
pony_assert(ast != NULL);

// set it as a bare function
token_id cap = TK_AT;

BUILD(runtime_override_defaults, ast,
NODE(TK_FUN, AST_SCOPE
NODE(cap)
ID("runtime_override_defaults") // name
NONE // typeparams
NODE(TK_PARAMS, NODE(TK_PARAM, ID("rto") NODE(TK_NOMINAL, ID("$0") ID("RuntimeOptions") NONE NONE NONE) NONE)) // params
NONE // return type
NONE // error
NODE(TK_SEQ, NODE(TK_TRUE))
NONE
));

return runtime_override_defaults;
}

static ast_t* make_create(ast_t* ast)
{
pony_assert(ast != NULL);
Expand Down Expand Up @@ -82,6 +104,19 @@ bool has_member(ast_t* members, const char* name)
}


static void add_default_runtime_override_defaults_method(ast_t* ast)
{
pony_assert(ast != NULL);
ast_t* members = ast_childidx(ast, 4);

// If have no @runtime_override_defaults method, add one.
if(has_member(members, "runtime_override_defaults"))
return;

ast_append(members, make_runtime_override_defaults(ast));
}


static void add_default_constructor(ast_t* ast)
{
pony_assert(ast != NULL);
Expand Down Expand Up @@ -182,6 +217,9 @@ static ast_result_t sugar_entity(pass_opt_t* opt, ast_t* ast, bool add_create,

AST_GET_CHILDREN(ast, id, typeparams, defcap, traits, members);

if(ast_name(id) == stringtab("Main"))
add_default_runtime_override_defaults_method(ast);

if(add_create)
add_default_constructor(ast);

Expand Down
5 changes: 5 additions & 0 deletions src/libponyc/type/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -1757,6 +1757,11 @@ bool is_env(ast_t* type)
return is_literal(type, "Env");
}

bool is_runtime_options(ast_t* type)
{
return is_literal(type, "RuntimeOptions");
}

bool is_bool(ast_t* type)
{
return is_literal(type, "Bool");
Expand Down
2 changes: 2 additions & 0 deletions src/libponyc/type/subtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ bool is_none(ast_t* type);

bool is_env(ast_t* type);

bool is_runtime_options(ast_t* type);

bool is_bool(ast_t* type);

bool is_float(ast_t* type);
Expand Down
129 changes: 129 additions & 0 deletions src/libponyc/verify/fun.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,136 @@
#include "fun.h"
#include "../type/lookup.h"
#include "../type/subtype.h"
#include "ponyassert.h"
#include <string.h>

static bool verify_calls_runtime_override(pass_opt_t* opt, ast_t* ast)
{
token_id tk = ast_id(ast);
if((tk == TK_NEWREF) || (tk == TK_NEWBEREF) ||
(tk == TK_FUNREF) || (tk == TK_BEREF))
{
ast_t* method = ast_sibling(ast_child(ast));
ast_t* receiver = ast_child(ast);

// Look up the original method definition for this method call.
deferred_reification_t* method_def = lookup(opt, ast, ast_type(receiver),
ast_name(method));
ast_t* method_ast = method_def->ast;

// The deferred reification doesn't own the underlying AST so we can free it
// safely.
deferred_reify_free(method_def);

if(ast_id(ast_parent(ast_parent(method_ast))) != TK_PRIMITIVE)
{
ast_error(opt->check.errors, ast,
"the runtime_override_defaults method of the Main actor can only call functions on primitives");
return false;
}
else
{
// recursively check function call tree for other non-primitive method calls
if(!verify_calls_runtime_override(opt, method_ast))
return false;
}
}

ast_t* child = ast_child(ast);

while(child != NULL)
{
// recursively check all child nodes for non-primitive method calls
if(!verify_calls_runtime_override(opt, child))
return false;

child = ast_sibling(child);
}
return true;
}

static bool verify_main_runtime_override_defaults(pass_opt_t* opt, ast_t* ast)
{
if(ast_id(opt->check.frame->type) != TK_ACTOR)
return true;

ast_t* type_id = ast_child(opt->check.frame->type);

if(strcmp(ast_name(type_id), "Main"))
return true;

AST_GET_CHILDREN(ast, cap, id, typeparams, params, result, can_error, body);
ast_t* type = ast_parent(ast_parent(ast));

if(strcmp(ast_name(id), "runtime_override_defaults"))
return true;

bool ok = true;

if(ast_id(ast) != TK_FUN)
{
ast_error(opt->check.errors, ast,
"the runtime_override_defaults method of the Main actor must be a function");
ok = false;
}

if(ast_id(typeparams) != TK_NONE)
{
ast_error(opt->check.errors, typeparams,
"the runtime_override_defaults method of the Main actor must not take type parameters");
ok = false;
}

if(ast_childcount(params) != 1)
{
if(ast_pos(params) == ast_pos(type))
ast_error(opt->check.errors, params,
"The Main actor must have a runtime_override_defaults method which takes only a "
"single RuntimeOptions parameter");
else
ast_error(opt->check.errors, params,
"the runtime_override_defaults method of the Main actor must take only a single "
"RuntimeOptions parameter");
ok = false;
}

ast_t* param = ast_child(params);

if(param != NULL)
{
ast_t* p_type = ast_childidx(param, 1);

if(!is_runtime_options(p_type))
{
ast_error(opt->check.errors, p_type, "must be of type RuntimeOptions");
ok = false;
}
}

if(!is_none(result))
{
ast_error(opt->check.errors, result,
"the runtime_override_defaults method of the Main actor must return None");
ok = false;
}

bool bare = ast_id(cap) == TK_AT;

if(!bare)
{
ast_error(opt->check.errors, ast,
"the runtime_override_defaults method of the Main actor must be a bare function");
ok = false;
}

// check to make sure no function calls on non-primitives
if(!verify_calls_runtime_override(opt, body))
{
ok = false;
}

return ok;
}

static bool verify_main_create(pass_opt_t* opt, ast_t* ast)
{
Expand Down Expand Up @@ -367,6 +495,7 @@ bool verify_fun(pass_opt_t* opt, ast_t* ast)

// Run checks tailored to specific kinds of methods, if any apply.
if(!verify_main_create(opt, ast) ||
!verify_main_runtime_override_defaults(opt, ast) ||
!verify_primitive_init(opt, ast) ||
!verify_any_final(opt, ast) ||
!verify_any_serialise(opt, ast))
Expand Down
9 changes: 8 additions & 1 deletion src/libponyrt/options/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
#define OPT_ARG_OPTIONAL 1 << 1
#define OPT_ARG_NONE 1 << 2
#define OPT_ARGS_FINISH {NULL, 0, UINT32_MAX, UINT32_MAX}

/* NOTE: if you change any of the argument help details, update the docstrings
* in `RuntimeOptions` in the `builtin` package to keep them in sync.
*/
#define PONYRT_HELP \
"Runtime options for Pony programs (not for use with ponyc):\n" \
" --ponymaxthreads Use N scheduler threads. Defaults to the number of\n" \
Expand Down Expand Up @@ -40,7 +44,10 @@
" threads are pinned to CPUs. Requires `--ponypin` to\n" \
" be set to have any effect.\n" \
" --ponyversion Print the version of the compiler and exit.\n" \
" --ponyhelp Print the runtime usage options and exit.\n"
" --ponyhelp Print the runtime usage options and exit.\n" \
"\n" \
"NOTE: These can be programmatically overridden. See the docstring in the\n" \
" `RuntimeOptions` struct in the `builtin` package.\n"

typedef struct opt_arg_t
{
Expand Down
4 changes: 2 additions & 2 deletions src/libponyrt/sched/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,11 @@ uint32_t ponyint_cpu_count()
}

uint32_t ponyint_cpu_assign(uint32_t count, scheduler_t* scheduler,
bool nopin, bool pinasio)
bool pin, bool pinasio)
{
uint32_t asio_cpu = -1;

if(nopin)
if(!pin)
{
for(uint32_t i = 0; i < count; i++)
{
Expand Down
Loading