diff options
Diffstat (limited to 'src/cache')
| -rw-r--r-- | src/cache/backing.h | 13 | ||||
| -rw-r--r-- | src/cache/backings/file.c | 27 | ||||
| -rw-r--r-- | src/cache/backings/file.h | 4 | ||||
| -rw-r--r-- | src/cache/backings/file_common.c | 1 | ||||
| -rw-r--r-- | src/cache/backings/file_mapped.c | 39 | ||||
| -rw-r--r-- | src/cache/backings/memory.c | 27 | ||||
| -rw-r--r-- | src/cache/entry.c | 15 | ||||
| -rw-r--r-- | src/cache/entry.h | 9 | ||||
| -rw-r--r-- | src/cache/handle.c | 37 | ||||
| -rw-r--r-- | src/cache/handle.h | 2 | ||||
| -rw-r--r-- | src/cache/handler.h | 13 | ||||
| -rw-r--r-- | src/cache/handlers/cdio.c | 206 | ||||
| -rw-r--r-- | src/cache/handlers/cdio.h | 21 | ||||
| -rw-r--r-- | src/cache/handlers/file.c | 8 | ||||
| -rw-r--r-- | src/cache/handlers/file.h | 1 | ||||
| -rw-r--r-- | src/cache/handlers/http.c | 153 | ||||
| -rw-r--r-- | src/cache/handlers/http.h | 14 | ||||
| -rw-r--r-- | src/cache/meson.build | 2 | ||||
| -rw-r--r-- | src/cache/range.h | 23 | ||||
| -rw-r--r-- | src/cache/threaded_waits.c | 126 | ||||
| -rw-r--r-- | src/cache/threaded_waits.h | 15 | ||||
| -rw-r--r-- | src/cache/wait.h | 10 |
22 files changed, 567 insertions, 199 deletions
diff --git a/src/cache/backing.h b/src/cache/backing.h index 24aff44..d9d8bad 100644 --- a/src/cache/backing.h +++ b/src/cache/backing.h @@ -1,19 +1,26 @@ #pragma once #include <al/types.h> +#include <al/array.h> enum { - CCH_BACKING_READ = 0, - CCH_BACKING_MAPPED + CACHE_BACKING_READ = 0, + CACHE_BACKING_MAPPED +}; + +struct cch_range { + off_t start; + off_t end; }; struct cch_backing { u8 mode; + array(struct cch_range) available; + void (*lock)(struct cch_backing *); void (*write)(struct cch_backing *, u8 *, off_t, size_t *); void (*read)(struct cch_backing *, u8 *, off_t, size_t *); u8 *(*get_ptr)(struct cch_backing *, off_t, size_t *); void (*unlock)(struct cch_backing *); - void *(*get_mmap)(struct cch_backing *); off_t (*get_size_estimate)(struct cch_backing *); void (*resize)(struct cch_backing *, size_t); void (*free)(struct cch_backing **); diff --git a/src/cache/backings/file.c b/src/cache/backings/file.c index 4c5f344..153b1e4 100644 --- a/src/cache/backings/file.c +++ b/src/cache/backings/file.c @@ -1,6 +1,14 @@ +#include "../range.h" + #include "file.h" #include "file_common.c" +static void file_backing_lock(struct cch_backing *backing) +{ + struct cch_backing_file *file = (struct cch_backing_file *)backing; + aki_mutex_lock(&file->mutex); +} + static void file_backing_write(struct cch_backing *backing, u8 *buf, off_t index, size_t *size) { struct cch_backing_file *file = (struct cch_backing_file *)backing; @@ -10,10 +18,11 @@ static void file_backing_write(struct cch_backing *backing, u8 *buf, off_t index al_assert(file->u.pointer == index); } if (file->size >= 0 && index + ((off_t)*size) >= file->size) { - *size = file->size - index; + *size = AL_MAX(file->size - index, 0); } aki_file_write(&file->file, buf, *size); file->u.pointer += *size; + cch_backing_fill_range(&file->backing, index, *size); aki_mutex_unlock(&file->mutex); } @@ -26,13 +35,19 @@ static void file_backing_read(struct cch_backing *backing, u8 *buf, off_t index, al_assert(file->u.pointer == index); } if (file->size >= 0 && index + ((off_t)*size) >= file->size) { - *size = file->size - index; + *size = AL_MAX(file->size - index, 0); } aki_file_read(&file->file, buf, *size); file->u.pointer += *size; aki_mutex_unlock(&file->mutex); } +static void file_backing_unlock(struct cch_backing *backing) +{ + struct cch_backing_file *file = (struct cch_backing_file *)backing; + aki_mutex_unlock(&file->mutex); +} + static void file_backing_resize(struct cch_backing *backing, size_t size) { struct cch_backing_file *file = (struct cch_backing_file *)backing; @@ -49,6 +64,7 @@ static void file_backing_free(struct cch_backing **backing) { struct cch_backing_file *file = (struct cch_backing_file *)*backing; aki_file_close(&file->file); + al_array_free(file->backing.available); al_free(file); *backing = NULL; } @@ -56,15 +72,16 @@ static void file_backing_free(struct cch_backing **backing) struct cch_backing *cch_backing_file_create(str *path, size_t size) { struct cch_backing_file *file = al_alloc_object(struct cch_backing_file); - file->backing.mode = CCH_BACKING_READ; + file->backing.mode = CACHE_BACKING_READ; + file->backing.lock = file_backing_lock; file->backing.write = file_backing_write; file->backing.read = file_backing_read; file->backing.get_ptr = NULL; - file->backing.unlock = NULL; - file->backing.get_mmap = NULL; + file->backing.unlock = file_backing_unlock; file->backing.get_size_estimate = file_backing_get_size_estimate; file->backing.resize = file_backing_resize; file->backing.free = file_backing_free; + al_array_init(file->backing.available); if (!file_open_internal(file, path, size)) { al_free(file); return NULL; diff --git a/src/cache/backings/file.h b/src/cache/backings/file.h index 9794bbb..3f8437b 100644 --- a/src/cache/backings/file.h +++ b/src/cache/backings/file.h @@ -13,8 +13,8 @@ struct cch_backing_file { off_t size; off_t filesize; union { - void *map; // CCH_BACKING_MAPPED - off_t pointer; // CCH_BACKING_READ + void *map; // CACHE_BACKING_MAPPED + off_t pointer; // CACHE_BACKING_READ } u; struct aki_mutex mutex; }; diff --git a/src/cache/backings/file_common.c b/src/cache/backings/file_common.c index 232580a..35322fd 100644 --- a/src/cache/backings/file_common.c +++ b/src/cache/backings/file_common.c @@ -14,6 +14,7 @@ static bool file_open_internal(struct cch_backing_file *file, str *path, size_t file->filesize = aki_file_get_filesize(&file->file); if (!size) { file->size = file->filesize; + cch_backing_fill_range(&file->backing, 0, file->size); } else { file->size = -1; if (file->filesize < (off_t)size) { diff --git a/src/cache/backings/file_mapped.c b/src/cache/backings/file_mapped.c index 4277c21..6c6fc94 100644 --- a/src/cache/backings/file_mapped.c +++ b/src/cache/backings/file_mapped.c @@ -1,14 +1,23 @@ +#include "../range.h" + #include "file.h" #include "file_common.c" +static void file_backing_lock(struct cch_backing *backing) +{ + struct cch_backing_file *file = (struct cch_backing_file *)backing; + aki_mutex_lock(&file->mutex); +} + static void file_backing_write(struct cch_backing *backing, u8 *buf, off_t index, size_t *size) { struct cch_backing_file *file = (struct cch_backing_file *)backing; aki_mutex_lock(&file->mutex); - if (file->size >= 0 && index + ((off_t)*size) >= file->size) { - *size = file->size - index; + if (file->size >= 0 && index + (off_t)*size >= file->size) { + *size = AL_MAX(file->size - index, 0); } al_memcpy(file->u.map + index, buf, *size); + cch_backing_fill_range(&file->backing, index, *size); aki_mutex_unlock(&file->mutex); } @@ -16,8 +25,8 @@ static u8 *file_backing_get_ptr(struct cch_backing *backing, off_t index, size_t { struct cch_backing_file *file = (struct cch_backing_file *)backing; aki_mutex_lock(&file->mutex); - if (file->size >= 0 && index + ((off_t)*size) >= file->size) { - *size = file->size - index; + if (file->size >= 0 && index + (off_t)*size >= file->size) { + *size = AL_MAX(file->size - index, 0); } return (u8 *)(file->u.map + index); } @@ -28,24 +37,15 @@ static void file_backing_unlock(struct cch_backing *backing) aki_mutex_unlock(&file->mutex); } -static void *file_backing_get_mmap(struct cch_backing *backing) -{ - struct cch_backing_file *file = (struct cch_backing_file *)backing; - aki_mutex_lock(&file->mutex); - void *map = file->u.map; - aki_mutex_unlock(&file->mutex); - return map; -} - static void file_backing_resize(struct cch_backing *backing, size_t size) { struct cch_backing_file *file = (struct cch_backing_file *)backing; aki_mutex_lock(&file->mutex); file->size = size; if ((off_t)size > file->filesize) { - // TODO: Unmap, truncate, map? - file->u.map = aki_file_mremap(&file->file, size, file->u.map); + aki_file_munmap(&file->file, file->u.map); aki_file_truncate(&file->file, size); + file->u.map = aki_file_mmap(&file->file); file->filesize = size; } aki_mutex_unlock(&file->mutex); @@ -56,6 +56,7 @@ static void file_backing_free(struct cch_backing **backing) struct cch_backing_file *file = (struct cch_backing_file *)*backing; aki_file_munmap(&file->file, file->u.map); aki_file_close(&file->file); + al_array_free(file->backing.available); al_free(file); *backing = NULL; } @@ -63,20 +64,20 @@ static void file_backing_free(struct cch_backing **backing) struct cch_backing *cch_backing_file_create(str *path, size_t size) { struct cch_backing_file *file = al_alloc_object(struct cch_backing_file); - file->backing.mode = CCH_BACKING_MAPPED; + file->backing.mode = CACHE_BACKING_MAPPED; + file->backing.lock = file_backing_lock; file->backing.write = file_backing_write; file->backing.read = NULL; - file->backing.get_mmap = file_backing_get_mmap; file->backing.get_ptr = file_backing_get_ptr; file->backing.unlock = file_backing_unlock; file->backing.get_size_estimate = file_backing_get_size_estimate; file->backing.resize = file_backing_resize; file->backing.free = file_backing_free; - if (!file_open_internal(file, path, size)) { + al_array_init(file->backing.available); + if (!file_open_internal(file, path, size) || !(file->u.map = aki_file_mmap(&file->file))) { al_free(file); return NULL; } - file->u.map = aki_file_mmap(&file->file); aki_mutex_init(&file->mutex); return (struct cch_backing *)file; } diff --git a/src/cache/backings/memory.c b/src/cache/backings/memory.c index 194d66c..1dca8f9 100644 --- a/src/cache/backings/memory.c +++ b/src/cache/backings/memory.c @@ -1,22 +1,31 @@ +#include "../range.h" + #include "memory.h" static void ensure_alloced(struct cch_backing_memory *mem, size_t size) { if (size > mem->alloc) { - mem->data = al_realloc(mem->data, size); - mem->alloc = size; + mem->alloc = al_next_power_of_two(size); + mem->data = al_realloc(mem->data, mem->alloc); } } +static void memory_backing_lock(struct cch_backing *backing) +{ + struct cch_backing_memory *mem = (struct cch_backing_memory *)backing; + aki_mutex_lock(&mem->mutex); +} + static void memory_backing_write(struct cch_backing *backing, u8 *buf, off_t index, size_t *size) { struct cch_backing_memory *mem = (struct cch_backing_memory *)backing; aki_mutex_lock(&mem->mutex); - if (mem->size >= 0 && index + ((off_t)*size) >= mem->size) { - *size = mem->size - index; + if (mem->size >= 0 && index + (off_t)*size >= mem->size) { + *size = AL_MAX(mem->size - index, 0); } ensure_alloced(mem, index + *size); al_memcpy(mem->data + index, buf, *size); + cch_backing_fill_range(&mem->backing, index, *size); aki_mutex_unlock(&mem->mutex); } @@ -24,8 +33,8 @@ static u8 *memory_backing_get_ptr(struct cch_backing *backing, off_t index, size { struct cch_backing_memory *mem = (struct cch_backing_memory *)backing; aki_mutex_lock(&mem->mutex); - if (mem->size >= 0 && index + ((off_t)*size) >= mem->size) { - *size = mem->size - index; + if (mem->size >= 0 && index + (off_t)*size >= mem->size) { + *size = AL_MAX(mem->size - index, 0); } return (u8 *)(mem->data + index); } @@ -59,6 +68,7 @@ static void memory_backing_free(struct cch_backing **backing) struct cch_backing_memory *mem = (struct cch_backing_memory *)*backing; al_free(mem->data); aki_mutex_destroy(&mem->mutex); + al_array_free(mem->backing.available); al_free(mem); *backing = NULL; } @@ -66,9 +76,9 @@ static void memory_backing_free(struct cch_backing **backing) struct cch_backing *cch_backing_memory_create(size_t size) { struct cch_backing_memory *mem = al_alloc_object(struct cch_backing_memory); - mem->backing.mode = CCH_BACKING_MAPPED; + mem->backing.mode = CACHE_BACKING_MAPPED; + mem->backing.lock = memory_backing_lock; mem->backing.write = memory_backing_write; - mem->backing.get_mmap = NULL; mem->backing.get_ptr = memory_backing_get_ptr; mem->backing.unlock = memory_backing_unlock; mem->backing.get_size_estimate = memory_backing_get_size_estimate; @@ -78,5 +88,6 @@ struct cch_backing *cch_backing_memory_create(size_t size) mem->alloc = size; mem->size = -1; aki_mutex_init(&mem->mutex); + al_array_init(mem->backing.available); return (struct cch_backing *)mem; } diff --git a/src/cache/entry.c b/src/cache/entry.c index 04e8f92..8068397 100644 --- a/src/cache/entry.c +++ b/src/cache/entry.c @@ -16,13 +16,26 @@ bool cch_entry_get_handle(struct cch_entry *entry, struct cch_handle *handle) return true; } +str *cch_entry_get_liana(struct cch_entry *entry) +{ + return &entry->handler->liana; +} + void cch_entry_set_size(struct cch_entry *entry, off_t filesize) { - return entry->backing->resize(entry->backing, filesize); + if (filesize == 0) { + // The backing will keep size = -1 internally. + entry->unknown_size = true; + return; + } + entry->backing->resize(entry->backing, filesize); } off_t cch_entry_get_size(struct cch_entry *entry) { + if (entry->unknown_size) { + return 0; + } return entry->backing->get_size_estimate(entry->backing); } diff --git a/src/cache/entry.h b/src/cache/entry.h index 305fb1f..3196809 100644 --- a/src/cache/entry.h +++ b/src/cache/entry.h @@ -6,15 +6,24 @@ #include "backing.h" #include "handler.h" +struct cch_chapter { + off_t start; + off_t end; +}; + struct cch_entry { u32 hash; s32 ref_count; + bool unknown_size; + array(struct cch_chapter) chapters; + struct cch_chapter *chapter; struct cch_backing *backing; struct cch_handler *handler; struct aki_mutex mutex; }; bool cch_entry_get_handle(struct cch_entry *entry, struct cch_handle *handle); +str *cch_entry_get_liana(struct cch_entry *entry); void cch_entry_set_size(struct cch_entry *entry, off_t filesize); off_t cch_entry_get_size(struct cch_entry *entry); void cch_entry_return_handle(struct cch_entry *entry, struct cch_handle *handle); diff --git a/src/cache/handle.c b/src/cache/handle.c index 9dfaf70..5bc868c 100644 --- a/src/cache/handle.c +++ b/src/cache/handle.c @@ -6,26 +6,32 @@ #include "handle.h" #include "entry.h" +#include "threaded_waits.h" -static bool wait_for_size(struct cch_handle *handle, struct cch_handler *handler) +static off_t wait_for_size(struct cch_handle *handle, struct cch_handler *handler) { - handle->wait.id = al_rand_u16(); handle->wait.end = -1; - return handler->wait_for_range(handler, &handle->wait); + if (!handler->wait_for_range(handler, &handle->wait)) { + return -1; + } + return cch_entry_get_size(handler->entry); } s32 cch_handle_read(struct cch_handle *handle, u8 *buf, s32 size) { struct cch_handler *handler = handle->entry->handler; - wait_for_size(handle, handler); + // filesize < 0: Size is yet to be evaluated and we can wait on it. + // filesize = 0: Size is explicitly unknown. off_t filesize = cch_entry_get_size(handle->entry); - if (filesize >= 0) { + if (size < 0) { + filesize = wait_for_size(handle, handler); + } + if (filesize > 0) { if (handle->pointer >= filesize) return CAMU_ERR_EOF; if (handle->pointer + size >= filesize) { size = filesize - handle->pointer; } } - handle->wait.id = al_rand_u16(); handle->wait.start = handle->pointer; handle->wait.end = handle->pointer + size; if (!handler->wait_for_range(handler, &handle->wait)) { @@ -33,25 +39,27 @@ s32 cch_handle_read(struct cch_handle *handle, u8 *buf, s32 size) } struct cch_backing *backing = handle->entry->backing; size_t available = (size_t)size; - if (backing->mode == CCH_BACKING_MAPPED) { + if (backing->mode == CACHE_BACKING_MAPPED) { u8 *ptr = backing->get_ptr(backing, handle->pointer, &available); if (ptr && available > 0) { al_memcpy(buf, ptr, available); } backing->unlock(backing); - } else if (backing->mode == CCH_BACKING_READ) { + } else if (backing->mode == CACHE_BACKING_READ) { backing->read(backing, buf, handle->pointer, &available); } handle->pointer += available; - return (available > 0) ? (s32)available : CAMU_ERR_EOF; + return available > 0 ? (s32)available : CAMU_ERR_EOF; } off_t cch_handle_seek(struct cch_handle *handle, off_t offset, s32 whence) { struct cch_handler *handler = handle->entry->handler; - wait_for_size(handle, handler); off_t filesize = cch_entry_get_size(handle->entry); - if (filesize < 0) return -1; + if (filesize < 0) { + filesize = wait_for_size(handle, handler); + } + if (filesize <= 0) return -1; if (whence == CAMU_SEEK_SIZE) { al_log_debug("cache_handle", "seek_size"); return filesize; @@ -91,10 +99,5 @@ bool cch_handle_can_seek(struct cch_handle *handle) void cch_handle_disable(struct cch_handle *handle) { - aki_mutex_lock(&handle->wait.mutex); - handle->wait.disabled = true; - if (aki_cond_is_waiting(&handle->wait.cond)) { - aki_cond_signal(&handle->wait.cond); - } - aki_mutex_unlock(&handle->wait.mutex); + cch_threaded_wait_disable(&handle->wait); } diff --git a/src/cache/handle.h b/src/cache/handle.h index 55223aa..ae936f3 100644 --- a/src/cache/handle.h +++ b/src/cache/handle.h @@ -3,7 +3,7 @@ #include <al/types.h> #include <aki/thread.h> -#include "handler.h" +#include "wait.h" struct cch_entry; struct cch_handle { diff --git a/src/cache/handler.h b/src/cache/handler.h index 5a2861e..9dd1896 100644 --- a/src/cache/handler.h +++ b/src/cache/handler.h @@ -2,15 +2,10 @@ #include <al/types.h> #include <al/array.h> +#include <al/str.h> #include <aki/thread.h> -struct cch_handler_wait { - u16 id; - off_t start, end; - struct aki_cond cond; - struct aki_mutex mutex; - bool disabled; -}; +#include "handle.h" struct cch_handler { bool (*can_seek)(struct cch_handler *); @@ -18,6 +13,8 @@ struct cch_handler { bool (*wait_for_range)(struct cch_handler *, struct cch_handler_wait *); void (*free)(struct cch_handler **); struct cch_entry *entry; - struct cch_backing *backing; + str liana; + bool disabled; + struct aki_mutex mutex; array(struct cch_handler_wait *) waits; }; diff --git a/src/cache/handlers/cdio.c b/src/cache/handlers/cdio.c new file mode 100644 index 0000000..23f87b6 --- /dev/null +++ b/src/cache/handlers/cdio.c @@ -0,0 +1,206 @@ +#include <al/log.h> + +#include "../backings/file.h" +#include "../backings/memory.h" +#include "../threaded_waits.h" +#include "../range.h" + +#include "cdio.h" + +// 1 sector at a time to minimize skips +#define SECTORS_PER_STEP 1 +#define BYTES_PER_STEP (SECTORS_PER_STEP * CDIO_CD_FRAMESIZE_RAW) + +static void cdio_log_messages(struct cch_handler_cdio *cdio) +{ + char *error_messages = cdio_cddap_errors(cdio->drive); + char *messages = cdio_cddap_messages(cdio->drive); + if (error_messages) { + al_log_error("cdio", "\n%s", error_messages); + cdio_cddap_free_messages(error_messages); + } + if (messages) { + al_log_info("cdio", "\n%s", messages); + cdio_cddap_free_messages(messages); + } +} + +static bool handler_cdio_can_seek(struct cch_handler *handler) +{ + (void)handler; + return true; +} + +static aki_thread_result AKI_THREADCALL cd_read_thread(void *userdata) +{ + struct cch_handler_cdio *cdio = (struct cch_handler_cdio *)userdata; + while (1) { + if (!al_atomic_load(s32)(&cdio->running, AL_ATOMIC_RELAXED)) { + // We don't care about how complete the disc read was. + return 0; + } + // cdio_read() here can block for a very long time so we buffer each read + // before writing it to the backing. + long ret = cdio_cddap_read(cdio->drive, aki_buffer_get_ptr(&cdio->buffer, 0), cdio->sector, SECTORS_PER_STEP); + cdio_log_messages(cdio); + al_assert(ret <= SECTORS_PER_STEP); + off_t pointer = cdio->sector * CDIO_CD_FRAMESIZE_RAW; + size_t size; + u8 *ptr = cdio->backing->get_ptr(cdio->backing, pointer, &size); + if (size < BYTES_PER_STEP) { + al_log_error("cdio", "Tried to write over expected size during normal operation."); + cdio->backing->unlock(cdio->backing); + break; + } + if (ret == SECTORS_PER_STEP) { + al_memcpy(ptr, aki_buffer_get_ptr(&cdio->buffer, 0), BYTES_PER_STEP); + } else if (ret == -10) { + al_log_error("cdio", "I/O error, skipping sector."); + al_memset(ptr, 0, BYTES_PER_STEP); + ret = SECTORS_PER_STEP; + } else if (ret < 0) { + al_log_error("cdio", "Fatal error (%d).", ret); + cdio->backing->unlock(cdio->backing); + break; + } + cch_backing_fill_range(cdio->backing, pointer, BYTES_PER_STEP); + cdio->sector += ret; + cdio->backing->unlock(cdio->backing); + cch_threaded_waits_evaluate(&cdio->handler, cdio->backing); + if (ret < SECTORS_PER_STEP || cdio->sector > cdio->end) { + al_log_debug("cdio", "Normal CD EOF."); + break; + } + } + if (cdio->sector <= cdio->end) { + al_log_warn("cdio", "Disc read cut short (sectors: %zd, end: %zd).", cdio->sector, cdio->end); + // We didn't read the full disc, disable waiters. + cch_threaded_waits_disable_all(&cdio->handler); + } + return 0; +} + +static void handler_cdio_maybe_spawn_worker(struct cch_handler *handler, size_t index) +{ + struct cch_handler_cdio *cdio = (struct cch_handler_cdio *)handler; + if (cdio->start == (lsn_t)index) return; + if (al_atomic_load(s32)(&cdio->running, AL_ATOMIC_ACQUIRE)) { + al_atomic_store(s32)(&cdio->running, 0, AL_ATOMIC_RELEASE); + aki_thread_join(&cdio->thread); + } + cdio->start = index; + cdio->sector = cdio->start; + al_atomic_store(s32)(&cdio->running, 1, AL_ATOMIC_RELEASE); + aki_thread_create(&cdio->thread, cd_read_thread, cdio); +} + +static bool handler_cdio_wait_for_range(struct cch_handler *handler, struct cch_handler_wait *wait) +{ + struct cch_handler_cdio *cdio = (struct cch_handler_cdio *)handler; + return cch_threaded_wait_for_range(&cdio->handler, cdio->backing, wait); +} + +static void handler_cdio_free(struct cch_handler **handler) +{ + struct cch_handler_cdio *cdio = (struct cch_handler_cdio *)*handler; + cch_threaded_waits_disable_all(&cdio->handler); + if (al_atomic_load(s32)(&cdio->running, AL_ATOMIC_RELAXED)) { + al_atomic_store(s32)(&cdio->running, 0, AL_ATOMIC_RELAXED); + aki_thread_join(&cdio->thread); + } + if (cdio->drive) { + cdio_log_messages(cdio); + cdio_cddap_close(cdio->drive); + } + cch_threaded_waits_close(&cdio->handler); + al_str_free(&cdio->handler.liana); + al_free(cdio); + *handler = NULL; +} + +static bool open_cd_drive(struct cch_handler_cdio *cdio) +{ + cdrom_drive_t *drive = NULL; + char **cd_drives; + CdIo_t *cdio_handle = NULL; + + cd_drives = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false); + if (cd_drives) { + // Use first drive. + cdio_handle = cdio_open(*cd_drives, DRIVER_UNKNOWN); + drive = cdio_cddap_identify_cdio(cdio_handle, CDDA_MESSAGE_LOGIT, NULL); + } else { + al_log_error("cdio", "No compatible disc found."); + return false; + } + cdio_free_device_list(cd_drives); + + cdio_cddap_verbose_set(drive, CDDA_MESSAGE_LOGIT, CDDA_MESSAGE_LOGIT); + + s32 ret = cdio_cddap_open(drive); + if (ret != 0) { + al_log_error("cdio", "Unable to open disc (%d).", ret); + return false; + } + + ret = cdio_cddap_speed_set(drive, 4); + if (ret != 0) { + al_log_warn("cdio", "Failed to set drive speed (%d).", ret); + } + + // Not sure what this is based on, but it can seemingly just be wrong. + track_t tracks = cdio_cddap_tracks(drive); + al_log_info("cdio", "CD with %d tracks loaded.", tracks); + al_array_reserve(cdio->handler.entry->chapters, tracks); + + lsn_t start = cdio_cddap_disc_firstsector(drive); + lsn_t end = cdio_cddap_disc_lastsector(drive); + lsn_t offset = start; + s32 track = CDIO_INVALID_TRACK; + while (offset < end) { + track = cdio_cddap_sector_gettrack(drive, offset); + al_assert(track != CDIO_INVALID_TRACK); + lsn_t track_start = cdio_cddap_track_firstsector(drive, track); + lsn_t track_end = cdio_cddap_track_lastsector(drive, track); + al_array_push(cdio->handler.entry->chapters, ((struct cch_chapter){ track_start, track_end })); + offset = track_end + 1; + } + // end is a valid index so add one framesize. + cch_entry_set_size(cdio->handler.entry, ((end - start) + 1) * CDIO_CD_FRAMESIZE_RAW); + cdio->start = -1; + cdio->end = end; + + cdio->drive = drive; + + cdio_log_messages(cdio); + + return true; +} + +struct cch_entry *cch_handler_cdio_create(void) +{ + struct cch_backing *backing = cch_backing_file_create(al_str_c("/tmp/camu_cd_data"), 4096); + if (!backing) return NULL; + struct cch_handler_cdio *cdio = al_alloc_object(struct cch_handler_cdio); + cdio->backing = backing; + struct cch_entry *entry = al_alloc_object(struct cch_entry); + entry->backing = backing; + entry->handler = (struct cch_handler *)cdio; + entry->ref_count = 0; + entry->unknown_size = false; + entry->hash = 0; + aki_mutex_init(&entry->mutex); + entry->handler->can_seek = handler_cdio_can_seek; + entry->handler->maybe_spawn_worker = handler_cdio_maybe_spawn_worker; + entry->handler->wait_for_range = handler_cdio_wait_for_range; + entry->handler->free = handler_cdio_free; + entry->handler->entry = entry; + al_str_from(&entry->handler->liana, "cdio"); + if (!open_cd_drive(cdio)) return NULL; + al_atomic_store(s32)(&cdio->running, 0, AL_ATOMIC_RELAXED); + aki_buffer_init(&cdio->buffer); + aki_buffer_ensure_space(&cdio->buffer, BYTES_PER_STEP); + aki_buffer_set_size(&cdio->buffer, BYTES_PER_STEP); + cch_threaded_waits_init(&cdio->handler); + return entry; +} diff --git a/src/cache/handlers/cdio.h b/src/cache/handlers/cdio.h new file mode 100644 index 0000000..953c988 --- /dev/null +++ b/src/cache/handlers/cdio.h @@ -0,0 +1,21 @@ +#include <al/atomic.h> +#include <aki/common.h> +#include <cdio/paranoia/cdda.h> +#include <cdio/cd_types.h> + +#include "../handler.h" +#include "../entry.h" + +struct cch_handler_cdio { + struct cch_handler handler; + struct cch_backing *backing; + cdrom_drive_t *drive; + lsn_t start; + lsn_t end; + lsn_t sector; + atomic(s32) running; + struct aki_thread thread; + struct aki_buffer buffer; +}; + +struct cch_entry *cch_handler_cdio_create(void); diff --git a/src/cache/handlers/file.c b/src/cache/handlers/file.c index ec6a146..03e2814 100644 --- a/src/cache/handlers/file.c +++ b/src/cache/handlers/file.c @@ -25,6 +25,7 @@ static bool handler_file_wait_for_range(struct cch_handler *handler, struct cch_ static void handler_file_free(struct cch_handler **handler) { struct cch_handler_file *file = (struct cch_handler_file *)*handler; + al_str_free(&file->handler.liana); al_free(file); *handler = NULL; } @@ -33,17 +34,20 @@ struct cch_entry *cch_handler_file_create(str *path) { struct cch_backing *backing = cch_backing_file_create(path, 0); if (!backing) return NULL; + struct cch_handler_file *file = al_alloc_object(struct cch_handler_file); + file->backing = backing; struct cch_entry *entry = al_alloc_object(struct cch_entry); entry->backing = backing; + entry->handler = (struct cch_handler *)file; entry->ref_count = 0; + entry->unknown_size = false; entry->hash = 0; - entry->handler = (struct cch_handler *)al_alloc_object(struct cch_handler_file); aki_mutex_init(&entry->mutex); entry->handler->can_seek = handler_file_can_seek; entry->handler->maybe_spawn_worker = handler_file_maybe_spawn_worker; entry->handler->wait_for_range = handler_file_wait_for_range; entry->handler->free = handler_file_free; entry->handler->entry = entry; - entry->handler->backing = entry->backing; + al_str_from(&entry->handler->liana, "codec"); return entry; } diff --git a/src/cache/handlers/file.h b/src/cache/handlers/file.h index 52e5c94..85e1c7f 100644 --- a/src/cache/handlers/file.h +++ b/src/cache/handlers/file.h @@ -7,6 +7,7 @@ struct cch_handler_file { struct cch_handler handler; + struct cch_backing *backing; }; struct cch_entry *cch_handler_file_create(str *path); diff --git a/src/cache/handlers/http.c b/src/cache/handlers/http.c index 789b247..71ae186 100644 --- a/src/cache/handlers/http.c +++ b/src/cache/handlers/http.c @@ -1,8 +1,10 @@ #include <al/random.h> #include <al/log.h> -#include "../backings/file.h" #include "../backings/memory.h" +#include "../backings/file.h" +#include "../threaded_waits.h" +#include "../wait.h" #include "http.h" @@ -14,21 +16,6 @@ static bool handler_http_can_seek(struct cch_handler *handler) return false; } -static void disable_waiters_internal(struct cch_handler_http *http) -{ - aki_mutex_lock(&http->mutex); - // Don't allow any further waits. - http->disabled = true; - struct cch_handler_wait *wait; - al_array_foreach(http->handler.waits, i, wait) { - aki_mutex_lock(&wait->mutex); - wait->disabled = true; - aki_cond_signal(&wait->cond); - aki_mutex_unlock(&wait->mutex); - } - aki_mutex_unlock(&http->mutex); -} - static size_t http_callback(void *userdata, u8 op, u8 *buf, s64 int0) { struct cch_handler_http *http = (struct cch_handler_http *)userdata; @@ -39,8 +26,7 @@ static size_t http_callback(void *userdata, u8 op, u8 *buf, s64 int0) break; } case AKI_HTTP_ERROR: { - al_log_debug("cache_handler_http", "Error."); - disable_waiters_internal(http); + cch_threaded_waits_disable_all(&http->handler); break; } case AKI_HTTP_RESPONSE_CODE: { @@ -54,37 +40,18 @@ static size_t http_callback(void *userdata, u8 op, u8 *buf, s64 int0) case AKI_HTTP_CONTENT_LENGTH: { off_t content_length = (off_t)int0; al_log_debug("cache_handler_http", "Content-Length: %lld.", content_length); - aki_mutex_lock(&http->mutex); cch_entry_set_size(http->handler.entry, content_length); - struct cch_handler_wait *wait; - al_array_foreach(http->handler.waits, i, wait) { - if (wait->end < 0) { - aki_mutex_lock(&wait->mutex); - aki_cond_signal(&wait->cond); - al_array_remove_at_iter(http->handler.waits, i); - aki_mutex_unlock(&wait->mutex); - } - } - aki_mutex_unlock(&http->mutex); + cch_threaded_waits_signal_any(&http->handler); break; } case AKI_HTTP_WRITE: { - http->handler.backing->write(http->handler.backing, buf, http->pointer, &ret); - aki_mutex_lock(&http->mutex); - http->pointer += ret; - struct cch_handler_wait *wait; - al_array_foreach(http->handler.waits, i, wait) { - aki_mutex_lock(&wait->mutex); - // Assume that if we get data before a length, we won't get a length. - // So, signal the waiter (wait_for_range returns false). - if (wait->end < 0) wait->disabled = true; - if (wait->disabled || http->pointer >= wait->end) { - aki_cond_signal(&wait->cond); - al_array_remove_at_iter(http->handler.waits, i); - } - aki_mutex_unlock(&wait->mutex); + // If we got data before a Content-Length, assume the size is unknown. + if (cch_entry_get_size(http->handler.entry) < 0) { + cch_entry_set_size(http->handler.entry, 0); } - aki_mutex_unlock(&http->mutex); + http->backing->write(http->backing, buf, http->pointer, &ret); + http->pointer += ret; + cch_threaded_waits_evaluate(&http->handler, http->backing); break; } default: @@ -93,123 +60,63 @@ static size_t http_callback(void *userdata, u8 op, u8 *buf, s64 int0) return ret; } -static void signal_callback(void *userdata) -{ - struct cch_handler_http *http = (struct cch_handler_http *)userdata; - u32 size; - s32 index; - do { - camu_queue_try_pop(http->queue, size, index); - if (size == 0) break; - if (index == -1) { - aki_event_loop_break(&http->loop); - break; - } - al_array_push(http->requests, (struct aki_http){0}); - struct aki_http *request = &al_array_last(http->requests); - aki_http_init(request); - aki_http_set_url(request, &http->url); - aki_http_set_user_agent(request, USER_AGENT); - aki_http_request_stream(request, AKI_HTTP_GET, &http->loop, http_callback, http); - } while (1); -} - static void handler_http_maybe_spawn_worker(struct cch_handler *handler, size_t index) { struct cch_handler_http *http = (struct cch_handler_http *)handler; - camu_queue_push(http->queue, index); - aki_signal_send(&http->signal); + (void)index; + al_array_push(http->requests, (struct aki_http){ 0 }); + struct aki_http *request = &al_array_last(http->requests); + aki_http_init(request); + aki_http_set_url(request, &http->url); + aki_http_set_user_agent(request, USER_AGENT); + aki_http_request_stream(request, AKI_HTTP_GET, http->loop, http_callback, http); } static bool handler_http_wait_for_range(struct cch_handler *handler, struct cch_handler_wait *wait) { struct cch_handler_http *http = (struct cch_handler_http *)handler; - aki_mutex_lock(&http->mutex); - bool canceled = http->disabled; - if (!canceled && (cch_entry_get_size(http->handler.entry) < 0 || http->pointer < wait->end)) { - aki_mutex_lock(&wait->mutex); - if (!wait->disabled) { - al_array_push(http->handler.waits, wait); - aki_mutex_unlock(&http->mutex); - aki_cond_wait(&wait->cond, &wait->mutex); - } else { - aki_mutex_unlock(&wait->mutex); - aki_mutex_unlock(&http->mutex); - return false; - } - aki_mutex_unlock(&wait->mutex); - aki_mutex_lock(&http->mutex); - if (wait->disabled) { - u16 id = wait->id; - al_array_foreach(http->handler.waits, i, wait) { - if (wait->id == id) { - al_array_remove_at_iter(http->handler.waits, i); - break; - } - } - canceled = true; - } - } - aki_mutex_unlock(&http->mutex); - return !canceled; + return cch_threaded_wait_for_range(&http->handler, http->backing, wait); } static void handler_http_free(struct cch_handler **handler) { struct cch_handler_http *http = (struct cch_handler_http *)*handler; - disable_waiters_internal(http); - camu_queue_push(http->queue, -1); - aki_signal_send(&http->signal); - aki_thread_join(&http->thread); - aki_mutex_destroy(&http->mutex); + cch_threaded_waits_disable_all(&http->handler); struct aki_http *request; al_array_foreach_ptr(http->requests, i, request) { aki_http_close(request); } + cch_threaded_waits_close(&http->handler); al_array_free(http->requests); - camu_queue_free(http->queue); al_str_free(&http->url); - al_array_free(http->handler.waits); + al_str_free(&http->handler.liana); al_free(http); *handler = NULL; } -static aki_thread_result AKI_THREADCALL event_loop_thread(void *userdata) -{ - struct cch_handler_http *http = (struct cch_handler_http *)userdata; - aki_event_loop_run(&http->loop); - aki_event_loop_destroy(&http->loop); - return 0; -} - -struct cch_entry *cch_handler_http_create(str *url) +struct cch_entry *cch_handler_http_create(str *url, struct aki_event_loop *loop) { - //struct cch_backing *backing = cch_backing_file_create(al_str_c("/tmp/camu_http_data"), 1024 * 128); struct cch_backing *backing = cch_backing_memory_create(1024 * 128); if (!backing) return NULL; + struct cch_handler_http *http = al_alloc_object(struct cch_handler_http); + http->backing = backing; struct cch_entry *entry = al_alloc_object(struct cch_entry); entry->backing = backing; + entry->handler = (struct cch_handler *)http; entry->ref_count = 0; + entry->unknown_size = false; entry->hash = 0; - entry->handler = (struct cch_handler *)al_alloc_object(struct cch_handler_http); aki_mutex_init(&entry->mutex); entry->handler->can_seek = handler_http_can_seek; entry->handler->maybe_spawn_worker = handler_http_maybe_spawn_worker; entry->handler->wait_for_range = handler_http_wait_for_range; entry->handler->free = handler_http_free; entry->handler->entry = entry; - entry->handler->backing = entry->backing; - al_array_init(entry->handler->waits); - struct cch_handler_http *http = (struct cch_handler_http *)entry->handler; + al_str_from(&entry->handler->liana, "codec"); + http->loop = loop; al_str_clone(&http->url, url); http->pointer = 0; - http->disabled = false; al_array_init(http->requests); - camu_queue_init(http->queue); - aki_event_loop_init(&http->loop); - aki_signal_init(&http->signal, signal_callback, http); - aki_signal_start(&http->signal, &http->loop); - aki_mutex_init(&http->mutex); - aki_thread_create(&http->thread, event_loop_thread, http); + cch_threaded_waits_init(&http->handler); return entry; } diff --git a/src/cache/handlers/http.h b/src/cache/handlers/http.h index e8fb2e0..5f9df86 100644 --- a/src/cache/handlers/http.h +++ b/src/cache/handlers/http.h @@ -4,22 +4,16 @@ #include <aki/thread.h> #include <aki/signal.h> -#include "../../util/queue.h" - #include "../handler.h" #include "../entry.h" struct cch_handler_http { struct cch_handler handler; - off_t pointer; + struct cch_backing *backing; str url; - bool disabled; + off_t pointer; array(struct aki_http) requests; - queue(s32) queue; - struct aki_event_loop loop; - struct aki_signal signal; - struct aki_mutex mutex; - struct aki_thread thread; + struct aki_event_loop *loop; }; -struct cch_entry *cch_handler_http_create(str *url); +struct cch_entry *cch_handler_http_create(str *url, struct aki_event_loop *loop); diff --git a/src/cache/meson.build b/src/cache/meson.build index 099b2a6..8684ccd 100644 --- a/src/cache/meson.build +++ b/src/cache/meson.build @@ -1,7 +1,9 @@ cache_src = [ 'entry.c', 'handle.c', + 'threaded_waits.c', 'handlers/file.c', + 'handlers/cdio.c', 'backings/memory.c' ] diff --git a/src/cache/range.h b/src/cache/range.h new file mode 100644 index 0000000..598b655 --- /dev/null +++ b/src/cache/range.h @@ -0,0 +1,23 @@ +#include <al/types.h> + +#include "backing.h" + +AL_UNUSED_FUNCTION_PUSH + +// TODO: cch_backing_remove_range. +// +// TODO: report overlap, don't unlock in backing_write() and handle fill in handler + +static void cch_backing_fill_range(struct cch_backing *backing, off_t index, off_t size) +{ + struct cch_range *range; + al_array_foreach_ptr(backing->available, i, range) { + if (index >= range->start && index <= range->end) { + range->end = AL_MAX(index + size, range->end); + return; + } + } + al_array_push(backing->available, ((struct cch_range){ index, index + size })); +} + +AL_UNUSED_FUNCTION_POP diff --git a/src/cache/threaded_waits.c b/src/cache/threaded_waits.c new file mode 100644 index 0000000..a93fe85 --- /dev/null +++ b/src/cache/threaded_waits.c @@ -0,0 +1,126 @@ +#include "threaded_waits.h" +#include "entry.h" + +void cch_threaded_waits_init(struct cch_handler *handler) +{ + handler->disabled = false; + aki_mutex_init(&handler->mutex); + al_array_init(handler->waits); +} + +static bool wait_range_satisfied(struct cch_backing *backing, struct cch_handler_wait *wait) +{ + if (wait->end < 0) return true; + struct cch_range *range; + al_array_foreach_ptr(backing->available, i, range) { + if (wait->start >= range->start && range->end >= wait->end) { + return true; + } + } + return false; +} + +bool cch_threaded_wait_for_range(struct cch_handler *handler, struct cch_backing *backing, + struct cch_handler_wait *wait) +{ + aki_mutex_lock(&handler->mutex); + bool canceled = handler->disabled; + off_t size = cch_entry_get_size(handler->entry); + backing->lock(backing); + bool satisfied = size >= 0 && wait_range_satisfied(backing, wait); + backing->unlock(backing); + if (!canceled && !satisfied) { + aki_mutex_lock(&wait->mutex); + if (!wait->disabled) { + al_array_push(handler->waits, wait); + aki_mutex_unlock(&handler->mutex); + aki_cond_wait(&wait->cond, &wait->mutex); + } else { + aki_mutex_unlock(&wait->mutex); + aki_mutex_unlock(&handler->mutex); + return false; + } + aki_mutex_unlock(&wait->mutex); + aki_mutex_lock(&handler->mutex); + if (wait->disabled) { + struct cch_handler_wait *rwait; + al_array_foreach(handler->waits, i, rwait) { + if (rwait == wait) { + al_array_remove_at(handler->waits, i); + break; + } + } + canceled = true; + } + } + aki_mutex_unlock(&handler->mutex); + return !canceled; +} + +void cch_threaded_waits_signal_any(struct cch_handler *handler) +{ + aki_mutex_lock(&handler->mutex); + struct cch_handler_wait *wait; + al_array_foreach_rev(handler->waits, i, wait) { + if (wait->end < 0) { + al_array_remove_at(handler->waits, i); + aki_mutex_lock(&wait->mutex); + aki_cond_signal(&wait->cond); + aki_mutex_unlock(&wait->mutex); + } + } + aki_mutex_unlock(&handler->mutex); +} + +void cch_threaded_wait_disable(struct cch_handler_wait *wait) +{ + aki_mutex_lock(&wait->mutex); + wait->disabled = true; + // cond_is_waiting() could be false if the handler is disabled. + if (aki_cond_is_waiting(&wait->cond)) { + aki_cond_signal(&wait->cond); + } + aki_mutex_unlock(&wait->mutex); +} + +void cch_threaded_waits_disable_all(struct cch_handler *handler) +{ + aki_mutex_lock(&handler->mutex); + if (handler->disabled) { + aki_mutex_unlock(&handler->mutex); + return; + } + // Disallow any further waits. + handler->disabled = true; + struct cch_handler_wait *wait; + al_array_foreach(handler->waits, i, wait) { + aki_mutex_lock(&wait->mutex); + wait->disabled = true; + aki_cond_signal(&wait->cond); + aki_mutex_unlock(&wait->mutex); + } + aki_mutex_unlock(&handler->mutex); +} + +void cch_threaded_waits_evaluate(struct cch_handler *handler, struct cch_backing *backing) +{ + aki_mutex_lock(&handler->mutex); + backing->lock(backing); + struct cch_handler_wait *wait; + al_array_foreach_rev(handler->waits, i, wait) { + aki_mutex_lock(&wait->mutex); + if (wait_range_satisfied(backing, wait)) { + aki_cond_signal(&wait->cond); + al_array_remove_at(handler->waits, i); + } + aki_mutex_unlock(&wait->mutex); + } + backing->unlock(backing); + aki_mutex_unlock(&handler->mutex); +} + +void cch_threaded_waits_close(struct cch_handler *handler) +{ + aki_mutex_destroy(&handler->mutex); + al_array_free(handler->waits); +} diff --git a/src/cache/threaded_waits.h b/src/cache/threaded_waits.h new file mode 100644 index 0000000..edbee03 --- /dev/null +++ b/src/cache/threaded_waits.h @@ -0,0 +1,15 @@ +#pragma once + +#include <al/types.h> + +#include "handler.h" +#include "backing.h" + +void cch_threaded_waits_init(struct cch_handler *handler); +bool cch_threaded_wait_for_range(struct cch_handler *handler, struct cch_backing *backing, + struct cch_handler_wait *wait); +void cch_threaded_waits_signal_any(struct cch_handler *handler); +void cch_threaded_wait_disable(struct cch_handler_wait *wait); +void cch_threaded_waits_disable_all(struct cch_handler *handler); +void cch_threaded_waits_evaluate(struct cch_handler *handler, struct cch_backing *backing); +void cch_threaded_waits_close(struct cch_handler *handler); diff --git a/src/cache/wait.h b/src/cache/wait.h new file mode 100644 index 0000000..8654362 --- /dev/null +++ b/src/cache/wait.h @@ -0,0 +1,10 @@ +#pragma once + +#include <aki/thread.h> + +struct cch_handler_wait { + off_t start, end; + struct aki_cond cond; + struct aki_mutex mutex; + bool disabled; +}; |