Simplified loading of news: no more threading in main_menu.
git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@7129 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
parent
83a9993f68
commit
13241636d4
@ -21,6 +21,7 @@
|
||||
#include <curl/curl.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#if defined(WIN32) && !defined(__CYGWIN__)
|
||||
# define isnan _isnan
|
||||
#else
|
||||
@ -33,40 +34,151 @@
|
||||
#include "states_screens/addons_screen.hpp"
|
||||
#include "states_screens/main_menu_screen.hpp"
|
||||
|
||||
#if defined(WIN32) && !defined(__CYGWIN__)
|
||||
// Use Sleep, which takes time in msecs. It must be defined after the
|
||||
// includes, since otherwise irrlicht's sleep function is changed.
|
||||
# define sleep(s) Sleep(1000*(s))
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
||||
pthread_mutex_t download_mutex;
|
||||
NetworkHttp * network_http = 0;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Create a thread that handles all network functions independent of the
|
||||
* main program.
|
||||
*/
|
||||
NetworkHttp::NetworkHttp()
|
||||
{
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, &NetworkHttp::checkNewServer, this);
|
||||
}
|
||||
pthread_mutex_init(&m_mutex_news, NULL);
|
||||
m_news_message = "";
|
||||
|
||||
pthread_mutex_init(&m_mutex_command, NULL);
|
||||
pthread_cond_init(&m_cond_command, NULL);
|
||||
m_command = HC_SLEEP;
|
||||
m_abort = false;
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||
pthread_create(&m_thread_id, &attr, &NetworkHttp::mainLoop, this);
|
||||
pthread_attr_destroy(&attr);
|
||||
} // NetworkHttp
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void * NetworkHttp::checkNewServer(void * obj)
|
||||
/** The actual main loop, which is started as a separate thread from the
|
||||
* constructor. After testing for a new server, fetching news, the list
|
||||
* of packages to download, it will wait for commands to be issued.
|
||||
* \param obj: A pointer to this object, passed on by pthread_create
|
||||
*/
|
||||
void *NetworkHttp::mainLoop(void *obj)
|
||||
{
|
||||
NetworkHttp * pthis = (NetworkHttp *)obj;
|
||||
std::string newserver = pthis->downloadToStr("redirect");
|
||||
NetworkHttp *me=(NetworkHttp*)obj;
|
||||
me->checkNewServer();
|
||||
const std::string tmp_str = me->downloadToStr("news");
|
||||
|
||||
pthread_mutex_lock(&(me->m_mutex_news));
|
||||
{
|
||||
printf("tmp %s\n", tmp_str.c_str());
|
||||
// Only lock the actual assignment, not the downloading!
|
||||
me->m_news_message = tmp_str;
|
||||
}
|
||||
pthread_mutex_unlock(&(me->m_mutex_news));
|
||||
|
||||
// Allow this thread to be cancelled anytime
|
||||
// FIXME: this mechanism will later not be necessary anymore!
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
||||
|
||||
// Wait in the main loop till a command is received
|
||||
// (atm only QUIT is used).
|
||||
pthread_mutex_lock(&me->m_mutex_command);
|
||||
while(1)
|
||||
{
|
||||
pthread_cond_wait(&me->m_cond_command, &me->m_mutex_command);
|
||||
switch(me->m_command)
|
||||
{
|
||||
case HC_QUIT:
|
||||
pthread_exit(NULL);
|
||||
break;
|
||||
case HC_SLEEP: break;
|
||||
case HC_NEWS:
|
||||
const std::string tmp_str = me->downloadToStr("news");
|
||||
pthread_mutex_lock(&(me->m_mutex_news));
|
||||
{
|
||||
me->m_news_message = tmp_str;
|
||||
}
|
||||
pthread_mutex_unlock(&(me->m_mutex_news));
|
||||
break;
|
||||
} // switch(m_command)
|
||||
} // while !m_abort
|
||||
return NULL;
|
||||
} // mainLoop
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Aborts the thread running here, and returns then.
|
||||
*/
|
||||
NetworkHttp::~NetworkHttp()
|
||||
{
|
||||
m_abort=1; // Doesn't really need a mutex
|
||||
|
||||
pthread_mutex_lock(&m_mutex_command);
|
||||
{
|
||||
m_command=HC_QUIT;
|
||||
pthread_cond_signal(&m_cond_command);
|
||||
}
|
||||
pthread_mutex_unlock(&m_mutex_command);
|
||||
|
||||
void *result;
|
||||
pthread_join(m_thread_id, &result);
|
||||
|
||||
pthread_mutex_destroy(&m_mutex_news);
|
||||
pthread_mutex_destroy(&m_mutex_command);
|
||||
pthread_cond_destroy(&m_cond_command);
|
||||
} // ~NetworkHttp
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Checks if a redirect is received, causing a new server to be used for
|
||||
* downloading addons.
|
||||
*/
|
||||
void NetworkHttp::checkNewServer()
|
||||
{
|
||||
std::string newserver = downloadToStr("redirect");
|
||||
if (newserver != "")
|
||||
{
|
||||
newserver.replace(newserver.find("\n"), 1, "");
|
||||
|
||||
std::cout << "[Addons] Current server: " << UserConfigParams::m_server_addons.toString() << std::endl;
|
||||
if(UserConfigParams::m_verbosity>=4)
|
||||
{
|
||||
std::cout << "[Addons] Current server: "
|
||||
<< UserConfigParams::m_server_addons.toString()
|
||||
<< std::endl
|
||||
<< "[Addons] New server: " << newserver << std::endl;
|
||||
}
|
||||
UserConfigParams::m_server_addons = newserver;
|
||||
std::cout << "[Addons] New server: " << newserver << std::endl;
|
||||
user_config->saveConfig();
|
||||
}
|
||||
else
|
||||
else if(UserConfigParams::m_verbosity>=4)
|
||||
{
|
||||
std::cout << "[Addons] No new server." << std::endl;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
} // checkNewServer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the last loaded news message (using mutex to make sure a valid
|
||||
* value is available).
|
||||
*/
|
||||
const std::string NetworkHttp::getNewsMessage() const
|
||||
{
|
||||
pthread_mutex_lock(&m_mutex_news);
|
||||
const std::string tmp_str = m_news_message;
|
||||
pthread_mutex_unlock(&m_mutex_news);
|
||||
return tmp_str;
|
||||
} // getNewsMessage
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
size_t NetworkHttp::writeStr(char ptr [], size_t size, size_t nb_char, std::string * stream)
|
||||
{
|
||||
@ -113,9 +225,9 @@ bool download(std::string file, const std::string &save, int * progress_data)
|
||||
curl_easy_setopt(session, CURLOPT_URL, full_url.c_str());
|
||||
FILE * fout;
|
||||
if(save != "")
|
||||
fout = fopen(std::string(file_manager->getConfigDir() + "/" + save).c_str(), "w");
|
||||
fout = fopen((file_manager->getAddonsDir() + "/" + save).c_str(), "w");
|
||||
else
|
||||
fout = fopen(std::string(file_manager->getConfigDir() + "/" + file).c_str(), "w");
|
||||
fout = fopen((file_manager->getAddonsDir() + "/" + file).c_str(), "w");
|
||||
|
||||
|
||||
//from and out
|
||||
|
@ -24,14 +24,50 @@
|
||||
#include <string>
|
||||
class NetworkHttp
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
NetworkHttp();
|
||||
static void * checkNewServer(void * obj);
|
||||
static size_t writeStr(char str [], size_t size, size_t nb_char,
|
||||
std::string * stream);
|
||||
std::string downloadToStr(std::string url);
|
||||
public:
|
||||
/** List of 'http commands' for this object:
|
||||
* HC_SLEEP: No command, sleep
|
||||
* HC_QUIT: Stop loop and terminate thread.
|
||||
* HC_NEWS: Update the news
|
||||
*/
|
||||
enum HttpCommands {HC_SLEEP,
|
||||
HC_QUIT,
|
||||
HC_NEWS } ;
|
||||
private:
|
||||
|
||||
/** The news message from the server. This is guarded by m_mutex_news. */
|
||||
std::string m_news_message;
|
||||
|
||||
/** A mutex for accessing m_news_message. Exclude this so that
|
||||
* getter can be declared const. */
|
||||
mutable pthread_mutex_t m_mutex_news;
|
||||
|
||||
/** Which command to execute next. Access to this variable is guarded
|
||||
* by m_mutex_command and m_cond_command. */
|
||||
HttpCommands m_command;
|
||||
/** A mutex for accessing m_commands. */
|
||||
pthread_mutex_t m_mutex_command;
|
||||
/** A conditional variable to wake up the main loop. */
|
||||
pthread_cond_t m_cond_command;
|
||||
|
||||
/** Thread id of the thread running in this object. */
|
||||
pthread_t m_thread_id;
|
||||
|
||||
/** Signals that the main loop is to be aborted. */
|
||||
bool m_abort;
|
||||
|
||||
static void *mainLoop(void *obj);
|
||||
void checkNewServer();
|
||||
|
||||
public:
|
||||
NetworkHttp();
|
||||
~NetworkHttp();
|
||||
static size_t writeStr(char str [], size_t size, size_t nb_char,
|
||||
std::string * stream);
|
||||
std::string downloadToStr(std::string url);
|
||||
|
||||
const std::string
|
||||
getNewsMessage() const;
|
||||
};
|
||||
|
||||
extern NetworkHttp *network_http;
|
||||
|
@ -63,30 +63,7 @@ MainMenuScreen::MainMenuScreen() : Screen("main.stkgui")
|
||||
{
|
||||
}
|
||||
#endif
|
||||
#ifdef ADDONS_MANAGER
|
||||
void MainMenuScreen::changeNewsText(std::string action, std::string content)
|
||||
{
|
||||
if(action == "news")
|
||||
{
|
||||
pthread_testcancel(); // check if thread was cancelled
|
||||
|
||||
// enter the mutex; this will prevent the shutdown procedure
|
||||
// from continuing until this critical section is over
|
||||
pthread_mutex_lock(&(this->m_mutex_news_text));
|
||||
|
||||
m_news_text = content;
|
||||
|
||||
pthread_mutex_unlock(&(this->m_mutex_news_text));
|
||||
}
|
||||
if(action == "offline")
|
||||
{
|
||||
pthread_testcancel(); // check if thread was cancelled
|
||||
pthread_mutex_lock(&(this->m_mutex_news_text));
|
||||
m_news_text = "offline";
|
||||
pthread_mutex_unlock(&(this->m_mutex_news_text));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
void MainMenuScreen::loadedFromFile()
|
||||
@ -117,13 +94,6 @@ void MainMenuScreen::init()
|
||||
// the key bindings for the first player the default again.
|
||||
input_manager->getDeviceList()->clearLatestUsedDevice();
|
||||
|
||||
#ifdef ADDONS_MANAGER
|
||||
// FIXME: this is wrong, init may be called several times in the object's
|
||||
// lifespan, and the pthread docs state that initing several times the same
|
||||
// mutex can cause undefined behavior
|
||||
pthread_mutex_init(&(this->m_mutex_news_text), NULL);
|
||||
pthread_create(&m_thread_news_text, NULL, &MainMenuScreen::downloadNews, this);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ADDONS_MANAGER
|
||||
@ -132,14 +102,13 @@ void MainMenuScreen::onUpdate(float delta, irr::video::IVideoDriver* driver)
|
||||
{
|
||||
//FIXME:very bad for performance
|
||||
LabelWidget* w = this->getWidget<LabelWidget>("info_addons");
|
||||
pthread_mutex_lock(&(this->m_mutex_news_text));
|
||||
if(m_news_text == "offline")
|
||||
const std::string &news_text = network_http->getNewsMessage();
|
||||
if(news_text == "")
|
||||
{
|
||||
w->setText("Can't access stkaddons server...");
|
||||
}
|
||||
else
|
||||
w->setText(m_news_text.c_str());
|
||||
pthread_mutex_unlock(&(this->m_mutex_news_text));
|
||||
w->setText(news_text.c_str());
|
||||
}
|
||||
#endif
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
@ -237,48 +206,4 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, cons
|
||||
StateManager::get()->pushScreen(AddonsScreen::getInstance());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ADDONS_MANAGER
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
void * MainMenuScreen::downloadNews( void * pthis)
|
||||
{
|
||||
// enable thread to be cancelled
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
|
||||
|
||||
// FIXME: this code is wrong, "pt" might have been deleted by the
|
||||
// time the download is done (by having switched to another screen,
|
||||
// or exiting the game, etc...)
|
||||
//MainMenuScreen * pt = (MainMenuScreen*)pthis;
|
||||
|
||||
std::string news = network_http->downloadToStr("news");
|
||||
if (news != "")
|
||||
{
|
||||
pthread_testcancel(); // check if thread was cancelled
|
||||
MainMenuScreen * pt = (MainMenuScreen*)pthis;
|
||||
pt->changeNewsText("news", news);
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_testcancel(); // check if thread was cancelled
|
||||
MainMenuScreen * pt = (MainMenuScreen*)pthis;
|
||||
pt->changeNewsText("offline");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
#ifdef ADDONS_MANAGER
|
||||
void MainMenuScreen::tearDown()
|
||||
{
|
||||
fprintf(stdout, "canceling the thread\n");
|
||||
|
||||
// grab the mutex before returning to make sure the screen is not deleted while the thread is
|
||||
// changing the text, which would result in weird results (tearDown does not automatically mean
|
||||
// the screen is going to be destroyed but let's play on the safe side)
|
||||
pthread_mutex_lock(&(this->m_mutex_news_text));
|
||||
pthread_cancel(m_thread_news_text);
|
||||
pthread_mutex_unlock(&(this->m_mutex_news_text));
|
||||
}
|
||||
#endif
|
||||
}
|
@ -34,13 +34,8 @@ class MainMenuScreen : public GUIEngine::Screen, public GUIEngine::ScreenSinglet
|
||||
public:
|
||||
|
||||
#ifdef ADDONS_MANAGER
|
||||
void changeNewsText(std::string action, std::string content="");
|
||||
pthread_mutex_t m_mutex_news_text;
|
||||
pthread_t m_thread_news_text;
|
||||
|
||||
std::string m_news_text;
|
||||
virtual void onUpdate(float delta, irr::video::IVideoDriver* driver);
|
||||
virtual void tearDown();
|
||||
#endif
|
||||
|
||||
/** \brief implement callback from parent class GUIEngine::Screen */
|
||||
@ -50,11 +45,7 @@ public:
|
||||
virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, const int playerID);
|
||||
|
||||
/** \brief implement callback from parent class GUIEngine::Screen */
|
||||
virtual void init();
|
||||
|
||||
/** This function is used to download a text from the server to show the news. */
|
||||
static void * downloadNews(void *);
|
||||
|
||||
virtual void init();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user