Goto sanos source index
//
// object.c
//
// Object manager
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <os/krnl.h>
struct waitable_timer *timer_list = NULL;
int nexttid = 1;
//
// insert_in_waitlist
//
// Insert thread wait block in wait list for object
//
static void insert_in_waitlist(struct object *obj, struct waitblock *wb) {
wb->next_wait = NULL;
wb->prev_wait = obj->waitlist_tail;
if (obj->waitlist_tail) obj->waitlist_tail->next_wait = wb;
obj->waitlist_tail = wb;
if (!obj->waitlist_head) obj->waitlist_head = wb;
}
//
// remove_from_waitlist
//
// Remove thread wait block from wait list for object
//
static void remove_from_waitlist(struct waitblock *wb) {
if (wb->next_wait) wb->next_wait->prev_wait = wb->prev_wait;
if (wb->prev_wait) wb->prev_wait->next_wait = wb->next_wait;
if (wb == wb->object->waitlist_head) wb->object->waitlist_head = wb->next_wait;
if (wb == wb->object->waitlist_tail) wb->object->waitlist_tail = wb->prev_wait;
wb->object = NULL;
wb->next_wait = wb->prev_wait = NULL;
}
//
// thread_ready_to_run
//
// Test if thread is ready to run, i.e. all waitall objects on the
// waiting list are signaled or one waitany object is signaled.
// This routine also sets the waitkey for the thread from the waitblock.
//
int thread_ready_to_run(struct thread *t) {
int any = 0;
int all = 1;
struct waitblock *wb = t->waitlist;
while (wb) {
//kprintf("check wb %p type %d key %d signaled %d\n", wb, wb->waittype, wb->waitkey, wb->object->signaled);
if (wb->waittype == WAIT_ANY) {
if (wb->object->signaled) {
any = 1;
t->waitkey = wb->waitkey;
}
} else {
if (!wb->object->signaled) {
all = 0;
} else {
t->waitkey = wb->waitkey;
}
}
wb = wb->next;
}
return any || all;
}
//
// cancel_wait
//
// Remove thread from all wait blocks
//
void cancel_wait(struct thread *t) {
// Remove thread from wait lists
struct waitblock *wb = t->waitlist;
while (wb) {
remove_from_waitlist(wb);
wb = wb->next;
}
// Clear wait list for thread
t->waitlist = NULL;
}
//
// release_thread
//
// Release thread and mark it as ready to run
//
void release_thread(struct thread *t) {
// Remove thread from wait lists
cancel_wait(t);
// Mark thread as ready
mark_thread_ready(t, 1, 1);
}
//
// release_waiters
//
// Release all threads waiting for object
//
void release_waiters(struct object *o, int waitkey) {
struct waitblock *wb;
struct waitblock *next;
wb = o->waitlist_head;
while (wb) {
next = wb->next_wait;
if (thread_ready_to_run(wb->thread)) {
wb->thread->waitkey = waitkey;
release_thread(wb->thread);
}
wb = next;
}
}
//
// enter_object
//
// Called when an object waits on a signaled object.
//
int enter_object(struct object *obj) {
int rc = 0;
switch (obj->type) {
case OBJECT_THREAD:
// Set thread exit code as wait key
rc = ((struct thread *) obj)->exitcode;
break;
case OBJECT_EVENT:
// Reset auto-reset event
if (!((struct event *) obj)->manual_reset) obj->signaled = 0;
break;
case OBJECT_TIMER:
// Do nothing
break;
case OBJECT_MUTEX:
// Set state to nonsignaled and set current thread as owner
obj->signaled = 0;
((struct mutex *) obj)->owner = self();
((struct mutex *) obj)->recursion = 1;
break;
case OBJECT_SEMAPHORE:
// Decrease count and set to nonsignaled if count reaches zero
if (--(((struct sem *) obj)->count) == 0) obj->signaled = 0;
break;
case OBJECT_FILE:
// Do nothing
break;
case OBJECT_SOCKET:
// Do nothing
break;
case OBJECT_IOMUX:
rc = dequeue_event_from_iomux((struct iomux *) obj);
break;
case OBJECT_FILEMAP:
obj->signaled = 0;
break;
}
return rc;
}
//
// wait_for_object
//
// Wait for object to become signaled.
//
int wait_for_object(object_t hobj, unsigned int timeout) {
return wait_for_one_object(hobj, timeout, 0);
}
//
// wait_for_one_object
//
// Wait for object to become signaled.
//
int wait_for_one_object(object_t hobj, unsigned int timeout, int alertable) {
struct object *obj = (struct object *) hobj;
struct thread *t = self();
struct waitblock wb;
// If object is signaled we do not have to wait
if (obj->signaled) return enter_object(obj);
if (obj->type == OBJECT_MUTEX && ((struct mutex *) obj)->owner == self()) {
// Mutex is already owned by current thread, increase recursion count
((struct mutex *) obj)->recursion++;
return 0;
}
if (timeout == 0) return -ETIMEOUT;
// Insert thread in waitlist for object
t->waitlist = &wb;
wb.thread = t;
wb.object = obj;
wb.waittype = WAIT_ANY;
wb.waitkey = 0;
wb.next = NULL;
insert_in_waitlist(obj, &wb);
if (timeout == INFINITE) {
// Wait for object to become signaled
if (alertable) {
int rc = enter_alertable_wait(THREAD_WAIT_OBJECT);
if (rc < 0) {
cancel_wait(t);
t->waitkey = rc;
}
} else {
enter_wait(THREAD_WAIT_OBJECT);
}
// Return waitkey
return t->waitkey;
} else {
struct waitable_timer timer;
struct waitblock wbtmo;
// Initialize timer
if (timeout < MSECS_PER_TICK) timeout = MSECS_PER_TICK;
init_waitable_timer(&timer, ticks + timeout / MSECS_PER_TICK);
wb.next = &wbtmo;
wbtmo.thread = t;
wbtmo.object = &timer.object;
wbtmo.waittype = WAIT_ANY;
wbtmo.waitkey = -ETIMEOUT;
wbtmo.next = NULL;
insert_in_waitlist(&timer.object, &wbtmo);
// Wait for object to become signaled or time out
if (alertable) {
int rc = enter_alertable_wait(THREAD_WAIT_OBJECT);
if (rc < 0) {
cancel_wait(t);
t->waitkey = rc;
}
} else {
enter_wait(THREAD_WAIT_OBJECT);
}
// Stop timer
cancel_waitable_timer(&timer);
// Return wait key
return t->waitkey;
}
}
//
// wait_for_all_objects
//
// Wait for all objects to become signaled
//
int wait_for_all_objects(struct object **objs, int count, unsigned int timeout, int alertable) {
int n;
struct thread *t = self();
struct waitblock wb[MAX_WAIT_OBJECTS];
struct waitblock wbtmo;
struct waitable_timer timer;
int all;
if (count == 0) {
if (timeout == INFINITE) return -EINVAL;
if (msleep(timeout) > 0) return -EINTR;
return 0;
}
if (count > MAX_WAIT_OBJECTS) return -EINVAL;
// Check for all objects signaled
all = 1;
for (n = 0; n < count; n++) {
if (objs[n]->type == OBJECT_MUTEX) {
if (!objs[n]->signaled || ((struct mutex *) objs[n])->owner != self()) {
all = 0;
break;
}
} else {
if (!objs[n]->signaled) {
all = 0;
break;
}
}
}
// If all objects are signaled enter all objects and return
if (all) {
int rc;
rc = 0;
for (n = 0; n < count; n++) {
if (objs[n]->signaled) {
rc |= enter_object(objs[n]);
} else if (objs[n]->type == OBJECT_MUTEX && ((struct mutex *) objs[n])->owner == self()) {
// Mutex is already owned by current thread, increase recursion count
((struct mutex *) objs[n])->recursion++;
}
}
return rc;
}
// Return immediately if timeout is zero
if (timeout == 0) return -ETIMEOUT;
// Setup waitblocks
for (n = 0; n < count; n++) {
// Insert thread in waitlist for object
if (n == 0) {
t->waitlist = &wb[n];
} else {
wb[n - 1].next = &wb[n];
}
wb[n].thread = t;
wb[n].object = objs[n];
wb[n].waittype = WAIT_ALL;
wb[n].waitkey = n;
wb[n].next = NULL;
insert_in_waitlist(objs[n], &wb[n]);
}
// Add waitable timer for timeout
if (timeout != INFINITE) {
if (timeout < MSECS_PER_TICK) timeout = MSECS_PER_TICK;
init_waitable_timer(&timer, ticks + timeout / MSECS_PER_TICK);
wb[count - 1].next = &wbtmo;
wbtmo.thread = t;
wbtmo.object = &timer.object;
wbtmo.waittype = WAIT_ANY;
wbtmo.waitkey = -ETIMEOUT;
wbtmo.next = NULL;
insert_in_waitlist(&timer.object, &wbtmo);
}
// Wait for all objects to become signaled or time out
if (alertable) {
int rc = enter_alertable_wait(THREAD_WAIT_OBJECT);
if (rc < 0) {
cancel_wait(t);
t->waitkey = rc;
}
} else {
enter_wait(THREAD_WAIT_OBJECT);
}
// Stop timer
if (timeout != INFINITE) cancel_waitable_timer(&timer);
// Return wait key
return t->waitkey;
}
//
// wait_for_any_object
//
// Wait for some object to become signaled
//
int wait_for_any_object(struct object **objs, int count, unsigned int timeout, int alertable) {
int n;
struct thread *t = self();
struct waitblock wb[MAX_WAIT_OBJECTS];
struct waitblock wbtmo;
struct waitable_timer timer;
if (count == 0) {
if (timeout == INFINITE) return -EINVAL;
if (msleep(timeout) > 0) return -EINTR;
return 0;
}
if (count > MAX_WAIT_OBJECTS) return -EINVAL;
// Check for any object signaled
for (n = 0; n < count; n++) {
if (objs[n]->signaled) {
int rc = enter_object(objs[n]);
return rc ? rc : n;
} else if (objs[n]->type == OBJECT_MUTEX && ((struct mutex *) objs[n])->owner == self()) {
// Mutex is already owned by current thread, increase recursion count
((struct mutex *) objs[n])->recursion++;
return n;
}
}
// Return immediately if timeout is zero
if (timeout == 0) return -ETIMEOUT;
// Setup waitblocks
for (n = 0; n < count; n++) {
// Insert thread in waitlist for object
if (n == 0) {
t->waitlist = &wb[n];
} else {
wb[n - 1].next = &wb[n];
}
wb[n].thread = t;
wb[n].object = objs[n];
wb[n].waittype = WAIT_ANY;
wb[n].waitkey = n;
wb[n].next = NULL;
insert_in_waitlist(objs[n], &wb[n]);
}
// Add waitable timer for timeout
if (timeout != INFINITE) {
if (timeout < MSECS_PER_TICK) timeout = MSECS_PER_TICK;
init_waitable_timer(&timer, ticks + timeout / MSECS_PER_TICK);
wb[count - 1].next = &wbtmo;
wbtmo.thread = t;
wbtmo.object = &timer.object;
wbtmo.waittype = WAIT_ANY;
wbtmo.waitkey = -ETIMEOUT;
wbtmo.next = NULL;
insert_in_waitlist(&timer.object, &wbtmo);
}
// Wait for any object to become signaled or time out
if (alertable) {
int rc = enter_alertable_wait(THREAD_WAIT_OBJECT);
if (rc < 0) {
cancel_wait(t);
t->waitkey = rc;
}
} else {
enter_wait(THREAD_WAIT_OBJECT);
}
// Stop timer
if (timeout != INFINITE) cancel_waitable_timer(&timer);
// Return wait key
return t->waitkey;
}
//
// init_object
//
// Initialize object
//
void init_object(struct object *o, int type) {
o->type = type;
o->signaled = 0;
o->handle_count = 0;
o->lock_count = 0;
o->waitlist_head = NULL;
o->waitlist_tail = NULL;
}
//
// close_object
//
// Close object after all handles have been released
//
int close_object(struct object *o) {
// Release all waitblocks waiting on object
while (o->waitlist_head) {
o->waitlist_head->thread->waitkey = -EINTR;
release_thread(o->waitlist_head->thread);
}
switch (o->type) {
case OBJECT_THREAD:
case OBJECT_EVENT:
case OBJECT_MUTEX:
case OBJECT_SEMAPHORE:
case OBJECT_FILEMAP:
return 0;
case OBJECT_TIMER:
cancel_waitable_timer((struct waitable_timer *) o);
return 0;
case OBJECT_FILE:
return close((struct file *) o);
case OBJECT_SOCKET:
return closesocket((struct socket *) o);
case OBJECT_IOMUX:
return close_iomux((struct iomux *) o);
}
return -EBADF;
}
//
// destroy_object
//
// Destroy object after all handles have been closed and
// all locks have been released
//
int destroy_object(struct object *o) {
switch (o->type) {
case OBJECT_THREAD:
return destroy_thread((struct thread *) o);
case OBJECT_EVENT:
case OBJECT_TIMER:
case OBJECT_MUTEX:
case OBJECT_SEMAPHORE:
case OBJECT_IOMUX:
case OBJECT_SOCKET:
case OBJECT_FILEMAP:
kfree(o);
return 0;
case OBJECT_FILE:
return destroy((struct file *) o);
}
return -EBADF;
}
//
// init_thread
//
// Initialize thread object
//
void init_thread(struct thread *t, int priority) {
memset(t, 0, sizeof(struct thread));
init_object(&t->object, OBJECT_THREAD);
t->priority = t->base_priority = priority;
t->state = THREAD_STATE_INITIALIZED;
t->id = nexttid++;
}
//
// exit_thread
//
// Called when a thread exits
//
void exit_thread(struct thread *t) {
// Set signaled state
t->object.signaled = 1;
// Release all waiting threads
release_waiters(&t->object, t->exitcode);
}
//
// init_event
//
// Initialize event object
//
void init_event(struct event *e, int manual_reset, int initial_state) {
init_object(&e->object, OBJECT_EVENT);
e->manual_reset = manual_reset;
e->object.signaled = initial_state;
}
//
// pulse_event
//
// Pulse event
//
void pulse_event(struct event *e) {
struct waitblock *wb;
struct waitblock *wb_next;
// Set the event object to the signaled state
e->object.signaled = 1;
// Release waiting threads
wb = e->object.waitlist_head;
while (wb) {
wb_next = wb->next_wait;
if (thread_ready_to_run(wb->thread)) {
// Release waiting thread
release_thread(wb->thread);
// Auto reset only releases one thread
if (!e->manual_reset) break;
wb = wb_next;
}
}
// Set the event object to the nonsignaled state
e->object.signaled = 0;
}
//
// set_event
//
// Set event
//
void set_event(struct event *e) {
struct waitblock *wb;
struct waitblock *wb_next;
// Set the event object to the signaled state
e->object.signaled = 1;
// Release waiting threads
wb = e->object.waitlist_head;
while (wb) {
wb_next = wb->next_wait;
if (thread_ready_to_run(wb->thread)) {
// Release waiting thread
release_thread(wb->thread);
// Auto reset only releases one thread
if (!e->manual_reset) {
e->object.signaled = 0;
break;
}
}
wb = wb_next;
}
}
//
// reset_event
//
// Reset event
//
void reset_event(struct event *e) {
// Set the event object to the nonsignaled state
e->object.signaled = 0;
}
//
// init_sem
//
// Initialize semaphore object
//
void init_sem(struct sem *s, unsigned int initial_count) {
init_object(&s->object, OBJECT_SEMAPHORE);
s->count = initial_count;
s->object.signaled = (s->count > 0);
}
//
// release_sem
//
// Release 'count' resouces for the semaphore
//
unsigned int release_sem(struct sem *s, unsigned int count) {
unsigned int prevcount;
struct waitblock *wb;
struct waitblock *wb_next;
// Increase semaphore count
prevcount = s->count;
s->count += count;
if (s->count > 0) {
// Set semaphore to signaled state
s->object.signaled = 1;
// Release waiting threads
wb = s->object.waitlist_head;
while (wb) {
wb_next = wb->next_wait;
if (thread_ready_to_run(wb->thread)) {
// Decrease semaphore count
s->count--;
// Release waiting thread
release_thread(wb->thread);
// If count reaches zero then set semaphore to the nonsignaled state
if (s->count == 0) {
s->object.signaled = 0;
break;
}
}
wb = wb_next;
}
}
return prevcount;
}
//
// set_sem
//
// Set semaphore resource count
//
unsigned int set_sem(struct sem *s, unsigned int count) {
unsigned int prevcount;
struct waitblock *wb;
struct waitblock *wb_next;
// Increase semaphore count
prevcount = s->count;
s->count = count;
if (s->count > 0) {
// Set semaphore to signaled state
s->object.signaled = 1;
// Release waiting threads
wb = s->object.waitlist_head;
while (wb) {
wb_next = wb->next_wait;
if (thread_ready_to_run(wb->thread)) {
// Decrease semaphore count
s->count--;
// Release waiting thread
release_thread(wb->thread);
// If count reaches zero then set semaphore to the nonsignaled state
if (s->count == 0) {
s->object.signaled = 0;
break;
}
}
wb = wb_next;
}
}
return prevcount;
}
//
// init_mutex
//
// Initialize mutex object
//
void init_mutex(struct mutex *m, int owned) {
init_object(&m->object, OBJECT_MUTEX);
if (owned) {
m->owner = self();
m->object.signaled = 0;
m->recursion = 1;
} else {
m->owner = NULL;
m->object.signaled = 1;
m->recursion = 0;
}
}
//
// release_mutex
//
// Release mutex
//
int release_mutex(struct mutex *m) {
struct waitblock *wb;
// Check that caller is the owner
if (m->owner != self()) return -EPERM;
// Check for recursion
if (--m->recursion > 0) return 0;
// Set the mutex to the signaled state
m->object.signaled = 1;
m->owner = NULL;
// Release first waiting thread
wb = m->object.waitlist_head;
while (wb) {
if (thread_ready_to_run(wb->thread)) break;
wb = wb->next_wait;
}
if (wb != NULL) {
// Set mutex to nonsignal state
m->owner = wb->thread;
m->recursion = 1;
m->object.signaled = 0;
// Release waiting thread
release_thread(wb->thread);
}
return 0;
}
//
// unlock_filemap
//
// Unlock filemap
//
int unlock_filemap(struct filemap *m) {
struct waitblock *wb;
// Set the filemap to the signaled state
m->object.signaled = 1;
// Release first waiting thread
wb = m->object.waitlist_head;
while (wb) {
if (thread_ready_to_run(wb->thread)) break;
wb = wb->next_wait;
}
if (wb != NULL) {
// Set filemap to nonsignal state
m->object.signaled = 0;
// Release waiting thread
release_thread(wb->thread);
}
return 0;
}
//
// expire_waitable_timer
//
// Expire waitable timer
//
static void expire_waitable_timer(void *arg) {
struct waitable_timer *t = arg;
// Set signaled state
t->object.signaled = 1;
// Release all waiting threads
release_waiters(&t->object, 0);
}
//
// init_waitable_timer
//
// Initialize timer
//
void init_waitable_timer(struct waitable_timer *t, unsigned int expires) {
init_object(&t->object, OBJECT_TIMER);
init_timer(&t->timer, expire_waitable_timer, t);
t->timer.expires = expires;
if (time_before_eq(expires, ticks)) {
// Set timer to signaled state immediately
t->object.signaled = 1;
} else {
// Start timer
add_timer(&t->timer);
}
}
//
// modify_waitable_timer
//
// Modify timer
//
void modify_waitable_timer(struct waitable_timer *t, unsigned int expires) {
mod_timer(&t->timer, expires);
}
//
// cancel_waitable_timer
//
// Cancel timer
//
void cancel_waitable_timer(struct waitable_timer *t) {
if (t->timer.active) del_timer(&t->timer);
}