From 4a548dc196a16202624274cfe921d4b1486c2d84 Mon Sep 17 00:00:00 2001 From: Dipin Hora Date: Fri, 25 Sep 2020 21:49:28 -0400 Subject: [PATCH] Trigger GC for actors when they tell the cycle detector they're blocked 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. --- src/libponyrt/actor/actor.c | 42 +++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/libponyrt/actor/actor.c b/src/libponyrt/actor/actor.c index fe9156c34dd..246c6d5c067 100644 --- a/src/libponyrt/actor/actor.c +++ b/src/libponyrt/actor/actor.c @@ -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) { @@ -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; } @@ -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); } } @@ -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); } } } @@ -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)