* 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
+85 -64
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 pid_t LastPid() const
{ {
return fLastPidOperation; return fLastPidOperation;
@@ -175,42 +211,6 @@ public:
return fValue; 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) void WakeUpThread(bool waitingForZero)
{ {
InterruptsSpinLocker _(gThreadSpinlock); InterruptsSpinLocker _(gThreadSpinlock);
@@ -269,7 +269,6 @@ public:
TRACE(("XsiSemaphoreSet::~XsiSemaphoreSet(): removing semaphore " TRACE(("XsiSemaphoreSet::~XsiSemaphoreSet(): removing semaphore "
"set %d\n", fID)); "set %d\n", fID));
mutex_destroy(&fLock); mutex_destroy(&fLock);
UnsetID();
delete[] fSemaphores; delete[] fSemaphores;
} }
@@ -340,7 +339,7 @@ public:
return HasPermission(); return HasPermission();
} }
int ID() const int ID() const
{ {
return fID; return fID;
} }
@@ -477,7 +476,12 @@ public:
return &fSemaphores[nth]; return &fSemaphores[nth];
} }
// Implemented after sSemaphoreHashTable is declared uint32 SequenceNumber() const
{
return fSequenceNumber;
}
// Implemented after sGlobalSequenceNumber is declared
void SetID(); void SetID();
void SetIpcKey(key_t key) void SetIpcKey(key_t key)
@@ -507,9 +511,6 @@ public:
return fUndoList; return fUndoList;
} }
// Implemented after sSemaphoreHashTable is declared
void UnsetID();
HashTableLink<XsiSemaphoreSet>* Link() HashTableLink<XsiSemaphoreSet>* Link()
{ {
return &fLink; return &fLink;
@@ -522,6 +523,7 @@ private:
time_t fLastSemopTime; // sem_otime time_t fLastSemopTime; // sem_otime
ushort fNumberOfSemaphores; // sem_nsems ushort fNumberOfSemaphores; // sem_nsems
struct ipc_perm fPermissions; // sem_perm struct ipc_perm fPermissions; // sem_perm
uint32 fSequenceNumber; // used as a second id
XsiSemaphore *fSemaphores; // array of semaphores XsiSemaphore *fSemaphores; // array of semaphores
UndoList fUndoList; // undo list requests UndoList fUndoList; // undo list requests
mutex fLock; // private lock mutex fLock; // private lock
@@ -618,15 +620,17 @@ struct IpcHashTableDefinition {
}; };
// Arbitrary limit // Arbitrary limit
#define MAX_XSI_SEMAPHORE 2048 #define MAX_XSI_SEMAPHORE 4096
#define MAX_XSI_SEMAPHORE_SET 2048
static OpenHashTable<IpcHashTableDefinition> sIpcHashTable; static OpenHashTable<IpcHashTableDefinition> sIpcHashTable;
static OpenHashTable<SemaphoreHashTableDefinition> sSemaphoreHashTable; static OpenHashTable<SemaphoreHashTableDefinition> sSemaphoreHashTable;
static mutex sIpcLock; static mutex sIpcLock;
static mutex sXsiSemaphoreSetLock; static mutex sXsiSemaphoreSetLock;
static vint32 sNextAvailableID = 1; static uint32 sGlobalSequenceNumber = 1;
static vint32 sXsiSemaphoreCount = 0; static vint32 sXsiSemaphoreCount = 0;
static vint32 sXsiSemaphoreSetCount = 0;
// #pragma mark - // #pragma mark -
@@ -635,20 +639,15 @@ static vint32 sXsiSemaphoreCount = 0;
void void
XsiSemaphoreSet::SetID() XsiSemaphoreSet::SetID()
{ {
fID = real_time_clock();
// The lock is held before calling us // The lock is held before calling us
while (true) { while (true) {
if (sSemaphoreHashTable.Lookup(sNextAvailableID) == NULL) if (sSemaphoreHashTable.Lookup(fID) == NULL)
break; break;
sNextAvailableID++; fID = (fID + 1) % INT_MAX;
} }
fID = sNextAvailableID++; sGlobalSequenceNumber = (sGlobalSequenceNumber + 1) % UINT_MAX;
} fSequenceNumber = sGlobalSequenceNumber;
void
XsiSemaphoreSet::UnsetID()
{
sNextAvailableID = fID;
} }
@@ -781,7 +780,8 @@ _user_xsi_semget(key_t key, int numberOfSemaphores, int flags)
TRACE_ERROR(("xsi_semget: numberOfSemaphores out of range\n")); TRACE_ERROR(("xsi_semget: numberOfSemaphores out of range\n"));
return EINVAL; 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 " TRACE_ERROR(("xsi_semget: reached limit of maximum number of "
"semaphores allowed\n")); "semaphores allowed\n"));
return ENOSPC; return ENOSPC;
@@ -795,6 +795,7 @@ _user_xsi_semget(key_t key, int numberOfSemaphores, int flags)
return ENOMEM; return ENOMEM;
} }
atomic_add(&sXsiSemaphoreCount, numberOfSemaphores); atomic_add(&sXsiSemaphoreCount, numberOfSemaphores);
atomic_add(&sXsiSemaphoreSetCount, 1);
MutexLocker _(sXsiSemaphoreSetLock); MutexLocker _(sXsiSemaphoreSetLock);
semaphoreSet->SetID(); semaphoreSet->SetID();
@@ -805,6 +806,8 @@ _user_xsi_semget(key_t key, int numberOfSemaphores, int flags)
ipcKey->SetSemaphoreSetID(semaphoreSet); ipcKey->SetSemaphoreSetID(semaphoreSet);
} }
sSemaphoreHashTable.Insert(semaphoreSet); sSemaphoreHashTable.Insert(semaphoreSet);
TRACE(("semget: new set = %d created, sequence = %ld\n",
semaphoreSet->ID(), semaphoreSet->SequenceNumber()));
} }
return semaphoreSet->ID(); return semaphoreSet->ID();
@@ -1030,6 +1033,7 @@ _user_xsi_semctl(int semaphoreID, int semaphoreNumber, int command,
if (key != -1) if (key != -1)
delete ipcKey; delete ipcKey;
atomic_add(&sXsiSemaphoreCount, -semaphoreSet->NumberOfSemaphores()); atomic_add(&sXsiSemaphoreCount, -semaphoreSet->NumberOfSemaphores());
atomic_add(&sXsiSemaphoreSetCount, -1);
// Remove any sem_undo request // Remove any sem_undo request
while (struct sem_undo *entry while (struct sem_undo *entry
= semaphoreSet->GetUndoList().RemoveHead()) { = semaphoreSet->GetUndoList().RemoveHead()) {
@@ -1154,23 +1158,40 @@ _user_xsi_semop(int semaphoreID, struct sembuf *ops, size_t numOps)
if (result != 0) if (result != 0)
return result; 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; bool waitOnZero = true;
if (operations[i].sem_op != 0) if (operations[i].sem_op != 0)
waitOnZero = false; waitOnZero = false;
setLocker.Unlock(); struct thread *thread = thread_get_current_thread();
result = semaphore->Wait((int32)operations[i].sem_op, waitOnZero); queued_thread queueEntry(thread, (int32)operations[i].sem_op);
TRACE(("xsi_semop: back to life\n")); semaphore->Enqueue(&queueEntry, waitOnZero);
// We are back to life. uint32 sequenceNumber = semaphoreSet->SequenceNumber();
// Find out why!
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(); setHashLocker.Lock();
semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID); semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID);
if (result == EIDRM || result == B_INTERRUPTED if (result == EIDRM || semaphoreSet == NULL || (semaphoreSet != NULL
|| semaphoreSet == NULL) { && sequenceNumber != semaphoreSet->SequenceNumber())) {
TRACE_ERROR(("xsi_semop: semaphore set id %d got destroyed\n", TRACE_ERROR(("xsi_semop: semaphore set id %d (sequence = %ld) "
semaphoreID)); "got destroyed\n", semaphoreID, sequenceNumber));
notDone = false;
result = EIDRM; 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; notDone = false;
} else { } else {
setLocker.Lock(); setLocker.Lock();