You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The mechanism around locking the session has been problematic in the
number of ways.
**1. Late locking**:
The primary issue was that requests would not lock
lock the session for reading until their futures were polled, which
meant that the following code could lead to request failure due to
session having been closed:
```c
CassFuture *exec_fut = cass_session_execute(cass_session, cass_statement);
CassFuture *close_fut = cass_session_close(cass_session);
cass_future_wait(exec_fut);
cass_future_wait(close_fut);
```
This is because locking the session for reading is done asynchronously
to the code that follows `cass_session_execute` invocation.
The same issue was present in all request-making functions, that is,
`cass_session_execute`, `cass_session_execute_batch`,
and all of the `cass_session_prepare*` family of functions.
**2. Thread-blocking locking**:
The second issue was that, in some cases, the session was locked for
reading by blocking the current thread idly. This was the case for
`cass_session_get_metrics`, `cass_session_get_schema_meta`,
and `cass_session_get_client_id`. This could lead to deadlocks,
especially when the number of threads in the thread pool were low
(with `current_thread` tokio executor being the most vulnerable case).
**3. Runtime-blocking locking**:
The third issue was that, in the case of `cass_session_connect*`
functions, the session was locked for writing by blocking the current
thread as the executor thread for the awaited future. While this was
designed with the `current_thread` executor in mind and worked perfectly
for its case, it showed to cause panic when called by a tokio executor
thread.
**Solution**:
This commit addresses all of the above issues by adopting an asymmetric
locking mechanism for the session.
The session is now locked for reading in advance yet fallibly, by
calling `try_read(_owned)` on the session rwlock. This is done in
the request-making functions, so that the session is guaranteed to be
locked for reading when the request future is returned.
The session is still locked for writing (upon connecting or closing)
asynchronously, by calling and awaiting `write(_owned)` on the rwlock.
This is done in the `cass_session_connect*` and `cass_session_close`.
A downside of this approach is that the session is not guaranteed to be
locked for writing when the `cass_session_connect*` or
`cass_session_close` futures are returned, but this is not a problem
because closing and connecting are considered to be "long-running",
complex operations and thus are not expected to have conducted a
specific part of their logic by the time their future is returned.
**Results**:
All enabled tests still pass, while the `callbacks` example now passes,
too! This best part is that the number and complexity of the required
changes is minimal, and the code is now much more robust.
I hope @Lorak-mmk will be happy with this solution, as compared to the
complex requests pending mechanism and atomics.
Note that the test for `cass_session_get_client_id` had to be adjusted.
This is because the session has the client ID set only in the connect
future instead of in the connect function synchronously, so the test
(which did not await the connect future) would fail after the changes.
0 commit comments