There is also a more obscure side-effect exhibited by sqlite only: if you issue consecutive unfinalized select statements, it never releases the write lock (although they should be atomic), but it does trip the deadlock mechanism; any writer in the wait will receive SQLITE_BUSY at the very moment the second read statement is issued, regardless of the busy_timeout value. This happens in non-WAL mode.
The usual culprit are one-record results, like SELECT COUNT(*)... usually the client expects one row and reads one row, but you need to either try to read 2 rows or explicitly free the cursor. Do note (for evangelism sake) that DBs with random-access cursors do not free the read lock even when you read past the last record, because you could decide to rewind. So always free result sets.