feat(runtime/object): given mk_task(o), mark objects reachable from o as MTHeap
This commit is contained in:
parent
d100f95469
commit
827021a6c5
2 changed files with 94 additions and 33 deletions
|
|
@ -777,10 +777,68 @@ static task_object * alloc_task(obj_arg v) {
|
|||
return new (alloc_heap_object(sizeof(task_object))) task_object(v); // NOLINT
|
||||
}
|
||||
|
||||
void to_mt(object * o);
|
||||
static obj_res to_mt_fn(obj_arg o) {
|
||||
to_mt(o);
|
||||
dec(o);
|
||||
return box(0);
|
||||
}
|
||||
|
||||
void to_mt(object * o) {
|
||||
if (is_scalar(o) || !is_st_heap_obj(o)) return;
|
||||
o->m_mem_kind = static_cast<unsigned>(object_memory_kind::MTHeap);
|
||||
|
||||
switch (get_kind(o)) {
|
||||
case object_kind::ScalarArray:
|
||||
case object_kind::String:
|
||||
case object_kind::MPZ:
|
||||
return;
|
||||
case object_kind::PArrayPop:
|
||||
case object_kind::PArrayPush:
|
||||
case object_kind::PArraySet:
|
||||
case object_kind::PArrayRoot:
|
||||
/* `to_mt` cannot be used with parray. They must be copied when used in multiple threads. */
|
||||
lean_unreachable();
|
||||
return;
|
||||
case object_kind::External: {
|
||||
object * fn = alloc_closure(reinterpret_cast<void*>(to_mt_fn), 1, 0);
|
||||
to_external(o)->for_each_nested(fn);
|
||||
dec(fn);
|
||||
return;
|
||||
}
|
||||
case object_kind::Task:
|
||||
to_mt(task_get(o));
|
||||
return;
|
||||
case object_kind::Constructor: {
|
||||
object ** it = cnstr_obj_cptr(o);
|
||||
object ** end = it + cnstr_num_objs(o);
|
||||
for (; it != end; ++it) to_mt(*it);
|
||||
break;
|
||||
}
|
||||
case object_kind::Closure: {
|
||||
object ** it = closure_arg_cptr(o);
|
||||
object ** end = it + closure_num_fixed(o);
|
||||
for (; it != end; ++it) to_mt(*it);
|
||||
break;
|
||||
}
|
||||
case object_kind::Array: {
|
||||
object ** it = array_cptr(o);
|
||||
object ** end = it + array_size(o);
|
||||
for (; it != end; ++it) to_mt(*it);
|
||||
break;
|
||||
}
|
||||
case object_kind::Thunk:
|
||||
if (object * c = to_thunk(o)->m_closure) to_mt(c);
|
||||
if (object * v = to_thunk(o)->m_value) to_mt(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
obj_res mk_task(obj_arg c, unsigned prio) {
|
||||
if (!g_task_manager) {
|
||||
return mk_thunk(c);
|
||||
} else {
|
||||
to_mt(c);
|
||||
task_object * new_task = alloc_task(c, prio);
|
||||
g_task_manager->enqueue(new_task);
|
||||
return new_task;
|
||||
|
|
|
|||
|
|
@ -77,6 +77,38 @@ struct constructor_object : public object {
|
|||
object(object_kind::Constructor, m), m_tag(tag), m_num_objs(num_objs), m_scalar_size(scalar_sz) {}
|
||||
};
|
||||
|
||||
/*
|
||||
In our runtime, a Lean function consume the reference counter (RC) of its argument or not.
|
||||
We say this behavior is part of the "calling convention" for the function. We say an argument uses:
|
||||
|
||||
1- "standard" calling convention if it consumes/decrements the RC.
|
||||
In this calling convention each argument should be viewed as a resource that is consumed by the function.
|
||||
This is roughly equivalent to `S && a` in C++, where `S` is a smart pointer, and `a` is the argument.
|
||||
When this calling convention is used for an argument `x`, then it is safe to perform destructive updates to
|
||||
`x` if its RC is 1.
|
||||
|
||||
2- "borrowed" calling convention if it doesn't consume/decrement the RC, and it is the responsability of the caller
|
||||
to decrement the RC.
|
||||
This is roughly equivalent to `S const & a` in C++, where `S` is a smart pointer, and `a` is the argument.
|
||||
|
||||
For returning objects, we also have two conventions
|
||||
|
||||
1- "standard" result. The caller is responsible for consuming the RC of the result.
|
||||
This is roughly equivalent to returning a smart point `S` by value in C++.
|
||||
|
||||
2- "borrowed" result. The caller is not responsible for decreasing the RC.
|
||||
This is roughly equivalent to returning a smart point reference `S const &` in C++.
|
||||
|
||||
Functions stored in closures use the "standard" calling convention.
|
||||
*/
|
||||
|
||||
/* The following typedef's are used to document the calling convention for the primitives. */
|
||||
typedef object * obj_arg; /* Standard object argument. */
|
||||
typedef object * b_obj_arg; /* Borrowed object argument. */
|
||||
typedef object * u_obj_arg; /* Unique (aka non shared) object argument. */
|
||||
typedef object * obj_res; /* Standard object result. */
|
||||
typedef object * b_obj_res; /* Borrowed object result. */
|
||||
|
||||
/* Array of objects.
|
||||
Header size: 16 bytes in 32 bit machines and 32 bytes in 64 bit machines. */
|
||||
struct array_object : public object {
|
||||
|
|
@ -175,8 +207,11 @@ struct task_object : public object {
|
|||
For example, we use it to wrap the Lean environment object. */
|
||||
struct external_object : public object {
|
||||
explicit external_object(object_memory_kind m = c_init_mem_kind): object(object_kind::External, m) {}
|
||||
virtual void dealloc() {}
|
||||
virtual ~external_object() {}
|
||||
virtual void dealloc() {}
|
||||
/* For each nested object `a`, execute `apply_1(fn, a)`.
|
||||
Recall that apply_1 consumes the RC of `fn` and `a`. */
|
||||
virtual void for_each_nested(b_obj_arg /* fn */) {}
|
||||
};
|
||||
|
||||
inline bool is_null(object * o) { return o == nullptr; }
|
||||
|
|
@ -473,38 +508,6 @@ bool int_big_eq(object * a1, object * a2);
|
|||
bool int_big_le(object * a1, object * a2);
|
||||
bool int_big_lt(object * a1, object * a2);
|
||||
|
||||
/*
|
||||
In our runtime, a Lean function consume the reference counter (RC) of its argument or not.
|
||||
We say this behavior is part of the "calling convention" for the function. We say an argument uses:
|
||||
|
||||
1- "standard" calling convention if it consumes/decrements the RC.
|
||||
In this calling convention each argument should be viewed as a resource that is consumed by the function.
|
||||
This is roughly equivalent to `S && a` in C++, where `S` is a smart pointer, and `a` is the argument.
|
||||
When this calling convention is used for an argument `x`, then it is safe to perform destructive updates to
|
||||
`x` if its RC is 1.
|
||||
|
||||
2- "borrowed" calling convention if it doesn't consume/decrement the RC, and it is the responsability of the caller
|
||||
to decrement the RC.
|
||||
This is roughly equivalent to `S const & a` in C++, where `S` is a smart pointer, and `a` is the argument.
|
||||
|
||||
For returning objects, we also have two conventions
|
||||
|
||||
1- "standard" result. The caller is responsible for consuming the RC of the result.
|
||||
This is roughly equivalent to returning a smart point `S` by value in C++.
|
||||
|
||||
2- "borrowed" result. The caller is not responsible for decreasing the RC.
|
||||
This is roughly equivalent to returning a smart point reference `S const &` in C++.
|
||||
|
||||
Functions stored in closures use the "standard" calling convention.
|
||||
*/
|
||||
|
||||
/* The following typedef's are used to document the calling convention for the primitives. */
|
||||
typedef object * obj_arg; /* Standard object argument. */
|
||||
typedef object * b_obj_arg; /* Borrowed object argument. */
|
||||
typedef object * u_obj_arg; /* Unique (aka non shared) object argument. */
|
||||
typedef object * obj_res; /* Standard object result. */
|
||||
typedef object * b_obj_res; /* Borrowed object result. */
|
||||
|
||||
// =======================================
|
||||
// Constructor objects
|
||||
inline obj_res alloc_cnstr(unsigned tag, unsigned num_objs, unsigned scalar_sz) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue