HydrogenAudio

Hosted Forums => foobar2000 => Development - (fb2k) => Topic started by: MordredKLB on 2018-04-07 22:22:22

Title: dispatch_refresh inside a threaded_process_callback?
Post by: MordredKLB on 2018-04-07 22:22:22
My component queries last.fm to retrieve scrobbles of songs. Users can select a batch of songs, right-click and last.fm will be queried, and the scrobbles will be saved to an index-data DB. This process takes time so I'm trying to move it from something that blocks foobar into a threaded_process_callback.

This appears to be working just fine, except when I try to call dispatch_refresh inside the callback, when it crashes with:
Unhandled exception at 0x74F508F2 (KernelBase.dll) in foobar2000.exe: 0xAA67913C.

My Relevant Code:
Code: [Select]
class get_lastfm_scrobbles : public threaded_process_callback {
public:
get_lastfm_scrobbles(metadb_handle_list_cref items) : m_items(items) {}
void on_init(HWND p_wnd) {}
void run(threaded_process_status & p_status, abort_callback & p_abort) {
try {
for (size_t t = 0; t < m_items.get_count(); t++) {
p_status.set_progress(t, m_items.get_size());
p_status.set_item_path(m_items[t]->get_path());

metadb_index_hash hash;
g_client->hashHandle(m_items[t], hash);

record_t record = getRecord(hash);
getLastFmPlaytimes(hash, record);  // queries last.fm
setRecord(hash, record);

g_api->dispatch_refresh(guid_foo_enhanced_playcount_index, hash); // crashes here on first item
}
} catch (std::exception const & e) {
m_failMsg = e.what();
}
}
void on_done(HWND p_wnd, bool p_was_aborted) {
if (!p_was_aborted) {
if (!m_failMsg.is_empty()) {
popup_message::g_complain("Could not retrieve last.fm scrobbles", m_failMsg);
} else {
// finished succesfully
}
}
}
private:
pfc::string8 m_failMsg;
const metadb_handle_list m_items;
};

Am I missing something? I can't see where dispatch_refresh isn't allowed inside a thread. Do I need to batch these up and call them after a successful run or what?
Title: Re: dispatch_refresh inside a threaded_process_callback?
Post by: fbuser on 2018-04-08 06:20:59
Look at the first line of metadb.h:
Quote
//! API for tag read/write operations. Legal to call from main thread only, except for hint_multi_async() / hint_async() / hint_reader().
You need to implement main_thread_callback and dispatch the refresh from there.
Title: Re: dispatch_refresh inside a threaded_process_callback?
Post by: MordredKLB on 2018-04-08 18:34:19
Look at the first line of metadb.h:
Quote
//! API for tag read/write operations. Legal to call from main thread only, except for hint_multi_async() / hint_async() / hint_reader().
You need to implement main_thread_callback and dispatch the refresh from there.
Crap, I do remember reading that months ago when I was initially doing this stuff.  So I implemented main_thread_callback for doing the dispatch_refresh and that works fine now.

However, my getRecord and setRecord functions call get_user_data_here() and set_user_data() respectively, neither of which is apparently thread safe (although they aren't causing crashes currently). Since I need to loop over all the handles in the list, and call get_user_data_here and set_user_data on each, how should I handle that if it has to be done inside a main_thread_callback? Do I need to retrieve all records before even calling my threaded_process_callback, and then write everything afterwards? I don't want to move the entire logic into a main_thread_callback because that kind of defeats the purpose as it'd be back to blocking foobar for up to 1 sec per track.
Title: Re: dispatch_refresh inside a threaded_process_callback?
Post by: MordredKLB on 2018-04-08 21:48:35
Okay, this turned out to not be that difficult to solve.

Before I call the threaded_process_callback, I retrieve all the hashes and records for every entry in the metadb_handle_list then in the thread I call last.fm to get the scrobbles, and in my main_thread_callback I write the updated record back and then dispatch a refresh.