#include <malloc.h>
#include <stdio.h>
#include <kernel/kernel.h>
#include <kernel/handle.h>
#include <kernel/proc.h>
#include <kernel/thread.h>
handle_t *hnd_first, *hnd_last;
extern byte scode[];
//! Implements the hndAlloc() macro.
/*!
* hndAlloc() is implemented as a macro. It calls this function, passing the
* name of the file and the line from which it was called. This
* information can later be used to debug the kernel.
*
* This function allocates a block of kernel memory and associates it with
* the specified process. Use this function instead of malloc() if
* possible.
*
* The memory will be freed when the process exits. At exit, if there are any
* handles still allocated, the locations of their allocation will be
* printed for debugging purposes.
*
* Handles not allocated to a specific process should be allocted to
* &proc_idle.
*
* \param size The size of the block to be allocated
* \param proc The process in which the block is to be allocated
* \param file The name of the current source file (usually __FILE__)
* \param line The line from which this function is called (usually
* __LINE__)
* \return A pointer to the start of the block, or NULL if the block could not
* be allocated.
*/
void* _hndAlloc(size_t size, struct process_t* proc, const char* file, int line)
{
handle_t* hnd = malloc(sizeof(handle_t) + size);
hnd->next = NULL;
if (hnd_last)
hnd_last->next = hnd;
if (!hnd_first)
hnd_first = hnd;
hnd_last = hnd;
if (proc == NULL)
proc = current->process;
hnd->refs = 0;
hnd->signal = false;
hnd->process = proc;
hnd->file = file;
hnd->line = line;
hnd->queue.first = hnd->queue.last = hnd->queue.current = NULL;
return hnd + 1;
}
//! Increments the reference count on a handle
/*!
* Handles maintain a reference count internally: they are only freed when
* their count reaches zero. Use hndAddRef() to increment this count
* manually (such as when making a copy of the pointer, in the same or
* a different process); use hndFree() to decrement it and potentially
* free the block entirely.
*
* \param buf A pointer to the handle, returned by hndAlloc()
* \return The new reference count on the handle
*/
int hndAddRef(void* buf)
{
return ++hndHandle(buf)->refs;
}
//! Decrements the reference count on a handle, and potentially frees the block
/*!
* See the documentation for hndAddRef(). The handle is only freed when its
* reference count reaches zero.
*
* \param buf A pointer to the handle, returned by hndAlloc()
* \return The new reference count on the handle; this is zero if the block
* was freed.
*/
int hndFree(void* buf)
{
handle_t *hnd, *prev;
if (buf == NULL)
return 0;
hnd = hndHandle(buf);
assert((addr_t) hnd >= (addr_t) &scode);
if (hnd->refs == 0)
{
assert(hnd->queue.first == NULL);
for (prev = hnd_first; prev && prev->next != hnd; prev = prev->next)
;
if (prev)
prev->next = hnd->next;
if (hnd == hnd_last)
hnd_last = prev;
if (hnd == hnd_first)
hnd_first = hnd->next;
free(hnd);
return 0;
}
else
return --hnd->refs;
}
//! Sets the signal on a handle
/*!
* Handles double as signallable objects. This function either sets or resets
* a handle's signal. Any threads waiting on the handle will be woken next
* time thrSchedule() is called.
*
* \param buf A pointer to the handle to be signalled
* \param signal The new state of the signal
*/
void hndSignal(void* buf, bool signal)
{
thread_t *thr, *next;
handle_t *hnd;
if (buf != NULL)
{
hnd = hndHandle(buf);
hnd->signal = signal;
if (signal)
{
//if (hnd->queue.first)
//wprintf(L"Signalled handle %S(%d)\n", hnd->file, hnd->line);
for (thr = hnd->queue.first; thr; thr = next)
{
//wprintf(L"hndSignal: Checking thread %d...", thr->id);
next = thr->next_queue;
thrDequeue(thr, &hnd->queue);
if (thrWaitFinished(thr->wait.handle.handles,
thr->wait.handle.num_handles,
thr->wait.handle.wait_all))
{
//wprintf(L"running\n");
hndFree(thr->wait.handle.handles);
thr->wait.handle.handles = NULL;
thr->wait.handle.num_handles = 0;
//wprintf(L"%d: wait on %S(%d) finished\n",
//thr->id, hnd->file, hnd->line);
thrRun(thr);
}
//else
//wprintf(L"not running\n");
}
}
}
}
//! Returns the state of a handle's signal
/*!
* See the documentation of hndSignal().
* \param buf A pointer to the handle to be tested
*/
bool hndIsSignalled(void* buf)
{
if (buf == NULL)
return false;
else
return hndHandle(buf)->signal;
}
//! Iterates through the handles allocated to the specified process, and prints the locations of their allocations.
/*!
* This function is useful as a debugging checkpoint. It is called, for
* instance, by the process termination code, which ensures that
* all handles allocated to a process are freed.
*/
void hndEnum(process_t* proc)
{
handle_t* hnd;
int i = 0;
for (hnd = hnd_first; hnd; hnd = hnd->next)
if (proc == NULL || hnd->process == proc)
{
wprintf(L"%S(%d)\n", hnd->file, hnd->line);
i++;
}
if (i)
wprintf(L"%d handle(s)\n", i);
}