help with threads
shadyvillian

I'm trying to put this function on a separate thread so it can run while the user still using the program. heres the function:

#SelectExpand
1void core::saveDeck() 2{ 3 double start = al_get_time(); 4 string path = "Decks/"+deckName+".deck"; 5 remove(path.c_str()); 6 ofstream oFile(path.c_str()); 7 CURL *curl; 8 FILE *file; 9 10 string flag = "n"; 11 vector<card> mainboard = listItemViewer[MAIN_TAB_DECK].getCardList(), sideboard = listItemViewer[MAIN_TAB_DECK+1].getCardList(); 12 13 if(sideboard.size() > 0) 14 { 15 flag = "y"; 16 } 17 18 for(unsigned int i = 0; i < mainboard.size(); i++) 19 { 20 oFile << mainboard[i].set << "*" << mainboard[i].numberOf << "*" << mainboard[i].name << "*" << endl; 21 } 22 23 oFile << "sideboard " + flag << endl; 24 25 if(flag == "y") 26 { 27 for(unsigned int i = 0; i < sideboard.size(); i++) 28 { 29 oFile << sideboard[i].set << "*" << sideboard[i].numberOf << "*" << sideboard[i].name << "*" << endl; 30 } 31 } 32 33 oFile.close(); 34 35 file = fopen(("Decks/"+deckName+".deck").c_str(), "rb"); 36 37 curl_global_init(CURL_GLOBAL_ALL); 38 39 /* get a curl handle */ 40 curl = curl_easy_init(); 41 42 if(curl) 43 { 44 /* enable uploading */ 45 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); 46 47 curl_easy_setopt(curl, CURLOPT_USERPWD, "username:password"); 48 49 /* specify target */ 50 curl_easy_setopt(curl,CURLOPT_URL, ("ftp://216.51.232.61/UserData/"+userName+"/"+deckName+".deck").c_str()); 51 52 /* now specify which file to upload */ 53 curl_easy_setopt(curl, CURLOPT_READDATA, file); 54 55 /* Now run off and do what you've been told! */ 56 curl_easy_perform(curl); 57 58 /* always cleanup */ 59 curl_easy_cleanup(curl); 60 } 61 62 fclose(file); 63 curl_global_cleanup(); 64 cout << "save deck took: " << al_get_time()-start<< " seconds" << endl; 65}

It uses variables from the main thread and everything is wrapped in a class.

processingThread = al_create_thread(saveDeck, NULL);
al_start_thread(processingThread);

Not sure what I need for the second parameter, I'm not returning any data. Can anyone help me out?

Todd Cope

The second parameter can be NULL if you don't need it. You should use mutexes (al_lock_mutex()/al_unlock_mutex) to lock data that is being accessed from the main thread. You mainly want to make sure none of the data you are writing to the file is going to be modified by the main thread while this thread is writing the file.

weapon_S

It uses variables from the main thread

Put those in a struct and use as argument? (Might make it harder to ensure main-thread doesn't F them up.)
Did you just post an address and a p-word? :-X

axilmar

Can anyone help me out?

What is the actual problem?

shadyvillian

It doesn't like the function I'm supplying it. Does the thread have to be a paramter in the function? What if everything is just in a class scope?

EDIT: messing around with this I might almost have it

#SelectExpand
1struct SaveData 2{ 3 std::vector<card> mainboard, sideboard; 4 std::string userName, deckName; 5 6 SaveData(std::vector<card> main, std::vector<card> side, std::string user, std::string deck) 7 { 8 mainboard = main; 9 sideboard = side; 10 userName = user; 11 deckName = deck; 12 } 13}; 14 15 SaveData *data = arg(*SaveData); 16 17 ofstream oFile(("Decks/"+data->deckName+".deck").c_str()); 18 CURL *curl; 19 FILE *file; 20 21 string flag = "n"; 22 23 if(data.sideboard.size() > 0) 24 { 25 flag = "y"; 26 } 27 28 for(unsigned int i = 0; i < data->mainboard.size(); i++) 29 { 30 oFile << data->mainboard[i].set << "*" << data->mainboard[i].numberOf << "*" << data->mainboard[i].name << "*" << endl; 31 } 32 33 oFile << "sideboard " + flag << endl; 34 35 if(flag == "y") 36 { 37 for(unsigned int i = 0; i < data->sideboard.size(); i++) 38 { 39 oFile << data->sideboard[i].set << "*" << data->sideboard[i].numberOf << "*" << data->sideboard[i].name << "*" << endl; 40 } 41 } 42 43 oFile.close(); 44 file = fopen(("Decks/"+data->deckName+".deck").c_str(), "rb"); 45 curl_global_init(CURL_GLOBAL_ALL); 46 curl = curl_easy_init(); 47 48 if(curl) 49 { 50 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); 51 curl_easy_setopt(curl, CURLOPT_USERPWD, "username:password"); 52 curl_easy_setopt(curl,CURLOPT_URL, ("ftp://216.51.232.61/UserData/"+data->userName+"/"+data->deckName+".deck").c_str()); 53 curl_easy_setopt(curl, CURLOPT_READDATA, file); 54 curl_easy_perform(curl); 55 curl_easy_cleanup(curl); 56 } 57 58 fclose(file); 59 curl_global_cleanup(); 60} 61 62SaveData data = SaveData(listItemViewer[MAIN_TAB_DECK].getCardList(), listItemViewer[MAIN_TAB_DECK+1].getCardList(), userName, deckName); 63 processingThread = al_create_thread(saveDeck(processingThread, &data), NULL); //error here 64 al_start_thread(processingThread);

