Rewritten the msg queue code to use a doubly linked list of port_msgs.
Right now, it allocates/frees the msgs from the kernel heap for every message sent - that might be too slow for real world usage. Also removed all known race conditions from the code. Not tested at all yet, though. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6953 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+178
-194
@@ -13,13 +13,10 @@
|
|||||||
#include <OS.h>
|
#include <OS.h>
|
||||||
#include <port.h>
|
#include <port.h>
|
||||||
#include <sem.h>
|
#include <sem.h>
|
||||||
#include <kernel.h>
|
#include <util/list.h>
|
||||||
#include <arch/int.h>
|
#include <arch/int.h>
|
||||||
#include <debug.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
#include <cbuf.h>
|
#include <cbuf.h>
|
||||||
#include <Errors.h>
|
|
||||||
#include <int.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@@ -32,25 +29,24 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
struct port_msg {
|
typedef struct port_msg {
|
||||||
int msg_code;
|
list_link link;
|
||||||
cbuf *data_cbuf;
|
int32 code;
|
||||||
size_t data_len;
|
cbuf *buffer_chain;
|
||||||
};
|
size_t size;
|
||||||
|
} port_msg;
|
||||||
|
|
||||||
struct port_entry {
|
struct port_entry {
|
||||||
port_id id;
|
port_id id;
|
||||||
team_id owner;
|
team_id owner;
|
||||||
int32 capacity;
|
int32 capacity;
|
||||||
spinlock lock;
|
spinlock lock;
|
||||||
char *name;
|
const char *name;
|
||||||
sem_id read_sem;
|
sem_id read_sem;
|
||||||
sem_id write_sem;
|
sem_id write_sem;
|
||||||
int head;
|
int32 total_count;
|
||||||
int tail;
|
struct list msg_queue;
|
||||||
int total_count;
|
bool closed;
|
||||||
bool closed;
|
|
||||||
struct port_msg* msg_queue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// internal API
|
// internal API
|
||||||
@@ -91,6 +87,10 @@ port_init(kernel_args *ka)
|
|||||||
panic("unable to allocate kernel port table!\n");
|
panic("unable to allocate kernel port table!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToDo: investigate preallocating a list of port_msgs to
|
||||||
|
// speed up actual message sending/receiving, a slab allocator
|
||||||
|
// might do it as well, though :-)
|
||||||
|
|
||||||
memset(gPorts, 0, size);
|
memset(gPorts, 0, size);
|
||||||
for (i = 0; i < MAX_PORTS; i++)
|
for (i = 0; i < MAX_PORTS; i++)
|
||||||
gPorts[i].id = -1;
|
gPorts[i].id = -1;
|
||||||
@@ -228,9 +228,7 @@ _dump_port_info(struct port_entry *port)
|
|||||||
dprintf("PORT: %p\n", port);
|
dprintf("PORT: %p\n", port);
|
||||||
dprintf("name: '%s'\n", port->name);
|
dprintf("name: '%s'\n", port->name);
|
||||||
dprintf("owner: 0x%lx\n", port->owner);
|
dprintf("owner: 0x%lx\n", port->owner);
|
||||||
dprintf("cap: %ld\n", port->capacity);
|
dprintf("capacity: %ld\n", port->capacity);
|
||||||
dprintf("head: %d\n", port->head);
|
|
||||||
dprintf("tail: %d\n", port->tail);
|
|
||||||
get_sem_count(port->read_sem, &cnt);
|
get_sem_count(port->read_sem, &cnt);
|
||||||
dprintf("read_sem: %ld\n", cnt);
|
dprintf("read_sem: %ld\n", cnt);
|
||||||
get_sem_count(port->write_sem, &cnt);
|
get_sem_count(port->write_sem, &cnt);
|
||||||
@@ -321,71 +319,89 @@ delete_owned_ports(team_id owner)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
put_port_msg(port_msg *msg)
|
||||||
|
{
|
||||||
|
cbuf_free_chain(msg->buffer_chain);
|
||||||
|
free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static port_msg *
|
||||||
|
get_port_msg(int32 code, size_t bufferSize)
|
||||||
|
{
|
||||||
|
// ToDo: investigate preallocation of port_msgs (or use a slab allocator)
|
||||||
|
cbuf *bufferChain = NULL;
|
||||||
|
|
||||||
|
port_msg *msg = (port_msg *)malloc(sizeof(port_msg));
|
||||||
|
if (msg == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (bufferSize > 0) {
|
||||||
|
bufferChain = cbuf_get_chain(bufferSize);
|
||||||
|
if (bufferChain == NULL) {
|
||||||
|
free(msg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg->code = code;
|
||||||
|
msg->buffer_chain = bufferChain;
|
||||||
|
msg->size = bufferSize;
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
// public kernel API
|
// public kernel API
|
||||||
|
|
||||||
|
|
||||||
port_id
|
port_id
|
||||||
create_port(int32 queue_length, const char *name)
|
create_port(int32 queueLength, const char *name)
|
||||||
{
|
{
|
||||||
int i;
|
cpu_status state;
|
||||||
int state;
|
char nameBuffer[B_OS_NAME_LENGTH];
|
||||||
sem_id sem_r, sem_w;
|
sem_id readSem, writeSem;
|
||||||
port_id retval;
|
port_id returnValue;
|
||||||
int name_len;
|
|
||||||
char *temp_name;
|
|
||||||
struct port_msg *q;
|
|
||||||
team_id owner;
|
team_id owner;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (ports_active == false)
|
if (ports_active == false)
|
||||||
return B_BAD_PORT_ID;
|
return B_BAD_PORT_ID;
|
||||||
|
|
||||||
// check queue length
|
// check queue length
|
||||||
if (queue_length < 1)
|
if (queueLength < 1
|
||||||
return EINVAL;
|
|| queueLength > MAX_QUEUE_LENGTH)
|
||||||
if (queue_length > MAX_QUEUE_LENGTH)
|
return B_BAD_VALUE;
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
// check & dup name
|
// check & dup name
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
name = "unnamed port";
|
name = "unnamed port";
|
||||||
|
|
||||||
name_len = strlen(name) + 1;
|
// ToDo: we could save the memory and use the semaphore name only instead
|
||||||
name_len = min(name_len, B_OS_NAME_LENGTH);
|
strlcpy(nameBuffer, name, B_OS_NAME_LENGTH);
|
||||||
temp_name = (char *)malloc(name_len);
|
name = strdup(nameBuffer);
|
||||||
if (temp_name == NULL)
|
if (name == NULL)
|
||||||
return ENOMEM;
|
return B_NO_MEMORY;
|
||||||
strlcpy(temp_name, name, name_len);
|
|
||||||
|
|
||||||
// alloc queue
|
// create read sem with owner set to -1
|
||||||
q = (struct port_msg *)malloc(queue_length * sizeof(struct port_msg));
|
// ToDo: should be B_SYSTEM_TEAM
|
||||||
if (q == NULL) {
|
readSem = create_sem_etc(0, name, -1);
|
||||||
free(temp_name); // dealloc name, too
|
if (readSem < B_OK) {
|
||||||
return ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
// init cbuf list of the queue
|
|
||||||
for (i = 0; i < queue_length; i++)
|
|
||||||
q[i].data_cbuf = 0;
|
|
||||||
|
|
||||||
// create sem_r with owner set to -1
|
|
||||||
sem_r = create_sem_etc(0, temp_name, -1);
|
|
||||||
if (sem_r < 0) {
|
|
||||||
// cleanup
|
// cleanup
|
||||||
free(temp_name);
|
free((char *)name);
|
||||||
free(q);
|
return readSem;
|
||||||
return sem_r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create sem_w
|
// create write sem
|
||||||
sem_w = create_sem_etc(queue_length, temp_name, -1);
|
writeSem = create_sem_etc(queueLength, name, -1);
|
||||||
if (sem_w < 0) {
|
if (writeSem < 0) {
|
||||||
// cleanup
|
// cleanup
|
||||||
delete_sem(sem_r);
|
delete_sem(readSem);
|
||||||
free(temp_name);
|
free((char *)name);
|
||||||
free(q);
|
return writeSem;
|
||||||
return sem_w;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
owner = team_get_current_team_id();
|
owner = team_get_current_team_id();
|
||||||
|
|
||||||
state = disable_interrupts();
|
state = disable_interrupts();
|
||||||
@@ -404,37 +420,36 @@ create_port(int32 queue_length, const char *name)
|
|||||||
gPorts[i].id = gNextPort++;
|
gPorts[i].id = gNextPort++;
|
||||||
RELEASE_PORT_LIST_LOCK();
|
RELEASE_PORT_LIST_LOCK();
|
||||||
|
|
||||||
gPorts[i].capacity = queue_length;
|
gPorts[i].capacity = queueLength;
|
||||||
gPorts[i].name = temp_name;
|
gPorts[i].owner = owner;
|
||||||
|
gPorts[i].name = name;
|
||||||
|
|
||||||
|
gPorts[i].read_sem = readSem;
|
||||||
|
gPorts[i].write_sem = writeSem;
|
||||||
|
|
||||||
|
list_init(&gPorts[i].msg_queue);
|
||||||
|
gPorts[i].total_count = 0;
|
||||||
|
returnValue = gPorts[i].id;
|
||||||
|
|
||||||
// assign sem
|
|
||||||
gPorts[i].read_sem = sem_r;
|
|
||||||
gPorts[i].write_sem = sem_w;
|
|
||||||
gPorts[i].msg_queue = q;
|
|
||||||
gPorts[i].head = 0;
|
|
||||||
gPorts[i].tail = 0;
|
|
||||||
gPorts[i].total_count= 0;
|
|
||||||
gPorts[i].owner = owner;
|
|
||||||
retval = gPorts[i].id;
|
|
||||||
RELEASE_PORT_LOCK(gPorts[i]);
|
RELEASE_PORT_LOCK(gPorts[i]);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// not enough gPorts...
|
// not enough gPorts...
|
||||||
RELEASE_PORT_LIST_LOCK();
|
RELEASE_PORT_LIST_LOCK();
|
||||||
retval = B_NO_MORE_PORTS;
|
returnValue = B_NO_MORE_PORTS;
|
||||||
dprintf("create_port(): B_NO_MORE_PORTS\n");
|
dprintf("create_port(): B_NO_MORE_PORTS\n");
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
delete_sem(sem_w);
|
delete_sem(writeSem);
|
||||||
delete_sem(sem_r);
|
delete_sem(readSem);
|
||||||
free(temp_name);
|
free((char *)name);
|
||||||
free(q);
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
|
|
||||||
return retval;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -474,18 +489,14 @@ close_port(port_id id)
|
|||||||
status_t
|
status_t
|
||||||
delete_port(port_id id)
|
delete_port(port_id id)
|
||||||
{
|
{
|
||||||
|
cpu_status state;
|
||||||
|
sem_id readSem, writeSem;
|
||||||
|
const char *name;
|
||||||
|
struct list list;
|
||||||
|
port_msg *msg;
|
||||||
int slot;
|
int slot;
|
||||||
int state;
|
|
||||||
sem_id r_sem, w_sem;
|
|
||||||
int capacity;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
char *old_name;
|
if (ports_active == false || id < 0)
|
||||||
struct port_msg *q;
|
|
||||||
|
|
||||||
if (ports_active == false)
|
|
||||||
return B_BAD_PORT_ID;
|
|
||||||
if (id < 0)
|
|
||||||
return B_BAD_PORT_ID;
|
return B_BAD_PORT_ID;
|
||||||
|
|
||||||
slot = id % MAX_PORTS;
|
slot = id % MAX_PORTS;
|
||||||
@@ -502,31 +513,28 @@ delete_port(port_id id)
|
|||||||
|
|
||||||
/* mark port as invalid */
|
/* mark port as invalid */
|
||||||
gPorts[slot].id = -1;
|
gPorts[slot].id = -1;
|
||||||
old_name = gPorts[slot].name;
|
name = gPorts[slot].name;
|
||||||
q = gPorts[slot].msg_queue;
|
list = gPorts[slot].msg_queue;
|
||||||
r_sem = gPorts[slot].read_sem;
|
readSem = gPorts[slot].read_sem;
|
||||||
w_sem = gPorts[slot].write_sem;
|
writeSem = gPorts[slot].write_sem;
|
||||||
capacity = gPorts[slot].capacity;
|
|
||||||
gPorts[slot].name = NULL;
|
gPorts[slot].name = NULL;
|
||||||
|
|
||||||
RELEASE_PORT_LOCK(gPorts[slot]);
|
RELEASE_PORT_LOCK(gPorts[slot]);
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
|
|
||||||
// delete the cbuf's that are left in the queue (if any)
|
// free the queue
|
||||||
for (i = 0; i < capacity; i++) {
|
while ((msg = (port_msg *)list_remove_head_item(&list)) != NULL) {
|
||||||
if (q[i].data_cbuf != NULL)
|
put_port_msg(msg);
|
||||||
cbuf_free_chain(q[i].data_cbuf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(q);
|
free((char *)name);
|
||||||
free(old_name);
|
|
||||||
|
|
||||||
// release the threads that were blocking on this port by deleting the sem
|
// release the threads that were blocking on this port by deleting the sem
|
||||||
// read_port() will see the B_BAD_SEM_ID acq_sem() return value, and act accordingly
|
// read_port() will see the B_BAD_SEM_ID acq_sem() return value, and act accordingly
|
||||||
delete_sem(r_sem);
|
delete_sem(readSem);
|
||||||
delete_sem(w_sem);
|
delete_sem(writeSem);
|
||||||
|
|
||||||
return B_NO_ERROR;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -682,9 +690,9 @@ port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout)
|
|||||||
cpu_status state;
|
cpu_status state;
|
||||||
sem_id cachedSem;
|
sem_id cachedSem;
|
||||||
status_t status;
|
status_t status;
|
||||||
|
port_msg *msg;
|
||||||
ssize_t size;
|
ssize_t size;
|
||||||
int slot;
|
int slot;
|
||||||
int tail;
|
|
||||||
|
|
||||||
if (!ports_active || id < 0)
|
if (!ports_active || id < 0)
|
||||||
return B_BAD_PORT_ID;
|
return B_BAD_PORT_ID;
|
||||||
@@ -706,12 +714,8 @@ port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout)
|
|||||||
RELEASE_PORT_LOCK(gPorts[slot]);
|
RELEASE_PORT_LOCK(gPorts[slot]);
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
|
|
||||||
// block if no message,
|
// block if no message, or, if B_TIMEOUT flag set, block with timeout
|
||||||
// if TIMEOUT flag set, block with timeout
|
|
||||||
|
|
||||||
// XXX - is it a race condition to acquire a sem just after we
|
|
||||||
// unlocked the port ?
|
|
||||||
// XXX: call an acquire_sem which does the release lock, restore int & block the right way
|
|
||||||
status = acquire_sem_etc(cachedSem, 1, flags, timeout);
|
status = acquire_sem_etc(cachedSem, 1, flags, timeout);
|
||||||
|
|
||||||
if (status == B_BAD_SEM_ID) {
|
if (status == B_BAD_SEM_ID) {
|
||||||
@@ -732,12 +736,11 @@ port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// determine tail & get the length of the message
|
// determine tail & get the length of the message
|
||||||
tail = gPorts[slot].tail;
|
msg = list_get_first_item(&gPorts[slot].msg_queue);
|
||||||
if (tail < 0)
|
if (msg == NULL)
|
||||||
panic("port %ld: tail < 0", gPorts[slot].id);
|
panic("port %ld: no messages found", gPorts[slot].id);
|
||||||
if (tail > gPorts[slot].capacity)
|
|
||||||
panic("port %ld: tail > cap %ld", gPorts[slot].id, gPorts[slot].capacity);
|
size = msg->size;
|
||||||
size = gPorts[slot].msg_queue[tail].data_len;
|
|
||||||
|
|
||||||
RELEASE_PORT_LOCK(gPorts[slot]);
|
RELEASE_PORT_LOCK(gPorts[slot]);
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
@@ -757,8 +760,7 @@ port_count(port_id id)
|
|||||||
int state;
|
int state;
|
||||||
int32 count;
|
int32 count;
|
||||||
|
|
||||||
if (ports_active == false
|
if (ports_active == false || id < 0)
|
||||||
|| id < 0)
|
|
||||||
return B_BAD_PORT_ID;
|
return B_BAD_PORT_ID;
|
||||||
|
|
||||||
slot = id % MAX_PORTS;
|
slot = id % MAX_PORTS;
|
||||||
@@ -774,7 +776,7 @@ port_count(port_id id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
get_sem_count(gPorts[slot].read_sem, &count);
|
get_sem_count(gPorts[slot].read_sem, &count);
|
||||||
// do not return negative numbers
|
// do not return negative numbers
|
||||||
if (count < 0)
|
if (count < 0)
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
@@ -794,24 +796,21 @@ read_port(port_id port, int32 *msgCode, void *msgBuffer, size_t bufferSize)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
read_port_etc(port_id id, int32 *msgCode, void *msgBuffer, size_t bufferSize,
|
read_port_etc(port_id id, int32 *_msgCode, void *msgBuffer, size_t bufferSize,
|
||||||
uint32 flags, bigtime_t timeout)
|
uint32 flags, bigtime_t timeout)
|
||||||
{
|
{
|
||||||
cpu_status state;
|
cpu_status state;
|
||||||
sem_id cachedSem;
|
sem_id cachedSem;
|
||||||
size_t size;
|
|
||||||
status_t status;
|
status_t status;
|
||||||
int tail;
|
|
||||||
cbuf *msgStore;
|
|
||||||
int32 code;
|
|
||||||
bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0;
|
bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0;
|
||||||
|
port_msg *msg;
|
||||||
|
size_t size;
|
||||||
int slot;
|
int slot;
|
||||||
|
|
||||||
if (ports_active == false
|
if (!ports_active || id < 0)
|
||||||
|| id < 0)
|
|
||||||
return B_BAD_PORT_ID;
|
return B_BAD_PORT_ID;
|
||||||
|
|
||||||
if (msgCode == NULL
|
if (_msgCode == NULL
|
||||||
|| (msgBuffer == NULL && bufferSize > 0)
|
|| (msgBuffer == NULL && bufferSize > 0)
|
||||||
|| timeout < 0)
|
|| timeout < 0)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
@@ -835,16 +834,9 @@ read_port_etc(port_id id, int32 *msgCode, void *msgBuffer, size_t bufferSize,
|
|||||||
RELEASE_PORT_LOCK(gPorts[slot]);
|
RELEASE_PORT_LOCK(gPorts[slot]);
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
|
|
||||||
// XXX -> possible race condition if port gets deleted (->sem deleted too), therefore
|
|
||||||
// sem_id is cached in local variable up here
|
|
||||||
|
|
||||||
status = acquire_sem_etc(cachedSem, 1, flags, timeout);
|
status = acquire_sem_etc(cachedSem, 1, flags, timeout);
|
||||||
// get 1 entry from the queue, block if needed
|
// get 1 entry from the queue, block if needed
|
||||||
|
|
||||||
// XXX: possible race condition if port read by two threads...
|
|
||||||
// both threads will read in 2 different slots allocated above, simultaneously
|
|
||||||
// slot is a thread-local variable
|
|
||||||
|
|
||||||
if (status == B_BAD_SEM_ID || status == B_INTERRUPTED) {
|
if (status == B_BAD_SEM_ID || status == B_INTERRUPTED) {
|
||||||
/* somebody deleted the port or the sem went away */
|
/* somebody deleted the port or the sem went away */
|
||||||
return B_BAD_PORT_ID;
|
return B_BAD_PORT_ID;
|
||||||
@@ -861,47 +853,49 @@ read_port_etc(port_id id, int32 *msgCode, void *msgBuffer, size_t bufferSize,
|
|||||||
state = disable_interrupts();
|
state = disable_interrupts();
|
||||||
GRAB_PORT_LOCK(gPorts[slot]);
|
GRAB_PORT_LOCK(gPorts[slot]);
|
||||||
|
|
||||||
tail = gPorts[slot].tail;
|
// first, let's check if the port is still alive
|
||||||
if (tail < 0)
|
if (gPorts[slot].id == -1) {
|
||||||
panic("port %ld: tail < 0", gPorts[slot].id);
|
// the port has been deleted in the meantime
|
||||||
if (tail > gPorts[slot].capacity)
|
RELEASE_PORT_LOCK(gPorts[slot]);
|
||||||
panic("port %ld: tail > cap %ld", gPorts[slot].id, gPorts[slot].capacity);
|
restore_interrupts(state);
|
||||||
|
return B_BAD_PORT_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = list_get_first_item(&gPorts[slot].msg_queue);
|
||||||
|
if (msg == NULL)
|
||||||
|
panic("port %ld: no messages found", gPorts[slot].id);
|
||||||
|
|
||||||
|
list_remove_link(msg);
|
||||||
|
|
||||||
gPorts[slot].tail = (gPorts[slot].tail + 1) % gPorts[slot].capacity;
|
|
||||||
gPorts[slot].total_count++;
|
gPorts[slot].total_count++;
|
||||||
|
|
||||||
msgStore = gPorts[slot].msg_queue[tail].data_cbuf;
|
|
||||||
code = gPorts[slot].msg_queue[tail].msg_code;
|
|
||||||
|
|
||||||
// mark queue entry unused
|
|
||||||
gPorts[slot].msg_queue[tail].data_cbuf = NULL;
|
|
||||||
|
|
||||||
// check output buffer size
|
|
||||||
size = min(bufferSize, gPorts[slot].msg_queue[tail].data_len);
|
|
||||||
|
|
||||||
cachedSem = gPorts[slot].write_sem;
|
cachedSem = gPorts[slot].write_sem;
|
||||||
|
|
||||||
RELEASE_PORT_LOCK(gPorts[slot]);
|
RELEASE_PORT_LOCK(gPorts[slot]);
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
|
|
||||||
|
// check output buffer size
|
||||||
|
size = min(bufferSize, msg->size);
|
||||||
|
|
||||||
// copy message
|
// copy message
|
||||||
*msgCode = code;
|
*_msgCode = msg->code;
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
if (userCopy) {
|
if (userCopy) {
|
||||||
if ((status = cbuf_user_memcpy_from_chain(msgBuffer, msgStore, 0, size) < B_OK)) {
|
if ((status = cbuf_user_memcpy_from_chain(msgBuffer, msg->buffer_chain, 0, size) < B_OK)) {
|
||||||
// leave the port intact, for other threads that might not crash
|
// leave the port intact, for other threads that might not crash
|
||||||
cbuf_free_chain(msgStore);
|
put_port_msg(msg);
|
||||||
release_sem(cachedSem);
|
release_sem(cachedSem);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
cbuf_memcpy_from_chain(msgBuffer, msgStore, 0, size);
|
cbuf_memcpy_from_chain(msgBuffer, msg->buffer_chain, 0, size);
|
||||||
}
|
}
|
||||||
// free the cbuf
|
put_port_msg(msg);
|
||||||
cbuf_free_chain(msgStore);
|
|
||||||
|
|
||||||
// make one spot in queue available again for write
|
// make one spot in queue available again for write
|
||||||
release_sem(cachedSem);
|
release_sem(cachedSem);
|
||||||
|
// ToDo: we might think about setting B_NO_RESCHEDULE here
|
||||||
|
// from time to time (always?)
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
@@ -918,16 +912,14 @@ status_t
|
|||||||
write_port_etc(port_id id, int32 msgCode, const void *msgBuffer,
|
write_port_etc(port_id id, int32 msgCode, const void *msgBuffer,
|
||||||
size_t bufferSize, uint32 flags, bigtime_t timeout)
|
size_t bufferSize, uint32 flags, bigtime_t timeout)
|
||||||
{
|
{
|
||||||
int slot;
|
|
||||||
cpu_status state;
|
cpu_status state;
|
||||||
status_t status;
|
|
||||||
sem_id cachedSem;
|
sem_id cachedSem;
|
||||||
int head;
|
status_t status;
|
||||||
cbuf *msgStore;
|
port_msg *msg;
|
||||||
bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0;
|
bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0;
|
||||||
|
int slot;
|
||||||
|
|
||||||
if (ports_active == false
|
if (!ports_active || id < 0)
|
||||||
|| id < 0)
|
|
||||||
return B_BAD_PORT_ID;
|
return B_BAD_PORT_ID;
|
||||||
|
|
||||||
// mask irrelevant flags (for acquire_sem() usage)
|
// mask irrelevant flags (for acquire_sem() usage)
|
||||||
@@ -960,16 +952,9 @@ write_port_etc(port_id id, int32 msgCode, const void *msgBuffer,
|
|||||||
RELEASE_PORT_LOCK(gPorts[slot]);
|
RELEASE_PORT_LOCK(gPorts[slot]);
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
|
|
||||||
// XXX -> possible race condition if port gets deleted (->sem deleted too),
|
|
||||||
// and queue is full therefore sem_id is cached in local variable up here
|
|
||||||
|
|
||||||
status = acquire_sem_etc(cachedSem, 1, flags, timeout);
|
status = acquire_sem_etc(cachedSem, 1, flags, timeout);
|
||||||
// get 1 entry from the queue, block if needed
|
// get 1 entry from the queue, block if needed
|
||||||
|
|
||||||
// XXX: possible race condition if port written by two threads...
|
|
||||||
// both threads will write in 2 different slots allocated above, simultaneously
|
|
||||||
// slot is a thread-local variable
|
|
||||||
|
|
||||||
if (status == B_BAD_SEM_ID || status == B_INTERRUPTED) {
|
if (status == B_BAD_SEM_ID || status == B_INTERRUPTED) {
|
||||||
/* somebody deleted the port or the sem while we were waiting */
|
/* somebody deleted the port or the sem while we were waiting */
|
||||||
return B_BAD_PORT_ID;
|
return B_BAD_PORT_ID;
|
||||||
@@ -983,37 +968,37 @@ write_port_etc(port_id id, int32 msgCode, const void *msgBuffer,
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bufferSize > 0) {
|
msg = get_port_msg(msgCode, bufferSize);
|
||||||
msgStore = cbuf_get_chain(bufferSize);
|
if (msg == NULL)
|
||||||
if (msgStore == NULL)
|
return B_NO_MEMORY;
|
||||||
return B_NO_MEMORY;
|
|
||||||
|
|
||||||
|
if (bufferSize > 0) {
|
||||||
if (userCopy) {
|
if (userCopy) {
|
||||||
// copy from user memory
|
// copy from user memory
|
||||||
if ((status = cbuf_user_memcpy_to_chain(msgStore, 0, msgBuffer, bufferSize)) < B_OK)
|
if ((status = cbuf_user_memcpy_to_chain(msg->buffer_chain, 0, msgBuffer, bufferSize)) < B_OK)
|
||||||
return status;
|
return status;
|
||||||
} else {
|
} else {
|
||||||
// copy from kernel memory
|
// copy from kernel memory
|
||||||
if ((status = cbuf_memcpy_to_chain(msgStore, 0, msgBuffer, bufferSize)) < 0)
|
if ((status = cbuf_memcpy_to_chain(msg->buffer_chain, 0, msgBuffer, bufferSize)) < 0)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
msgStore = NULL;
|
|
||||||
|
|
||||||
// attach copied message to queue
|
// attach message to queue
|
||||||
state = disable_interrupts();
|
state = disable_interrupts();
|
||||||
GRAB_PORT_LOCK(gPorts[slot]);
|
GRAB_PORT_LOCK(gPorts[slot]);
|
||||||
|
|
||||||
head = gPorts[slot].head;
|
// first, let's check if the port is still alive
|
||||||
if (head < 0)
|
if (gPorts[slot].id == -1) {
|
||||||
panic("port %ld: head < 0", gPorts[slot].id);
|
// the port has been deleted in the meantime
|
||||||
if (head >= gPorts[slot].capacity)
|
RELEASE_PORT_LOCK(gPorts[slot]);
|
||||||
panic("port %ld: head > cap %ld", gPorts[slot].id, gPorts[slot].capacity);
|
restore_interrupts(state);
|
||||||
|
|
||||||
gPorts[slot].msg_queue[head].msg_code = msgCode;
|
put_port_msg(msg);
|
||||||
gPorts[slot].msg_queue[head].data_cbuf = msgStore;
|
return B_BAD_PORT_ID;
|
||||||
gPorts[slot].msg_queue[head].data_len = bufferSize;
|
}
|
||||||
gPorts[slot].head = (gPorts[slot].head + 1) % gPorts[slot].capacity;
|
|
||||||
|
list_add_item(&gPorts[slot].msg_queue, msg);
|
||||||
|
|
||||||
// store sem_id in local variable
|
// store sem_id in local variable
|
||||||
cachedSem = gPorts[slot].read_sem;
|
cachedSem = gPorts[slot].read_sem;
|
||||||
@@ -1034,8 +1019,7 @@ set_port_owner(port_id id, team_id team)
|
|||||||
int slot;
|
int slot;
|
||||||
int state;
|
int state;
|
||||||
|
|
||||||
if (ports_active == false
|
if (!ports_active || id < 0)
|
||||||
|| id < 0)
|
|
||||||
return B_BAD_PORT_ID;
|
return B_BAD_PORT_ID;
|
||||||
|
|
||||||
slot = id % MAX_PORTS;
|
slot = id % MAX_PORTS;
|
||||||
@@ -1046,7 +1030,7 @@ set_port_owner(port_id id, team_id team)
|
|||||||
if (gPorts[slot].id != id) {
|
if (gPorts[slot].id != id) {
|
||||||
RELEASE_PORT_LOCK(gPorts[slot]);
|
RELEASE_PORT_LOCK(gPorts[slot]);
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
dprintf("set_port_owner: invalid port_id %ld\n", id);
|
TRACE(("set_port_owner: invalid port_id %ld\n", id));
|
||||||
return B_BAD_PORT_ID;
|
return B_BAD_PORT_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user