Skip to content

Conversation

@boutinb
Copy link
Contributor

@boutinb boutinb commented Sep 24, 2025

When working on the new feature to generate reports with feeding jasp files with other datafiles (#6025), I came upon an issue that sometimes the synchronization hangs.
This was due to the fact that after opening a JASP file, the Filter model generates some SQL statements, but the synchronization task was already started: this lead to concurrency issues with the database. Especially the connection object to SQLite is thread dependent, but _transactionWriteDepth and _transactionReadDepth are not. So one thread could increment _transactionWriteDepth, and another thread checking _transactionWriteDepth would think that a transaction was already started, and then would not execute a 'BEGIN EXCLUSIVE' statement.
So the solution is to make _transactionWriteDepth and _transactionReadDepth thread dependent.

Also the connection object is stored in a map, but a std map does not use mutex: so setting and querying the map must be done under a mutex to be sure that the manipulation of the map is thread safe.

This fix solves probably some hanging issues that some users report.

@boutinb boutinb requested a review from JorisGoosen September 24, 2025 15:34
sqlite3* connection = _dbs.at(std::this_thread::get_id());
_loadMutex.unlock();
return;
return connection;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you returning this?


void create(); ///< Creates a new sqlite database in sessiondir and loads it
void load(); ///< Loads a sqlite database from sessiondir (after loading a jaspfile)
sqlite3* load(); ///< Loads a sqlite database from sessiondir (after loading a jaspfile)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I on purpose kept sqlite3 stuff out of the api because this is supposed to abstract away from that so we could change sqlite for some other sql some day.

Why did you add it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true, this is not necessary anymore (this is just a relic of old attempts). I'll change this

Copy link
Contributor

@JorisGoosen JorisGoosen Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ill do so, or not

@JorisGoosen JorisGoosen merged commit 2e470d6 into jasp-stats:development Sep 25, 2025
1 check passed
if(!_dbs.count(id))
load();

return _dbs.at(id);
Copy link
Contributor Author

@boutinb boutinb Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RensDofferhoff and me thought also reading in a map is not really thread-safe: a map has no mutex, so if a thread add an element to the map at the same moment that count or at is called, it cannot guarantee the right value.
But as for the moment only one extra thread (the _loaderThread from MainWindow) is used, the map will have in fact only 1 value (the other connection is set in _dbCreated. So that's ok for now...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which is why I put mutexes in the load and creation function...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And there are waaaaay more threads being used so im not sure why you think there is only one?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class InitColumnTask : public QRunnable look for that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't matter you only guard against concurrent writes into the map with that mutex.
Reads can happen during load and creation.
count() and at() of std::map are not safe when the map is not const.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then reading in the map should also be enclosed by the mutex.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we still have that potential problem then you mean?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then reading in the map should also be enclosed by the mutex.

Yes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well Ill add it to the PR im opening in a few minutes then!

JorisGoosen added a commit to JorisGoosen/jasp-desktop that referenced this pull request Sep 25, 2025
JorisGoosen added a commit to JorisGoosen/jasp-desktop that referenced this pull request Sep 25, 2025
JorisGoosen added a commit that referenced this pull request Sep 25, 2025
* for ordinal and nominal columns not the labelid but the label as shown to the user determines whether it is a level or not

fixes jasp-stats/jasp-issues#3721

* follow the good advice of Rens and Bruno and adding a mutex

advice is here: #6030

* lock more carefully
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants