148 lines
3.4 KiB
C
148 lines
3.4 KiB
C
#include "sys/char/ring.h"
|
|
#include "sys/thread.h"
|
|
#include "user/errno.h"
|
|
#include "sys/assert.h"
|
|
#include "sys/sched.h"
|
|
#include "sys/debug.h"
|
|
#include "sys/heap.h"
|
|
|
|
int ring_readable(struct ring *ring) {
|
|
// | . . . . R X X X W . . . |
|
|
if (ring->rd <= ring->wr) {
|
|
return ring->wr - ring->rd;
|
|
// | X X W . . . . . . R X X |
|
|
} else {
|
|
return ring->wr + (ring->cap - ring->rd);
|
|
}
|
|
}
|
|
|
|
static int ring_writable(struct ring *ring) {
|
|
if (ring->wr == ring->cap - 1 && ring->rd == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// | X X X X R . . . W X X X |
|
|
if (ring->rd <= ring->wr) {
|
|
return ring->wr + (ring->cap - ring->rd);
|
|
// | . . W X X X X R . . . . |
|
|
} else {
|
|
if (ring->rd == ring->wr + 1) {
|
|
return 0;
|
|
}
|
|
return ring->rd - ring->wr;
|
|
}
|
|
}
|
|
|
|
static void ring_advance_read(struct ring *ring) {
|
|
if (ring->rd == ring->cap - 1) {
|
|
ring->rd = 0;
|
|
} else {
|
|
++ring->rd;
|
|
}
|
|
}
|
|
|
|
static void ring_advance_write(struct ring *ring) {
|
|
if (ring->wr == ring->cap - 1) {
|
|
ring->wr = 0;
|
|
} else {
|
|
++ring->wr;
|
|
}
|
|
}
|
|
|
|
//static void ring_notify_reader(struct ring *ring) {
|
|
// if (ring->reader_head) {
|
|
// struct thread *thr = ring->reader_head;
|
|
// ring->reader_head = NULL; //thr->wait_next;
|
|
//
|
|
// sched_queue(thr);
|
|
// }
|
|
//}
|
|
|
|
int ring_getc(struct thread *ctx, struct ring *ring, char *c, int err) {
|
|
int res;
|
|
if (err) {
|
|
if (!ring_readable(ring)) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
do {
|
|
// TODO: better handling of EOF condition?
|
|
if (ring->flags & (RING_SIGNAL_BRK | RING_SIGNAL_EOF)) {
|
|
if (!ring_readable(ring)) {
|
|
ring->flags &= ~RING_SIGNAL_BRK;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (!ring_readable(ring)) {
|
|
if ((res = thread_wait_io(ctx, &ring->wait)) != 0) {
|
|
_assert(res == -EINTR);
|
|
return res;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
} while (1);
|
|
}
|
|
|
|
*c = ring->base[ring->rd];
|
|
ring_advance_read(ring);
|
|
thread_notify_io(&ring->writer_wait);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ring_putc(struct thread *ctx, struct ring *ring, char c, int wait) {
|
|
if (wait) {
|
|
int res;
|
|
while (!ring_writable(ring)) {
|
|
if (ring->flags & RING_SIGNAL_EOF) {
|
|
return -EPIPE;
|
|
}
|
|
if ((res = thread_wait_io(ctx, &ring->writer_wait)) != 0) {
|
|
_assert(res == -EINTR);
|
|
return res;
|
|
}
|
|
}
|
|
}
|
|
if (ring->flags & RING_SIGNAL_EOF) {
|
|
return -EPIPE;
|
|
}
|
|
|
|
ring->base[ring->wr] = c;
|
|
ring_advance_write(ring);
|
|
thread_notify_io(&ring->wait);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ring_write(struct thread *ctx, struct ring *ring, const void *buf, size_t len, int wait) {
|
|
for (size_t i = 0; i < len; ++i) {
|
|
if (ring_putc(ctx, ring, ((const char *) buf)[i], wait) != 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
void ring_signal(struct ring *r, int s) {
|
|
r->flags |= s;
|
|
thread_notify_io(&r->wait);
|
|
thread_notify_io(&r->writer_wait);
|
|
}
|
|
|
|
int ring_init(struct ring *r, size_t cap) {
|
|
r->cap = cap;
|
|
r->rd = 0;
|
|
r->wr = 0;
|
|
r->flags = 0;
|
|
thread_wait_io_init(&r->wait);
|
|
thread_wait_io_init(&r->writer_wait);
|
|
|
|
if (!(r->base = kmalloc(cap))) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|