Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » help with threads

This thread is locked; no one can reply to it. rss feed Print
 1   2 
help with threads
shadyvillian
Member #12,426
December 2010

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?

Software Engineer by day, hacker by night.

Todd Cope
Member #998
November 2000
avatar

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
Member #7,859
October 2006
avatar

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
Member #1,204
April 2001

Can anyone help me out?

What is the actual problem?

shadyvillian
Member #12,426
December 2010

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]|

Software Engineer by day, hacker by night.

J-Gamer
Member #12,491
January 2011
avatar

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.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

shadyvillian
Member #12,426
December 2010

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?

Software Engineer by day, hacker by night.

Edgar Reynaldo
Member #8,592
May 2007
avatar

shadyvillian
Member #12,426
December 2010

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);

Software Engineer by day, hacker by night.

Edgar Reynaldo
Member #8,592
May 2007
avatar

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
Member #12,491
January 2011
avatar

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.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Edgar Reynaldo
Member #8,592
May 2007
avatar

J-Gamer
Member #12,491
January 2011
avatar

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).

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Edgar Reynaldo
Member #8,592
May 2007
avatar

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
Member #12,491
January 2011
avatar

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++.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Edgar Reynaldo
Member #8,592
May 2007
avatar

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
Member #12,491
January 2011
avatar

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.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Edgar Reynaldo
Member #8,592
May 2007
avatar

J-Gamer
Member #12,491
January 2011
avatar

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.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

shadyvillian
Member #12,426
December 2010

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?)

Software Engineer by day, hacker by night.

J-Gamer
Member #12,491
January 2011
avatar

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.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

shadyvillian
Member #12,426
December 2010

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?

Software Engineer by day, hacker by night.

J-Gamer
Member #12,491
January 2011
avatar

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.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

shadyvillian
Member #12,426
December 2010

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.

Software Engineer by day, hacker by night.

Audric
Member #907
January 2001

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.

 1   2 


Go to: