/* doc: <routine name="rt_processor_application_loop" return_type="void" export="shared"> doc: <summary> The main loop of a SCOOP processor. doc: This is the entry point for every processor and will be the called by doc: rt_processor_registry.c:spawn_main(). The root thread of the application will also doc: enter this loop after execution of the root feature. </summary> doc: <param name="self" type="struct rt_processor*"> The processor object. Must not be NULL. </param> doc: <thread_safety> Not safe. </thread_safety> doc: <synchronization> Only execute this feature on the thread behind processor 'self'. </synchronization> doc: </routine> */ rt_shared void rt_processor_application_loop (struct rt_processor* self) { EIF_BOOLEAN is_stopped = EIF_FALSE; REQUIRE ("self_not_null", self); self->is_active = EIF_FALSE; while (!is_stopped) { struct rt_message next_job; /* When all processors are idle, trigger a * garbage collection cycle. This is sufficient for * program termination, but it isn't useful to free * processors early, before RT_MAX_SCOOP_PROCESSOR_COUNT * has been reached. * TODO: Since the introduction of the early GC technique, * the mechanism here has become obsolete and can be * removed. */ if (decrement_and_fetch_active_processor_count() == 0 && rt_message_channel_is_empty (&self->queue_of_queues)) { plsc(); } rt_message_channel_receive_with_gc (&self->queue_of_queues, &next_job); if (next_job.message_type == SCOOP_MESSAGE_ADD_QUEUE) { increment_active_processor_count(); self->is_active = EIF_TRUE; /* TODO: The information (self->client) is not used by SCOOP itself, * but may be used in the debugger. The idea is to display current * client region of a processor. * However, here we use the client processor instead, because we * don't have the information about the region available. In the future * it may be useful to add a new field to rt_message or in the private * queue that shows the current client. */ self->client = next_job.sender_processor->pid; rt_processor_process_private_queue (self, next_job.queue); self->is_active = EIF_FALSE; self->client = EIF_NULL_PROCESSOR; } else { CHECK ("shutdown_message", next_job.message_type == SCOOP_MESSAGE_SHUTDOWN); is_stopped = EIF_TRUE; } } /* Check disabled because queue-of-queues might contain multiple shutdown messages. */ /*CHECK ("empty_queue_of_queues", rt_message_channel_is_empty (&self->queue_of_queues)); */ }
/* doc: <routine name="rt_request_gc_cycle" return_type="void" export="shared"> doc: <summary> Run a GC cycle when 'fingerprint' has not changed since the last call. </summary> doc: <param name="fingerprint" type="int*"> The fingerprint value. </param> doc: <thread_safety> Safe. </thread_safety> doc: <synchronization> None required, done internally through atomic operations. </synchronization> doc: </routine> */ rt_shared void rt_request_gc_cycle (int* fingerprint) { static volatile int gc_fingerprint = 0; int previous_fingerprint = * fingerprint; int current_fingerprint = RTS_ACAS_I32 (&gc_fingerprint, previous_fingerprint + 1, previous_fingerprint); if (current_fingerprint == previous_fingerprint) { /* The fingerprint is unchanged since last call, no GC was run, do it now. */ /* Record newly written fingerprint for the next time. */ * fingerprint = previous_fingerprint + 1; /* Run GC. */ plsc(); } else { /* Record current fingerprint for the next time. */ * fingerprint = current_fingerprint; } }
rt_public main(void) { /* Tests for the local variable stack */ int i; char *a1, *a2; printf("> Starting tests for local variable stack.\n"); /* Check the stack */ printf(">> Checking the stack management routines.\n"); printf(">>> Pushing one item.\n"); epush(&loc_stack, (char *) 0); stack_stats(); printf(">>> Poping the stack.\n"); epop(&loc_stack, 1); stack_stats(); /* With 10000 items */ printf(">>> Pushing 20000 items.\n"); for (i = 0; i < 20000; i++) evpush(1, &i); stack_stats(); printf(">>> Poping one item.\n"); epop(&loc_stack, 1); stack_stats(); printf(">>> Poping 9999 items.\n"); epop(&loc_stack, 9999); stack_stats(); printf(">>> Poping 10000 items (stack should be empty).\n"); epop(&loc_stack, 10000); stack_stats(); /* Test collection of local vars */ printf(">> Testing collection of local vars.\n"); printf(">>> Creating object A (remembered)\n"); a1 = emalloc(0); eremb(a1); printf(">>> Creating object B (not remembered)\n"); a2 = emalloc(0); printf(">>> Pushing A and B in local stack.\n"); evpush(2, &a1, &a2); stack_stats(); collect_stats(); printf(">>>> Address of A: 0x%lx\n", a1); printf(">>>> Address of B: 0x%lx\n", a2); printf(">>> Running a full collection.\n"); plsc(); stack_stats(); collect_stats(); printf(">>>> Address of A: 0x%lx (changed)\n", a1); printf(">>>> Address of B: 0x%lx (changed)\n", a2); printf(">>> Running a full collection again.\n"); plsc(); stack_stats(); collect_stats(); printf(">>>> Address of A: 0x%lx (same as first one)\n", a1); printf(">>>> Address of B: 0x%lx (same as first one)\n", a2); printf("> End of tests.\n"); exit(0); }