I get this error. Does my function need to be static or something?

error: invalid conversion from 'void*' to 'void* (*)(ALLEGRO_THREAD*, void*)' [-fpermissive]|

J-Gamer

It does indeed have trouble with member functions that aren't static(ie: tied to one instance of the class). If it has to be tied to a class instance, you'd need the mem_fn functions of the std library. Also, the function that you put in al_create_thread should accept a void* parameter.

shadyvillian

I decided to make it a non-member function and just pass a struct to make it easier now. Now I'm getting a crash on

oFile << data->mainboard[i].set << "*" << data->mainboard[i].numberOf << "*" << data->mainboard[i].name << "*" << endl;

#0 005ADCC5 std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) () (??:??)
#1 0040B0CD saveDeck(thread=0x87e68a0, arg=0x28f608)(deck_utility.cpp:87)
#2 004343C8 thread_func_trampoline () (??:??)
#3 0043A3F0 thread_proc_trampoline@4 () (??:??)
#4 763D1287 msvcrt!_itow_s() (C:\Windows\syswow64\msvcrt.dll:??)
#5 763D1328 msvcrt!_endthreadex() (C:\Windows\syswow64\msvcrt.dll:??)
#6 74F1339A KERNEL32!BaseCleanupAppcompatCacheSupport() (C:\Windows\syswow64\kernel32.dll:??)
#7 06685F30 ?? () (??:??)
#8 77629EF2 ntdll!RtlpNtSetValueKey() (C:\Windows\system32\ntdll.dll:??)
#9 06685F30 ?? () (??:??)
#10 77629EC5 ntdll!RtlpNtSetValueKey() (C:\Windows\system32\ntdll.dll:??)
#11 763D12E5 msvcrt!_endthreadex() (C:\Windows\syswow64\msvcrt.dll:??)
#12 ?? ?? () (??:??)

Is there something else I need to do? Do I need to use a mutex?

Edgar Reynaldo

Uhm, looks like you accessed an out of bounds string... That's my first guess.

shadyvillian

Their fine before they go through to the thread. Is there something I have to do to be able to use them or pass them different? latest code:

#SelectExpand
1void *saveDeck(ALLEGRO_THREAD *thread, void *arg) 2{ 3 SaveData *data = (SaveData*)arg; 4 CURL *curl; 5 stringstream buffer(stringstream::out); 6 string flag = "n"; 7 8 al_lock_mutex(data->mutex); 9 10 if(data->sideboard.size() > 0) 11 { 12 flag = "y"; 13 } 14 15 for(unsigned int i = 0; i < data->mainboard.size(); i++) 16 { 17 buffer << data->mainboard[i].set << "*" << data->mainboard[i].numberOf << "*" << data->mainboard[i].name << "*" << endl; 18 } 19 20 buffer << "sideboard " + flag << endl; 21 22 if(flag == "y") 23 { 24 for(unsigned int i = 0; i < data->sideboard.size(); i++) 25 { 26 buffer << data->sideboard[i].set << "*" << data->sideboard[i].numberOf << "*" << data->sideboard[i].name << "*" << endl; 27 } 28 } 29 curl_global_init(CURL_GLOBAL_ALL); 30 curl = curl_easy_init(); 31 32 if(curl) 33 { 34 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); 35 curl_easy_setopt(curl, CURLOPT_USERPWD, "username:password"); 36 curl_easy_setopt(curl,CURLOPT_URL, ("ftp://216.51.232.61/UserData/"+data->userName+"/"+data->deckName+".deck").c_str()); 37 curl_easy_setopt(curl, CURLOPT_READDATA, &buffer); 38 curl_easy_perform(curl); 39 curl_easy_cleanup(curl); 40 } 41 42 curl_global_cleanup(); 43 al_unlock_mutex(data->mutex); 44 return NULL; 45} 46 47SaveData data = SaveData(listItemViewer[MAIN_TAB_DECK].getCardList(), listItemViewer[MAIN_TAB_DECK+1].getCardList(), userName, deckName); 48processingThread = al_create_thread(saveDeck, &data); 49al_start_thread(processingThread);

Edgar Reynaldo

