In order to modify or insert a record in an innodb table, the transaction needs to acquire an exclusive lock in MySQL:
UPDATE ... WHERE ... sets an exclusive next-key lock on every record the search encounters. However, only an index record lock is required for statements that lock rows using a unique index to search for a unique row.
...
INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row.
If the same record (or gap) is already locked by another transaction, then MySQL waits for the lock to be released or the above mentioned timeout occurs.
Based on the above code there is no way we can tell what has gone wrong (if anything). You can check out innodb status monitor to get more information, but without a deadlock its use will be limited as well.
This behaviour is intrinsic to MySQL, the application's programming language and libraries cannot influence this.