essence-os/kernel/objects.cpp

754 lines
22 KiB
C++

// This file is part of the Essence operating system.
// It is released under the terms of the MIT license -- see LICENSE.md.
// Written by: nakst.
// TODO Reject opening handles if the handle table has been destroyed!!
#ifndef IMPLEMENTATION
struct Handle {
void *object;
uint32_t flags;
KernelObjectType type;
};
struct ConstantBuffer {
volatile size_t handles;
size_t bytes;
bool isPaged;
// Data follows.
};
struct Pipe {
#define PIPE_READER (1)
#define PIPE_WRITER (2)
#define PIPE_BUFFER_SIZE (256)
#define PIPE_CLOSED (0)
volatile char buffer[PIPE_BUFFER_SIZE]; // TODO Make this 4KB?
volatile size_t writers, readers;
volatile uintptr_t writePosition, readPosition, unreadData;
KEvent canWrite, canRead;
KMutex mutex;
size_t Access(void *buffer, size_t bytes, bool write, bool userBlockRequest);
};
struct MessageQueue {
bool SendMessage(void *target, EsMessage *message); // Returns false if the message queue is full.
bool SendMessage(_EsMessageWithObject *message); // Returns false if the message queue is full.
bool GetMessage(_EsMessageWithObject *message);
#define MESSAGE_QUEUE_MAX_LENGTH (4096)
Array<_EsMessageWithObject, K_FIXED> messages;
uintptr_t mouseMovedMessage,
windowResizedMessage,
eyedropResultMessage,
keyRepeatMessage;
bool pinged;
KMutex mutex;
KEvent notEmpty;
};
size_t totalHandleCount;
struct HandleTableL2 {
#define HANDLE_TABLE_L2_ENTRIES (256)
Handle t[HANDLE_TABLE_L2_ENTRIES];
};
struct HandleTableL1 {
#define HANDLE_TABLE_L1_ENTRIES (256)
HandleTableL2 *t[HANDLE_TABLE_L1_ENTRIES];
uint16_t u[HANDLE_TABLE_L1_ENTRIES];
};
struct HandleTable {
HandleTableL1 l1r;
KMutex lock;
struct Process *process;
bool destroyed;
uint32_t handleCount;
// Be careful putting handles in the handle table!
// The process will be able to immediately close it.
// If this fails, the handle is closed and ES_INVALID_HANDLE is returned.
EsHandle OpenHandle(void *_object, uint32_t _flags, KernelObjectType _type, EsHandle at = ES_INVALID_HANDLE);
bool CloseHandle(EsHandle handle);
void ModifyFlags(EsHandle handle, uint32_t newFlags);
// Resolve the handle if it is valid.
// The initial value of type is used as a mask of expected object types for the handle.
#define RESOLVE_HANDLE_FAILED (0)
#define RESOLVE_HANDLE_NO_CLOSE (1)
#define RESOLVE_HANDLE_NORMAL (2)
uint8_t ResolveHandle(Handle *outHandle, EsHandle inHandle, KernelObjectType typeMask);
void Destroy();
};
void InitialiseObjectManager();
#endif
#ifdef IMPLEMENTATION
// A lock used to change the handle count on several objects.
// TODO Make changing handle count lockless wherever possible?
KMutex objectHandleCountChange;
// TODO Use uint64_t for handle counts, or restrict OpenHandleToObject to some maximum (...but most callers don't check if OpenHandleToObject succeeds).
bool OpenHandleToObject(void *object, KernelObjectType type, uint32_t flags) {
bool hadNoHandles = false, failed = false;
switch (type) {
case KERNEL_OBJECT_EVENT: {
KMutexAcquire(&objectHandleCountChange);
KEvent *event = (KEvent *) object;
if (!event->handles) hadNoHandles = true;
else event->handles++;
KMutexRelease(&objectHandleCountChange);
} break;
case KERNEL_OBJECT_PROCESS: {
hadNoHandles = 0 == __sync_fetch_and_add(&((Process *) object)->handles, 1);
} break;
case KERNEL_OBJECT_THREAD: {
hadNoHandles = 0 == __sync_fetch_and_add(&((Thread *) object)->handles, 1);
} break;
case KERNEL_OBJECT_SHMEM: {
MMSharedRegion *region = (MMSharedRegion *) object;
KMutexAcquire(&region->mutex);
if (!region->handles) hadNoHandles = true;
else region->handles++;
KMutexRelease(&region->mutex);
} break;
case KERNEL_OBJECT_WINDOW: {
// NOTE The handle count of Window object is modified elsewhere.
Window *window = (Window *) object;
hadNoHandles = 0 == __sync_fetch_and_add(&window->handles, 1);
} break;
case KERNEL_OBJECT_EMBEDDED_WINDOW: {
EmbeddedWindow *window = (EmbeddedWindow *) object;
hadNoHandles = 0 == __sync_fetch_and_add(&window->handles, 1);
} break;
case KERNEL_OBJECT_CONSTANT_BUFFER: {
ConstantBuffer *buffer = (ConstantBuffer *) object;
KMutexAcquire(&objectHandleCountChange);
if (!buffer->handles) hadNoHandles = true;
else buffer->handles++;
KMutexRelease(&objectHandleCountChange);
} break;
#ifdef ENABLE_POSIX_SUBSYSTEM
case KERNEL_OBJECT_POSIX_FD: {
POSIXFile *file = (POSIXFile *) object;
KMutexAcquire(&file->mutex);
if (!file->handles) hadNoHandles = true;
else file->handles++;
KMutexRelease(&file->mutex);
} break;
#endif
case KERNEL_OBJECT_NODE: {
failed = ES_SUCCESS != FSNodeOpenHandle((KNode *) object, flags, FS_NODE_OPEN_HANDLE_STANDARD);
} break;
case KERNEL_OBJECT_PIPE: {
Pipe *pipe = (Pipe *) object;
KMutexAcquire(&pipe->mutex);
if (((flags & PIPE_READER) && !pipe->readers)
|| ((flags & PIPE_WRITER) && !pipe->writers)) {
hadNoHandles = true;
} else {
if (flags & PIPE_READER) {
pipe->readers++;
}
if (flags & PIPE_WRITER) {
pipe->writers++;
}
}
KMutexRelease(&pipe->mutex);
} break;
case KERNEL_OBJECT_CONNECTION: {
NetConnection *connection = (NetConnection *) object;
hadNoHandles = 0 == __sync_fetch_and_add(&connection->handles, 1);
} break;
case KERNEL_OBJECT_DEVICE: {
KDeviceOpenHandle((KDevice *) object, flags);
} break;
default: {
KernelPanic("OpenHandleToObject - Cannot open object of type %x.\n", type);
} break;
}
if (hadNoHandles) {
KernelPanic("OpenHandleToObject - Object %x of type %x had no handles.\n", object, type);
}
return !failed;
}
void CloseHandleToObject(void *object, KernelObjectType type, uint32_t flags) {
switch (type) {
case KERNEL_OBJECT_PROCESS: {
Process *process = (Process *) object;
uintptr_t previous = __sync_fetch_and_sub(&process->handles, 1);
KernelLog(LOG_VERBOSE, "Scheduler", "close process handle", "Closed handle to process %d; %d handles remain.\n", process->id, process->handles);
if (previous == 0) {
KernelPanic("CloseHandleToProcess - All handles to process %x have been closed.\n", process);
} else if (previous == 1) {
ProcessRemove(process);
}
} break;
case KERNEL_OBJECT_THREAD: {
Thread *thread = (Thread *) object;
uintptr_t previous = __sync_fetch_and_sub(&thread->handles, 1);
if (previous == 0) {
KernelPanic("CloseHandleToObject - All handles to thread %x have been closed.\n", thread);
} else if (previous == 1) {
ThreadRemove(thread);
}
} break;
case KERNEL_OBJECT_NODE: {
FSNodeCloseHandle((KNode *) object, flags);
} break;
case KERNEL_OBJECT_EVENT: {
KEvent *event = (KEvent *) object;
KMutexAcquire(&objectHandleCountChange);
bool destroy = event->handles == 1;
event->handles--;
KMutexRelease(&objectHandleCountChange);
if (destroy) {
EsHeapFree(event, sizeof(KEvent), K_FIXED);
}
} break;
case KERNEL_OBJECT_CONSTANT_BUFFER: {
ConstantBuffer *buffer = (ConstantBuffer *) object;
KMutexAcquire(&objectHandleCountChange);
bool destroy = buffer->handles == 1;
buffer->handles--;
KMutexRelease(&objectHandleCountChange);
if (destroy) {
EsHeapFree(object, sizeof(ConstantBuffer) + buffer->bytes, buffer->isPaged ? K_PAGED : K_FIXED);
}
} break;
case KERNEL_OBJECT_SHMEM: {
MMSharedRegion *region = (MMSharedRegion *) object;
KMutexAcquire(&region->mutex);
bool destroy = region->handles == 1;
region->handles--;
KMutexRelease(&region->mutex);
if (destroy) {
MMSharedDestroyRegion(region);
}
} break;
case KERNEL_OBJECT_WINDOW: {
Window *window = (Window *) object;
unsigned previous = __sync_fetch_and_sub(&window->handles, 1);
if (!previous) KernelPanic("CloseHandleToObject - Window %x has no handles.\n", window);
if (previous == 2) {
KEventSet(&windowManager.windowsToCloseEvent, true /* maybe already set */);
} else if (previous == 1) {
window->Destroy();
}
} break;
case KERNEL_OBJECT_EMBEDDED_WINDOW: {
EmbeddedWindow *window = (EmbeddedWindow *) object;
unsigned previous = __sync_fetch_and_sub(&window->handles, 1);
if (!previous) KernelPanic("CloseHandleToObject - EmbeddedWindow %x has no handles.\n", window);
if (previous == 2) {
KEventSet(&windowManager.windowsToCloseEvent, true /* maybe already set */);
} else if (previous == 1) {
window->Destroy();
}
} break;
#ifdef ENABLE_POSIX_SUBSYSTEM
case KERNEL_OBJECT_POSIX_FD: {
POSIXFile *file = (POSIXFile *) object;
KMutexAcquire(&file->mutex);
file->handles--;
bool destroy = !file->handles;
KMutexRelease(&file->mutex);
if (destroy) {
if (file->type == POSIX_FILE_NORMAL || file->type == POSIX_FILE_DIRECTORY) CloseHandleToObject(file->node, KERNEL_OBJECT_NODE, file->openFlags);
if (file->type == POSIX_FILE_PIPE) CloseHandleToObject(file->pipe, KERNEL_OBJECT_PIPE, file->openFlags);
EsHeapFree(file->path, 0, K_FIXED);
EsHeapFree(file->directoryBuffer, file->directoryBufferLength, K_PAGED);
EsHeapFree(file, sizeof(POSIXFile), K_FIXED);
}
} break;
#endif
case KERNEL_OBJECT_PIPE: {
Pipe *pipe = (Pipe *) object;
KMutexAcquire(&pipe->mutex);
if (flags & PIPE_READER) {
pipe->readers--;
if (!pipe->readers) {
// If there are no more readers, wake up any blocking writers.
KEventSet(&pipe->canWrite, true);
}
}
if (flags & PIPE_WRITER) {
pipe->writers--;
if (!pipe->writers) {
// If there are no more writers, wake up any blocking readers.
KEventSet(&pipe->canRead, true);
}
}
bool destroy = pipe->readers == 0 && pipe->writers == 0;
KMutexRelease(&pipe->mutex);
if (destroy) {
EsHeapFree(pipe, sizeof(Pipe), K_PAGED);
}
} break;
case KERNEL_OBJECT_CONNECTION: {
NetConnection *connection = (NetConnection *) object;
unsigned previous = __sync_fetch_and_sub(&connection->handles, 1);
if (!previous) KernelPanic("CloseHandleToObject - NetConnection %x has no handles.\n", connection);
if (previous == 1) NetConnectionClose(connection);
} break;
case KERNEL_OBJECT_DEVICE: {
KDeviceCloseHandle((KDevice *) object, flags);
} break;
default: {
KernelPanic("CloseHandleToObject - Cannot close object of type %x.\n", type);
} break;
}
}
uintptr_t HandleShare(Handle share, Process *process, uint32_t mode, EsHandle at = ES_INVALID_HANDLE) {
#define HANDLE_SHARE_TYPE_MASK ((KernelObjectType) (KERNEL_OBJECT_SHMEM | KERNEL_OBJECT_CONSTANT_BUFFER | KERNEL_OBJECT_PROCESS \
| KERNEL_OBJECT_DEVICE | KERNEL_OBJECT_NODE | KERNEL_OBJECT_EVENT | KERNEL_OBJECT_PIPE))
if ((share.type & HANDLE_SHARE_TYPE_MASK) == 0) {
KernelPanic("HandleShare - Invalid object type %x; allowed types are %x.\n", share.type, HANDLE_SHARE_TYPE_MASK);
}
uint32_t sharedFlags = share.flags;
// TODO Sort out flag modes.
if (share.type == KERNEL_OBJECT_SHMEM) {
sharedFlags &= mode;
} else if (share.type == KERNEL_OBJECT_NODE) {
sharedFlags = (mode & 1) && (share.flags & (ES_FILE_WRITE_SHARED | ES_FILE_WRITE)) ? ES_FILE_READ_SHARED : share.flags;
if (mode & 2) sharedFlags &= ~ES__NODE_DIRECTORY_WRITE;
} else if (share.type == KERNEL_OBJECT_PIPE) {
}
if (!OpenHandleToObject(share.object, share.type, sharedFlags)) {
return ES_ERROR_PERMISSION_NOT_GRANTED;
} else {
return process->handleTable.OpenHandle(share.object, sharedFlags, share.type, at);
}
}
bool HandleTable::CloseHandle(EsHandle handle) {
if (handle > HANDLE_TABLE_L1_ENTRIES * HANDLE_TABLE_L2_ENTRIES) {
return false;
}
KMutexAcquire(&lock);
HandleTableL1 *l1 = &l1r;
HandleTableL2 *l2 = l1->t[handle / HANDLE_TABLE_L2_ENTRIES];
if (!l2) { KMutexRelease(&lock); return false; }
Handle *_handle = l2->t + (handle % HANDLE_TABLE_L2_ENTRIES);
KernelObjectType type = _handle->type;
uint64_t flags = _handle->flags;
void *object = _handle->object;
if (!object) { KMutexRelease(&lock); return false; }
EsMemoryZero(_handle, sizeof(Handle));
l1->u[handle / HANDLE_TABLE_L2_ENTRIES]--;
handleCount--;
KMutexRelease(&lock);
__sync_fetch_and_sub(&totalHandleCount, 1);
CloseHandleToObject(object, type, flags);
return true;
}
void HandleTable::ModifyFlags(EsHandle handle, uint32_t newFlags) {
KMutexAcquire(&lock);
EsDefer(KMutexRelease(&lock));
if ((!handle) || handle >= HANDLE_TABLE_L1_ENTRIES * HANDLE_TABLE_L2_ENTRIES) return;
HandleTableL2 *l2 = l1r.t[handle / HANDLE_TABLE_L2_ENTRIES];
if (!l2) return;
Handle *_handle = l2->t + (handle % HANDLE_TABLE_L2_ENTRIES);
if (!_handle->object) return;
_handle->flags = newFlags;
}
uint8_t HandleTable::ResolveHandle(Handle *outHandle, EsHandle inHandle, KernelObjectType typeMask) {
// Special handles.
if (inHandle == ES_CURRENT_THREAD && (typeMask & KERNEL_OBJECT_THREAD)) {
outHandle->type = KERNEL_OBJECT_THREAD;
outHandle->object = GetCurrentThread();
outHandle->flags = 0;
return RESOLVE_HANDLE_NO_CLOSE;
} else if (inHandle == ES_CURRENT_PROCESS && (typeMask & KERNEL_OBJECT_PROCESS)) {
outHandle->type = KERNEL_OBJECT_PROCESS;
outHandle->object = GetCurrentThread()->process;
outHandle->flags = 0;
return RESOLVE_HANDLE_NO_CLOSE;
} else if (inHandle == ES_INVALID_HANDLE && (typeMask & KERNEL_OBJECT_NONE)) {
outHandle->type = KERNEL_OBJECT_NONE;
outHandle->object = nullptr;
outHandle->flags = 0;
return RESOLVE_HANDLE_NO_CLOSE;
}
// Check that the handle is within the correct bounds.
if ((!inHandle) || inHandle >= HANDLE_TABLE_L1_ENTRIES * HANDLE_TABLE_L2_ENTRIES) {
return RESOLVE_HANDLE_FAILED;
}
KMutexAcquire(&lock);
EsDefer(KMutexRelease(&lock));
HandleTableL2 *l2 = l1r.t[inHandle / HANDLE_TABLE_L2_ENTRIES];
if (!l2) return RESOLVE_HANDLE_FAILED;
Handle *_handle = l2->t + (inHandle % HANDLE_TABLE_L2_ENTRIES);
if ((_handle->type & typeMask) && (_handle->object)) {
// Open a handle to the object so that it can't be destroyed while the system call is still using it.
// The handle is closed in the KObject's destructor.
if (OpenHandleToObject(_handle->object, _handle->type, _handle->flags)) {
*outHandle = *_handle;
return RESOLVE_HANDLE_NORMAL;
}
}
return RESOLVE_HANDLE_FAILED;
}
// TODO Switch the order of flags and type, so that the default value of flags can be 0.
EsHandle HandleTable::OpenHandle(void *object, uint32_t flags, KernelObjectType type, EsHandle at) {
KMutexAcquire(&lock);
EsDefer(KMutexRelease(&lock));
Handle handle = {};
handle.object = object;
handle.flags = flags;
handle.type = type;
{
if (destroyed) goto error;
if (!handle.object) {
KernelPanic("HandleTable::OpenHandle - Invalid object.\n");
}
HandleTableL1 *l1 = &l1r;
uintptr_t l1Index = HANDLE_TABLE_L1_ENTRIES;
for (uintptr_t i = 1 /* The first set of handles are reserved. */; i < HANDLE_TABLE_L1_ENTRIES; i++) {
if (at) i = at / HANDLE_TABLE_L2_ENTRIES;
if (l1->u[i] != HANDLE_TABLE_L2_ENTRIES) {
l1->u[i]++;
handleCount++;
l1Index = i;
break;
}
if (at) goto error;
}
if (l1Index == HANDLE_TABLE_L1_ENTRIES) goto error;
if (!l1->t[l1Index]) l1->t[l1Index] = (HandleTableL2 *) EsHeapAllocate(sizeof(HandleTableL2), true, K_FIXED);
HandleTableL2 *l2 = l1->t[l1Index];
if (!l2) goto error;
uintptr_t l2Index = HANDLE_TABLE_L2_ENTRIES;
for (uintptr_t i = 0; i < HANDLE_TABLE_L2_ENTRIES; i++) {
if (at) i = at % HANDLE_TABLE_L2_ENTRIES;
if (!l2->t[i].object) {
l2Index = i;
break;
}
if (at) goto error;
}
if (l2Index == HANDLE_TABLE_L2_ENTRIES) KernelPanic("HandleTable::OpenHandle - Unexpected lack of free handles.\n");
Handle *_handle = l2->t + l2Index;
*_handle = handle;
__sync_fetch_and_add(&totalHandleCount, 1);
EsHandle index = l2Index + (l1Index * HANDLE_TABLE_L2_ENTRIES);
return index;
}
error:;
CloseHandleToObject(object, type, flags);
return ES_INVALID_HANDLE;
}
void HandleTable::Destroy() {
KMutexAcquire(&lock);
EsDefer(KMutexRelease(&lock));
if (destroyed) {
return;
}
destroyed = true;
HandleTableL1 *l1 = &l1r;
for (uintptr_t i = 0; i < HANDLE_TABLE_L1_ENTRIES; i++) {
if (!l1->u[i]) continue;
for (uintptr_t k = 0; k < HANDLE_TABLE_L2_ENTRIES; k++) {
Handle *handle = &l1->t[i]->t[k];
if (handle->object) CloseHandleToObject(handle->object, handle->type, handle->flags);
}
EsHeapFree(l1->t[i], 0, K_FIXED);
}
}
ConstantBuffer *ConstantBufferCreate(K_USER_BUFFER const void *data, size_t bytes) {
ConstantBuffer *buffer = (ConstantBuffer *) EsHeapAllocate(sizeof(ConstantBuffer) + bytes, false, K_FIXED);
if (!buffer) return nullptr;
EsMemoryZero(buffer, sizeof(ConstantBuffer));
buffer->handles = 1;
buffer->bytes = bytes;
EsMemoryCopy(buffer + 1, data, buffer->bytes);
return buffer;
}
EsHandle ConstantBufferCreate(K_USER_BUFFER const void *data, size_t bytes, Process *process) {
void *object = ConstantBufferCreate(data, bytes);
return object ? process->handleTable.OpenHandle(object, 0, KERNEL_OBJECT_CONSTANT_BUFFER) : ES_INVALID_HANDLE;
}
size_t Pipe::Access(void *_buffer, size_t bytes, bool write, bool user) {
size_t amount = 0;
Thread *currentThread = GetCurrentThread();
// EsPrint("--> %z %d\n", write ? "Write" : "Read", bytes);
while (bytes) {
if (user) currentThread->terminatableState = THREAD_USER_BLOCK_REQUEST;
if (write) {
// Wait until we can write to the pipe.
KEventWait(&canWrite, ES_WAIT_NO_TIMEOUT);
} else {
// Wait until we can read from the pipe.
KEventWait(&canRead, ES_WAIT_NO_TIMEOUT);
}
if (user) {
currentThread->terminatableState = THREAD_IN_SYSCALL;
if (currentThread->terminating) goto done;
}
KMutexAcquire(&mutex);
EsDefer(KMutexRelease(&mutex));
if (write) {
// EsPrint("Write:\n");
size_t spaceAvailable = PIPE_BUFFER_SIZE - unreadData;
size_t toWrite = bytes > spaceAvailable ? spaceAvailable : bytes;
size_t spaceAvailableRight = PIPE_BUFFER_SIZE - writePosition;
size_t toWriteRight = toWrite > spaceAvailableRight ? spaceAvailableRight : toWrite;
size_t toWriteLeft = toWrite - toWriteRight;
// EsPrint("\tunread: %d; wp: %d\n", unreadData, writePosition);
// EsPrint("\t%d, %d, %d, %d, %d\n", spaceAvailable, spaceAvailableRight, toWrite, toWriteRight, toWriteLeft);
if (_buffer) EsMemoryCopy((uint8_t *) buffer + writePosition, _buffer, toWriteRight);
if (_buffer) EsMemoryCopy((uint8_t *) buffer, (uint8_t *) _buffer + toWriteRight, toWriteLeft);
writePosition += toWrite;
writePosition %= PIPE_BUFFER_SIZE;
unreadData += toWrite;
bytes -= toWrite;
if (_buffer) _buffer = (uint8_t *) _buffer + toWrite;
amount += toWrite;
KEventSet(&canRead, true);
if (!readers) {
// EsPrint("\tPipe closed\n");
// Nobody is reading from the pipe, so there's no point writing to it.
goto done;
} else if (PIPE_BUFFER_SIZE == unreadData) {
KEventReset(&canWrite);
// EsPrint("\treset canWrite\n");
}
} else {
// EsPrint("Read:\n");
size_t dataAvailable = unreadData;
size_t toRead = bytes > dataAvailable ? dataAvailable : bytes;
size_t spaceAvailableRight = PIPE_BUFFER_SIZE - readPosition;
size_t toReadRight = toRead > spaceAvailableRight ? spaceAvailableRight : toRead;
size_t toReadLeft = toRead - toReadRight;
// EsPrint("\tunread: %d; rp: %d\n", unreadData, readPosition);
// EsPrint("\t%d, %d, %d, %d, %d\n", dataAvailable, spaceAvailableRight, toRead, toReadRight, toReadLeft);
if (_buffer) EsMemoryCopy(_buffer, (uint8_t *) buffer + readPosition, toReadRight);
if (_buffer) EsMemoryCopy((uint8_t *) _buffer + toReadRight, (uint8_t *) buffer, toReadLeft);
readPosition += toRead;
readPosition %= PIPE_BUFFER_SIZE;
unreadData -= toRead;
bytes -= toRead;
if (_buffer) _buffer = (uint8_t *) _buffer + toRead;
amount += toRead;
KEventSet(&canWrite, true);
if (!writers) {
// Nobody is writing to the pipe, so there's no point reading from it.
// EsPrint("\tPipe closed\n");
goto done;
} else if (!unreadData) {
KEventReset(&canRead);
}
// Don't block when reading from pipes after the first chunk of data.
// TODO Change this behaviour?
goto done;
}
}
done:;
// EsPrint("<-- %d (%d remaining, %z)\n", amount, bytes, write ? "Write" : "Read");
return amount;
}
bool MessageQueue::SendMessage(void *object, EsMessage *_message) {
// TODO Remove unnecessary copy.
_EsMessageWithObject message = { object, *_message };
return SendMessage(&message);
}
bool MessageQueue::SendMessage(_EsMessageWithObject *_message) {
// TODO Don't send messages if the process has been terminated.
KMutexAcquire(&mutex);
EsDefer(KMutexRelease(&mutex));
if (messages.Length() == MESSAGE_QUEUE_MAX_LENGTH) {
KernelLog(LOG_ERROR, "Messages", "message dropped", "Message of type %d and target %x has been dropped because queue %x was full.\n",
_message->message.type, _message->object, this);
return false;
}
#define MERGE_MESSAGES(variable, change) \
do { \
if (variable && messages[variable - 1].object == _message->object) { \
if (change) EsMemoryCopy(&messages[variable - 1], _message, sizeof(_EsMessageWithObject)); \
} else if (messages.AddPointer(_message)) { \
variable = messages.Length(); \
} else { \
return false; \
} \
} while (0)
// NOTE Don't forget to update GetMessage with the merged messages!
if (_message->message.type == ES_MSG_MOUSE_MOVED) {
MERGE_MESSAGES(mouseMovedMessage, true);
} else if (_message->message.type == ES_MSG_WINDOW_RESIZED) {
MERGE_MESSAGES(windowResizedMessage, true);
} else if (_message->message.type == ES_MSG_EYEDROP_REPORT) {
MERGE_MESSAGES(eyedropResultMessage, true);
} else if (_message->message.type == ES_MSG_KEY_DOWN && _message->message.keyboard.repeat) {
MERGE_MESSAGES(keyRepeatMessage, false);
} else {
if (!messages.AddPointer(_message)) {
return false;
}
if (_message->message.type == ES_MSG_PING) {
pinged = true;
}
}
KEventSet(&notEmpty, true);
return true;
}
bool MessageQueue::GetMessage(_EsMessageWithObject *_message) {
KMutexAcquire(&mutex);
EsDefer(KMutexRelease(&mutex));
if (!messages.Length()) {
return false;
}
*_message = messages[0];
messages.Delete(0);
if (mouseMovedMessage) mouseMovedMessage--;
if (windowResizedMessage) windowResizedMessage--;
if (eyedropResultMessage) eyedropResultMessage--;
if (keyRepeatMessage) keyRepeatMessage--;
pinged = false;
if (!messages.Length()) {
KEventReset(&notEmpty);
}
return true;
}
#endif