blob: 2d0dabadb3cc7018b98cd7adf9622216723dda4a [file] [log] [blame]
/*-------------------------------------------------------------------------
*
* sem.c
* BeOS System V Semaphores Emulation
*
* Copyright (c) 1999-2000, Cyril VELTER
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <errno.h>
#include <unistd.h>
#include <OS.h>
/*#define TDBG*/
#ifdef TDBG
#define TRACEDBG(x) printf(x);printf("\n")
#define TRACEDBGP(x,y) printf(x,y);printf("\n")
#define TRACEDBGPP(x,y,z) printf(x,y,z);printf("\n")
#else
#define TRACEDBG(x)
#define TRACEDBGP(x,y)
#define TRACEDBGPP(x,y,z)
#endif
/* Control of a semaphore pool. The pool is an area in which we stored all
the semIds of the pool. The first 4 bytes are the number of semaphore allocated
in the pool followed by SemIds */
int
semctl(int semId, int semNum, int flag, union semun semun)
{
int32 *Address;
area_info info;
TRACEDBG("->semctl");
/* Try to find the pool */
if (get_area_info(semId, &info) != B_OK)
{
/* pool is invalid (BeOS area id is invalid) */
errno = EINVAL;
TRACEDBG("<-semctl invalid pool");
return -1;
}
/* Get the pool address */
Address = (int32 *) info.address;
TRACEDBGP("--semctl address %d", Address);
/* semNum might be 0 */
/* semun.array contain the sem initial values */
/* Fix the count of all sem of the pool to semun.array */
if (flag == SETALL)
{
long i;
TRACEDBG("--semctl setall");
for (i = 0; i < Address[0]; i++)
{
int32 cnt;
/* Get the current count */
get_sem_count(Address[2 * i + 1], &cnt);
TRACEDBGP("--semctl setall %d", semun.array[i]);
/* Compute and set the new count (relative to the old one) */
cnt -= semun.array[i];
TRACEDBGPP("--semctl acquire id : %d cnt : %d", Address[2 * i + 1], cnt);
if (cnt > 0)
while (acquire_sem_etc(Address[2 * i + 1], cnt, 0, 0) == B_INTERRUPTED);
if (cnt < 0)
release_sem_etc(Address[2 * i + 1], -cnt, 0);
}
return 1;
}
/* Fix the count of one semaphore to semun.val */
if (flag == SETVAL)
{
int32 cnt;
TRACEDBGP("--semctl setval %d", semun.val);
/* Get the current count */
get_sem_count(Address[2 * semNum + 1], &cnt);
/* Compute and set the new count (relative to the old one) */
cnt -= semun.val;
TRACEDBGPP("--semctl acquire id : %d cnt : %d", Address[2 * semNum + 1], cnt);
if (cnt > 0)
while (acquire_sem_etc(Address[2 * semNum + 1], cnt, 0, 0) == B_INTERRUPTED);
if (cnt < 0)
release_sem_etc(Address[2 * semNum + 1], -cnt, 0);
return 1;
}
/* Get the last pid which accessed the sem */
if (flag == GETPID)
{
TRACEDBG("->semctl getpid");
return Address[2 * semNum + 2];
}
/* Delete the pool */
if (flag == IPC_RMID)
{
long i;
thread_info ti;
TRACEDBG("->semctl rmid");
get_thread_info(find_thread(NULL), &ti);
/* Loop over all semaphore to delete them */
TRACEDBGP("->semctl nmbre %d", Address[0]);
for (i = 0; i < Address[0]; i++)
{
/*
* Make sure to have ownership of the semaphore (if created by
* another team)
*/
TRACEDBGP("->semctl id %d", Address[2 * i + 1]);
set_sem_owner(Address[2 * i + 1], ti.team);
/* Delete the semaphore */
delete_sem(Address[2 * i + 1]);
/*
* Reset to an invalid semId (in case other process try to get the
* infos from a cloned area
*/
Address[2 * i + 1] = 0;
}
/* Set the semaphore count to 0 */
Address[0] = 0;
/*
* Delete the area (it might be cloned by other process. Let them live
* with it, in all cases semIds are 0 so if another process try to use
* it, it will fail
*/
delete_area(semId);
return 1;
}
/* Get the current semaphore count */
if (flag == GETNCNT)
{
/* TO BE IMPLEMENTED */
TRACEDBG("--semctl getncnt");
elog(ERROR, "semctl error: GETNCNT not implemented");
return 0;
}
/* Get the current semaphore count of the first semaphore in the pool */
if (flag == GETVAL)
{
int32 cnt;
TRACEDBG("--semctl getval");
get_sem_count(Address[2 * semNum + 1], &cnt);
TRACEDBGP("--semctl val %d", cnt);
return cnt;
}
elog(ERROR, "semctl error: unknown flag");
TRACEDBG("<-semctl unknown flag");
return 0;
}
/* Find a pool id based on IPC key */
int
semget(int semKey, int semNum, int flags)
{
char Nom[50];
area_id parea;
void *Address;
TRACEDBGPP("->semget key : %d num : %d", semKey, semNum);
/* Name of the area to find */
sprintf(Nom, "SYSV_IPC_SEM : %d", semKey);
/* find area */
parea = find_area(Nom);
/* Test of area existence */
if (parea != B_NAME_NOT_FOUND)
{
/* Area exist and creation is requested, error */
if ((flags & IPC_CREAT) && (flags & IPC_EXCL))
{
errno = EEXIST;
return -1;
}
/* Get an area clone (in case it's not in our address space) */
/*
* TODO : a check of address space might be done to avoid duplicate
* areas in the same address space
*/
parea = clone_area(Nom, &Address, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, parea);
return parea;
}
else
{
/* Area does not exist, but creation is requested, so create it */
if (flags & IPC_CREAT)
{
int32 *Address;
void *Ad;
long i;
/*
* Limit to 250 (8 byte per sem : 4 for the semid and 4 for the
* last pid which accessed the semaphore in a pool
*/
if (semNum > 250)
{
errno = ENOSPC;
return -1;
}
/* Create the shared memory area which will hold the pool */
parea = create_area(Nom, &Ad, B_ANY_ADDRESS, 4096, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
if ((parea == B_BAD_VALUE) || (parea == B_NO_MEMORY) || (parea == B_ERROR))
{
errno = ENOMEM;
return -1;
}
/* fill up informations (sem number and sem ids) */
Address = (int32 *) Ad;
Address[0] = semNum;
for (i = 0; i < Address[0]; i++)
{
/* Create the semaphores */
Address[2 * i + 1] = create_sem(0, Nom);
if ((Address[2 * i + 1] == B_BAD_VALUE) || (Address[2 * i + 1] == B_NO_MEMORY) || (Address[2 * i + 1] == B_NO_MORE_SEMS))
{
errno = ENOMEM;
return -1;
}
}
return parea;
}
else
{
/* Area does not exist and no creation is requested */
errno = ENOENT;
return -1;
}
}
}
/* Acquire or release in the semaphore pool */
int
semop(int semId, struct sembuf * sops, int nsops)
{
int32 *Address; /* Pool address */
area_info info;
long i;
long ret;
/* Get the pool address (semId IS an area id) */
get_area_info(semId, &info);
Address = (int32 *) info.address;
/* Check the validity of semId (it should be an area id) */
if ((semId == B_BAD_VALUE) || (semId == B_NO_MEMORY) || (semId == B_ERROR))
{
errno = EINVAL;
return -1;
}
/* Perform acquire or release */
for (i = 0; i < nsops; i++)
{
/* remember the PID */
Address[2 * (sops[i].sem_num) + 2] = getpid();
/* For each sem in the pool, check the operation to perform */
if (sops[i].sem_op < 0)
{
/*
* Try acquiring the semaphore till we are not interrupted by a
* signal
*/
if (sops[i].sem_flg == IPC_NOWAIT)
{
/* Try to lock ... */
while ((ret = acquire_sem_etc(Address[2 * (sops[i].sem_num) + 1], -sops[i].sem_op, B_RELATIVE_TIMEOUT, 0)) == B_INTERRUPTED);
if (ret != B_OK)
return EWOULDBLOCK;
}
else
while (acquire_sem_etc(Address[2 * (sops[i].sem_num) + 1], -sops[i].sem_op, 0, 0) == B_INTERRUPTED);
}
if (sops[i].sem_op > 0)
release_sem_etc(Address[2 * (sops[i].sem_num) + 1], sops[i].sem_op, 0);
}
return 0;
}