mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
Structure the image cache as a state machine
This commit is contained in:
parent
05fd04ac8d
commit
91b566cffd
1 changed files with 70 additions and 78 deletions
|
@ -36,30 +36,26 @@ fn image_cache_task(resource_task: ResourceTask) -> ImageCacheTask {
|
||||||
ImageCache {
|
ImageCache {
|
||||||
resource_task: resource_task,
|
resource_task: resource_task,
|
||||||
from_client: from_client,
|
from_client: from_client,
|
||||||
prefetch_map: url_map(),
|
state_map: url_map()
|
||||||
future_image_map: url_map(),
|
|
||||||
image_map: url_map(),
|
|
||||||
error_map: url_map()
|
|
||||||
}.run();
|
}.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: All these maps just represent states in the image lifecycle.
|
|
||||||
// Would be more efficient to just have a single state_map.
|
|
||||||
struct ImageCache {
|
struct ImageCache {
|
||||||
/// A handle to the resource task for fetching the image binaries
|
/// A handle to the resource task for fetching the image binaries
|
||||||
resource_task: ResourceTask;
|
resource_task: ResourceTask;
|
||||||
/// The port on which we'll receive client requests
|
/// The port on which we'll receive client requests
|
||||||
from_client: port<Msg>;
|
from_client: port<Msg>;
|
||||||
/// A map from URLs to the partially loaded compressed image data.
|
/// The state of processsing an image for a URL
|
||||||
/// Once the data is complete it is then sent to a decoder
|
state_map: UrlMap<ImageState>;
|
||||||
prefetch_map: UrlMap<@PrefetchData>;
|
}
|
||||||
/// A list of clients waiting on images that are currently being decoded
|
|
||||||
future_image_map: UrlMap<@FutureData>;
|
enum ImageState {
|
||||||
/// The cache of decoded images
|
Init,
|
||||||
image_map: UrlMap<@arc<~Image>>;
|
Prefetching(@PrefetchData),
|
||||||
/// Map of images which could not be obtained
|
Decoding(@FutureData),
|
||||||
error_map: UrlMap<()>;
|
Decoded(@arc<~Image>),
|
||||||
|
Failed
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PrefetchData {
|
struct PrefetchData {
|
||||||
|
@ -86,59 +82,46 @@ impl ImageCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*priv*/ fn get_state(url: url) -> ImageState {
|
||||||
|
match self.state_map.find(copy url) {
|
||||||
|
some(state) => state,
|
||||||
|
none => Init
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*priv*/ fn set_state(url: url, state: ImageState) {
|
||||||
|
self.state_map.insert(copy url, state);
|
||||||
|
}
|
||||||
|
|
||||||
/*priv*/ fn prefetch(url: url) {
|
/*priv*/ fn prefetch(url: url) {
|
||||||
if self.error_map.contains_key(copy url) {
|
match self.get_state(url) {
|
||||||
// We already know this image can't be used
|
Init => {
|
||||||
return
|
let response_port = port();
|
||||||
|
self.resource_task.send(resource_task::Load(copy url, response_port.chan()));
|
||||||
|
|
||||||
|
let prefetch_data = @PrefetchData {
|
||||||
|
response_port: response_port,
|
||||||
|
data: ~[]
|
||||||
|
};
|
||||||
|
|
||||||
|
self.set_state(url, Prefetching(prefetch_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
Prefetching(*)
|
||||||
|
| Decoding(*)
|
||||||
|
| Decoded(*)
|
||||||
|
| Failed => {
|
||||||
|
// We've already begun working on this image
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.image_map.contains_key(copy url) {
|
|
||||||
// We've already decoded this image
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.future_image_map.contains_key(copy url) {
|
|
||||||
// We've already begun decoding this image
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.prefetch_map.contains_key(copy url) {
|
|
||||||
// We're already waiting for this image
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let response_port = port();
|
|
||||||
self.resource_task.send(resource_task::Load(copy url, response_port.chan()));
|
|
||||||
|
|
||||||
let prefetch_data = @PrefetchData {
|
|
||||||
response_port: response_port,
|
|
||||||
data: ~[]
|
|
||||||
};
|
|
||||||
|
|
||||||
self.prefetch_map.insert(copy url, prefetch_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*priv*/ fn get_image(url: url, response: chan<ImageResponseMsg>) {
|
/*priv*/ fn get_image(url: url, response: chan<ImageResponseMsg>) {
|
||||||
match self.image_map.find(copy url) {
|
|
||||||
some(image) => {
|
|
||||||
response.send(ImageReady(clone_arc(image)));
|
|
||||||
return
|
|
||||||
}
|
|
||||||
none => ()
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.future_image_map.find(copy url) {
|
match self.get_state(url) {
|
||||||
some(future_data) => {
|
Init => fail ~"Request for image before prefetch",
|
||||||
// We've started decoding this image but haven't recieved it back yet.
|
|
||||||
// Put this client on the wait list
|
|
||||||
vec::push(future_data.waiters, response);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
none => ()
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.prefetch_map.find(copy url) {
|
Prefetching(prefetch_data) => {
|
||||||
some(prefetch_data) => {
|
|
||||||
|
|
||||||
let mut image_sent = false;
|
let mut image_sent = false;
|
||||||
|
|
||||||
|
@ -166,8 +149,7 @@ impl ImageCache {
|
||||||
waiters: ~[]
|
waiters: ~[]
|
||||||
};
|
};
|
||||||
|
|
||||||
self.prefetch_map.remove(copy url);
|
self.set_state(copy url, Decoding(future_data));
|
||||||
self.future_image_map.insert(copy url, future_data);
|
|
||||||
|
|
||||||
image_sent = true;
|
image_sent = true;
|
||||||
break;
|
break;
|
||||||
|
@ -176,8 +158,7 @@ impl ImageCache {
|
||||||
// There was an error loading the image binary. Put it
|
// There was an error loading the image binary. Put it
|
||||||
// in the error map so we remember the error for future
|
// in the error map so we remember the error for future
|
||||||
// requests.
|
// requests.
|
||||||
self.prefetch_map.remove(copy url);
|
self.set_state(copy url, Failed);
|
||||||
self.error_map.insert(copy url, ());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,24 +167,28 @@ impl ImageCache {
|
||||||
if !image_sent {
|
if !image_sent {
|
||||||
response.send(ImageNotReady);
|
response.send(ImageNotReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
none => ()
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.error_map.find(copy url) {
|
Decoding(future_data) => {
|
||||||
some(*) => {
|
// We've started decoding this image but haven't recieved it back yet.
|
||||||
|
// Put this client on the wait list
|
||||||
|
vec::push(future_data.waiters, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
Decoded(image) => {
|
||||||
|
response.send(ImageReady(clone_arc(image)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Failed => {
|
||||||
response.send(ImageNotReady);
|
response.send(ImageNotReady);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
none => fail ~"got a request for image data without prefetch"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*priv*/ fn store_image(url: url, image: &arc<~Image>) {
|
/*priv*/ fn store_image(url: url, image: &arc<~Image>) {
|
||||||
match self.future_image_map.find(copy url) {
|
|
||||||
some(future_data) => {
|
match self.get_state(url) {
|
||||||
|
Decoding(future_data) => {
|
||||||
|
|
||||||
let mut waiters = ~[];
|
let mut waiters = ~[];
|
||||||
waiters <-> future_data.waiters;
|
waiters <-> future_data.waiters;
|
||||||
|
@ -213,11 +198,18 @@ impl ImageCache {
|
||||||
for waiters.each |waiter| {
|
for waiters.each |waiter| {
|
||||||
waiter.send(ImageReady(clone_arc(image)))
|
waiter.send(ImageReady(clone_arc(image)))
|
||||||
}
|
}
|
||||||
self.image_map.insert(copy url, @clone_arc(image));
|
|
||||||
self.future_image_map.remove(copy url);
|
self.set_state(url, Decoded(@clone_arc(image)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Init
|
||||||
|
| Prefetching(*)
|
||||||
|
| Decoded(*)
|
||||||
|
| Failed => {
|
||||||
|
fail ~"incorrect state in store_image"
|
||||||
}
|
}
|
||||||
none => fail ~"storing an image that isn't in the future map"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue