Skip to content

Commit

Permalink
Trigger GC for actors when they tell the cycle detector they're blocked
Browse files Browse the repository at this point in the history
Prior to this commit, if an actor blocked, it did not run GC to free
any memory it no longer needed. This would result in blocked actors
holding on to (potentially lots of) memory unnecessarily.

This commit causes GC to be triggered when the cycle detector asks
an actor if it is blocked and the actor responds telling the cycle
detector that it is blocked and also when the actor initially tells
the cycle detector that it is blocked. This should result in memory
being held by blocked actors to be freed more quickly even if the
cycle detector doesn't end up detecting a cycle and reaping the
actors.
  • Loading branch information
dipinhora committed Sep 26, 2020
1 parent f7996d0 commit 4a548dc
Showing 1 changed file with 29 additions and 13 deletions.
42 changes: 29 additions & 13 deletions src/libponyrt/actor/actor.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ static void send_unblock(pony_actor_t* actor)
ponyint_cycle_unblock(actor);
}

static void send_block(pony_actor_t* actor)
{
// We're blocked, send block message.
set_flag(actor, FLAG_BLOCKED_SENT);
set_flag(actor, FLAG_CD_CONTACTED);
pony_assert(ctx->current == actor);
ponyint_cycle_block(actor, &actor->gc);

// trigger a GC if we're sending a block message to the CD
// GC will get run next time `try_gc` is called which should
// happen once all messages get processed in the messageq
// at the time `pony_actor_run` was called if not earlier
// when an app message is processed
pony_triggergc(ctx);
}

static bool handle_message(pony_ctx_t* ctx, pony_actor_t* actor,
pony_msg_t* msg)
{
Expand Down Expand Up @@ -192,13 +208,9 @@ static bool handle_message(pony_ctx_t* ctx, pony_actor_t* actor,
#endif

pony_assert(!ponyint_is_cycle(actor));

if(has_flag(actor, FLAG_BLOCKED) && !has_flag(actor, FLAG_BLOCKED_SENT))
{
// We're blocked, send block message.
set_flag(actor, FLAG_BLOCKED_SENT);
pony_assert(ctx->current == actor);
ponyint_cycle_block(actor, &actor->gc);
}
send_block(actor);

return false;
}
Expand Down Expand Up @@ -418,9 +430,12 @@ bool ponyint_actor_run(pony_ctx_t* ctx, pony_actor_t* actor, bool polling)
// in the future we will wait for the CD to reach out and ask
// if we're blocked or not.
// But, only if gc.rc > 0 because if gc.rc == 0 we are a zombie.
set_flag(actor, FLAG_BLOCKED_SENT);
set_flag(actor, FLAG_CD_CONTACTED);
ponyint_cycle_block(actor, &actor->gc);
send_block(actor);

// Try and run GC because we're blocked and sending a block message
// to the CD and we're past the normal point at which `try_gc` is
// called as part of this function
try_gc(ctx, actor);
}

}
Expand Down Expand Up @@ -479,9 +494,7 @@ bool ponyint_actor_run(pony_ctx_t* ctx, pony_actor_t* actor, bool polling)
// have a reference to this actor (rc is 0) so another actor can not
// send it an application message that results this actor becoming
// unblocked (which would create a race condition).
set_flag(actor, FLAG_BLOCKED_SENT);
set_flag(actor, FLAG_CD_CONTACTED);
ponyint_cycle_block(actor, &actor->gc);
send_block(actor);
}
}
}
Expand Down Expand Up @@ -866,7 +879,10 @@ void* pony_alloc_large_final(pony_ctx_t* ctx, size_t size)
PONY_API void pony_triggergc(pony_ctx_t* ctx)
{
pony_assert(ctx->current != NULL);
ctx->current->heap.next_gc = 0;

// only trigger gc if actor allocated something on the heap
if(ctx->current->heap.used > 0)
ctx->current->heap.next_gc = 0;
}

PONY_API void pony_schedule(pony_ctx_t* ctx, pony_actor_t* actor)
Expand Down

0 comments on commit 4a548dc

Please sign in to comment.