/* * Route request handling to the appropriate handlers * */ static int process_request(sp_session *session, struct request *req) { int now = get_millisecs(); if(session->connectionstate != SP_CONNECTION_STATE_LOGGED_IN && (req->type != REQ_TYPE_LOGIN && req->type != REQ_TYPE_LOGOUT)) { if(req->state == REQ_STATE_NEW) { DSFYDEBUG("Postponing request <type %s, state %s, input %p> 10 seconds due to not logged in\n", REQUEST_TYPE_STR(req->type), REQUEST_STATE_STR(req->state), req->input); req->next_timeout = now + 10*1000; return 0; } else if(req->state == REQ_STATE_RUNNING) { DSFYDEBUG("Failing request <type %s, state %s, input %p> due to not logged in\n", REQUEST_TYPE_STR(req->type), REQUEST_STATE_STR(req->state), req->input); return request_set_result(session, req, SP_ERROR_OTHER_TRANSIENT, NULL); } } else if(req->state == REQ_STATE_NEW && session->num_channels >= 16) { DSFYDEBUG("%d channels active, postponing request <type %s, state %s, input %p>\n", session->num_channels, REQUEST_TYPE_STR(req->type), REQUEST_STATE_STR(req->state), req->input); if(req->next_timeout < now + 100) req->next_timeout = now + 100; return 0; } switch(req->type) { case REQ_TYPE_LOGIN: return process_login_request(session, req); break; case REQ_TYPE_LOGOUT: return process_logout_request(session, req); break; case REQ_TYPE_PC_LOAD: case REQ_TYPE_PLAYLIST_LOAD: case REQ_TYPE_PLAYLIST_CHANGE: return playlist_process(session, req); break; case REQ_TYPE_TOPLISTBROWSE: return toplistbrowse_process_request(session, req); break; case REQ_TYPE_SEARCH: return search_process_request(session, req); break; case REQ_TYPE_USER: return user_process_request(session, req); break; case REQ_TYPE_IMAGE: return osfy_image_process_request(session, req); break; case REQ_TYPE_ALBUMBROWSE: case REQ_TYPE_ARTISTBROWSE: case REQ_TYPE_BROWSE_ALBUM: case REQ_TYPE_BROWSE_ARTIST: case REQ_TYPE_BROWSE_PLAYLIST_TRACKS: case REQ_TYPE_BROWSE_TRACK: return browse_process(session, req); break; case REQ_TYPE_PLAYER_KEY: case REQ_TYPE_PLAYER_SUBSTREAM: case REQ_TYPE_PLAY_TOKEN_ACQUIRE: case REQ_TYPE_PLAY_TOKEN_LOST: return player_process_request(session, req); break; case REQ_TYPE_CACHE_PERIODIC: return cache_process(session, req); break; default: DSFYDEBUG("BUG: Unhandled request type '%s'\n", REQUEST_TYPE_STR(req->type)); break; } return 0; }
DWORD WINAPI iothread(LPVOID data) { #else void *iothread(void *data) { #endif sp_session *s = (sp_session *)data; struct request *req; int ret; int now; #ifdef _WIN32 /* Initialize Winsock */ WSADATA wsadata; WSAStartup(MAKEWORD(2,2), &wsadata); #endif for(;;) { request_cleanup(s); /* No locking needed since we're in control of request_cleanup() */ now = get_millisecs(); for(req = s->requests; req; req = req->next) { if(req->state != REQ_STATE_NEW && req->state != REQ_STATE_RUNNING) continue; if(req->next_timeout > now) continue; DSFYDEBUG("Processing request <type %s, state %s, input %p, timeout %d>\n", REQUEST_TYPE_STR(req->type), REQUEST_STATE_STR(req->state), req->input, req->next_timeout); ret = process_request(s, req); DSFYDEBUG("Request processing returned %d\n", ret); /* FIXME: Drop connection on errors! */ if(ret != 0) { DSFYDEBUG("Request failed, good bye\n"); #ifdef _WIN32 #else exit(1); #endif } } /* Packets can only be processed once we're logged in */ if(s->connectionstate != SP_CONNECTION_STATE_LOGGED_IN) { #ifdef _WIN32 WaitForSingleObject(s->request_mutex, INFINITE); #else pthread_mutex_lock(&s->request_mutex); #endif if(s->requests == NULL) { DSFYDEBUG("Sleeping because there's nothing to do\n"); #ifdef _WIN32 ReleaseMutex(s->request_mutex); WaitForSingleObject(s->idle_wakeup, INFINITE); WaitForSingleObject(s->request_mutex, INFINITE); #else pthread_cond_wait(&s->idle_wakeup, &s->request_mutex); #endif DSFYDEBUG("Woke up, a new request was posted\n"); } #ifdef _WIN32 ReleaseMutex(s->request_mutex); #else pthread_mutex_unlock(&s->request_mutex); #endif continue; } /* * Read and process zero or more packets * Will sleep somewhere around 64ms if no * data is available */ ret = packet_read_and_process(s); if(ret < 0) { DSFYDEBUG("process_packets() returned %d, disconnecting!\n", ret); #ifdef _WIN32 closesocket(s->sock); #else close(s->sock); #endif s->sock = -1; s->connectionstate = SP_CONNECTION_STATE_DISCONNECTED; request_post_result(s, REQ_TYPE_LOGOUT, SP_ERROR_OTHER_TRANSIENT, NULL); } } }
SP_LIBEXPORT(void) sp_session_process_events(sp_session *session, int *next_timeout) { struct request *request; sp_albumbrowse *alb; sp_artistbrowse *arb; sp_toplistbrowse *toplistbrowse; sp_search *search; sp_image *image; sp_playlist *playlist; sp_playlistcontainer *pc; int i, value; while((request = request_fetch_next_result(session, next_timeout)) != NULL) { DSFYDEBUG("Event processing for request <type %s, state %s, input %p, timeout %d>" " with output <error %d, output %p>\n", REQUEST_TYPE_STR(request->type), REQUEST_STATE_STR(request->state), request->input, request->next_timeout, request->error, request->output); /* FIXME: Verify that these callbacks are indeed called from the main thread! */ switch(request->type) { case REQ_TYPE_LOGIN: if(session->callbacks->logged_in == NULL) break; session->callbacks->logged_in(session, request->error); break; case REQ_TYPE_LOGOUT: if(session->callbacks->logged_out == NULL) break; session->callbacks->logged_out(session); break; case REQ_TYPE_PLAY_TOKEN_LOST: if(session->callbacks->play_token_lost == NULL) break; session->callbacks->play_token_lost(session); break; case REQ_TYPE_NOTIFY: if(session->callbacks->message_to_user == NULL) break; /* We'll leak memory here for each login made :( */ session->callbacks->message_to_user(session, request->output); break; case REQ_TYPE_PC_LOAD: pc = session->playlistcontainer; for(i = 0; i < pc->num_callbacks; i++) if(pc->callbacks[i]->container_loaded) pc->callbacks[i]->container_loaded(pc, pc->userdata[i]); break; case REQ_TYPE_PC_PLAYLIST_ADD: value = *(int *)request->output; /* position */ pc = session->playlistcontainer; playlist = pc->playlists[value]; for(i = 0; i < pc->num_callbacks; i++) if(pc->callbacks[i]->playlist_added) pc->callbacks[i]->playlist_added(pc, playlist, value, pc->userdata[i]); break; case REQ_TYPE_PLAYLIST_RENAME: pc = session->playlistcontainer; playlist = (sp_playlist *)request->output; for(i = 0; i < playlist->num_callbacks; i++) if(playlist->callbacks[i]->playlist_renamed) playlist->callbacks[i]->playlist_renamed(playlist, playlist->userdata[i]); break; case REQ_TYPE_PLAYLIST_STATE_CHANGED: pc = session->playlistcontainer; playlist = NULL; for(i = 0; i < pc->num_playlists; i++) { if(memcmp(pc->playlists[i]->id, request->output, 17)) continue; playlist = pc->playlists[i]; break; } if(!playlist) break; for(i = 0; i < playlist->num_callbacks; i++) if(playlist->callbacks[i]->playlist_state_changed) playlist->callbacks[i]->playlist_state_changed(playlist, playlist->userdata[i]); break; case REQ_TYPE_PLAYLIST_LOAD: pc = session->playlistcontainer; playlist = (sp_playlist *)request->output; for(i = 0; i < playlist->num_callbacks; i++) if(playlist->callbacks[i]->tracks_added) playlist->callbacks[i]->tracks_added(playlist, (sp_track *const *)playlist->tracks, playlist->num_tracks, 0, playlist->userdata[i]); break; case REQ_TYPE_ALBUMBROWSE: alb = (sp_albumbrowse *)request->output; if(alb->callback) alb->callback(alb, alb->userdata); break; case REQ_TYPE_ARTISTBROWSE: arb = (sp_artistbrowse *)request->output; if(arb->callback) arb->callback(arb, arb->userdata); break; case REQ_TYPE_BROWSE_ALBUM: case REQ_TYPE_BROWSE_ARTIST: case REQ_TYPE_BROWSE_TRACK: case REQ_TYPE_BROWSE_PLAYLIST_TRACKS: DSFYDEBUG("Metadata updated for request <type %s, state %s, input %p> in main thread\n", REQUEST_TYPE_STR(request->type), REQUEST_STATE_STR(request->type), request->input); if(session->callbacks->metadata_updated != NULL) session->callbacks->metadata_updated(session); break; case REQ_TYPE_TOPLISTBROWSE: toplistbrowse = (sp_toplistbrowse *)request->output; if(toplistbrowse->callback) toplistbrowse->callback(toplistbrowse, toplistbrowse->userdata); break; case REQ_TYPE_SEARCH: search = (sp_search *)request->output; if(search->callback) search->callback(search, search->userdata); break; case REQ_TYPE_IMAGE: image = (sp_image *)request->output; if(image->callback) image->callback(image, image->userdata); break; default: break; } /* Now that we've delievered the result, mark it for deletion */ request_mark_processed(session, request); } }