1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
/* read-mem.c, liboop, copyright 2000 Ian jackson
This is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License, version 2.1 or later.
See the file COPYING for details. */
#include "oop.h"
#include "oop-read.h"
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
typedef struct {
oop_readable ra;
oop_source *oop;
int processing;
enum { state_cancelled, state_active, state_dying } state;
const char *data;
size_t remaining;
oop_readable_call *call;
void *opaque;
} ram_intern;
static void *process(oop_source *oop, struct timeval when, void *ram_void);
static int set_time(ram_intern *ram) {
int err;
err=
(ram->oop->on_time(ram->oop,OOP_TIME_NOW,process,ram), 0); /* fixme */
if (err) return err;
ram->processing= 1;
return 0;
}
static void *process(oop_source *oop, struct timeval when, void *ram_void) {
ram_intern *ram= ram_void;
void *ret;
int err;
assert(oop == ram->oop);
assert(ram->processing);
ret= OOP_CONTINUE;
while (ram->state == state_active && ret == OOP_CONTINUE) {
ret= ram->call(oop,&ram->ra,ram->opaque);
}
switch (ram->state) {
case state_active:
err= set_time(ram);
if (err)
assert(!"must not lose flow of control");
/* AAARGH! No way to avoid this I think. Happens when:
* - program calls on_read which works, setting immediate callback;
* - process calls the application's function, which returns
* OOP_HALT or some such, but without calling on_cancel;
* Now we have to set another immediate callback.
* If this fails and we were to ignore it then:
* - program reenters event loop, expecting to deal with the rest
* of the oop_readable_mem data. But we've lost the flow
* of control and the callback never happens, so
* oop_sys_run or whatever would (lyingly) exit straight
* away with OOP_CONTINUE.
* Alternatively we could ignore the application's request
* to abort the event loop, which seems just as bad.
*/
break;
case state_cancelled:
ram->processing= 0;
break;
case state_dying:
oop_free(ram);
break;
}
return ret;
}
static int on_read(oop_readable *ra, oop_readable_call *call, void *opaque) {
ram_intern *ram= (void*)ra;
assert(ram->state != state_dying);
ram->state= state_active;
ram->call= call;
ram->opaque= opaque;
if (ram->processing)
return 0;
return
set_time(ram);
}
static void on_cancel(struct oop_readable *ra) {
ram_intern *ram= (void*)ra;
assert(ram->state != state_dying);
ram->state= state_cancelled;
}
static ssize_t try_read(oop_readable *ra, void *buffer, size_t length) {
ram_intern *ram= (void*)ra;
if (length > SSIZE_MAX)
length= SSIZE_MAX;
if (length > ram->remaining)
length= ram->remaining;
memcpy(buffer,ram->data,length);
ram->data += length;
ram->remaining -= length;
return length;
}
static void delete_kill(struct oop_readable *ra) {
ram_intern *ram= (void*)ra;
assert(ram->state != state_dying);
ram->state= state_dying;
if (!ram->processing)
oop_free(ram);
}
static int delete_tidy(struct oop_readable *ra) {
delete_kill(ra);
return 0;
}
static const oop_readable functions= {
on_read, on_cancel, try_read, delete_tidy, delete_kill
};
oop_readable *oop_readable_mem(oop_source *oop, const void *data, size_t length) {
ram_intern *ram;
ram= oop_malloc(sizeof(*ram)); if (!ram) return 0;
ram->ra= functions;
ram->oop= oop;
ram->processing= 0;
ram->state= state_cancelled;
ram->data= data;
ram->remaining= length;
return (oop_readable*)ram;
}
|