* split Wait() method into Enqueue() - BlockAndUnlock() - Deque() in order to

remove a race condition pointed out by Ingo, hopefully in a clean way. The
  set is now unlocked right before blocking
* Reworked the way ID were assgned: the current time is now used as ID. This
  lower quite a lot the probability of having an ID reused.
* Introduced a sequence number field in the set class as a second ID, as
  suggested by Ingo. It is used on wake up in order not to confuse a no more
  existing set with a new one with the same ID
* Removed a now unnecessary UnsetID() method
* Increased the arbitraty limit of max sempahore allowed in the system, and
  introduced a new one for the total number of set.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27320 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Salvatore Benedetto 2008-09-04 12:34:26 +00:00
parent b2bc136efc
commit c0c7a73dbe

View File

@ -136,6 +136,42 @@ public:
}
}
status_t BlockAndUnlock(struct thread *thread, MutexLocker *setLocker)
{
thread_prepare_to_block(thread, B_CAN_INTERRUPT,
THREAD_BLOCK_TYPE_OTHER, (void*)"xsi semaphore");
// Unlock the set before blocking
setLocker->Unlock();
InterruptsSpinLocker _(gThreadSpinlock);
return thread_block_locked(thread);
}
void Deque(queued_thread *queueEntry, bool waitForZero)
{
if (queueEntry->queued) {
if (waitForZero) {
fWaitingToBeZeroQueue.Remove(queueEntry);
fThreadsWaitingToBeZero--;
} else {
fWaitingToIncreaseQueue.Remove(queueEntry);
fThreadsWaitingToIncrease--;
}
}
}
void Enqueue(queued_thread *queueEntry, bool waitForZero)
{
if (waitForZero) {
fWaitingToBeZeroQueue.Add(queueEntry);
fThreadsWaitingToBeZero++;
} else {
fWaitingToIncreaseQueue.Add(queueEntry);
fThreadsWaitingToIncrease++;
}
queueEntry->queued = true;
}
pid_t LastPid() const
{
return fLastPidOperation;
@ -175,42 +211,6 @@ public:
return fValue;
}
status_t Wait(int32 count, bool waitForZero)
{
TRACE(("XsiSemaphore::Wait: going to sleep\n"));
// enqueue the thread in the appropriate
// queue and get ready to wait
struct thread *thread = thread_get_current_thread();
queued_thread queueEntry(thread, count);
if (waitForZero) {
fWaitingToBeZeroQueue.Add(&queueEntry);
fThreadsWaitingToBeZero++;
} else {
fWaitingToIncreaseQueue.Add(&queueEntry);
fThreadsWaitingToIncrease++;
}
queueEntry.queued = true;
thread_prepare_to_block(thread, B_CAN_INTERRUPT,
THREAD_BLOCK_TYPE_OTHER, (void*)"xsi semaphore");
InterruptsSpinLocker _(gThreadSpinlock);
status_t result = thread_block_locked(thread);
if (queueEntry.queued) {
// If we are still queued, we failed to acquire
// the semaphore for some reason
if (waitForZero) {
fWaitingToBeZeroQueue.Remove(&queueEntry);
fThreadsWaitingToBeZero--;
} else {
fWaitingToIncreaseQueue.Remove(&queueEntry);
fThreadsWaitingToIncrease--;
}
}
return result;
}
void WakeUpThread(bool waitingForZero)
{
InterruptsSpinLocker _(gThreadSpinlock);
@ -269,7 +269,6 @@ public:
TRACE(("XsiSemaphoreSet::~XsiSemaphoreSet(): removing semaphore "
"set %d\n", fID));
mutex_destroy(&fLock);
UnsetID();
delete[] fSemaphores;
}
@ -340,7 +339,7 @@ public:
return HasPermission();
}
int ID() const
int ID() const
{
return fID;
}
@ -477,7 +476,12 @@ public:
return &fSemaphores[nth];
}
// Implemented after sSemaphoreHashTable is declared
uint32 SequenceNumber() const
{
return fSequenceNumber;
}
// Implemented after sGlobalSequenceNumber is declared
void SetID();
void SetIpcKey(key_t key)
@ -507,9 +511,6 @@ public:
return fUndoList;
}
// Implemented after sSemaphoreHashTable is declared
void UnsetID();
HashTableLink<XsiSemaphoreSet>* Link()
{
return &fLink;
@ -522,6 +523,7 @@ private:
time_t fLastSemopTime; // sem_otime
ushort fNumberOfSemaphores; // sem_nsems
struct ipc_perm fPermissions; // sem_perm
uint32 fSequenceNumber; // used as a second id
XsiSemaphore *fSemaphores; // array of semaphores
UndoList fUndoList; // undo list requests
mutex fLock; // private lock
@ -618,15 +620,17 @@ struct IpcHashTableDefinition {
};
// Arbitrary limit
#define MAX_XSI_SEMAPHORE 2048
#define MAX_XSI_SEMAPHORE 4096
#define MAX_XSI_SEMAPHORE_SET 2048
static OpenHashTable<IpcHashTableDefinition> sIpcHashTable;
static OpenHashTable<SemaphoreHashTableDefinition> sSemaphoreHashTable;
static mutex sIpcLock;
static mutex sXsiSemaphoreSetLock;
static vint32 sNextAvailableID = 1;
static uint32 sGlobalSequenceNumber = 1;
static vint32 sXsiSemaphoreCount = 0;
static vint32 sXsiSemaphoreSetCount = 0;
// #pragma mark -
@ -635,20 +639,15 @@ static vint32 sXsiSemaphoreCount = 0;
void
XsiSemaphoreSet::SetID()
{
fID = real_time_clock();
// The lock is held before calling us
while (true) {
if (sSemaphoreHashTable.Lookup(sNextAvailableID) == NULL)
if (sSemaphoreHashTable.Lookup(fID) == NULL)
break;
sNextAvailableID++;
fID = (fID + 1) % INT_MAX;
}
fID = sNextAvailableID++;
}
void
XsiSemaphoreSet::UnsetID()
{
sNextAvailableID = fID;
sGlobalSequenceNumber = (sGlobalSequenceNumber + 1) % UINT_MAX;
fSequenceNumber = sGlobalSequenceNumber;
}
@ -781,7 +780,8 @@ _user_xsi_semget(key_t key, int numberOfSemaphores, int flags)
TRACE_ERROR(("xsi_semget: numberOfSemaphores out of range\n"));
return EINVAL;
}
if (sXsiSemaphoreCount >= MAX_XSI_SEMAPHORE) {
if (sXsiSemaphoreCount >= MAX_XSI_SEMAPHORE
|| sXsiSemaphoreSetCount >= MAX_XSI_SEMAPHORE_SET) {
TRACE_ERROR(("xsi_semget: reached limit of maximum number of "
"semaphores allowed\n"));
return ENOSPC;
@ -795,6 +795,7 @@ _user_xsi_semget(key_t key, int numberOfSemaphores, int flags)
return ENOMEM;
}
atomic_add(&sXsiSemaphoreCount, numberOfSemaphores);
atomic_add(&sXsiSemaphoreSetCount, 1);
MutexLocker _(sXsiSemaphoreSetLock);
semaphoreSet->SetID();
@ -805,6 +806,8 @@ _user_xsi_semget(key_t key, int numberOfSemaphores, int flags)
ipcKey->SetSemaphoreSetID(semaphoreSet);
}
sSemaphoreHashTable.Insert(semaphoreSet);
TRACE(("semget: new set = %d created, sequence = %ld\n",
semaphoreSet->ID(), semaphoreSet->SequenceNumber()));
}
return semaphoreSet->ID();
@ -1030,6 +1033,7 @@ _user_xsi_semctl(int semaphoreID, int semaphoreNumber, int command,
if (key != -1)
delete ipcKey;
atomic_add(&sXsiSemaphoreCount, -semaphoreSet->NumberOfSemaphores());
atomic_add(&sXsiSemaphoreSetCount, -1);
// Remove any sem_undo request
while (struct sem_undo *entry
= semaphoreSet->GetUndoList().RemoveHead()) {
@ -1154,23 +1158,40 @@ _user_xsi_semop(int semaphoreID, struct sembuf *ops, size_t numOps)
if (result != 0)
return result;
// We have to wait: first enqueue the thread
// in the appropriate set waiting list, then
// unlock the set itself and block the thread.
bool waitOnZero = true;
if (operations[i].sem_op != 0)
waitOnZero = false;
setLocker.Unlock();
result = semaphore->Wait((int32)operations[i].sem_op, waitOnZero);
TRACE(("xsi_semop: back to life\n"));
struct thread *thread = thread_get_current_thread();
queued_thread queueEntry(thread, (int32)operations[i].sem_op);
semaphore->Enqueue(&queueEntry, waitOnZero);
// We are back to life.
// Find out why!
uint32 sequenceNumber = semaphoreSet->SequenceNumber();
TRACE(("xsi_semop: thread %d going to sleep\n", (int)thread->id));
result = semaphore->BlockAndUnlock(thread, &setLocker);
TRACE(("xsi_semop: thread %d back to life\n", (int)thread->id));
// We are back to life. Find out why!
// Make sure the set hasn't been deleted or worst yet
// replaced.
setHashLocker.Lock();
semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID);
if (result == EIDRM || result == B_INTERRUPTED
|| semaphoreSet == NULL) {
TRACE_ERROR(("xsi_semop: semaphore set id %d got destroyed\n",
semaphoreID));
if (result == EIDRM || semaphoreSet == NULL || (semaphoreSet != NULL
&& sequenceNumber != semaphoreSet->SequenceNumber())) {
TRACE_ERROR(("xsi_semop: semaphore set id %d (sequence = %ld) "
"got destroyed\n", semaphoreID, sequenceNumber));
notDone = false;
result = EIDRM;
} else if (result == B_INTERRUPTED) {
TRACE_ERROR(("xsi_semop: thread %d got interrupted while "
"waiting on semaphore set id %d\n",(int)thread->id,
semaphoreID));
semaphore->Deque(&queueEntry, waitOnZero);
result = EINTR;
notDone = false;
} else {
setLocker.Lock();