Merge branch 'mach' into bytecode_cleanup
This commit is contained in:
31
num_torture.cm
Normal file
31
num_torture.cm
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// num_torture.cm — integer math torture test
|
||||||
|
// Pure integer arithmetic so it stays on the fast int path.
|
||||||
|
// Returns the final checksum so the caller can verify correctness.
|
||||||
|
|
||||||
|
var n = 5000000
|
||||||
|
var sum = 0
|
||||||
|
var i = 0
|
||||||
|
var a = 0
|
||||||
|
var b = 0
|
||||||
|
|
||||||
|
while (i < n) {
|
||||||
|
a = (i * 7 + 13) % 10007
|
||||||
|
b = (a * a) % 10007
|
||||||
|
sum = (sum + b) % 1000000007
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return function(n) {
|
||||||
|
var i = 0
|
||||||
|
var a = 0
|
||||||
|
var b = 0
|
||||||
|
var sum = 0
|
||||||
|
while (i < n) {
|
||||||
|
a = (i * 7 + 13) % 10007
|
||||||
|
b = (a * a) % 10007
|
||||||
|
sum = (sum + b) % 1000000007
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum
|
||||||
|
}
|
||||||
@@ -460,6 +460,7 @@ int cell_init(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (scheduler_actor_count() > 0) {
|
if (scheduler_actor_count() > 0) {
|
||||||
|
scheduler_enable_quiescence();
|
||||||
actor_loop();
|
actor_loop();
|
||||||
exit_handler();
|
exit_handler();
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ typedef struct cell_rt {
|
|||||||
double ar_secs; // time for unneeded
|
double ar_secs; // time for unneeded
|
||||||
|
|
||||||
int disrupt;
|
int disrupt;
|
||||||
|
int is_quiescent; // tracked by scheduler for quiescence detection
|
||||||
int main_thread_only;
|
int main_thread_only;
|
||||||
int affinity;
|
int affinity;
|
||||||
|
|
||||||
@@ -82,6 +83,7 @@ void actor_loop();
|
|||||||
void actor_initialize(void);
|
void actor_initialize(void);
|
||||||
void actor_free(cell_rt *actor);
|
void actor_free(cell_rt *actor);
|
||||||
int scheduler_actor_count(void);
|
int scheduler_actor_count(void);
|
||||||
|
void scheduler_enable_quiescence(void);
|
||||||
|
|
||||||
uint64_t cell_ns();
|
uint64_t cell_ns();
|
||||||
void cell_sleep(double seconds);
|
void cell_sleep(double seconds);
|
||||||
|
|||||||
@@ -47,9 +47,11 @@ static struct {
|
|||||||
actor_node *main_head; // Main Thread Queue Head
|
actor_node *main_head; // Main Thread Queue Head
|
||||||
actor_node *main_tail; // Main Thread Queue Tail
|
actor_node *main_tail; // Main Thread Queue Tail
|
||||||
|
|
||||||
int shutting_down;
|
int shutting_down;
|
||||||
|
int quiescence_enabled; // set after bootstrap, before actor_loop
|
||||||
pthread_t *worker_threads;
|
_Atomic int quiescent_count; // actors idle with no messages and no timers
|
||||||
|
|
||||||
|
pthread_t *worker_threads;
|
||||||
int num_workers;
|
int num_workers;
|
||||||
pthread_t timer_thread;
|
pthread_t timer_thread;
|
||||||
} engine;
|
} engine;
|
||||||
@@ -258,6 +260,10 @@ void actor_initialize(void) {
|
|||||||
|
|
||||||
void actor_free(cell_rt *actor)
|
void actor_free(cell_rt *actor)
|
||||||
{
|
{
|
||||||
|
if (actor->is_quiescent) {
|
||||||
|
actor->is_quiescent = 0;
|
||||||
|
atomic_fetch_sub(&engine.quiescent_count, 1);
|
||||||
|
}
|
||||||
lockless_shdel(actors, actor->id);
|
lockless_shdel(actors, actor->id);
|
||||||
|
|
||||||
// Do not go forward with actor destruction until the actor is completely free
|
// Do not go forward with actor destruction until the actor is completely free
|
||||||
@@ -318,6 +324,21 @@ int scheduler_actor_count(void) {
|
|||||||
return (int)lockless_shlen(actors);
|
return (int)lockless_shlen(actors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void scheduler_enable_quiescence(void) {
|
||||||
|
engine.quiescence_enabled = 1;
|
||||||
|
// Check if all actors are already quiescent
|
||||||
|
int qc = atomic_load(&engine.quiescent_count);
|
||||||
|
int total = (int)lockless_shlen(actors);
|
||||||
|
if (qc >= total && total > 0) {
|
||||||
|
pthread_mutex_lock(&engine.lock);
|
||||||
|
engine.shutting_down = 1;
|
||||||
|
pthread_cond_broadcast(&engine.wake_cond);
|
||||||
|
pthread_cond_broadcast(&engine.timer_cond);
|
||||||
|
pthread_cond_broadcast(&engine.main_cond);
|
||||||
|
pthread_mutex_unlock(&engine.lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void exit_handler(void) {
|
void exit_handler(void) {
|
||||||
static int already_exiting = 0;
|
static int already_exiting = 0;
|
||||||
if (already_exiting) return;
|
if (already_exiting) return;
|
||||||
@@ -371,9 +392,13 @@ void set_actor_state(cell_rt *actor)
|
|||||||
|
|
||||||
case ACTOR_IDLE:
|
case ACTOR_IDLE:
|
||||||
if (arrlen(actor->letters)) {
|
if (arrlen(actor->letters)) {
|
||||||
|
if (actor->is_quiescent) {
|
||||||
|
actor->is_quiescent = 0;
|
||||||
|
atomic_fetch_sub(&engine.quiescent_count, 1);
|
||||||
|
}
|
||||||
actor->state = ACTOR_READY;
|
actor->state = ACTOR_READY;
|
||||||
actor->ar = 0;
|
actor->ar = 0;
|
||||||
|
|
||||||
actor_node *n = malloc(sizeof(actor_node));
|
actor_node *n = malloc(sizeof(actor_node));
|
||||||
n->actor = actor;
|
n->actor = actor;
|
||||||
n->next = NULL;
|
n->next = NULL;
|
||||||
@@ -398,21 +423,46 @@ void set_actor_state(cell_rt *actor)
|
|||||||
}
|
}
|
||||||
pthread_mutex_unlock(&engine.lock);
|
pthread_mutex_unlock(&engine.lock);
|
||||||
|
|
||||||
} else if (!arrlen(actor->letters) && !hmlen(actor->timers)) {
|
} else if (!hmlen(actor->timers)) {
|
||||||
// Schedule remove timer
|
// No messages AND no timers
|
||||||
static uint32_t global_timer_id = 1;
|
// Only count as quiescent if no $unneeded callback registered
|
||||||
uint32_t id = global_timer_id++;
|
int has_unneeded = !JS_IsNull(actor->unneeded_ref.val);
|
||||||
actor->ar = id;
|
if (!actor->is_quiescent && actor->id && !has_unneeded) {
|
||||||
|
actor->is_quiescent = 1;
|
||||||
uint64_t now = cell_ns();
|
int qc = atomic_fetch_add(&engine.quiescent_count, 1) + 1;
|
||||||
uint64_t execute_at = now + (uint64_t)(actor->ar_secs * 1e9);
|
int total = (int)lockless_shlen(actors);
|
||||||
|
if (qc >= total && total > 0 && engine.quiescence_enabled) {
|
||||||
pthread_mutex_lock(&engine.lock);
|
pthread_mutex_lock(&engine.lock);
|
||||||
heap_push(execute_at, actor, id, TIMER_NATIVE_REMOVE);
|
engine.shutting_down = 1;
|
||||||
if (timer_heap[0].timer_id == id) {
|
pthread_cond_broadcast(&engine.wake_cond);
|
||||||
pthread_cond_signal(&engine.timer_cond);
|
pthread_cond_broadcast(&engine.timer_cond);
|
||||||
|
pthread_cond_broadcast(&engine.main_cond);
|
||||||
|
pthread_mutex_unlock(&engine.lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!engine.shutting_down) {
|
||||||
|
// Schedule remove timer
|
||||||
|
static uint32_t global_timer_id = 1;
|
||||||
|
uint32_t id = global_timer_id++;
|
||||||
|
actor->ar = id;
|
||||||
|
|
||||||
|
uint64_t now = cell_ns();
|
||||||
|
uint64_t execute_at = now + (uint64_t)(actor->ar_secs * 1e9);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&engine.lock);
|
||||||
|
heap_push(execute_at, actor, id, TIMER_NATIVE_REMOVE);
|
||||||
|
if (timer_heap[0].timer_id == id) {
|
||||||
|
pthread_cond_signal(&engine.timer_cond);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&engine.lock);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Has timers but no letters — waiting, not quiescent
|
||||||
|
if (actor->is_quiescent) {
|
||||||
|
actor->is_quiescent = 0;
|
||||||
|
atomic_fetch_sub(&engine.quiescent_count, 1);
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&engine.lock);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -539,6 +589,12 @@ const char *register_actor(const char *id, cell_rt *actor, int mainthread, doubl
|
|||||||
free(actor->id);
|
free(actor->id);
|
||||||
return "Actor with given ID already exists.";
|
return "Actor with given ID already exists.";
|
||||||
}
|
}
|
||||||
|
// Now that actor is in the registry, track its quiescent state
|
||||||
|
if (actor->state == ACTOR_IDLE && !arrlen(actor->letters)
|
||||||
|
&& !hmlen(actor->timers) && JS_IsNull(actor->unneeded_ref.val)) {
|
||||||
|
actor->is_quiescent = 1;
|
||||||
|
atomic_fetch_add(&engine.quiescent_count, 1);
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user