amiga-libnix3/extra/psem.c

301 lines
5.8 KiB
C

/**
* POSIX Semaphores Emulation for AmigaOS
* Copyright (C)2009 Diego Casorran
*
* Public Domain as long this copyright notice is left unchanged.
*
* Implementation conforms to POSIX.1-2008 where possible.
*/
#if __GNUC__ < 3
# define __RELID(S) \
static const char __Id[] __attribute__((unused)) = S
#else
# define __RELID(S) \
static char __Id[] __attribute__((used)) = S
#endif
__RELID("$Id: psem,v 0.3 2009/01/08 00:01:02 diegocr Exp $");
#include <proto/exec.h> /* imp core */
#include <proto/alib.h> /* NewList */
#include <proto/dos.h> /* FilePart */
#include <string.h> /* memset strcmp */
#include <stdio.h> /* snprintf */
#include <stdlib.h> /* atexit */
#include "psem.h"
const unsigned long _PSEM_MAGIC = __PSEM_MAGIC;
static unsigned __psem_uniqueid = 0;
struct MinList *__psem_list = NULL;
// #undef FreeVec
// #define FreeVec(s) DBG("---FreeVec(%s);\n",#s)
static void __psem_list_destruct( void )
{
psem_node_t *pn;
struct Task *ThisTask = FindTask(NULL);
/**
* The program using me is about to exit, proceed
* to safely clean up everything made by myself..
*/
ENTER();
Forbid();
while((pn = (psem_node_t *)RemTail((struct List *)__psem_list)))
{
/**
* Check if this semaphore was created by this task,
* and proceed to destroy it if so. but before we'll
* try to release any unlocked semaphore by the user..
*/
POINTER(pn);
while(_SSEM(pn->sem)->ss_Owner == ThisTask)
{
DBG("Releasing sema...\n");
ReleaseSemaphore(_SSEM(pn->sem));
}
if(pn->sem->owner == (unsigned)ThisTask)
{
VALUE(_SSEM(pn->sem)->ss_QueueCount);
if(_SSEM(pn->sem)->ss_QueueCount != -1)
{
/**
* The Semaphore is locked by someone!
* we'll not exit unless it's unlocked!
*/
struct timespec ts;
struct SemaphoreRequest *sr;
unsigned waiters = 0, owned = 0;
sr = (struct SemaphoreRequest *)_SSEM(pn->sem)->ss_WaitQueue.mlh_Head;
while(sr->sr_Link.mln_Succ)
{
waiters++;
/**
* hmm, is this a Shared Semaphore and the task
* didn't released all obtained semaphores?..
*/
if(sr->sr_Waiter == ThisTask)
{
owned++;
}
else if( sr->sr_Waiter != NULL )
{
Signal(sr->sr_Waiter,SIGBREAKF_CTRL_C);
}
sr = (struct SemaphoreRequest *)sr->sr_Link.mln_Succ;
}
if( waiters != owned )
{
VALUE(waiters);
Permit();
do {
ts.ts_sec = time(NULL) + 120;
ts.ts_nsec = 1000;
} while(sem_timedwait((sem_t *)pn->sem,&ts));
Forbid();
}
}
psem_destroy(pn->sem);
}
FreeVec(pn->sem);
FreeVec(pn);
}
Permit();
FreeVec(__psem_list);
LEAVE();
}
void psem_list_add(psem_t *sem)
{
psem_node_t *pn;
/* This is executed in forbid state! */
ENTER();
POINTER(sem);
VALUE(sem->semid);
if(__psem_list == NULL)
{
if(!(__psem_list = AllocVec(sizeof(*__psem_list),MEMF_PUBLIC)))
return;
NewList((struct List *)__psem_list);
atexit(__psem_list_destruct);
POINTER(__psem_list);
}
if((pn = AllocVec(sizeof(*pn),MEMF_PUBLIC)))
{
pn->sem = sem;
AddTail((struct List *)__psem_list,(struct Node *)pn);
}
LEAVE();
}
static void psem_list_rem(psem_t *sem)
{
psem_node_t *pn,*pnn;
/* This is executed in forbid state! */
ENTER();
if(__psem_list == NULL)
return;
POINTER(sem);
pn = (psem_node_t *)__psem_list->mlh_Head;
while((pnn = (psem_node_t *)pn->node.mln_Succ))
{
if( pn->sem->semid == sem->semid )
{
Remove((struct Node *)pn);
VALUE(pn->sem->semid);
memset(pn->sem,_PSEM_ERASEDBIT,sizeof(psem_t));
// FreeVec(pn->sem);
FreeVec(pn);
break;
}
pn = pnn;
}
LEAVE();
}
char *psem_name(const char *name,char *out,unsigned len)
{
if( name == NULL )
{
struct Task *ThisTask;
struct Process *ThisProcess;
ThisProcess = (struct Process *)(ThisTask = FindTask(NULL));
if((ThisTask->tc_Node.ln_Succ &&
(ThisTask->tc_Node.ln_Type==NT_PROCESS||
ThisTask->tc_Node.ln_Type==NT_TASK)))
{
if(ThisTask->tc_Node.ln_Type==NT_PROCESS && ThisProcess->pr_CLI)
{
struct CommandLineInterface *cli =
(struct CommandLineInterface *)BADDR(ThisProcess->pr_CLI);
if( cli && cli->cli_Module && cli->cli_CommandName )
{
char *taskname = (char *)(cli->cli_CommandName<<2);
int tnl = *taskname++;
if( tnl > 0 )
{
name = (const char *)taskname;
}
}
}
if(!(name && *name))
name = (const char *)ThisTask->tc_Node.ln_Name;
}
if(!name || *name < 32)
name = (const char *)"unnamed";
}
snprintf( out, len, _PSEM_NAME, FilePart(name));
return(out);
}
psem_t *psem_init(const char *name, int shared, unsigned int value)
{
psem_t *s;
ENTER();
if((s = AllocVec(sizeof(psem_t),MEMF_PUBLIC|MEMF_CLEAR)))
{
if( name == NULL )
{
s->flags |= SEMF_UNNAMED;
}
if( shared )
{
s->flags |= SEMF_SHARED;
}
Forbid();
_SSEM(s)->ss_Link.ln_Name = psem_name(name,s->name,sizeof(s->name)-1);
_SSEM(s)->ss_Link.ln_Pri = 1;
AddSemaphore(_SSEM(s));
s->value = value;
s->magic = _PSEM_MAGIC;
s->owner = (unsigned)FindTask(NULL);
if(__psem_uniqueid < 1)
__psem_uniqueid = s->owner;
s->semid = ++__psem_uniqueid;
psem_list_add(s);
Permit();
}
if(s == NULL)
s = (psem_t *)SEM_FAILED;
RETURN(s);
return((void *)s);
}
void psem_destroy(psem_t *sem)
{
ENTER();
POINTER(sem);
if((sem->owner == (unsigned)FindTask(NULL)) || (sem->flags & SEMF_OPENDUP))
{
VALUE(sem->flags);
if(!(sem->flags & SEMF_OPENDUP))
{
DBG("Trying to own the semaphore to be removed...\n");
ObtainSemaphore(_SSEM(sem));
}
else
{
DBG("Destroying sem_open()'ed semaphore...\n");
}
Forbid();
if(!(sem->flags & SEMF_OPENDUP))
{
RemSemaphore(_SSEM(sem));
}
psem_list_rem(sem);
memset(sem,_PSEM_ERASEDBIT,sizeof(*sem));
Permit();
}
LEAVE();
}