git @ Cat's Eye Technologies Bhuna / master src / lib / process.c
master

Tree @master (Download .tar.gz)

process.c @masterraw · history · blame

#include <stdio.h>
#include <stdlib.h>

#include "mem.h"
#include "process.h"
#include "vm.h"
#include "closure.h"
#include "ast.h"
#include "activation.h"

#define TIMESLICE	2048 /* 4096 */
extern int trace_scheduling;

struct process		*current_process = NULL;
struct process		*run_head = NULL;
struct process		*wait_head = NULL;

static int		 procno = 1;

struct process *
process_new(struct vm *vm)
{
	struct process *p;

	p = bhuna_malloc(sizeof(struct process));
	p->vm = vm;
	p->number = procno++;
	p->msg_head = NULL;
	p->asleep = 0;
	p->next = run_head;
	p->prev = NULL;
	if (run_head != NULL)
		run_head->prev = p;
	run_head = p;

	return(p);
}

void
process_free(struct process *p)
{
	if (p == NULL) return;

#ifdef DEBUG
	if (trace_scheduling)
		printf("vm activation stack hiwater was %d bytes\n",
		    p->vm->astack_hi - p->vm->astack);
#endif

	if (p->prev != NULL)
		p->prev->next = p->next;
	else if (p == run_head)
		run_head = p->next;
	else if (p == wait_head)
		wait_head = p->next;

	if (p->next != NULL)
		p->next->prev = p->prev;

	bhuna_free(p);
}

struct process *
process_spawn(struct closure *k)
{
	struct vm *vm;
	struct process *p;

	vm = vm_new(current_process->vm->program, current_process->vm->prog_size);
	vm_set_pc(vm, k->label);

	vm->current_ar = activation_new_on_heap(
	    k->arity + k->locals, NULL, k->ar);

	p = process_new(vm);
#ifdef DEBUG
	if (trace_scheduling)
		printf("process #%d created\n", p->number);
#endif

	return(p);
}

void
process_send(struct process *p, struct value v)
{
	struct message *m;

	m = bhuna_malloc(sizeof(struct message));
	m->next = p->msg_head;
	p->msg_head = m;
	m->payload = v;

#ifdef DEBUG
	if (trace_scheduling) {
		printf("send from process #%d to process #%d: ",
		    current_process->number, p->number);
		value_print(v);
		printf("\n");
	}
#endif

	process_awaken(p);
}

/*
 * Returns 1 if a message was obtained from the mailbox,
 * 0 if there were no messages waiting (indicating: go to sleep.)
 */
int
process_recv(struct value *v)
{
	struct message *m;

	if (current_process->msg_head == NULL)
		return(0);

	m = current_process->msg_head;
	*v = m->payload;
	current_process->msg_head = m->next;
	bhuna_free(m);

#ifdef DEBUG
	if (trace_scheduling) {
		printf("received in process #%d: ",
		    current_process->number);
		value_print(*v);
		printf("\n");
	}
#endif

	return(1);
}

void
process_sleep(struct process *p)
{
	if (p == NULL || p->asleep)
		return;

	/* remove from run list, add to wait list */
	if (p->prev != NULL)
		p->prev->next = p->next;
	else if (p == run_head)
		run_head = p->next;

	if (p->next != NULL)
		p->next->prev = p->prev;

	p->prev = NULL;
	p->next = wait_head;
	if (wait_head != NULL)
		wait_head->prev = p;
	wait_head = p;

	p->asleep = 1;
}

void
process_awaken(struct process *p)
{
	if (p == NULL || !p->asleep)
		return;

	/* remove from run list, add to run list */
	if (p->prev != NULL)
		p->prev->next = p->next;
	else if (p == wait_head)
		wait_head = p->next;

	if (p->next != NULL)
		p->next->prev = p->prev;

	p->prev = NULL;
	p->next = run_head;
	if (run_head != NULL)
		run_head->prev = p;
	run_head = p;

	p->asleep = 0;
}

/******** SCHEDULER ********/

void
process_scheduler(void)
{
	struct process *next;

	if ((current_process = run_head) == NULL)
		return;

	for (;;) {
		next = current_process->next;
#ifdef DEBUG
		if (trace_scheduling)
			printf("context switched to process #%d\n", current_process->number);
#endif
		switch (vm_run(current_process->vm, TIMESLICE)) {
		case VM_TERMINATED:
#ifdef DEBUG
			if (trace_scheduling)
				printf("process #%d terminated\n", current_process->number);
#endif
			process_free(current_process);
			break;
		case VM_RETURNED:
#ifdef DEBUG
			if (trace_scheduling)
				printf("process #%d returned\n", current_process->number);
#endif
			process_free(current_process);
			break;
		case VM_WAITING:
#ifdef DEBUG
			if (trace_scheduling)
				printf("process #%d falling asleep\n", current_process->number);
#endif
			process_sleep(current_process);
			break;
		case VM_TIME_EXPIRED:
		default:
			break;
		}
		if ((current_process = next) == NULL)
			if ((current_process = run_head) == NULL)
				return;
	}
}