⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 condition.c

📁 pebble
💻 C
字号:
/* 
 * Copyright 1999, 2000, 2001, 2002 Lucent Technologies Inc.
 * All Rights Reserved.
 * Information Sciences Research Center, Bell Labs.
 *
 * LUCENT TECHNOLOGIES DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE 
 * OR THE SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The
 * software is provided "as is" without expressed or implied warranty 
 * of any kind.
 *
 * These notices must be retained in any copies of any part of this
 * software.
 *
 */

/*
 * User-level condition manager.
 * The conditions mananger keeps the list of waiting threads on each condition.
 *
 * *** important ***
 * The condition manager must be initialized before any use of the conditions.
 */

#include <unistd.h>		/* for write() */
#include "diag.h"
#include "pebble.h"
#include "mem.h"
#include "synch.h"
#include "lock_free.h"
#include "signal.h"
#include "nucleus.h"
#include <assert.h>

enum	{
	N = 257,	/* number of bins in hash table */
};

/* per thread condition information */
typedef	struct ThreadCond ThreadCond;

/* the thread information is linked into two hash tables: */
/* the threads list, which is pointed by thread_table[], */
/* and the condition list, which is pointed by cond_table[] */
/* the thread information is always a member of the threads list and it */
/* is never removed from this list for the lifetime of the thread. */
/* The thread information is added and removed from the condition list */
/* as the thread is waiting on various conditions */
/* Note: there is a single element per thread, since a given thread */
/* may wait on a single condition at any given time */

struct	ThreadCond {
	void	*tid;		/* the thread */
	int	asid;		/* the protection domain that holds the */
				/* condition variable if it is allocated */
				/* in private memory. (not sharable) */
				/* zero indicates that the condition variable */
				/* is on the heap */
	void	*addr;		/* the address of the condition variable */
	int	sem;		/* the thread's semaphore */
	int	npost;		/* number of outstanding post requests on */
				/* semaphore */
	ThreadCond *next_thread;/* next thread in threads hash list */
	ThreadCond *prev_cond;	/* previous thread in condition list */
	ThreadCond *next_cond;	/* next thread in condition list */
};

/* doubly-linked list of threads */
typedef	struct	{
	Lock	lock;
	ThreadCond	*head, *tail;
} List;


static List thread_table[N];
static List cond_table[N];


static const char INIT[] = "condition initialization failed";
static const char NO_INIT[] = "condition not initialized";
static const char BAD_INIT[] = "condition initialized for a different variable";
static const char INV_TID[] = "thread not found";


/* add the thread condition to the tail of the specified conditions list */
/* assumption: the conditions list is locked */
void
add(int bin, ThreadCond *p)
{
	if (cond_table[bin].tail == NULL) {
		cond_table[bin].head = p;
		p->prev_cond = NULL;
	} else	{
		cond_table[bin].tail->next_cond = p;
		p->prev_cond = cond_table[bin].tail;
	}
	cond_table[bin].tail = p;
	p->next_cond = NULL;
}


/* remove the thread condition from the specified conditions list */
/* assumption: the conditions list is locked */
void
delete(int bin, ThreadCond *p)
{
	/* detect duplicate removal */
	if (p->prev_cond == NULL && p->next_cond == NULL &&
	    cond_table[bin].head != p)
		return;

	if (p->prev_cond == NULL)
		cond_table[bin].head = p->next_cond;
	else
		p->prev_cond->next_cond = p->next_cond;

	if (p->next_cond == NULL)
		cond_table[bin].tail = p->prev_cond;
	else
		p->next_cond->prev_cond = p->prev_cond;

	p->prev_cond = p->next_cond = NULL;
}


int
sys_cond_mgr_init(int asid, void *addr)
{
	void *tid;
	int bin;
	ThreadCond *p;

	tid = (void *)get_thread();
	if (on_heap(addr))
		asid = 0;

	DIAG(COND_DIAG, ("cond_mgr_init: tid=%p asid=%d addr=%p\n",
		tid, asid, addr));
	
	/* find the element corresponding to the thread */
	bin = (((ulong)tid)/sizeof(double)) %  N;
	mutex_lock(&thread_table[bin].lock);
	for (p = thread_table[bin].head; p != NULL && p->tid != tid;
	     p = p->next_thread)
		;

	if (p == NULL) {
		/* no element for the thread; must allocate a new one */
		if ((p = (ThreadCond *)calloc(1, sizeof(ThreadCond))) == NULL) {
			mutex_unlock(&thread_table[bin].lock);
			error(INIT);
		}
		DIAG(COND_DIAG,
			("cond_mgr_init: allocated new element %p for tid=%p\n",
			p, tid));
		p->tid = tid;
		if ((p->sem = sem_create(0)) < 0) {
			mutex_unlock(&thread_table[bin].lock);
			free(p);
			error(INIT);
		}

		/* add new element to the head of the threads list */
		p->next_thread = thread_table[bin].head;
		thread_table[bin].head = p;
		mutex_unlock(&thread_table[bin].lock);
		DIAG(COND_DIAG,
		("sys_cond_mgr_init: add element bin=%d p=%p p->tid=%p\n",
			bin, p, p->tid));
	} else	{
		/* found an element for the thread */
		mutex_unlock(&thread_table[bin].lock);

		DIAG(COND_DIAG,
			("cond_mgr_init: found element %p for tid=%p\n",
			p, tid));
		/* make sure that the semaphore value is zero */
		/* it could be that cond_signal() was sent after the thread */
		/* woke already from cond_twait() */
		while (p->npost > 0) {
			DIAG(COND_DIAG,
		("cond_mgr_init: thread %p sem_wait_caller_pri count=%d\n",
				tid, p->npost));
			if (sem_wait_caller_pri(p->sem) < 0)
				panic("sem_wait_caller_pri:");
			fadd(&p->npost, -1);
		}
	}

	/* place new condition information in element */
	p->asid = asid;
	p->addr = addr;

	/* add element to conditions list */
	bin = (((ulong)addr)/sizeof(int)) %  N;
	mutex_lock(&cond_table[bin].lock);
	add(bin, p);
	mutex_unlock(&cond_table[bin].lock);

	DIAG(COND_DIAG, ("cond_mgr_init: tid=%p addr=%p before return\n",
		tid, addr));
	
	return 0;
}


