+#if !USE_READ_THREAD
+static void readThreadStop() {}
+static void readThreadStart() {}
+#else
+static pthread_t read_thread_id;
+
+static pthread_cond_t read_thread_msg_avail;
+static pthread_cond_t read_thread_msg_done;
+static pthread_mutex_t read_thread_msg_lock;
+
+static pthread_cond_t sectorbuffer_cond;
+static pthread_mutex_t sectorbuffer_lock;
+
+static boolean read_thread_running = FALSE;
+static int read_thread_sector_start = -1;
+static int read_thread_sector_end = -1;
+
+typedef struct {
+ int sector;
+ long ret;
+ unsigned char data[CD_FRAMESIZE_RAW];
+} SectorBufferEntry;
+
+#define SECTOR_BUFFER_SIZE 4096
+
+static SectorBufferEntry *sectorbuffer;
+static size_t sectorbuffer_index;
+
+int (*sync_cdimg_read_func)(FILE *f, unsigned int base, void *dest, int sector);
+unsigned char *(*sync_CDR_getBuffer)(void);
+
+static unsigned char * CALLBACK ISOgetBuffer_async(void);
+static int cdread_async(FILE *f, unsigned int base, void *dest, int sector);
+
+static void *readThreadMain(void *param) {
+ int max_sector = -1;
+ int requested_sector_start = -1;
+ int requested_sector_end = -1;
+ int last_read_sector = -1;
+ int index = 0;
+
+ int ra_sector = -1;
+ int max_ra = 128;
+ int initial_ra = 1;
+ int speedmult_ra = 4;
+
+ int ra_count = 0;
+ int how_far_ahead = 0;
+
+ unsigned char tmpdata[CD_FRAMESIZE_RAW];
+ long ret;
+
+ max_sector = msf2sec(ti[numtracks].start) + msf2sec(ti[numtracks].length);
+
+ while(1) {
+ pthread_mutex_lock(&read_thread_msg_lock);
+
+ // If we don't have readahead and we don't have a sector request, wait for one.
+ // If we still have readahead to go, don't block, just keep going.
+ // And if we ever have a sector request pending, acknowledge and reset it.
+
+ if (!ra_count) {
+ if (read_thread_sector_start == -1 && read_thread_running) {
+ pthread_cond_wait(&read_thread_msg_avail, &read_thread_msg_lock);
+ }
+ }
+
+ if (read_thread_sector_start != -1) {
+ requested_sector_start = read_thread_sector_start;
+ requested_sector_end = read_thread_sector_end;
+ read_thread_sector_start = -1;
+ read_thread_sector_end = -1;
+ pthread_cond_signal(&read_thread_msg_done);
+ }
+
+ pthread_mutex_unlock(&read_thread_msg_lock);
+
+ if (!read_thread_running)
+ break;
+
+ // Readahead code, based on the implementation in mednafen psx's cdromif.cpp
+ if (requested_sector_start != -1) {
+ if (last_read_sector != -1 && last_read_sector == (requested_sector_start - 1)) {
+ how_far_ahead = ra_sector - requested_sector_end;
+
+ if(how_far_ahead <= max_ra)
+ ra_count = (max_ra - how_far_ahead + 1 ? max_ra - how_far_ahead + 1 : speedmult_ra);
+ else
+ ra_count++;
+ } else if (requested_sector_end != last_read_sector) {
+ ra_sector = requested_sector_end;
+ ra_count = initial_ra;
+ }
+
+ last_read_sector = requested_sector_end;
+ }
+
+ index = ra_sector % SECTOR_BUFFER_SIZE;
+
+ // check for end of CD
+ if (ra_count && ra_sector >= max_sector) {
+ ra_count = 0;
+ pthread_mutex_lock(§orbuffer_lock);
+ sectorbuffer[index].ret = -1;
+ sectorbuffer[index].sector = ra_sector;
+ pthread_cond_signal(§orbuffer_cond);
+ pthread_mutex_unlock(§orbuffer_lock);
+ }
+
+ if (ra_count) {
+ pthread_mutex_lock(§orbuffer_lock);
+ if (sectorbuffer[index].sector != ra_sector) {
+ pthread_mutex_unlock(§orbuffer_lock);
+
+ ret = sync_cdimg_read_func(cdHandle, 0, tmpdata, ra_sector);
+
+ pthread_mutex_lock(§orbuffer_lock);
+ sectorbuffer[index].ret = ret;
+ sectorbuffer[index].sector = ra_sector;
+ memcpy(sectorbuffer[index].data, tmpdata, CD_FRAMESIZE_RAW);
+ }
+ pthread_cond_signal(§orbuffer_cond);
+ pthread_mutex_unlock(§orbuffer_lock);
+
+ ra_sector++;
+ ra_count--;
+ }
+ }
+
+ return NULL;
+}
+
+static void readThreadStop() {
+ if (read_thread_running == TRUE) {
+ read_thread_running = FALSE;
+ pthread_cond_signal(&read_thread_msg_avail);
+ pthread_join(read_thread_id, NULL);
+ }
+
+ pthread_cond_destroy(&read_thread_msg_done);
+ pthread_cond_destroy(&read_thread_msg_avail);
+ pthread_mutex_destroy(&read_thread_msg_lock);
+
+ pthread_cond_destroy(§orbuffer_cond);
+ pthread_mutex_destroy(§orbuffer_lock);
+
+ CDR_getBuffer = sync_CDR_getBuffer;
+ cdimg_read_func = sync_cdimg_read_func;
+
+ free(sectorbuffer);
+ sectorbuffer = NULL;
+}
+
+static void readThreadStart() {
+ SysPrintf("Starting async CD thread\n");
+
+ if (read_thread_running == TRUE)
+ return;
+
+ read_thread_running = TRUE;
+ read_thread_sector_start = -1;
+ read_thread_sector_end = -1;
+ sectorbuffer_index = 0;
+
+ sectorbuffer = calloc(SECTOR_BUFFER_SIZE, sizeof(SectorBufferEntry));
+ if(!sectorbuffer)
+ goto error;
+
+ sectorbuffer[0].sector = -1; // Otherwise we might think we've already fetched sector 0!
+
+ sync_CDR_getBuffer = CDR_getBuffer;
+ CDR_getBuffer = ISOgetBuffer_async;
+ sync_cdimg_read_func = cdimg_read_func;
+ cdimg_read_func = cdread_async;
+
+ if (pthread_cond_init(&read_thread_msg_avail, NULL) ||
+ pthread_cond_init(&read_thread_msg_done, NULL) ||
+ pthread_mutex_init(&read_thread_msg_lock, NULL) ||
+ pthread_cond_init(§orbuffer_cond, NULL) ||
+ pthread_mutex_init(§orbuffer_lock, NULL) ||
+ pthread_create(&read_thread_id, NULL, readThreadMain, NULL))
+ goto error;
+
+ return;
+
+ error:
+ SysPrintf("Error starting async CD thread\n");
+ SysPrintf("Falling back to sync\n");
+
+ readThreadStop();
+}
+#endif
+