#include <acess.h>
#include <modules.h>
#include <fs_devfs.h>
+#include <semaphore.h>
// === CONSTANTS ===
#define DEFAULT_RING_SIZE 2048
int WritePos;
int BufSize;
char *Buffer;
+ tSemaphore Semaphore;
} tPipe;
// === PROTOTYPES ===
{
// Wait for buffer to fill
if(pipe->Flags & PF_BLOCKING) {
- while(pipe->ReadPos == pipe->WritePos) {
- Threads_Yield();
- //MAGIC_BREAK();
- }
+ len = Semaphore_Wait( &pipe->Semaphore, remaining );
}
else
+ {
if(pipe->ReadPos == pipe->WritePos)
return 0;
-
- // Read buffer
- if(pipe->WritePos - pipe->ReadPos < remaining)
- len = pipe->WritePos - pipe->ReadPos;
- else
- len = remaining;
+ // Read buffer
+ if(pipe->WritePos - pipe->ReadPos < remaining)
+ len = pipe->WritePos - pipe->ReadPos;
+ else
+ len = remaining;
+ }
// Check if read overflows buffer
if(len > pipe->BufSize - pipe->ReadPos)
while(remaining)
{
// Wait for buffer to empty
- if(pipe->Flags & PF_BLOCKING)
- while(pipe->ReadPos == (pipe->WritePos+1)%pipe->BufSize)
- Threads_Yield();
+ if(pipe->Flags & PF_BLOCKING) {
+ len = Semaphore_Signal( &pipe->Semaphore, remaining );
+ }
else
+ {
if(pipe->ReadPos == (pipe->WritePos+1)%pipe->BufSize)
return 0;
-
- // Write buffer
- if(pipe->ReadPos - pipe->WritePos < remaining)
- len = pipe->ReadPos - pipe->WritePos;
- else
- len = remaining;
+ // Write buffer
+ if(pipe->ReadPos - pipe->WritePos < remaining)
+ len = pipe->ReadPos - pipe->WritePos;
+ else
+ len = remaining;
+ }
// Check if write overflows buffer
if(len > pipe->BufSize - pipe->WritePos)
tPipe *FIFO_Int_NewPipe(int Size, char *Name)
{
tPipe *ret;
- int allocsize = sizeof(tPipe) + sizeof(tVFS_ACL) + Size;
+ int namelen = strlen(Name) + 1;
+ int allocsize = sizeof(tPipe) + sizeof(tVFS_ACL) + Size + namelen;
ret = malloc(allocsize);
if(!ret) return NULL;
// Clear Return
memset(ret, 0, allocsize);
-
- ret->Name = Name;
ret->Flags = PF_BLOCKING;
// Allocate Buffer
ret->BufSize = Size;
ret->Buffer = (void*)( (Uint)ret + sizeof(tPipe) + sizeof(tVFS_ACL) );
- if(!ret->Buffer) {
- free(ret);
- return NULL;
- }
+
+ // Set name (and FIFO name)
+ ret->Name = ret->Buffer + Size;
+ strcpy(ret->Name, Name);
+ // - Start empty, max of `Size`
+ Semaphore_Init( &ret->Semaphore, 0, Size, "FIFO", ret->Name );
// Set Node
ret->Node.Size = 0;
--- /dev/null
+/*
+ * Acess2 Kernel
+ * semaphore.h
+ * - Semaphore syncronisation primitive
+ */
+#ifndef _SEMAPHORE_H
+#define _SEMAPHORE_H
+
+#include <acess.h>
+
+/**
+ * \brief Semaphore typedef
+ */
+typedef struct sSemaphore tSemaphore;
+
+/**
+ * \brief Semaphore structure
+ */
+struct sSemaphore {
+ tShortSpinlock Protector; //!< Protector for the lock strucure
+ const char *ModName; //!< Human-readable module name
+ const char *Name; //!< Human-readable name
+ volatile int Value; //!< Current value
+ volatile int MaxValue; //!< Maximum value (signal will wait if it will go over this)
+
+ struct sThread *Waiting; //!< Waiting threads
+ struct sThread *LastWaiting; //!< Waiting threads
+ struct sThread *Signaling; //!< Waiting threads (from Semaphore_Signal)
+ struct sThread *LastSignaling; //!< Last waiting thread (from Semaphore_Signal)
+};
+
+/**
+ * \brief Initialise the semaphore
+ * \param Sem Semaphore structure to initialsie
+ * \param Value Initial value of the semaphore
+ * \param Module Module name
+ * \param Name Symbolic name
+ * \note Not always needed, as initialising to 0 is valid, but it is preferred
+ * if all semaphores have \a Name set
+ *
+ * \note \a Module and \a Name must be avaliable for as long as the semaphore is used
+ */
+extern void Semaphore_Init(tSemaphore *Sem, int InitValue, int MaxValue, const char *Module, const char *Name);
+/**
+ * \brief Acquire items from the semaphore
+ * \param Semaphore Semaphore structure to use
+ * \param MaxToTake Maximum number of items to take off the list (if zero, as much as possible is taken)
+ * \return Number of items fetched
+ * \retval 0 Semaphore interrupted
+ * \retval -1 Unspecified error
+ */
+extern int Semaphore_Wait(tSemaphore *Sem, int MaxToTake);
+/**
+ * \brief Add an "item" to the semaphore
+ * \param Sem Semaphore to use
+ * \param AmmountToAdd Number of items to add
+ * \return Actual number of items added
+ * \retval 0 Semaphore interrupted
+ * \retval -1 Unspecified error
+ */
+extern int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd);
+/**
+ * \brief Get the number of items waiting in a semaphore
+ * \param Sem Semaphore to use
+ * \return Number of waiting items
+ */
+extern int Semaphore_GetValue(tSemaphore *Sem);
+
+#endif
#include <acess.h>
#include <threads.h>
#include <errno.h>
+#include <semaphore.h>
// Configuration
#define DEBUG_TRACE_TICKETS 0 // Trace ticket counts
void Threads_Dump(void);
void Threads_DumpActive(void);
-void Mutex_Acquire(tMutex *Mutex);
+ int Mutex_Acquire(tMutex *Mutex);
void Mutex_Release(tMutex *Mutex);
int Mutex_IsLocked(tMutex *Mutex);
return new;
}
+/**
+ * \fn tThread *Threads_CloneTCB(Uint *Err, Uint Flags)
+ * \brief Clone the TCB of the current thread
+ */
+tThread *Threads_CloneThreadZero(void)
+{
+ tThread *cur, *new;
+ int i;
+ cur = Proc_GetCurThread();
+
+ // Allocate and duplicate
+ new = malloc(sizeof(tThread));
+ if(new == NULL) {
+ return NULL;
+ }
+ memcpy(new, &gThreadZero, sizeof(tThread));
+
+ new->CurCPU = -1;
+ new->Next = NULL;
+ memset( &new->IsLocked, 0, sizeof(new->IsLocked));
+ new->Status = THREAD_STAT_PREINIT;
+ new->RetStatus = 0;
+
+ // Get Thread ID
+ new->TID = giNextTID++;
+ new->Parent = 0;
+
+ // Clone Name
+ new->ThreadName = NULL;
+
+ // Messages are not inherited
+ new->Messages = NULL;
+ new->LastMessage = NULL;
+
+ // Set State
+ new->Remaining = new->Quantum = cur->Quantum;
+ new->Priority = cur->Priority;
+
+ // Set Signal Handlers
+ new->CurFaultNum = 0;
+ new->FaultHandler = cur->FaultHandler;
+
+ for( i = 0; i < NUM_CFG_ENTRIES; i ++ )
+ {
+ switch(cCONFIG_TYPES[i])
+ {
+ default:
+ new->Config[i] = cur->Config[i];
+ break;
+ case CFGT_HEAPSTR:
+ if(cur->Config[i])
+ new->Config[i] = (Uint) strdup( (void*)cur->Config[i] );
+ else
+ new->Config[i] = 0;
+ break;
+ }
+ }
+
+ // Maintain a global list of threads
+ SHORTLOCK( &glThreadListLock );
+ new->GlobalPrev = NULL; // Protect against bugs
+ new->GlobalNext = gAllThreads;
+ gAllThreads->GlobalPrev = new;
+ gAllThreads = new;
+ SHORTREL( &glThreadListLock );
+
+ return new;
+}
+
/**
* \brief Get a configuration pointer from the Per-Thread data area
* \param ID Config slot ID
case THREAD_STAT_MUTEXSLEEP:
Log(" Mutex Pointer: %p", thread->WaitPointer);
break;
+ case THREAD_STAT_SEMAPHORESLEEP:
+ Log(" Semaphore Pointer: %p", thread->WaitPointer);
+ Log(" Semaphore Name: %s:%s",
+ ((tSemaphore*)thread->WaitPointer)->ModName,
+ ((tSemaphore*)thread->WaitPointer)->Name
+ );
+ break;
case THREAD_STAT_ZOMBIE:
Log(" Return Status: %i", thread->RetStatus);
break;
// Clear Delete Queue
// - I should probably put this in a worker thread to avoid calling free() in the scheduler
+ // DEFINITELY - free() can deadlock in this case
while(gDeleteThreads)
{
thread = gDeleteThreads->Next;
* the oldest thread (top thread) on the queue is given the lock and
* restarted.
*/
-void Mutex_Acquire(tMutex *Mutex)
+int Mutex_Acquire(tMutex *Mutex)
{
tThread *us = Proc_GetCurThread();
if( Mutex != &glPhysAlloc )
LogF("Mutex %p taken by %i %p\n", Mutex, us->TID, __builtin_return_address(0));
#endif
+
+ return 0;
}
/**
return Mutex->Owner != NULL;
}
-/**
- * \brief Initialise the semaphore
- * \param Value Initial value of the semaphore
- * \param Label Symbolic name
- */
-void Semaphore_Init(tSemaphore *Sem, int Value, const char *Label)
+//
+// Initialise a semaphore
+//
+void Semaphore_Init(tSemaphore *Sem, int Value, int MaxValue, const char *Module, const char *Name)
{
Sem->Value = Value;
- Sem->Name = Label;
+ Sem->ModName = Module;
+ Sem->Name = Name;
}
-
-/**
- * \brief Acquire a "item" from the semaphore
- */
-void Semaphore_Wait(tSemaphore *Sem)
+//
+// Wait for items to be avaliable
+//
+int Semaphore_Wait(tSemaphore *Sem, int MaxToTake)
{
tThread *us;
+ int taken;
+ if( MaxToTake < 0 ) {
+ Log_Warning("Threads", "Semaphore_Wait: User bug - MaxToTake(%i) < 0, Sem=%p(%s)",
+ MaxToTake, Sem, Sem->Name);
+ }
SHORTLOCK( &Sem->Protector );
+
+ // Check if there's already items avaliable
if( Sem->Value > 0 ) {
- Sem->Value --;
+ // Take what we need
+ if( MaxToTake && Sem->Value > MaxToTake )
+ taken = MaxToTake;
+ else
+ taken = Sem->Value;
+ Sem->Value -= taken;
SHORTREL( &Sem->Protector );
- return ;
- }
-
- SHORTLOCK( &glThreadListLock );
-
- // - Remove from active list
- us = Threads_RemActive();
- us->Next = NULL;
- // - Mark as sleeping
- us->Status = THREAD_STAT_SEMAPHORESLEEP;
- us->WaitPointer = Sem;
-
- // - Add to waiting
- if(Sem->LastWaiting) {
- Sem->LastWaiting->Next = us;
- Sem->LastWaiting = us;
}
- else {
- Sem->Waiting = us;
- Sem->LastWaiting = us;
+ else
+ {
+ SHORTLOCK( &glThreadListLock );
+
+ // - Remove from active list
+ us = Threads_RemActive();
+ us->Next = NULL;
+ // - Mark as sleeping
+ us->Status = THREAD_STAT_SEMAPHORESLEEP;
+ us->WaitPointer = Sem;
+ us->RetStatus = MaxToTake; // Use RetStatus as a temp variable
+
+ // - Add to waiting
+ if(Sem->LastWaiting) {
+ Sem->LastWaiting->Next = us;
+ Sem->LastWaiting = us;
+ }
+ else {
+ Sem->Waiting = us;
+ Sem->LastWaiting = us;
+ }
+
+ SHORTREL( &glThreadListLock );
+ SHORTREL( &Sem->Protector );
+ while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield();
+ // We're only woken when there's something avaliable
+ us->WaitPointer = NULL;
+
+ taken = us->RetStatus;
+
+ // Get the lock again
+ SHORTLOCK( &Sem->Protector );
}
- SHORTREL( &glThreadListLock );
+ // While there is space, and there are thread waiting
+ // wake the first thread and give it what it wants (or what's left)
+ while( (Sem->MaxValue == 0 || Sem->Value < Sem->MaxValue) && Sem->Signaling )
+ {
+ int given;
+ tThread *toWake = Sem->Signaling;
+
+ Sem->Signaling = Sem->Signaling->Next;
+ // Reset ->LastWaiting to NULL if we have just removed the last waiting thread
+ if( Sem->Signaling == NULL )
+ Sem->LastSignaling = NULL;
+
+ // Figure out how much to give
+ if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue )
+ given = toWake->RetStatus;
+ else
+ given = Sem->MaxValue - Sem->Value;
+ Sem->Value -= given;
+
+ // Save the number we gave to the thread's status
+ toWake->RetStatus = given;
+
+ // Wake the sleeper
+ SHORTLOCK( &glThreadListLock );
+ if( toWake->Status != THREAD_STAT_ACTIVE )
+ Threads_AddActive(toWake);
+ SHORTREL( &glThreadListLock );
+ }
SHORTREL( &Sem->Protector );
- while(us->Status == THREAD_STAT_MUTEXSLEEP) Threads_Yield();
- // We're only woken when there's something avaliable
- us->WaitPointer = NULL;
+
+ return taken;
}
-/**
- * \brief Add an "item" to the semaphore
- */
-void Semaphore_Signal(tSemaphore *Sem)
+//
+// Add items to a semaphore
+//
+int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd)
{
+ int given;
+ int added;
+
+ if( AmmountToAdd < 0 ) {
+ Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)",
+ AmmountToAdd, Sem, Sem->Name);
+ }
SHORTLOCK( &Sem->Protector );
- Sem->Value ++;
- if( Sem->Waiting )
+ // Check if we have to block
+ if( Sem->MaxValue && Sem->Value == Sem->MaxValue )
+ {
+ tThread *us;
+ SHORTLOCK( &glThreadListLock );
+
+ // - Remove from active list
+ us = Threads_RemActive();
+ us->Next = NULL;
+ // - Mark as sleeping
+ us->Status = THREAD_STAT_SEMAPHORESLEEP;
+ us->WaitPointer = Sem;
+ us->RetStatus = AmmountToAdd; // Use RetStatus as a temp variable
+
+ // - Add to waiting
+ if(Sem->LastSignaling) {
+ Sem->LastSignaling->Next = us;
+ Sem->LastSignaling = us;
+ }
+ else {
+ Sem->Signaling = us;
+ Sem->LastSignaling = us;
+ }
+
+ SHORTREL( &glThreadListLock );
+ SHORTREL( &Sem->Protector );
+ while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield();
+ // We're only woken when there's something avaliable
+ us->WaitPointer = NULL;
+
+ added = us->RetStatus;
+
+ // Get the lock again
+ SHORTLOCK( &Sem->Protector );
+ }
+ // Non blocking
+ else
+ {
+ // Figure out how much we need to take off
+ if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue)
+ added = Sem->MaxValue - Sem->Value;
+ else
+ added = AmmountToAdd;
+ Sem->Value += added;
+ }
+
+ // While there are items avaliable, and there are thread waiting
+ // wake the first thread and give it what it wants (or what's left)
+ while( Sem->Value && Sem->Waiting )
{
tThread *toWake = Sem->Waiting;
- Sem->Waiting = Sem->Waiting->Next; // Next!
+ Sem->Waiting = Sem->Waiting->Next;
// Reset ->LastWaiting to NULL if we have just removed the last waiting thread
if( Sem->Waiting == NULL )
Sem->LastWaiting = NULL;
- // Wake new owner
+ // Figure out how much to give
+ if( toWake->RetStatus && Sem->Value > toWake->RetStatus )
+ given = toWake->RetStatus;
+ else
+ given = Sem->Value;
+ Sem->Value -= given;
+
+ // Save the number we gave to the thread's status
+ toWake->RetStatus = given;
+
+ // Wake the sleeper
SHORTLOCK( &glThreadListLock );
if( toWake->Status != THREAD_STAT_ACTIVE )
Threads_AddActive(toWake);
SHORTREL( &glThreadListLock );
-
- // Decrement (the value is now "owned" by `toWake`)
- Sem->Value --;
}
SHORTREL( &Sem->Protector );
+
+ return added;
+}
+
+//
+// Get the current value of a semaphore
+//
+int Semaphore_GetValue(tSemaphore *Sem)
+{
+ return Sem->Value;
}
// === EXPORTS ===
EXPORT(Mutex_Acquire);
EXPORT(Mutex_Release);
EXPORT(Mutex_IsLocked);
+EXPORT(Semaphore_Init);
+EXPORT(Semaphore_Wait);
+EXPORT(Semaphore_Signal);
#include <fs_devfs.h>
#include <drv_pci.h>
#include <tpl_drv_network.h>
+#include <semaphore.h>
// === CONSTANTS ===
#define MEM_START 0x40
Uint16 IOBase; //!< IO Port Address from PCI
Uint8 IRQ; //!< IRQ Assigned from PCI
- int NumWaitingPackets;
+ tSemaphore Semaphore;
+// int NumWaitingPackets;
int NextRXPage;
int NextMemPage; //!< Next Card Memory page to use
gpNe2k_Cards[ k ].Node.Write = Ne2k_Write;
gpNe2k_Cards[ k ].Node.Read = Ne2k_Read;
gpNe2k_Cards[ k ].Node.IOCtl = Ne2k_IOCtl;
+
+ // Initialise packet semaphore
+ // - Start at zero, no max
+ Semaphore_Init( &gpNe2k_Cards[k].Semaphore, 0, 0, "NE2000", gpNe2k_Cards[ k ].Name );
}
}
ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
- // TODO: Use MutexP/MutexV instead
- while(Card->NumWaitingPackets == 0) Threads_Yield();
+ // Wait for packets
+ Semaphore_Wait( &Card->Semaphore, 1 );
// Make sure that the card is in page 0
outb(Card->IOBase + CMD, 0|0x22); // Page 0, Start, NoDMA
outb( Card->IOBase + BNRY, page-1 );
// Set next RX Page and decrement the waiting list
Card->NextRXPage = page;
- Card->NumWaitingPackets --;
LEAVE('i', Length);
return Length;
// 0: Packet recieved (no error)
if( byte & 1 )
{
- gpNe2k_Cards[i].NumWaitingPackets ++;
- if( gpNe2k_Cards[i].NumWaitingPackets > MAX_PACKET_QUEUE )
- gpNe2k_Cards[i].NumWaitingPackets = MAX_PACKET_QUEUE;
+ //if( gpNe2k_Cards[i].NumWaitingPackets > MAX_PACKET_QUEUE )
+ // gpNe2k_Cards[i].NumWaitingPackets = MAX_PACKET_QUEUE;
+ Semaphore_Signal( &gpNe2k_Cards[i].Semaphore, 1 );
}
// 1: Packet sent (no error)
// 2: Recieved with error