It appears as if you're using a vector, and you use the size function, so your bounds should be right. That also means you're using C++, which means you could make this a member function of your SaveData class, instead of a global c-style function. That's how I would do it, but it's not necessary I guess.

As to your problem, your code should work as long as arg is a valid pointer to a valid SaveData object. And it should work fine from any thread as long as the mutex is locked properly, which it appears to be.

J-Gamer

That also means you're using C++, which means you could make this a member function of your SaveData class, instead of a global c-style function. That's how I would do it, but it's not necessary I guess.

It'd have to be static to be able to do that, though. The fun thing is that static functions can access the private parts if they have a pointer to an instance of their class.

Edgar Reynaldo

Errr... I don't see why it would have to be static.. Member functions can always access static data, it's just that static functions can't access non-static data members without a pointer or an object.

J-Gamer

The problem with member functions is that they need an extra parameter(this). And I honestly can't get mem_fun to work(edit: with the second parameter being a reference. mem_fun uses references itself for its parameters, and you can't have a reference to a reference).

Edgar Reynaldo

I've never had good luck with the standard library's mem_fun adaptors. If you really need something like that I would just break down and use Boost. (And FFS don't tell bambam I said that.)

What do you need them for?

J-Gamer

I had a class called Transformation, which had a function apply(Point& p). I wanted to use that function on all points in a list, so I tried doing it with this line of code:
for_each(points.begin(), points.end(), trans.apply);
Which obviously doesn't work for the reason I stated above. Then I tried using mem_fun, but I couldn't make it work:
for_each(points.begin(), points.end(), std::bind1st(&trans, mem_fun(&Transformation::apply));
Which fails because mem_fun internally uses a reference to the argument of apply, which is a Point&, and thus it tries to make a reference to a reference, which is impossible in C++.

Edgar Reynaldo

My first attempt to solve it would be to make Transformation::Apply take a pointer instead of a reference. That way you can still alter it, and mem_fun should be happy because it then has a reference to a pointer. ? My 2c. Like I said, I never had good luck with mem_fun.

J-Gamer

That should indeed work, but the project has been written and the deadline has passed. The problem would still be that it's still a list of Points, not of Point*'s. That would need another workaround: a function accepting a Point and calling the bind1st(mem_fun()) function on the pointer.

Edit:

void apply_point(Transformation* t, Point& p) {
    t->apply(p);
};

for_each(points.begin(), points.end(), bind1st(apply_point, &trans));

This is my workaround, but it uses more lines than a for-iteration over the list and manually calling apply.

Edgar Reynaldo

Do you really need to save three lines though? Seems like OCD....

J-Gamer

I just don't like writing extra functions for small things that aren't reusable at all. Just having a for-loop over the list is much more understandable code.

shadyvillian

Am I doing it right? I start the thread and both are just doing their thing. The second thread will only run for about 5 seconds. Is there something I need to do once the function is done executing on the second thread?(like at the end of the function or in the main thread? Join the threads?)

J-Gamer

You'll need to call al_destroy_thread to free up the resources. But only after you are 100% sure the thread has finished running. I think you can do that by calling al_join_thread.

shadyvillian

Even when I don't use any data in my function it still crashes when I excute it and its not telling me why :-/

EDIT: commenting out the libcurl stuff gets rid of the error... Does anyone know if libcurl's easy inference thread safe?

EDIT: Turns out curl_global_init isn't thread safe. For any libcurl users out there is there a workaround for this?

J-Gamer

Make it thread-safe yourself? Put a global mutex in and lock it each time you want to use libcurl. Then unlock it when you're done. If you do this consistently throughout your program, two threads will never try to access libcurl at the same time.

shadyvillian

I'm not sure of its because both threads are trying to access it or because this from the libcurl manual: curl_global_init - This function is not thread safe. You must not call it when any other thread in the program (i.e. a thread sharing the same memory) is running. This doesn't just mean no other thread that is using libcurl. Because curl_global_init() calls functions of other libraries that are similarly thread unsafe, it could conflict with any other thread that uses these other libraries.

Audric

As I understand it, it's even simpler than that: Call it once during the global initialization of your program. Long before you start creating extra threads.

shadyvillian

Ok I'm gonna start doing that. I'm still having problems with my data thats passed through arg though. For instance when I try to access the size of my vector with the size function its size is 15418846 when I know its zero. Does anyone know what would cause this? And how do I know if I need to use a mutex. ex_thread doesn't use one but ex_thread2 does. I'm passing a vector returned from a get function into my struct thats being passed into the second thread. I don't have to lock the mutex in the main thread for when I use the data because it's just a copy of the data?

EDIT: crashing at mutex lock...

Edgar Reynaldo

For instance when I try to access the size of my vector with the size function its size is 15418846 when I know its zero.

Somehow your vector is uninitialized or corrupt.

Thread #610479. Printed from Allegro.cc