int
sys_cond_mgr_twait(int asid, void *addr, Time when)
{
	void *tid;
	int bin;
	ThreadCond *p;
	int code;


	tid = (void *)get_thread();
	if (on_heap(addr))
		asid = 0;

	DIAG(COND_DIAG,
		("cond_mgr_twait: asid=%d addr=%p tid=%p when=%08x %08x\n",
		asid, addr, tid, (int)(when>>32), (int)when));
	
	/* find the element corresponding to the thread */
	bin = (((ulong)tid)/sizeof(double)) % N;
	for (p = thread_table[bin].head; p != NULL && p->tid != tid;
	     p = p->next_thread)
		;

	if (p == NULL) {
		printf("conditions manager before %s error: bin=%d tid=%p\n",
			NO_INIT, bin, tid);
		for (p = thread_table[bin].head; p != NULL; p = p->next_thread)
			printf("p=%p p->tid=%p p->asid=%d p->addr=%p\n",
				p, p->tid, p->asid, p->addr);
		error(NO_INIT);
	}

	if (p->asid != asid || p->addr != addr)
		error(BAD_INIT);

	code = sem_twait(p->sem, when);
	fadd(&p->npost, -1);

	if (code < 0) {
		/* remove element from condition list, because timeout */
		/* occured first */
		DIAG(COND_DIAG,
		("cond_mgr_twait: removing element %p from conditions list\n",
			p));

		bin = (((ulong)addr)/sizeof(int)) % N;
		mutex_lock(&cond_table[bin].lock);
		delete(bin, p);
		mutex_unlock(&cond_table[bin].lock);
	}

	DIAG(COND_DIAG, ("cond_mgr_twait: tid=%p return code=%d\n",
		tid, code));
	
	return code;
}


int
sys_cond_mgr_post(int asid, void *addr, void *tid)
{
	int bin;
	ThreadCond *p, *next_p;

	DIAG(COND_DIAG, ("cond_mgr_post: asid=%d addr=%p tid=%p\n",
		asid, addr, tid));

	if (on_heap(addr))
		asid = 0;

	bin = (((ulong)addr)/sizeof(int)) %  N;
	mutex_lock(&cond_table[bin].lock);
	if (tid == (void *)0) {
		/* wakeup first waiting thread */
		for (p = cond_table[bin].head; p != NULL; p = p->next_cond)
			if (p->asid == asid && p->addr == addr) {
				DIAG(COND_DIAG,
					("cond_mgr_post: waking thread %p\n",
					p->tid));
				fadd(&p->npost, 1);
				sem_post(p->sem);
				delete(bin, p);
				break;
			}
	} else if (tid == (void *)-1) {
		/* wakeup all waiting threads */
		for (p = cond_table[bin].head; p != NULL; p = next_p) {
			DIAG(COND_DIAG, ("cond_mgr_post: p=%p\n", p));
			next_p = p->next_cond;
			if (p->asid == asid && p->addr == addr) {
				DIAG(COND_DIAG,
					("cond_mgr_post: waking thread %p\n",
					p->tid));
				fadd(&p->npost, 1);
				sem_post(p->sem);
				delete(bin, p);
			}
		}
	} else	{
		/* wakeup a specific thread */
		for (p = cond_table[bin].head; p != NULL; p = p->next_cond)
			if (p->asid == asid && p->addr == addr && p->tid ==tid){
				DIAG(COND_DIAG,
					("cond_mgr_post: waking thread %p\n",
					p->tid));
				fadd(&p->npost, 1);
				sem_post(p->sem);
				delete(bin, p);
				break;
			}

		if (p == NULL) {
			mutex_unlock(&cond_table[bin].lock);
			error(INV_TID);
		}
	}
	mutex_unlock(&cond_table[bin].lock);

	DIAG(COND_DIAG, ("cond_mgr_post(%p, %p) returns\n", addr, tid));
	return 0;
}


/* conditions manager initialization */
int main()
{
	printf("conditions manager is active\n");

	/* verify that we are running in user mode with interrupts enabled */
	if (!check_psw(1,1)) {
		DIAG(LOCK_DIAG,
			("signals: invalid processor status: %08lx\n",
			get_psw()));
		task_exit(1);
	}

	if (portal_create(SYS_COND_MGR_INIT, "spaiii", 0,
	    (Func) sys_cond_mgr_init, 0) < 0)
		panic("portal_create for cond_mgr_init():");
	if (portal_create(SYS_COND_MGR_TWAIT, "spaiii", 0,
	    (Func) sys_cond_mgr_twait, 0) < 0)
		panic("portal_create for cond_mgr_twait():");
	if (portal_create(SYS_COND_MGR_POST, "spaiii", 0,
	    (Func) sys_cond_mgr_post, 0) < 0)
		panic("portal_create for cond_mgr_post():");

	DIAG(COND_DIAG, ("before returning to initialization code\n"));

	/*
	 * return to initialization code.
	 * cannot just "return", since the startup code (crt0.S) calls
	 * exit when main routine terminates.
	 */
	call_portal(SYS_RTN_RPC);
	return(1);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -